Extended `ast_map::Map` with an iterator over all node id's that match a path suffix.
Extended pretty printer to let users choose particular items to pretty print, either by indicating an integer node-id, or by providing a path suffix.
* Example 1: the suffix `typeck::check::check_struct` matches the item with the path `rustc::middle::typeck::check::check_struct` when compiling the `rustc` crate.
* Example 2: the suffix `and` matches `core::option::Option::and` and `core::result::Result::and` when compiling the `core` crate.
Refactored `pprust` slightly to support the pretty printer changes.
(See individual commits for more description.)
if [ ! -z "$CFG_ENABLE_LOCAL_RUST" ]
then
- if [ ! -f ${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF} ]
+ system_rustc=$(which rustc)
+ if [ -f ${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF} ]
then
- err "no local rust to use"
+ : # everything already configured
+ elif [ -n "$system_rustc" ]
+ then
+ # we assume that rustc is in a /bin directory
+ CFG_LOCAL_RUST_ROOT=${system_rustc%/bin/rustc}
else
- LRV=`${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF} --version`
- step_msg "using rustc at: ${CFG_LOCAL_RUST_ROOT} with version: $LRV"
+ err "no local rust to use"
fi
+
+ LRV=`${CFG_LOCAL_RUST_ROOT}/bin/rustc${BIN_SUF} --version`
+ step_msg "using rustc at: ${CFG_LOCAL_RUST_ROOT} with version: $LRV"
+ putvar CFG_LOCAL_RUST_ROOT
fi
# Force freebsd to build with clang; gcc doesn't like us there
get input from the keyboard. You can't have a guessing game without the ability
to guess!
+# Strings
+
+Strings are an important concept for any programmer to master. Rust's string
+handling system is a bit different than in other languages, due to its systems
+focus. Any time you have a data structure of variable size, things can get
+tricky, and strings are a re-sizable data structure. That said, Rust's strings
+also work differently than in some other systems languages, such as C.
+
+Let's dig into the details. A **string** is a sequence of unicode scalar values
+encoded as a stream of UTF-8 bytes. All strings are guaranteed to be
+validly-encoded UTF-8 sequences. Additionally, strings are not null-terminated
+and can contain null bytes.
+
+Rust has two main types of strings: `&str` and `String`.
+
+The first kind is a `&str`. This is pronounced a 'string slice.' String literals
+are of the type `&str`:
+
+```{rust}
+let string = "Hello there.";
+```
+
+This string is statically allocated, meaning that it's saved inside our
+compiled program, and exists for the entire duration it runs. The `string`
+binding is a reference to this statically allocated string. String slices
+have a fixed size, and cannot be mutated.
+
+A `String`, on the other hand, is an in-memory string. This string is
+growable, and is also guaranteed to be UTF-8.
+
+```{rust}
+let mut s = "Hello".to_string();
+println!("{}", s);
+
+s.push_str(", world.");
+println!("{}", s);
+```
+
+You can coerce a `String` into a `&str` with the `as_slice()` method:
+
+```{rust}
+fn takes_slice(slice: &str) {
+ println!("Got: {}", slice);
+}
+
+fn main() {
+ let s = "Hello".to_string();
+ takes_slice(s.as_slice());
+}
+```
+
+To compare a String to a constant string, prefer `as_slice()`...
+
+```{rust}
+fn compare(string: String) {
+ if string.as_slice() == "Hello" {
+ println!("yes");
+ }
+}
+```
+
+... over `to_string()`:
+
+```{rust}
+fn compare(string: String) {
+ if string == "Hello".to_string() {
+ println!("yes");
+ }
+}
+```
+
+Converting a `String` to a `&str` is cheap, but converting the `&str` to a
+`String` involves allocating memory. No reason to do that unless you have to!
+
+That's the basics of strings in Rust! They're probably a bit more complicated
+than you are used to, if you come from a scripting language, but when the
+low-level details matter, they really matter. Just remember that `String`s
+allocate memory and control their data, while `&str`s are a reference to
+another string, and you'll be all set.
+
# Standard Input
Getting input from the keyboard is pretty easy, but uses some things
# Patterns
+# Method Syntax
+
+Functions are great, but if you want to call a bunch of them on some data, it
+can be awkward. Consider this code:
+
+```{rust,ignore}
+baz(bar(foo(x)));
+```
+
+We would read this left-to right, and so we see 'baz bar foo.' But this isn't the
+order that the functions would get called in, that's inside-out: 'foo bar baz.'
+Wouldn't it be nice if we could do this instead?
+
+```{rust,ignore}
+x.foo().bar().baz();
+```
+
+Luckily, as you may have guessed with the leading question, you can! Rust provides
+the ability to use this **method call syntax** via the `impl` keyword.
+
+Here's how it works:
+
+```
+struct Circle {
+ x: f64,
+ y: f64,
+ radius: f64,
+}
+
+impl Circle {
+ fn area(&self) -> f64 {
+ std::f64::consts::PI * (self.radius * self.radius)
+ }
+}
+
+fn main() {
+ let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
+ println!("{}", c.area());
+}
+```
+
+This will print `12.566371`.
+
+We've made a struct that represents a circle. We then write an `impl` block,
+and inside it, define a method, `area`. Methods take a special first
+parameter, `&self`. There are three variants: `self`, `&self`, and `&mut self`.
+You can think of this first parameter as being the `x` in `x.foo()`. The three
+variants correspond to the three kinds of thing `x` could be: `self` if it's
+just a value on the stack, `&self` if it's a reference, and `&mut self` if it's
+a mutable reference. We should default to using `&self`, as it's the most
+common.
+
+Finally, as you may remember, the value of the area of a circle is `π*r²`.
+Because we took the `&self` parameter to `area`, we can use it just like any
+other parameter. Because we know it's a `Circle`, we can access the `radius`
+just like we would with any other struct. An import of π and some
+multiplications later, and we have our area.
+
+You can also define methods that do not take a `self` parameter. Here's a
+pattern that's very common in Rust code:
+
+```
+struct Circle {
+ x: f64,
+ y: f64,
+ radius: f64,
+}
+
+impl Circle {
+ fn new(x: f64, y: f64, radius: f64) -> Circle {
+ Circle {
+ x: x,
+ y: y,
+ radius: radius,
+ }
+ }
+}
+
+fn main() {
+ let c = Circle::new(0.0, 0.0, 2.0);
+}
+```
+
+This **static method** builds a new `Circle` for us. Note that static methods
+are called with the `Struct::method()` syntax, rather than the `ref.method()`
+syntax.
+
+
# Closures
So far, we've made lots of functions in Rust. But we've given them all names.
# Tasks
+Concurrency and parallelism are topics that are of increasing interest to a
+broad subsection of software developers. Modern computers are often multi-core,
+to the point that even embedded devices like cell phones have more than one
+processor. Rust's semantics lend themselves very nicely to solving a number of
+issues that programmers have with concurrency. Many concurrency errors that are
+runtime errors in other languages are compile-time errors in Rust.
+
+Rust's concurrency primitive is called a **task**. Tasks are lightweight, and
+do not share memory in an unsafe manner, preferring message passing to
+communicate. It's worth noting that tasks are implemented as a library, and
+not part of the language. This means that in the future, other concurrency
+libraries can be written for Rust to help in specific scenarios. Here's an
+example of creating a task:
+
+```{rust}
+spawn(proc() {
+ println!("Hello from a task!");
+});
+```
+
+The `spawn` function takes a proc as an argument, and runs that proc in a new
+task. A proc takes ownership of its entire environment, and so any variables
+that you use inside the proc will not be usable afterward:
+
+```{rust,ignore}
+let mut x = vec![1i, 2i, 3i];
+
+spawn(proc() {
+ println!("The value of x[0] is: {}", x[0]);
+});
+
+println!("The value of x[0] is: {}", x[0]); // error: use of moved value: `x`
+```
+
+`x` is now owned by the proc, and so we can't use it anymore. Many other
+languages would let us do this, but it's not safe to do so. Rust's type system
+catches the error.
+
+If tasks were only able to capture these values, they wouldn't be very useful.
+Luckily, tasks can communicate with each other through **channel**s. Channels
+work like this:
+
+```{rust}
+let (tx, rx) = channel();
+
+spawn(proc() {
+ tx.send("Hello from a task!".to_string());
+});
+
+let message = rx.recv();
+println!("{}", message);
+```
+
+The `channel()` function returns two endpoints: a `Receiver<T>` and a
+`Sender<T>`. You can use the `.send()` method on the `Sender<T>` end, and
+receive the message on the `Receiver<T>` side with the `recv()` method. This
+method blocks until it gets a message. There's a similar method, `.try_recv()`,
+which returns an `Option<T>` and does not block.
+
+If you want to send messages to the task as well, create two channels!
+
+```{rust}
+let (tx1, rx1) = channel();
+let (tx2, rx2) = channel();
+
+spawn(proc() {
+ tx1.send("Hello from a task!".to_string());
+ let message = rx2.recv();
+ println!("{}", message);
+});
+
+let message = rx1.recv();
+println!("{}", message);
+
+tx2.send("Goodbye from main!".to_string());
+```
+
+The proc has one sending end and one receiving end, and the main task has one
+of each as well. Now they can talk back and forth in whatever way they wish.
+
+Notice as well that because `Sender` and `Receiver` are generic, while you can
+pass any kind of information through the channel, the ends are strongly typed.
+If you try to pass a string, and then an integer, Rust will complain.
+
+## Futures
+
+With these basic primitives, many different concurrency patterns can be
+developed. Rust includes some of these types in its standard library. For
+example, if you wish to compute some value in the background, `Future` is
+a useful thing to use:
+
+```{rust}
+use std::sync::Future;
+
+let mut delayed_value = Future::spawn(proc() {
+ // just return anything for examples' sake
+
+ 12345i
+});
+println!("value = {}", delayed_value.get());
+```
+
+Calling `Future::spawn` works just like `spawn()`: it takes a proc. In this
+case, though, you don't need to mess with the channel: just have the proc
+return the value.
+
+`Future::spawn` will return a value which we can bind with `let`. It needs
+to be mutable, because once the value is computed, it saves a copy of the
+value, and if it were immutable, it couldn't update itself.
+
+The proc will go on processing in the background, and when we need the final
+value, we can call `get()` on it. This will block until the result is done,
+but if it's finished computing in the background, we'll just get the value
+immediately.
+
+## Success and failure
+
+Tasks don't always succeed, they can also fail. A task that wishes to fail
+can call the `fail!` macro, passing a message:
+
+```{rust}
+spawn(proc() {
+ fail!("Nope.");
+});
+```
+
+If a task fails, it is not possible for it to recover. However, it can
+notify other tasks that it has failed. We can do this with `task::try`:
+
+```{rust}
+use std::task;
+use std::rand;
+
+let result = task::try(proc() {
+ if rand::random() {
+ println!("OK");
+ } else {
+ fail!("oops!");
+ }
+});
+```
+
+This task will randomly fail or succeed. `task::try` returns a `Result`
+type, so we can handle the response like any other computation that may
+fail.
+
# Macros
# Unsafe
fn grow<T>(nelts: uint, loptr: &mut uint, elts: &mut Vec<Option<T>>) {
assert_eq!(nelts, elts.len());
let lo = *loptr;
- let newlen = nelts * 2;
- elts.reserve(newlen);
+ elts.reserve(nelts * 2);
+ let newlen = elts.capacity();
/* fill with None */
- for _ in range(elts.len(), elts.capacity()) {
+ for _ in range(elts.len(), newlen) {
elts.push(None);
}
assert_eq!(d.len(), 1);
}
+ #[test]
+ fn test_with_capacity_non_power_two() {
+ let mut d3 = RingBuf::with_capacity(3);
+ d3.push(1i);
+
+ // X = None, | = lo
+ // [|1, X, X]
+ assert_eq!(d3.pop_front(), Some(1));
+ // [X, |X, X]
+ assert_eq!(d3.front(), None);
+
+ // [X, |3, X]
+ d3.push(3);
+ // [X, |3, 6]
+ d3.push(6);
+ // [X, X, |6]
+ assert_eq!(d3.pop_front(), Some(3));
+
+ // Pushing the lo past half way point to trigger
+ // the 'B' scenario for growth
+ // [9, X, |6]
+ d3.push(9);
+ // [9, 12, |6]
+ d3.push(12);
+
+ d3.push(15);
+ // There used to be a bug here about how the
+ // RingBuf made growth assumptions about the
+ // underlying Vec which didn't hold and lead
+ // to corruption.
+ // (Vec grows to next power of two)
+ //good- [9, 12, 15, X, X, X, X, |6]
+ //bug- [15, 12, X, X, X, |6, X, X]
+ assert_eq!(d3.pop_front(), Some(6));
+
+ // Which leads us to the following state which
+ // would be a failure case.
+ //bug- [15, 12, X, X, X, X, |X, X]
+ assert_eq!(d3.front(), Some(&9));
+ }
+
#[test]
fn test_reserve_exact() {
let mut d = RingBuf::new();
/// Retains only the elements specified by the predicate.
///
/// In other words, remove all elements `e` such that `f(&e)` returns false.
- /// This method operates in place and preserves the order the retained elements.
+ /// This method operates in place and preserves the order of the retained elements.
///
/// # Example
///
use option::{None, Option, Some};
use iter::range_step;
-#[cfg(stage0)]
-use iter::Iterator; // NOTE(stage0): Remove after snapshot.
-
// UTF-8 ranges and tags for encoding characters
static TAG_CONT: u8 = 0b1000_0000u8;
static TAG_TWO_B: u8 = 0b1100_0000u8;
use fmt;
use intrinsics;
-#[cfg(stage0)]
-#[cold] #[inline(never)] // this is the slow path, always
-#[lang="fail_"]
-fn fail_(expr: &'static str, file: &'static str, line: uint) -> ! {
- format_args!(|args| -> () {
- begin_unwind(args, &(file, line));
- }, "{}", expr);
-
- unsafe { intrinsics::abort() }
-}
-
-#[cfg(stage0)]
-#[cold]
-#[lang="fail_bounds_check"]
-fn fail_bounds_check(file: &'static str, line: uint,
- index: uint, len: uint) -> ! {
- format_args!(|args| -> () {
- begin_unwind(args, &(file, line));
- }, "index out of bounds: the len is {} but the index is {}", len, index);
- unsafe { intrinsics::abort() }
-}
-
-#[cfg(not(stage0))]
#[cold] #[inline(never)] // this is the slow path, always
#[lang="fail_"]
fn fail_(expr_file_line: &(&'static str, &'static str, uint)) -> ! {
unsafe { intrinsics::abort() }
}
-#[cfg(not(stage0))]
#[cold] #[inline(never)]
#[lang="fail_bounds_check"]
fn fail_bounds_check(file_line: &(&'static str, uint),
use slice;
use str::StrSlice;
-#[cfg(stage0)]
-use iter::Iterator; // NOTE(stage0): Remove after snapshot.
-#[cfg(stage0)]
-use option::{Some, None}; // NOTE(stage0): Remove after snapshot.
-
/// A flag that specifies whether to use exponential (scientific) notation.
pub enum ExponentFormat {
/// Do not use exponential notation.
use num::{Int, cast, zero};
use slice::{ImmutableVector, MutableVector};
-#[cfg(stage0)]
-use iter::Iterator; // NOTE(stage0): Remove after snapshot.
-#[cfg(stage0)]
-use option::{Some, None}; // NOTE(stage0): Remove after snapshot.
-
/// A type that represents a specific radix
#[doc(hidden)]
trait GenericRadix {
macro_rules! def_fn_mut(
($($args:ident)*) => (
- #[cfg(not(stage0))]
impl<Result$(,$args)*>
FnMut<($($args,)*),Result>
for extern "Rust" fn($($args: $args,)*) -> Result {
use cmp::{PartialEq, Eq, PartialOrd, Equiv, Ordering, Less, Equal, Greater};
-#[cfg(stage0)]
-use iter::Iterator; // NOTE(stage0): Remove after snapshot.
-
pub use intrinsics::copy_memory;
pub use intrinsics::copy_nonoverlapping_memory;
pub use intrinsics::set_memory;
use option::{Option, Some};
use str::{Str, StrSlice, eq_slice};
- #[cfg(stage0)]
- use option::None; // NOTE(stage0): Remove after snapshot.
-
impl<'a> Ord for &'a str {
#[inline]
fn cmp(&self, other: & &'a str) -> Ordering {
pub mod os {
pub mod common {
pub mod posix01 {
- use types::os::arch::c95::{c_short, time_t, suseconds_t,
- c_long};
+ use types::os::arch::c95::{c_short, time_t, c_long};
use types::os::arch::extra::{int64, time64_t};
use types::os::arch::posix88::{dev_t, ino_t};
- use types::os::arch::posix88::mode_t;
// pub Note: this is the struct called stat64 in win32. Not stat,
// nor stati64.
pub struct stat {
pub st_dev: dev_t,
pub st_ino: ino_t,
- pub st_mode: mode_t,
+ pub st_mode: u16,
pub st_nlink: c_short,
pub st_uid: c_short,
pub st_gid: c_short,
}
pub struct timeval {
- pub tv_sec: time_t,
- pub tv_usec: suseconds_t,
+ pub tv_sec: c_long,
+ pub tv_usec: c_long,
}
pub struct timespec {
pub mod bsd44 {
use types::os::arch::c95::{c_char, c_int, c_uint, size_t};
- pub type SOCKET = c_uint;
+ pub type SOCKET = uint;
pub type socklen_t = c_int;
pub type sa_family_t = u16;
pub type in_port_t = u16;
}
pub struct sockaddr_storage {
pub ss_family: sa_family_t,
+ pub __ss_pad1: [u8, ..6],
pub __ss_align: i64,
pub __ss_pad2: [u8, ..112],
}
pub mod posix88 {
pub type off_t = i32;
pub type dev_t = u32;
- pub type ino_t = i16;
+ pub type ino_t = u16;
- #[cfg(target_arch = "x86")]
- pub type pid_t = i32;
- #[cfg(target_arch = "x86_64")]
- pub type pid_t = i64;
+ pub type pid_t = u32;
pub type useconds_t = u32;
pub type mode_t = u16;
pub dwPageSize: DWORD,
pub lpMinimumApplicationAddress: LPVOID,
pub lpMaximumApplicationAddress: LPVOID,
- pub dwActiveProcessorMask: DWORD,
+ pub dwActiveProcessorMask: uint,
pub dwNumberOfProcessors: DWORD,
pub dwProcessorType: DWORD,
pub dwAllocationGranularity: DWORD,
}
pub mod extra {
use types::os::arch::c95::c_int;
- use types::os::arch::extra::{WORD, DWORD, BOOL};
+ use types::os::arch::extra::{WORD, DWORD, BOOL, HANDLE};
pub static TRUE : BOOL = 1;
pub static FALSE : BOOL = 0;
pub static ERROR_IO_PENDING: c_int = 997;
pub static ERROR_FILE_INVALID : c_int = 1006;
pub static ERROR_NOT_FOUND: c_int = 1168;
- pub static INVALID_HANDLE_VALUE : c_int = -1;
+ pub static INVALID_HANDLE_VALUE: HANDLE = -1 as HANDLE;
pub static DELETE : DWORD = 0x00010000;
pub static READ_CONTROL : DWORD = 0x00020000;
pub static ENABLE_QUICK_EDIT_MODE: libc::DWORD = 0x40;
#[repr(C)]
+#[cfg(target_arch = "x86")]
pub struct WSADATA {
pub wVersion: libc::WORD,
pub wHighVersion: libc::WORD,
pub iMaxUdpDg: u16,
pub lpVendorInfo: *mut u8,
}
+#[repr(C)]
+#[cfg(target_arch = "x86_64")]
+pub struct WSADATA {
+ pub wVersion: libc::WORD,
+ pub wHighVersion: libc::WORD,
+ pub iMaxSockets: u16,
+ pub iMaxUdpDg: u16,
+ pub lpVendorInfo: *mut u8,
+ pub szDescription: [u8, ..WSADESCRIPTION_LEN + 1],
+ pub szSystemStatus: [u8, ..WSASYS_STATUS_LEN + 1],
+}
pub type LPWSADATA = *mut WSADATA;
dwFlagsAndAttributes,
ptr::mut_null())
};
- if handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE {
+ if handle == libc::INVALID_HANDLE_VALUE {
Err(super::last_error())
} else {
let fd = unsafe {
let wfd_ptr = malloc_raw(rust_list_dir_wfd_size() as uint);
let find_handle = libc::FindFirstFileW(path.as_ptr(),
wfd_ptr as libc::HANDLE);
- if find_handle as libc::c_int != libc::INVALID_HANDLE_VALUE {
+ if find_handle != libc::INVALID_HANDLE_VALUE {
let mut paths = vec!();
let mut more_files = 1 as libc::c_int;
while more_files != 0 {
libc::FILE_ATTRIBUTE_NORMAL,
ptr::mut_null())
};
- if handle as int == libc::INVALID_HANDLE_VALUE as int {
+ if handle == libc::INVALID_HANDLE_VALUE {
return Err(super::last_error())
}
// Specify (sz - 1) because the documentation states that it's the size
libc::FILE_FLAG_OVERLAPPED,
ptr::mut_null())
};
- if result != libc::INVALID_HANDLE_VALUE as libc::HANDLE {
+ if result != libc::INVALID_HANDLE_VALUE {
return Some(result)
}
libc::FILE_FLAG_OVERLAPPED,
ptr::mut_null())
};
- if result != libc::INVALID_HANDLE_VALUE as libc::HANDLE {
+ if result != libc::INVALID_HANDLE_VALUE {
return Some(result)
}
}
libc::FILE_FLAG_OVERLAPPED,
ptr::mut_null())
};
- if result != libc::INVALID_HANDLE_VALUE as libc::HANDLE {
+ if result != libc::INVALID_HANDLE_VALUE {
return Some(result)
}
}
// and such.
let addr_v = try!(to_utf16(addr));
let ret = unsafe { pipe(addr_v.as_ptr(), true) };
- if ret == libc::INVALID_HANDLE_VALUE as libc::HANDLE {
+ if ret == libc::INVALID_HANDLE_VALUE {
Err(super::last_error())
} else {
Ok(UnixListener { handle: ret, name: addr.clone() })
// create a second server pipe. If this fails, we disconnect the
// connected client and return an error (see comments above).
let new_handle = unsafe { pipe(name.as_ptr(), false) };
- if new_handle == libc::INVALID_HANDLE_VALUE as libc::HANDLE {
+ if new_handle == libc::INVALID_HANDLE_VALUE {
let ret = Err(super::last_error());
// If our disconnection fails, then there's not really a whole lot
// that we can do, so fail the task.
libc::OPEN_EXISTING,
0,
ptr::mut_null());
- if *slot == INVALID_HANDLE_VALUE as libc::HANDLE {
+ if *slot == INVALID_HANDLE_VALUE {
return Err(super::last_error())
}
}
Some(ref fd) => {
let orig = get_osfhandle(fd.fd()) as HANDLE;
- if orig == INVALID_HANDLE_VALUE as HANDLE {
+ if orig == INVALID_HANDLE_VALUE {
return Err(super::last_error())
}
if DuplicateHandle(cur_proc, orig, cur_proc, slot,
wShowWindow: 0,
cbReserved2: 0,
lpReserved2: ptr::mut_null(),
- hStdInput: libc::INVALID_HANDLE_VALUE as libc::HANDLE,
- hStdOutput: libc::INVALID_HANDLE_VALUE as libc::HANDLE,
- hStdError: libc::INVALID_HANDLE_VALUE as libc::HANDLE,
+ hStdInput: libc::INVALID_HANDLE_VALUE,
+ hStdOutput: libc::INVALID_HANDLE_VALUE,
+ hStdError: libc::INVALID_HANDLE_VALUE,
}
}
}
}
+#[cfg(windows)]
+pub fn ms_to_timeval(ms: u64) -> libc::timeval {
+ libc::timeval {
+ tv_sec: (ms / 1000) as libc::c_long,
+ tv_usec: ((ms % 1000) * 1000) as libc::c_long,
+ }
+}
+#[cfg(not(windows))]
pub fn ms_to_timeval(ms: u64) -> libc::timeval {
libc::timeval {
tv_sec: (ms / 1000) as libc::time_t,
ext_cx: ExtCtxt<'a>,
testfns: Vec<Test>,
reexport_mod_ident: ast::Ident,
+ reexport_test_harness_main: Option<InternedString>,
is_test_crate: bool,
config: ast::CrateConfig,
}
// command line options.
let should_test = attr::contains_name(krate.config.as_slice(), "test");
+ // Check for #[reexport_test_harness_main = "some_name"] which
+ // creates a `use some_name = __test::main;`. This needs to be
+ // unconditional, so that the attribute is still marked as used in
+ // non-test builds.
+ let reexport_test_harness_main =
+ attr::first_attr_value_str_by_name(krate.attrs.as_slice(),
+ "reexport_test_harness_main");
+
if should_test {
- generate_test_harness(sess, krate)
+ generate_test_harness(sess, reexport_test_harness_main, krate)
} else {
strip_test_functions(krate)
}
impl<'a> fold::Folder for TestHarnessGenerator<'a> {
fn fold_crate(&mut self, c: ast::Crate) -> ast::Crate {
- let folded = fold::noop_fold_crate(c, self);
+ let mut folded = fold::noop_fold_crate(c, self);
// Add a special __test module to the crate that will contain code
// generated for the test harness
- ast::Crate {
- module: add_test_module(&self.cx, &folded.module),
- .. folded
+ let (mod_, reexport) = mk_test_module(&self.cx, &self.cx.reexport_test_harness_main);
+ folded.module.items.push(mod_);
+ match reexport {
+ Some(re) => folded.module.view_items.push(re),
+ None => {}
}
+ folded
}
fn fold_item(&mut self, i: Gc<ast::Item>) -> SmallVector<Gc<ast::Item>> {
}
}
-fn generate_test_harness(sess: &Session, krate: ast::Crate) -> ast::Crate {
+fn generate_test_harness(sess: &Session,
+ reexport_test_harness_main: Option<InternedString>,
+ krate: ast::Crate) -> ast::Crate {
let mut cx: TestCtxt = TestCtxt {
sess: sess,
ext_cx: ExtCtxt::new(&sess.parse_sess, sess.opts.cfg.clone(),
}),
path: Vec::new(),
testfns: Vec::new(),
- reexport_mod_ident: token::str_to_ident("__test_reexports"),
+ reexport_mod_ident: token::gensym_ident("__test_reexports"),
+ reexport_test_harness_main: reexport_test_harness_main,
is_test_crate: is_test_crate(&krate),
config: krate.config.clone(),
};
attr::contains_name(i.attrs.as_slice(), "should_fail")
}
-fn add_test_module(cx: &TestCtxt, m: &ast::Mod) -> ast::Mod {
- let testmod = mk_test_module(cx);
- ast::Mod {
- items: m.items.clone().append_one(testmod),
- ..(*m).clone()
- }
-}
-
/*
We're going to be building a module that looks more or less like:
}
}
-fn mk_test_module(cx: &TestCtxt) -> Gc<ast::Item> {
+fn mk_test_module(cx: &TestCtxt, reexport_test_harness_main: &Option<InternedString>)
+ -> (Gc<ast::Item>, Option<ast::ViewItem>) {
// Link to test crate
let view_items = vec!(mk_std(cx));
};
let item_ = ast::ItemMod(testmod);
+ let mod_ident = token::gensym_ident("__test");
let item = ast::Item {
- ident: token::str_to_ident("__test"),
+ ident: mod_ident,
attrs: Vec::new(),
id: ast::DUMMY_NODE_ID,
node: item_,
vis: ast::Public,
span: DUMMY_SP,
- };
+ };
+ let reexport = reexport_test_harness_main.as_ref().map(|s| {
+ // building `use <ident> = __test::main`
+ let reexport_ident = token::str_to_ident(s.get());
+
+ let use_path =
+ nospan(ast::ViewPathSimple(reexport_ident,
+ path_node(vec![mod_ident, token::str_to_ident("main")]),
+ ast::DUMMY_NODE_ID));
+
+ ast::ViewItem {
+ node: ast::ViewItemUse(box(GC) use_path),
+ attrs: vec![],
+ vis: ast::Inherited,
+ span: DUMMY_SP
+ }
+ });
debug!("Synthetic test module:\n{}\n", pprust::item_to_string(&item));
- box(GC) item
+ (box(GC) item, reexport)
}
fn nospan<T>(t: T) -> codemap::Spanned<T> {
// The vector of test_descs for this crate
let test_descs = mk_test_descs(cx);
- (quote_item!(&cx.ext_cx,
- pub static TESTS : &'static [self::test::TestDescAndFn] =
- $test_descs
- ;
- )).unwrap()
+ // FIXME #15962: should be using quote_item, but that stringifies
+ // __test_reexports, causing it to be reinterned, losing the
+ // gensym information.
+ let sp = DUMMY_SP;
+ let ecx = &cx.ext_cx;
+ let struct_type = ecx.ty_path(ecx.path(sp, vec![ecx.ident_of("self"),
+ ecx.ident_of("test"),
+ ecx.ident_of("TestDescAndFn")]),
+ None);
+ let static_lt = ecx.lifetime(sp, token::special_idents::static_lifetime.name);
+ // &'static [self::test::TestDescAndFn]
+ let static_type = ecx.ty_rptr(sp,
+ ecx.ty(sp, ast::TyVec(struct_type)),
+ Some(static_lt),
+ ast::MutImmutable);
+ // static TESTS: $static_type = &[...];
+ ecx.item_static(sp,
+ ecx.ident_of("TESTS"),
+ static_type,
+ ast::MutImmutable,
+ test_descs)
}
fn is_test_crate(krate: &ast::Crate) -> bool {
}
fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> Gc<ast::Expr> {
+ // FIXME #15962: should be using quote_expr, but that stringifies
+ // __test_reexports, causing it to be reinterned, losing the
+ // gensym information.
+
let span = test.span;
let path = test.path.clone();
+ let ecx = &cx.ext_cx;
+ let self_id = ecx.ident_of("self");
+ let test_id = ecx.ident_of("test");
+
+ // creates self::test::$name
+ let test_path = |name| {
+ ecx.path(span, vec![self_id, test_id, ecx.ident_of(name)])
+ };
+ // creates $name: $expr
+ let field = |name, expr| ecx.field_imm(span, ecx.ident_of(name), expr);
debug!("encoding {}", ast_util::path_name_i(path.as_slice()));
- let name_lit: ast::Lit =
- nospan(ast::LitStr(token::intern_and_get_ident(
- ast_util::path_name_i(path.as_slice()).as_slice()),
- ast::CookedStr));
+ // path to the #[test] function: "foo::bar::baz"
+ let path_string = ast_util::path_name_i(path.as_slice());
+ let name_expr = ecx.expr_str(span, token::intern_and_get_ident(path_string.as_slice()));
- let name_expr = box(GC) ast::Expr {
- id: ast::DUMMY_NODE_ID,
- node: ast::ExprLit(box(GC) name_lit),
- span: span
- };
+ // self::test::StaticTestName($name_expr)
+ let name_expr = ecx.expr_call(span,
+ ecx.expr_path(test_path("StaticTestName")),
+ vec![name_expr]);
- let mut visible_path = vec![cx.reexport_mod_ident.clone()];
- visible_path.extend(path.move_iter());
- let fn_path = cx.ext_cx.path_global(DUMMY_SP, visible_path);
+ let ignore_expr = ecx.expr_bool(span, test.ignore);
+ let fail_expr = ecx.expr_bool(span, test.should_fail);
- let fn_expr = box(GC) ast::Expr {
- id: ast::DUMMY_NODE_ID,
- node: ast::ExprPath(fn_path),
- span: span,
- };
+ // self::test::TestDesc { ... }
+ let desc_expr = ecx.expr_struct(
+ span,
+ test_path("TestDesc"),
+ vec![field("name", name_expr),
+ field("ignore", ignore_expr),
+ field("should_fail", fail_expr)]);
- let t_expr = if test.bench {
- quote_expr!(&cx.ext_cx, self::test::StaticBenchFn($fn_expr) )
- } else {
- quote_expr!(&cx.ext_cx, self::test::StaticTestFn($fn_expr) )
- };
- let ignore_expr = if test.ignore {
- quote_expr!(&cx.ext_cx, true )
- } else {
- quote_expr!(&cx.ext_cx, false )
- };
+ let mut visible_path = vec![cx.reexport_mod_ident.clone()];
+ visible_path.extend(path.move_iter());
- let fail_expr = if test.should_fail {
- quote_expr!(&cx.ext_cx, true )
- } else {
- quote_expr!(&cx.ext_cx, false )
- };
+ let fn_expr = ecx.expr_path(ecx.path_global(span, visible_path));
- let e = quote_expr!(&cx.ext_cx,
- self::test::TestDescAndFn {
- desc: self::test::TestDesc {
- name: self::test::StaticTestName($name_expr),
- ignore: $ignore_expr,
- should_fail: $fail_expr
- },
- testfn: $t_expr,
- }
- );
- e
+ let variant_name = if test.bench { "StaticBenchFn" } else { "StaticTestFn" };
+ // self::test::$variant_name($fn_expr)
+ let testfn_expr = ecx.expr_call(span, ecx.expr_path(test_path(variant_name)), vec![fn_expr]);
+
+ // self::test::TestDescAndFn { ... }
+ ecx.expr_struct(span,
+ test_path("TestDescAndFn"),
+ vec![field("desc", desc_expr),
+ field("testfn", testfn_expr)])
}
libc::FILE_ATTRIBUTE_NORMAL,
ptr::mut_null())
};
- if handle as uint == libc::INVALID_HANDLE_VALUE as uint {
+ if handle == libc::INVALID_HANDLE_VALUE {
fail!("create file error: {}", os::last_os_error());
}
let mut overlapped: libc::OVERLAPPED = unsafe { mem::zeroed() };
impl<'a> fmt::Show for Item<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
// Write the breadcrumb trail header for the top
- try!(write!(fmt, "\n<h1 class='fqn'>"));
+ try!(write!(fmt, "\n<h1 class='fqn'><div class='in-band'>"));
match self.item.inner {
clean::ModuleItem(ref m) => if m.is_crate {
try!(write!(fmt, "Crate "));
let cur = self.cx.current.as_slice();
let amt = if self.ismodule() { cur.len() - 1 } else { cur.len() };
for (i, component) in cur.iter().enumerate().take(amt) {
- try!(write!(fmt, "<a href='{}index.html'>{}</a>::",
+ try!(write!(fmt, "<a href='{}index.html'>{}</a>​::",
"../".repeat(cur.len() - i - 1),
component.as_slice()));
}
shortty(self.item), self.item.name.get_ref().as_slice()));
// Write stability level
- try!(write!(fmt, "{}", Stability(&self.item.stability)));
+ try!(write!(fmt, "​{}", Stability(&self.item.stability)));
// Links to out-of-band information, i.e. src and stability dashboard
- try!(write!(fmt, "<span class='out-of-band'>"));
+ try!(write!(fmt, "</div><div class='out-of-band'>"));
// Write stability dashboard link
match self.item.inner {
try!(write!(fmt,
r##"<span id='render-detail'>
- <a id="collapse-all" href="#">[collapse all]</a>
- <a id="expand-all" href="#">[expand all]</a>
+ <a id="collapse-all" href="#">[-]
+ </a> <a id="expand-all" href="#">[+]</a>
</span>"##));
// Write `src` tag
}
}
- try!(write!(fmt, "</span>"));
+ try!(write!(fmt, "</div>"));
try!(write!(fmt, "</h1>\n"));
.docblock h3, .docblock h4, .docblock h5 { font-size: 1em; }
.content .out-of-band {
- float: right;
font-size: 23px;
+ width: 40%;
+ margin: 0px;
+ padding: 0px;
+ text-align: right;
+ display: inline-block;
+}
+
+.content .in-band {
+ width: 60%;
+ margin: 0px;
+ padding: 0px;
+ display: inline-block;
}
.content table {
}
.content .multi-column li { width: 100%; display: inline-block; }
-.content .method {
- font-size: 1em;
+.content .method {
+ font-size: 1em;
position: relative;
}
.content .methods .docblock { margin-left: 40px; }
top: 0;
right: 10px;
font-size: 150%;
- -webkit-transform: scaleX(-1);
- transform: scaleX(-1);
+ -webkit-transform: scaleX(-1);
+ transform: scaleX(-1);
}
.methods .section-header {
content: '\2002\00a7\2002';
}
-/* Media Queries */
-
-@media (max-width: 700px) {
- .sidebar {
- display: none;
- }
-
- .content {
- margin-left: 0px;
- }
-
- nav.sub {
- margin: 0 auto;
- }
-}
-
.collapse-toggle {
font-weight: 100;
position: absolute;
color: #999;
font-style: italic;
}
+
+
+
+/* Media Queries */
+
+@media (max-width: 700px) {
+ body {
+ padding-top: 0px;
+ }
+
+ .sidebar {
+ height: 40px;
+ min-height: 40px;
+ width: 100%;
+ margin: 0px;
+ padding: 0px;
+ position: static;
+ }
+
+ .sidebar .location {
+ float: left;
+ margin: 0px;
+ padding: 5px;
+ width: 60%;
+ background: inherit;
+ text-align: left;
+ font-size: 24px;
+ }
+
+ .sidebar img {
+ width: 35px;
+ margin-top: 5px;
+ margin-bottom: 0px;
+ float: left;
+ }
+
+ nav.sub {
+ margin: 0 auto;
+ }
+
+ .sidebar .block {
+ display: none;
+ }
+
+ .content {
+ margin-left: 0px;
+ }
+
+ .toggle-wrapper > .collapse-toggle {
+ left: 0px;
+ }
+
+ .toggle-wrapper {
+ height: 1.5em;
+ }
+}
}
/// This is the entry point of unwinding for fail!() and assert!().
-#[cfg(stage0)]
-#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
-pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
- // Note that this should be the only allocation performed in this code path.
- // Currently this means that fail!() on OOM will invoke this code path,
- // but then again we're not really ready for failing on OOM anyway. If
- // we do start doing this, then we should propagate this allocation to
- // be performed in the parent of this task instead of the task that's
- // failing.
-
- // see below for why we do the `Any` coercion here.
- begin_unwind_inner(box msg, &(file, line))
-}
-
-/// This is the entry point of unwinding for fail!() and assert!().
-#[cfg(not(stage0))]
#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, uint)) -> ! {
// Note that this should be the only allocation performed in this code path.
#![deny(unused_result, unused_must_use)]
#![allow(visible_private_types)]
+#![reexport_test_harness_main = "test_main"]
+
#[cfg(test)] extern crate green;
#[cfg(test)] extern crate debug;
#[cfg(test)] extern crate realrustuv = "rustuv";
pub use self::tty::TtyWatcher;
// Run tests with libgreen instead of libnative.
-//
-// FIXME: This egregiously hacks around starting the test runner in a different
-// threading mode than the default by reaching into the auto-generated
-// '__test' module.
#[cfg(test)] #[start]
fn start(argc: int, argv: *const *const u8) -> int {
- green::start(argc, argv, event_loop, __test::main)
+ green::start(argc, argv, event_loop, test_main)
}
mod macros;
let tmpdir = tmpdir();
let path = tmpdir.join("a");
check!(File::create(&path));
-
- check!(change_file_times(&path, 1000, 2000));
- assert_eq!(check!(path.stat()).accessed, 1000);
- assert_eq!(check!(path.stat()).modified, 2000);
+ // These numbers have to be bigger than the time in the day to account for timezones
+ // Windows in particular will fail in certain timezones with small enough values
+ check!(change_file_times(&path, 100000, 200000));
+ assert_eq!(check!(path.stat()).accessed, 100000);
+ assert_eq!(check!(path.stat()).modified, 200000);
})
iotest!(fn utime_noexist() {
use result::{Ok, Err};
use sync::atomic;
-#[cfg(stage0)]
-use iter::Iterator; // NOTE(stage0): Remove after snapshot.
-
/// A wrapper for a path to temporary directory implementing automatic
/// scope-based deletion.
pub struct TempDir {
#![allow(deprecated)]
#![deny(missing_doc)]
+#![reexport_test_harness_main = "test_main"]
+
// When testing libstd, bring in libuv as the I/O backend so tests can print
// things and all of the std::io tests have an I/O interface to run on top
// of
pub use core_sync::comm;
// Run tests with libgreen instead of libnative.
-//
-// FIXME: This egregiously hacks around starting the test runner in a different
-// threading mode than the default by reaching into the auto-generated
-// '__test' module.
#[cfg(test)] #[start]
fn start(argc: int, argv: *const *const u8) -> int {
- green::start(argc, argv, rustuv::event_loop, __test::main)
+ green::start(argc, argv, rustuv::event_loop, test_main)
}
/* Exported macros */
/// fail!("this is a {} {message}", "fancy", message = "message");
/// ```
#[macro_export]
-#[cfg(not(stage0))]
macro_rules! fail(
() => ({
fail!("explicit failure")
});
)
-#[macro_export]
-#[cfg(stage0)]
-macro_rules! fail(
- () => ({
- fail!("explicit failure")
- });
- ($msg:expr) => ({
- // static requires less code at runtime, more constant data
- static FILE_LINE: (&'static str, uint) = (file!(), line!());
- let (file, line) = FILE_LINE;
- ::std::rt::begin_unwind($msg, file, line)
- });
- ($fmt:expr, $($arg:tt)*) => ({
- // a closure can't have return type !, so we need a full
- // function to pass to format_args!, *and* we need the
- // file and line numbers right here; so an inner bare fn
- // is our only choice.
- //
- // LLVM doesn't tend to inline this, presumably because begin_unwind_fmt
- // is #[cold] and #[inline(never)] and because this is flagged as cold
- // as returning !. We really do want this to be inlined, however,
- // because it's just a tiny wrapper. Small wins (156K to 149K in size)
- // were seen when forcing this to be inlined, and that number just goes
- // up with the number of calls to fail!()
- #[inline(always)]
- fn run_fmt(fmt: &::std::fmt::Arguments) -> ! {
- static FILE_LINE: (&'static str, uint) = (file!(), line!());
- ::std::rt::begin_unwind_fmt(fmt, &FILE_LINE)
- }
- format_args!(run_fmt, $fmt, $($arg)*)
- });
-)
-
/// Ensure that a boolean expression is `true` at runtime.
///
/// This will invoke the `fail!` macro if the provided expression cannot be
//! Useful synchronization primitives
//!
-//! This modules contains useful safe and unsafe synchronization primitives.
+//! This module contains useful safe and unsafe synchronization primitives.
//! Most of the primitives in this module do not provide any sort of locking
//! and/or blocking at all, but rather provide the necessary tools to build
//! other types of concurrent primitives.
pub fn decompose_compatible(c: char, i: |char|) { d(c, i, true); }
fn d(c: char, i: |char|, k: bool) {
- #[cfg(stage0)]
- use core::iter::Iterator;
-
// 7-bit ASCII never decomposes
if c <= '\x7f' { i(c); return; }
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-/* Foreign builtins. */
-
-#include "valgrind/valgrind.h"
-
#include <stdint.h>
#include <time.h>
#include <string.h>
#endif
#endif
+/* Foreign builtins. */
+//include valgrind.h after stdint.h so that uintptr_t is defined for msys2 w64
+#include "valgrind/valgrind.h"
+
#ifdef __ANDROID__
time_t
timegm(struct tm *tm)
+S 2014-08-07 12e0f72
+ freebsd-x86_64 e55055a876ebbde0d3ed3bcb97579afab9264def
+ linux-i386 2665e45879f2ef77ce0c9015f971642fe424ac33
+ linux-x86_64 51ed1f4cf0707585a136bb149a443394067c074c
+ macos-i386 78f1996954a6e0718d684a3756b4870a6f8771ee
+ macos-x86_64 216f46f65866207a9f41c3ed654f5c1e085cb7f3
+ winnt-i386 95a9b8a8bf587761ae954392aee2ccee3758a533
+
S 2014-07-17 9fc8394
freebsd-x86_64 5a4b645e2b42ae06224cc679d4a43b3d89be1482
linux-i386 a5e1bb723020ac35173d49600e76b0935e257a6a
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags:--test
+
+// the `--test` harness creates modules with these textual names, but
+// they should be inaccessible from normal code.
+use x = __test; //~ ERROR unresolved import `__test`
+use y = __test_reexports; //~ ERROR unresolved import `__test_reexports`
+
+#[test]
+fn baz() {}
--- /dev/null
+-include ../tools.mk
+
+all:
+ # check that #[ignore(cfg(...))] does the right thing.
+ $(RUSTC) --test test-ignore-cfg.rs --cfg ignorecfg
+ $(call RUN,test-ignore-cfg) | grep 'shouldnotignore ... ok'
+ $(call RUN,test-ignore-cfg) | grep 'shouldignore ... ignored'
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#[test]
+#[ignore(cfg(ignorecfg))]
+fn shouldignore() {
+}
+
+#[test]
+#[ignore(cfg(noignorecfg))]
+fn shouldnotignore() {
+}
// instead of in std.
#![feature(macro_rules)]
+#![reexport_test_harness_main = "test_main"]
+
extern crate libc;
extern crate native;
#[cfg(test)] #[start]
fn start(argc: int, argv: *const *const u8) -> int {
- green::start(argc, argv, rustuv::event_loop, __test::main)
+ green::start(argc, argv, rustuv::event_loop, test_main)
}
iotest!(fn test_destroy_once() {
--- /dev/null
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// ignore-pretty
+// compile-flags:--test
+
+#![reexport_test_harness_main = "test_main"]
+
+#[cfg(test)]
+fn _unused() {
+ // should resolve to the entry point function the --test harness
+ // creates.
+ test_main();
+}
#![feature(macro_rules, globs)]
#![allow(experimental)]
+#![reexport_test_harness_main = "test_main"]
extern crate native;
extern crate green;
#[cfg(test)] #[start]
fn start(argc: int, argv: *const *const u8) -> int {
- green::start(argc, argv, rustuv::event_loop, __test::main)
+ green::start(argc, argv, rustuv::event_loop, test_main)
}
macro_rules! iotest (
+++ /dev/null
-// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// compile-flags: --test --cfg ignorecfg
-// ignore-pretty: does not work well with `--test`
-
-#[test]
-#[ignore(cfg(ignorecfg))]
-fn shouldignore() {
-}
-
-#[test]
-#[ignore(cfg(noignorecfg))]
-fn shouldnotignore() {
-}
-
-#[test]
-fn checktests() {
- // Pull the tests out of the secreturn test module
- let tests = __test::TESTS;
-
- assert!(
- tests.iter().any(|t| t.desc.name.to_string().as_slice() == "shouldignore" &&
- t.desc.ignore));
-
- assert!(
- tests.iter().any(|t| t.desc.name.to_string().as_slice() == "shouldnotignore" &&
- !t.desc.ignore));
-}