From a21f1b6c2a5734f39a1efe3fa84d6475843d14fe Mon Sep 17 00:00:00 2001 From: Yacin Tmimi Date: Sat, 27 Nov 2021 17:14:15 -0500 Subject: [PATCH] Conditionally compile tests based on CFG_RELEASE_CHANNEL env var Adds the ``nightly_only_test`` and ``stable_only_test`` attribute macros that prevent or allow certain tests to compile on nightly and stable respectively. This is achieved through conditionally outputting the tests TokenStream. If CFG_RELEASE_CHANNEL is not set, it's assumed that we're running in a nightly environment. To mark a test as nightly only: #[nightly_only_test] #[test] fn only_run_on_nightly() { ... } To mark a test a stable only: #[stable_only_test] #[test] fn only_run_on_stable() { ... } --- config_proc_macro/src/lib.rs | 42 ++++++++++++++++++ src/config/mod.rs | 82 +++++++++++++++--------------------- src/syntux/session.rs | 15 +++---- src/test/mod.rs | 13 +++--- 4 files changed, 86 insertions(+), 66 deletions(-) diff --git a/config_proc_macro/src/lib.rs b/config_proc_macro/src/lib.rs index 78e7e098ed9..51301821319 100644 --- a/config_proc_macro/src/lib.rs +++ b/config_proc_macro/src/lib.rs @@ -8,6 +8,8 @@ mod item_struct; mod utils; +use std::str::FromStr; + use proc_macro::TokenStream; use syn::parse_macro_input; @@ -23,3 +25,43 @@ pub fn config_type(_args: TokenStream, input: TokenStream) -> TokenStream { TokenStream::from(output) } + +/// Used to conditionally output the TokenStream for tests that need to be run on nightly only. +/// +/// ```rust +/// #[nightly_only_test] +/// #[test] +/// fn test_needs_nightly_rustfmt() { +/// assert!(true); +/// } +/// ``` +#[proc_macro_attribute] +pub fn nightly_only_test(_args: TokenStream, input: TokenStream) -> TokenStream { + // if CFG_RELEASE_CHANNEL is not set we default to nightly, hence why the default is true + if option_env!("CFG_RELEASE_CHANNEL").map_or(true, |c| c == "nightly" || c == "dev") { + input + } else { + // output an empty token stream if CFG_RELEASE_CHANNEL is not set to "nightly" or "dev" + TokenStream::from_str("").unwrap() + } +} + +/// Used to conditionally output the TokenStream for tests that need to be run on stable only. +/// +/// ```rust +/// #[stable_only_test] +/// #[test] +/// fn test_needs_stable_rustfmt() { +/// assert!(true); +/// } +/// ``` +#[proc_macro_attribute] +pub fn stable_only_test(_args: TokenStream, input: TokenStream) -> TokenStream { + // if CFG_RELEASE_CHANNEL is not set we default to nightly, hence why the default is false + if option_env!("CFG_RELEASE_CHANNEL").map_or(false, |c| c == "stable") { + input + } else { + // output an empty token stream if CFG_RELEASE_CHANNEL is not set or is not 'stable' + TokenStream::from_str("").unwrap() + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index c5419d860c9..5dbe532ac38 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -405,6 +405,8 @@ mod test { use super::*; use std::str; + use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test}; + #[allow(dead_code)] mod mock { use super::super::*; @@ -525,21 +527,17 @@ fn test_empty_string_license_template_path() { assert!(config.license_template.is_none()); } + #[nightly_only_test] #[test] fn test_valid_license_template_path() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#"license_template_path = "tests/license-template/lt.txt""#; let config = Config::from_toml(toml, Path::new("")).unwrap(); assert!(config.license_template.is_some()); } + #[nightly_only_test] #[test] fn test_override_existing_license_with_no_license() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#"license_template_path = "tests/license-template/lt.txt""#; let mut config = Config::from_toml(toml, Path::new("")).unwrap(); assert!(config.license_template.is_some()); @@ -634,48 +632,42 @@ fn test_dump_default_config() { assert_eq!(&toml, &default_config); } - // FIXME(#2183): these tests cannot be run in parallel because they use env vars. - // #[test] - // fn test_as_not_nightly_channel() { - // let mut config = Config::default(); - // assert_eq!(config.was_set().unstable_features(), false); - // config.set().unstable_features(true); - // assert_eq!(config.was_set().unstable_features(), false); - // } + #[stable_only_test] + #[test] + fn test_as_not_nightly_channel() { + let mut config = Config::default(); + assert_eq!(config.was_set().unstable_features(), false); + config.set().unstable_features(true); + assert_eq!(config.was_set().unstable_features(), false); + } - // #[test] - // fn test_as_nightly_channel() { - // let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from("")); - // ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly"); - // let mut config = Config::default(); - // config.set().unstable_features(true); - // assert_eq!(config.was_set().unstable_features(), false); - // config.set().unstable_features(true); - // assert_eq!(config.unstable_features(), true); - // ::std::env::set_var("CFG_RELEASE_CHANNEL", v); - // } + #[nightly_only_test] + #[test] + fn test_as_nightly_channel() { + let mut config = Config::default(); + config.set().unstable_features(true); + // When we don't set the config from toml or command line options it + // doesn't get marked as set by the user. + assert_eq!(config.was_set().unstable_features(), false); + config.set().unstable_features(true); + assert_eq!(config.unstable_features(), true); + } - // #[test] - // fn test_unstable_from_toml() { - // let mut config = Config::from_toml("unstable_features = true").unwrap(); - // assert_eq!(config.was_set().unstable_features(), false); - // let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from("")); - // ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly"); - // config = Config::from_toml("unstable_features = true").unwrap(); - // assert_eq!(config.was_set().unstable_features(), true); - // assert_eq!(config.unstable_features(), true); - // ::std::env::set_var("CFG_RELEASE_CHANNEL", v); - // } + #[nightly_only_test] + #[test] + fn test_unstable_from_toml() { + let config = Config::from_toml("unstable_features = true", Path::new("")).unwrap(); + assert_eq!(config.was_set().unstable_features(), true); + assert_eq!(config.unstable_features(), true); + } #[cfg(test)] mod deprecated_option_merge_imports { use super::*; + #[nightly_only_test] #[test] fn test_old_option_set() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#" unstable_features = true merge_imports = true @@ -684,11 +676,9 @@ fn test_old_option_set() { assert_eq!(config.imports_granularity(), ImportGranularity::Crate); } + #[nightly_only_test] #[test] fn test_both_set() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#" unstable_features = true merge_imports = true @@ -698,11 +688,9 @@ fn test_both_set() { assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); } + #[nightly_only_test] #[test] fn test_new_overridden() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#" unstable_features = true merge_imports = true @@ -712,11 +700,9 @@ fn test_new_overridden() { assert_eq!(config.imports_granularity(), ImportGranularity::Preserve); } + #[nightly_only_test] #[test] fn test_old_overridden() { - if !crate::is_nightly_channel!() { - return; - } let toml = r#" unstable_features = true imports_granularity = "Module" diff --git a/src/syntux/session.rs b/src/syntux/session.rs index cdb4893d443..dd7c7352686 100644 --- a/src/syntux/session.rs +++ b/src/syntux/session.rs @@ -286,10 +286,11 @@ fn lookup_line_range(&self, span: Span) -> LineRange { mod tests { use super::*; + use rustfmt_config_proc_macro::nightly_only_test; + mod emitter { use super::*; use crate::config::IgnoreList; - use crate::is_nightly_channel; use crate::utils::mk_sp; use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP}; use std::path::PathBuf; @@ -371,11 +372,9 @@ fn handles_fatal_parse_error_in_ignored_file() { assert_eq!(can_reset_errors.load(Ordering::Acquire), false); } + #[nightly_only_test] #[test] fn handles_recoverable_parse_error_in_ignored_file() { - if !is_nightly_channel!() { - return; - } let num_emitted_errors = Lrc::new(AtomicU32::new(0)); let can_reset_errors = Lrc::new(AtomicBool::new(false)); let ignore_list = get_ignore_list(r#"ignore = ["foo.rs"]"#); @@ -398,11 +397,9 @@ fn handles_recoverable_parse_error_in_ignored_file() { assert_eq!(can_reset_errors.load(Ordering::Acquire), true); } + #[nightly_only_test] #[test] fn handles_recoverable_parse_error_in_non_ignored_file() { - if !is_nightly_channel!() { - return; - } let num_emitted_errors = Lrc::new(AtomicU32::new(0)); let can_reset_errors = Lrc::new(AtomicBool::new(false)); let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); @@ -424,11 +421,9 @@ fn handles_recoverable_parse_error_in_non_ignored_file() { assert_eq!(can_reset_errors.load(Ordering::Acquire), false); } + #[nightly_only_test] #[test] fn handles_mix_of_recoverable_parse_error() { - if !is_nightly_channel!() { - return; - } let num_emitted_errors = Lrc::new(AtomicU32::new(0)); let can_reset_errors = Lrc::new(AtomicBool::new(false)); let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty())); diff --git a/src/test/mod.rs b/src/test/mod.rs index e2620508c34..cceb28dfea6 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -15,6 +15,8 @@ use crate::source_file; use crate::{is_nightly_channel, FormatReport, FormatReportFormatterBuilder, Input, Session}; +use rustfmt_config_proc_macro::nightly_only_test; + mod configuration_snippet; mod mod_resolver; mod parser; @@ -307,14 +309,11 @@ fn assert_output(source: &Path, expected_filename: &Path) { // Idempotence tests. Files in tests/target are checked to be unaltered by // rustfmt. +#[nightly_only_test] #[test] fn idempotence_tests() { init_log(); run_test_with(&TestSetting::default(), || { - // these tests require nightly - if !is_nightly_channel!() { - return; - } // Get all files in the tests/target directory. let files = get_test_files(Path::new("tests/target"), true); let (_reports, count, fails) = check_files(files, &None); @@ -332,13 +331,11 @@ fn idempotence_tests() { // Run rustfmt on itself. This operation must be idempotent. We also check that // no warnings are emitted. +// Issue-3443: these tests require nightly +#[nightly_only_test] #[test] fn self_tests() { init_log(); - // Issue-3443: these tests require nightly - if !is_nightly_channel!() { - return; - } let mut files = get_test_files(Path::new("tests"), false); let bin_directories = vec!["cargo-fmt", "git-rustfmt", "bin", "format-diff"]; for dir in bin_directories {