From e446f706a89e3d5c26c01318bd70904d492ab8b2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 14 Feb 2018 10:06:12 -0500 Subject: [PATCH] put the "unit test" logic into libtest Also make `std::termination` module public and rename feature. The lib feature needs a different name from the language feature. --- src/libstd/lib.rs | 5 +- src/libstd/termination.rs | 23 ++++--- src/libsyntax/test.rs | 60 ++++++++----------- src/libtest/lib.rs | 9 +++ .../termination-trait-main-wrong-type.rs | 2 +- .../termination-trait-not-satisfied.rs | 2 +- 6 files changed, 47 insertions(+), 54 deletions(-) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 3c9004cdd19..b247d121648 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -501,11 +501,10 @@ mod memchr; // The runtime entry point and a few unstable public functions used by the // compiler pub mod rt; -// The trait to support returning arbitrary types in the main function -mod termination; +// The trait to support returning arbitrary types in the main function #[unstable(feature = "termination_trait", issue = "43301")] -pub use self::termination::Termination; +pub mod termination; // Include a number of private modules that exist solely to provide // the rustdoc documentation for primitive types. Using `include!` diff --git a/src/libstd/termination.rs b/src/libstd/termination.rs index f02fad009d8..203870766a9 100644 --- a/src/libstd/termination.rs +++ b/src/libstd/termination.rs @@ -8,7 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Defines the meaning of the return value from `main`, and hence +//! controls what happens in a Rust program after `main` returns. + use fmt::Debug; + #[cfg(target_arch = "wasm32")] mod exit { pub const SUCCESS: i32 = 0; @@ -30,28 +34,21 @@ mod exit { /// The default implementations are returning `libc::EXIT_SUCCESS` to indicate /// a successful execution. In case of a failure, `libc::EXIT_FAILURE` is returned. #[cfg_attr(not(test), lang = "termination")] -#[unstable(feature = "termination_trait", issue = "43301")] +#[unstable(feature = "termination_trait_lib", issue = "43301")] #[rustc_on_unimplemented = "`main` can only return types that implement {Termination}, not `{Self}`"] pub trait Termination { /// Is called to get the representation of the value as status code. /// This status code is returned to the operating system. fn report(self) -> i32; - - /// Invoked when unit tests terminate. Should panic if the unit - /// test is considered a failure. By default, invokes `report()` - /// and checks for a `0` result. - fn assert_unit_test_successful(self) where Self: Sized { - assert_eq!(self.report(), 0); - } } -#[unstable(feature = "termination_trait", issue = "43301")] +#[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for () { fn report(self) -> i32 { exit::SUCCESS } } -#[unstable(feature = "termination_trait", issue = "43301")] +#[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for Result { fn report(self) -> i32 { match self { @@ -64,19 +61,19 @@ impl Termination for Result { } } -#[unstable(feature = "termination_trait", issue = "43301")] +#[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for ! { fn report(self) -> i32 { unreachable!(); } } -#[unstable(feature = "termination_trait", issue = "43301")] +#[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for bool { fn report(self) -> i32 { if self { exit::SUCCESS } else { exit::FAILURE } } } -#[unstable(feature = "termination_trait", issue = "43301")] +#[unstable(feature = "termination_trait_lib", issue = "43301")] impl Termination for i32 { fn report(self) -> i32 { self diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 6cbb7ab393f..b48713fcf7a 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -746,48 +746,36 @@ fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> P { }; visible_path.extend(path); - // If termination feature is enabled, create a wrapper that invokes the fn - // like this: + // Rather than directly give the test function to the test + // harness, we create a wrapper like this: // - // fn wrapper() { - // assert_eq!(0, real_function().report()); - // } + // || test::assert_test_result(real_function()) // - // and then put a reference to `wrapper` into the test descriptor. Otherwise, - // just put a direct reference to `real_function`. + // this will coerce into a fn pointer that is specialized to the + // actual return type of `real_function` (Typically `()`, but not always). let fn_expr = { - let base_fn_expr = ecx.expr_path(ecx.path_global(span, visible_path)); - if cx.features.termination_trait { - // ::std::Termination::assert_unit_test_successful - let assert_unit_test_successful = ecx.path_global( + // construct `real_function()` (this will be inserted into the overall expr) + let real_function_expr = ecx.expr_path(ecx.path_global(span, visible_path)); + // construct path `test::assert_test_result` + let assert_test_result = test_path("assert_test_result"); + // construct `|| {..}` + ecx.lambda( + span, + vec![], + // construct `assert_test_result(..)` + ecx.expr_call( span, + ecx.expr_path(assert_test_result), vec![ - ecx.ident_of("std"), - ecx.ident_of("Termination"), - ecx.ident_of("assert_unit_test_successful"), + // construct `real_function()` + ecx.expr_call( + span, + real_function_expr, + vec![], + ) ], - ); - // || {..} - ecx.lambda( - span, - vec![], - // ::std::Termination::assert_unit_test_successful(..) - ecx.expr_call( - span, - ecx.expr_path(assert_unit_test_successful), - vec![ - // $base_fn_expr() - ecx.expr_call( - span, - base_fn_expr, - vec![], - ) - ], - ), - ) - } else { - base_fn_expr - } + ), + ) }; let variant_name = if test.bench { "StaticBenchFn" } else { "StaticTestFn" }; diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 9ea5f39b71f..932952d649b 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -40,6 +40,7 @@ #![feature(set_stdio)] #![feature(panic_unwind)] #![feature(staged_api)] +#![feature(termination_trait_lib)] extern crate getopts; extern crate term; @@ -69,6 +70,7 @@ use std::iter::repeat; use std::path::PathBuf; use std::sync::mpsc::{channel, Sender}; use std::sync::{Arc, Mutex}; +use std::termination::Termination; use std::thread; use std::time::{Instant, Duration}; use std::borrow::Cow; @@ -322,6 +324,13 @@ pub fn test_main_static(tests: &[TestDescAndFn]) { test_main(&args, owned_tests, Options::new()) } +/// Invoked when unit tests terminate. Should panic if the unit +/// test is considered a failure. By default, invokes `report()` +/// and checks for a `0` result. +pub fn assert_test_result(result: T) { + assert_eq!(result.report(), 0); +} + #[derive(Copy, Clone, Debug)] pub enum ColorConfig { AutoColor, diff --git a/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs b/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs index a63162cf73d..2da51851952 100644 --- a/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs +++ b/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-main-wrong-type.rs @@ -10,6 +10,6 @@ #![feature(termination_trait)] fn main() -> char { -//~^ ERROR: the trait bound `char: std::Termination` is not satisfied +//~^ ERROR: the trait bound `char: std::termination::Termination` is not satisfied ' ' } diff --git a/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-not-satisfied.rs b/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-not-satisfied.rs index 788c38c55be..fac60d6d399 100644 --- a/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-not-satisfied.rs +++ b/src/test/compile-fail/rfc-1937-termination-trait/termination-trait-not-satisfied.rs @@ -12,6 +12,6 @@ struct ReturnType {} -fn main() -> ReturnType { //~ ERROR `ReturnType: std::Termination` is not satisfied +fn main() -> ReturnType { //~ ERROR `ReturnType: std::termination::Termination` is not satisfied ReturnType {} }