Skip to content

Commit

Permalink
Conditionally compile tests based on CFG_RELEASE_CHANNEL env var
Browse files Browse the repository at this point in the history
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() {
        ...
    }
  • Loading branch information
ytmimi authored and calebcartwright committed Nov 27, 2021
1 parent 67fd9ec commit a21f1b6
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 68 deletions.
42 changes: 42 additions & 0 deletions config_proc_macro/src/lib.rs
Expand Up @@ -8,6 +8,8 @@ mod item_enum;
mod item_struct;
mod utils;

use std::str::FromStr;

use proc_macro::TokenStream;
use syn::parse_macro_input;

Expand All @@ -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()
}
}
86 changes: 36 additions & 50 deletions src/config/mod.rs
Expand Up @@ -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::*;
Expand Down Expand Up @@ -525,21 +527,17 @@ mod test {
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());
Expand Down Expand Up @@ -634,48 +632,42 @@ make_backup = false
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);
// }

// #[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);
// }

// #[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);
// }
#[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);
}

#[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);
}

#[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
Expand All @@ -684,11 +676,9 @@ make_backup = false
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
Expand All @@ -698,11 +688,9 @@ make_backup = false
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
Expand All @@ -712,11 +700,9 @@ make_backup = false
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"
Expand Down
15 changes: 5 additions & 10 deletions src/syntux/session.rs
Expand Up @@ -286,10 +286,11 @@ impl LineRangeUtils for ParseSess {
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;
Expand Down Expand Up @@ -371,11 +372,9 @@ mod tests {
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"]"#);
Expand All @@ -398,11 +397,9 @@ mod tests {
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()));
Expand All @@ -424,11 +421,9 @@ mod tests {
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()));
Expand Down
13 changes: 5 additions & 8 deletions src/test/mod.rs
Expand Up @@ -15,6 +15,8 @@ use crate::rustfmt_diff::{make_diff, print_diff, DiffLine, Mismatch, ModifiedChu
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;
Expand Down Expand Up @@ -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);
Expand All @@ -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 {
Expand Down

0 comments on commit a21f1b6

Please sign in to comment.