Skip to content

fix for recent nightlies' TestOpts change #30

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@ region = { version = "2.1.2", optional = true }
[dev-dependencies]
serde = { version = "1.0.84", features = ["derive"] }

[build-dependencies]
version_check = "0.9.3"

[workspace]
members = [
"datatest-derive"
]

[features]
# Use `#[test_case]`-based test registration (only works on nightly when `custom_test_frameworks` feature is enabled).
# Without that feature turned on, the default test registration method is to use `ctor` crate to create a global list
# of all tests.

# Use `#[test_case]`-based test registration. If this is disabled, datatest will use the `ctor` crate to create a
# global list of all tests.
#
# Enabled by default, only takes effect on nightly and only works when custom_test_frameworks feature is enabled.
test_case_registration = []

# Use very, very, very sketchy way of intercepting a test runner on a stable Rust. Without that feature, there are two
Expand All @@ -46,7 +51,7 @@ test_case_registration = []
unsafe_test_runner = ["region"]

# Make this crate useable on stable Rust channel. Uses forbidden technique to allow usage of this crate on a stable
# Rust compiler. This, however, does not bring any guarantees above "nighly" -- this crate can break at any time.
# Rust compiler. This, however, does not bring any guarantees above "nightly" -- this crate can break at any time.
subvert_stable_guarantees = []

default = []
default = ["test_case_registration"]
29 changes: 27 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
extern crate version_check as rustc;

fn main() {
#[cfg(feature = "subvert_stable_guarantees")]
println!("cargo:rustc-env=RUSTC_BOOTSTRAP=1");
let subverted = cfg!(feature = "subvert_stable_guarantees");

if subverted {
// This will cease to work sometime between cargo 1.50 and 1.52. Nightly clippy
// --all-features at the time of writing warns that:
//
// warning: Cannot set `RUSTC_BOOTSTRAP=1` from build script of `datatest v0.6.4 (...)`
// note: Crates cannot set `RUSTC_BOOTSTRAP` themselves, as doing so would subvert the
// stability guarantees of Rust for your project.
println!("cargo:rustc-env=RUSTC_BOOTSTRAP=1");
}

let nightly = rustc::is_feature_flaggable().unwrap_or(false);
if nightly {
println!("cargo:rustc-cfg=feature=\"rustc_is_nightly\"");
} else {
println!("cargo:rustc-cfg=feature=\"rustc_is_stable\"");
if !subverted {
println!("cargo:warning=attempting to compile datatest on stable without opting in to subvert_stable_guarantees feature; will fail");
}
}
// See src/runner.rs
if rustc::is_min_version("1.52.0").unwrap_or(false) {
println!("cargo:rustc-cfg=feature=\"rustc_test_TestOpts_filters_vec\"");
}
}
20 changes: 9 additions & 11 deletions datatest-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,19 +374,17 @@ fn handle_common_attrs(func: &mut ItemFn, regular_test: bool) -> FuncInfo {
}

fn parse_should_panic(attr: &syn::Attribute) -> ShouldPanic {
if let Ok(meta) = attr.parse_meta() {
if let syn::Meta::List(list) = meta {
for item in list.nested {
match item {
syn::NestedMeta::Meta(syn::Meta::NameValue(ref nv))
if nv.path.is_ident("expected") =>
{
if let syn::Lit::Str(ref value) = nv.lit {
return ShouldPanic::YesWithMessage(value.value());
}
if let Ok(syn::Meta::List(list)) = attr.parse_meta() {
for item in list.nested {
match item {
syn::NestedMeta::Meta(syn::Meta::NameValue(ref nv))
if nv.path.is_ident("expected") =>
{
if let syn::Lit::Str(ref value) = nv.lit {
return ShouldPanic::YesWithMessage(value.value());
}
_ => {}
}
_ => {}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/interceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn intercept_test_main_static() {
unsafe {
let diff_bytes = diff.to_le_bytes();
let bytes = ptr as *mut u8;
let _handle = region::protect_with_handle(bytes, 5, region::Protection::WriteExecute)
let _handle = region::protect_with_handle(bytes, 5, region::Protection::WRITE_EXECUTE)
.expect("failed to modify page protection to intercept `test_main_static`");

std::ptr::write(bytes.offset(0), 0xe9);
Expand Down
15 changes: 10 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![feature(test)]
#![allow(incomplete_features)]
#![feature(specialization)]
#![feature(termination_trait_lib)]
//! Crate for supporting data-driven tests.
Expand Down Expand Up @@ -136,21 +137,25 @@ pub mod __internal {
pub use crate::runner::{
check_test_runner, register, RegistrationNode, RegularShouldPanic, RegularTestDesc,
};
#[cfg(not(feature = "test_case_registration"))]
// i.e. no TCR, use ctor instead
#[cfg(not(all(feature = "rustc_is_nightly", feature = "test_case_registration")))]
pub use datatest_derive::{data_ctor_internal, files_ctor_internal};
#[cfg(feature = "test_case_registration")]
// i.e. use TCR
#[cfg(all(feature = "rustc_is_nightly", feature = "test_case_registration"))]
pub use datatest_derive::{data_test_case_internal, files_test_case_internal};
}

pub use crate::runner::runner;

#[cfg(not(feature = "test_case_registration"))]
// i.e. no TCR, use ctor instead
#[cfg(not(all(feature = "rustc_is_nightly", feature = "test_case_registration")))]
pub use datatest_derive::{
data_ctor_registration as data, files_ctor_registration as files,
test_ctor_registration as test,
};

#[cfg(feature = "test_case_registration")]
// i.e. use TCR
#[cfg(all(feature = "rustc_is_nightly", feature = "test_case_registration"))]
pub use datatest_derive::{
data_test_case_registration as data, files_test_case_registration as files,
};
Expand All @@ -165,7 +170,7 @@ use std::path::Path;

/// `datatest` test harness entry point. Should be declared in the test module, like in the
/// following snippet:
/// ```rust,norun
/// ```rust,no_run
/// datatest::harness!();
/// ```
///
Expand Down
42 changes: 33 additions & 9 deletions src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,25 @@ fn real_name(name: &str) -> &str {
/// instead.
fn adjust_for_test_name(opts: &mut crate::rustc_test::TestOpts, name: &str) {
let real_test_name = real_name(name);
if opts.filter_exact && opts.filter.as_ref().map_or(false, |s| s == real_test_name) {
opts.filter_exact = false;
opts.filter = Some(format!("{}::", real_test_name));
// rustc 1.52.0 changes `filters` to accept multiple filters from the command line
#[cfg(feature = "rustc_test_TestOpts_filters_vec")]
{
if opts.filter_exact {
if let Some(test_name) = opts.filters.iter_mut().find(|s| *s == real_test_name) {
test_name.push_str("::");
opts.filter_exact = false;
}
}
}
// fallback for rust < 1.52
#[cfg(not(feature = "rustc_test_TestOpts_filters_vec"))]
{
if opts.filter_exact && opts.filter.as_ref().map_or(false, |s| s == real_test_name) {
if let Some(test_name) = opts.filter.as_mut() {
test_name.push_str("::");
opts.filter_exact = false;
}
}
}
}

Expand All @@ -276,15 +292,23 @@ pub fn register(new: &mut RegistrationNode) {
// `#[test]` tests on stable channel where we don't have a way to override test runner.
crate::interceptor::install_interceptor();

// REGISTRY is a linked list that all the registration functions attempt to push to. This is
// the push function.
//
// Since the registration functions are triggered by `rust-ctor` at executable startup, all the
// registration functions will run sequentially. Further, there will be no overlap between this
// list push and the list pops that execute during runner(). So it's all good.
let reg = &REGISTRY;
let mut current = reg.load(Ordering::SeqCst);
loop {
let previous = reg.compare_and_swap(current, new, Ordering::SeqCst);
if previous == current {
new.next = unsafe { previous.as_ref() };
return;
} else {
current = previous;
match reg.compare_exchange(current, new, Ordering::SeqCst, Ordering::SeqCst) {
Ok(previous) => {
new.next = unsafe { previous.as_ref() };
break;
}
Err(previous) => {
current = previous;
}
}
}
}
Expand Down
19 changes: 18 additions & 1 deletion tests/datatest.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
#![cfg(feature = "nightly")]
//! cargo +nightly test # test_case_registration enabled
//! cargo +nightly test --no-default-features # no test_case_registration, uses ctor

// self-testing config only:
#![cfg(all(feature = "rustc_is_nightly"))]
#![feature(custom_test_frameworks)]
#![test_runner(datatest::runner)]

// We want to share tests between "nightly" and "stable" suites. These have to be two different
// suites as we set `harness = false` for the "stable" one.
include!("tests/mod.rs");

// Regular tests still work

#[test]
fn regular_test() {
println!("regular tests also work!");
}

#[test]
fn regular_test_result() -> Result<(), Box<dyn std::error::Error>> {
println!("regular tests also work!");
Ok(())
}
37 changes: 24 additions & 13 deletions tests/datatest_stable.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
// We want to share tests between "nightly" and "stable" suites. These have to be two different
// suites as we set `harness = false` for the "stable" one.
include!("tests/mod.rs");
//! cargo +stable test --features subvert_stable_guarantees

// This test suite is configured with `harness = false` in Cargo.toml.
// So we need to make sure it has a main function when testing nightly
#[cfg(not(feature = "rustc_is_stable"))]
fn main() {}
// And uses the datatest harness when testing stable
#[cfg(feature = "rustc_is_stable")]
datatest::harness!();

// Regular test have to use `datatest` variant of `#[test]` to work.
use datatest::test;
#[cfg(feature = "rustc_is_stable")]
mod stable {
// Regular test have to use `datatest` variant of `#[test]` to work.
use datatest::test;

#[test]
fn regular_test() {
println!("regular tests also work!");
}
// We want to share tests between "rustc_is_nightly" and "rustc_is_stable" suites. These have to be two different
// suites as we set `harness = false` for the "stable" one.
include!("tests/mod.rs");

#[test]
fn regular_test() {
println!("regular tests also work!");
}

#[test]
fn regular_test_result() -> Result<(), Box<dyn std::error::Error>> {
println!("regular tests also work!");
Ok(())
#[test]
fn regular_test_result() -> Result<(), Box<dyn std::error::Error>> {
println!("regular tests also work!");
Ok(())
}
}
5 changes: 3 additions & 2 deletions tests/datatest_stable_unsafe.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Run with: `cargo +stable test --features unsafe_test_runner --test datatest_stable_unsafe`
//! cargo +stable test --features subvert_stable_guarantees,unsafe_test_runner

#[cfg(feature = "unsafe_test_runner")]
#![cfg(feature = "rustc_is_stable")]
#![cfg(feature = "unsafe_test_runner")]

// We want to share tests between "nightly" and "stable" suites. These have to be two different
// suites as we set `harness = false` for the "stable" one.
Expand Down
3 changes: 0 additions & 3 deletions tests/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#[cfg(feature = "stable")]
use datatest::test;

use serde::Deserialize;
use std::fmt;
use std::path::Path;
Expand Down
2 changes: 1 addition & 1 deletion tests/unicode.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![cfg(feature = "nightly")]
#![cfg(feature = "rustc_is_nightly")]
#![feature(non_ascii_idents)]
#![feature(custom_test_frameworks)]
#![test_runner(datatest::runner)]
Expand Down