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.
This commit is contained in:
Niko Matsakis 2018-02-14 10:06:12 -05:00
parent 0625d4c282
commit e446f706a8
6 changed files with 47 additions and 54 deletions

View File

@ -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!`

View File

@ -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<T: Termination, E: Debug> Termination for Result<T, E> {
fn report(self) -> i32 {
match self {
@ -64,19 +61,19 @@ impl<T: Termination, E: Debug> Termination for Result<T, E> {
}
}
#[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

View File

@ -746,48 +746,36 @@ fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> P<ast::Expr> {
};
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" };

View File

@ -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<T: Termination>(result: T) {
assert_eq!(result.report(), 0);
}
#[derive(Copy, Clone, Debug)]
pub enum ColorConfig {
AutoColor,

View File

@ -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
' '
}

View File

@ -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 {}
}