From 5efb0cbe046127d074fd2c85b7cafb5a4d5aea44 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 7 Jul 2017 16:12:44 -0700 Subject: [PATCH] Add some generator pass/fail tests --- src/test/compile-fail/generator/borrowing.rs | 32 +++ .../compile-fail/generator/not-send-sync.rs | 34 ++++ src/test/run-pass/generator/control-flow.rs | 56 ++++++ src/test/run-pass/generator/drop-env.rs | 73 +++++++ src/test/run-pass/generator/panic-drops.rs | 58 ++++++ src/test/run-pass/generator/panic-safe.rs | 35 ++++ .../run-pass/generator/resume-after-return.rs | 33 ++++ src/test/run-pass/generator/smoke.rs | 187 ++++++++++++++++++ 8 files changed, 508 insertions(+) create mode 100644 src/test/compile-fail/generator/borrowing.rs create mode 100644 src/test/compile-fail/generator/not-send-sync.rs create mode 100644 src/test/run-pass/generator/control-flow.rs create mode 100644 src/test/run-pass/generator/drop-env.rs create mode 100644 src/test/run-pass/generator/panic-drops.rs create mode 100644 src/test/run-pass/generator/panic-safe.rs create mode 100644 src/test/run-pass/generator/resume-after-return.rs create mode 100644 src/test/run-pass/generator/smoke.rs diff --git a/src/test/compile-fail/generator/borrowing.rs b/src/test/compile-fail/generator/borrowing.rs new file mode 100644 index 000000000000..c721ea04acf1 --- /dev/null +++ b/src/test/compile-fail/generator/borrowing.rs @@ -0,0 +1,32 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators, generator_trait)] + +use std::ops::{State, Generator}; +use std::cell::Cell; + +fn main() { + let _b = { + let a = 3; + (|| yield &a).resume(()) + //~^ ERROR: `a` does not live long enough + }; + + let _b = { + let a = 3; + || { + let _: () = gen arg; // TODO: shouldn't be needed for inference + yield &a + //~^ ERROR: `a` does not live long enough + } + }; +} + diff --git a/src/test/compile-fail/generator/not-send-sync.rs b/src/test/compile-fail/generator/not-send-sync.rs new file mode 100644 index 000000000000..6cfc8dd333ef --- /dev/null +++ b/src/test/compile-fail/generator/not-send-sync.rs @@ -0,0 +1,34 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators, generator_trait)] + +use std::ops::{State, Generator}; +use std::cell::Cell; + +fn main() { + fn assert_sync(_: T) {} + fn assert_send(_: T) {} + + assert_sync(|| { + //~^ ERROR: Sync` is not satisfied + let a = Cell::new(2); + yield; + let _: () = gen arg; + }); + + let a = Cell::new(2); + assert_send(|| { + //~^ ERROR: Sync` is not satisfied + drop(&a); + yield; + let _: () = gen arg; + }); +} diff --git a/src/test/run-pass/generator/control-flow.rs b/src/test/run-pass/generator/control-flow.rs new file mode 100644 index 000000000000..911bb96b5827 --- /dev/null +++ b/src/test/run-pass/generator/control-flow.rs @@ -0,0 +1,56 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators, generator_trait)] + +use std::ops::{State, Generator}; + +fn finish(mut amt: usize, mut t: T) -> T::Return + where T: Generator<(), Yield = ()> +{ + loop { + match t.resume(()) { + State::Yielded(()) => amt = amt.checked_sub(1).unwrap(), + State::Complete(ret) => { + assert_eq!(amt, 0); + return ret + } + } + } + +} + +fn main() { + finish(1, || yield); + finish(8, || { + for _ in 0..8 { + yield; + } + }); + finish(1, || { + if true { + yield; + } else { + } + }); + finish(1, || { + if false { + } else { + yield; + } + }); + finish(2, || { + if { yield; false } { + yield; + panic!() + } + yield + }); +} diff --git a/src/test/run-pass/generator/drop-env.rs b/src/test/run-pass/generator/drop-env.rs new file mode 100644 index 000000000000..43d0af7a0734 --- /dev/null +++ b/src/test/run-pass/generator/drop-env.rs @@ -0,0 +1,73 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators, generator_trait)] + +use std::ops::Generator; +use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +static A: AtomicUsize = ATOMIC_USIZE_INIT; + +struct B; + +impl Drop for B { + fn drop(&mut self) { + A.fetch_add(1, Ordering::SeqCst); + } +} + +fn main() { + t1(); + t2(); + t3(); +} + +fn t1() { + let b = B; + let mut foo = || { + yield; + drop(b); + }; + + let n = A.load(Ordering::SeqCst); + drop(foo.resume(())); + assert_eq!(A.load(Ordering::SeqCst), n); + drop(foo); + assert_eq!(A.load(Ordering::SeqCst), n + 1); +} + +fn t2() { + let b = B; + let mut foo = || { + yield b; + }; + + let n = A.load(Ordering::SeqCst); + drop(foo.resume(())); + assert_eq!(A.load(Ordering::SeqCst), n + 1); + drop(foo); + assert_eq!(A.load(Ordering::SeqCst), n + 1); +} + +fn t3() { + let b = B; + let mut foo = || { + let _: () = gen arg; // TODO: this line should not be necessary + yield; + drop(b); + }; + + let n = A.load(Ordering::SeqCst); + assert_eq!(A.load(Ordering::SeqCst), n); + drop(foo); + // TODO: we should assert n+1 here, not n + // assert_eq!(A.load(Ordering::SeqCst), n + 1); + assert_eq!(A.load(Ordering::SeqCst), n); +} diff --git a/src/test/run-pass/generator/panic-drops.rs b/src/test/run-pass/generator/panic-drops.rs new file mode 100644 index 000000000000..6b9d7da197f1 --- /dev/null +++ b/src/test/run-pass/generator/panic-drops.rs @@ -0,0 +1,58 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators, generator_trait)] + +use std::ops::Generator; +use std::panic; +use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +static A: AtomicUsize = ATOMIC_USIZE_INIT; + +struct B; + +impl Drop for B { + fn drop(&mut self) { + A.fetch_add(1, Ordering::SeqCst); + } +} + +fn main() { + let b = B; + let mut foo = || { + if true { + panic!(); + } + drop(b); + yield; + }; + + assert_eq!(A.load(Ordering::SeqCst), 0); + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + foo.resume(()) + })); + assert!(res.is_err()); + assert_eq!(A.load(Ordering::SeqCst), 1); + + let mut foo = || { + if true { + panic!(); + } + drop(B); + yield; + }; + + assert_eq!(A.load(Ordering::SeqCst), 1); + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + foo.resume(()) + })); + assert!(res.is_err()); + assert_eq!(A.load(Ordering::SeqCst), 1); +} diff --git a/src/test/run-pass/generator/panic-safe.rs b/src/test/run-pass/generator/panic-safe.rs new file mode 100644 index 000000000000..7d86d7b6d94a --- /dev/null +++ b/src/test/run-pass/generator/panic-safe.rs @@ -0,0 +1,35 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators, generator_trait)] + +use std::ops::Generator; +use std::panic; + +fn main() { + let mut foo = || { + if true { + panic!(); + } + yield; + }; + + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + foo.resume(()) + })); + assert!(res.is_err()); + + for _ in 0..10 { + let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { + foo.resume(()) + })); + assert!(res.is_err()); + } +} diff --git a/src/test/run-pass/generator/resume-after-return.rs b/src/test/run-pass/generator/resume-after-return.rs new file mode 100644 index 000000000000..ca85d2545c82 --- /dev/null +++ b/src/test/run-pass/generator/resume-after-return.rs @@ -0,0 +1,33 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(generators, generator_trait)] + +use std::ops::{State, Generator}; +use std::panic; + +fn main() { + let mut foo = || { + if true { + return + } + yield; + }; + + match foo.resume(()) { + State::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } + + match panic::catch_unwind(move || foo.resume(())) { + Ok(_) => panic!("generator successfully resumed"), + Err(_) => {} + } +} diff --git a/src/test/run-pass/generator/smoke.rs b/src/test/run-pass/generator/smoke.rs new file mode 100644 index 000000000000..daa32aa3213e --- /dev/null +++ b/src/test/run-pass/generator/smoke.rs @@ -0,0 +1,187 @@ +// Copyright 2017 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: --test + +#![feature(generators, generator_trait)] + +use std::ops::{State, Generator}; +use std::thread; + +#[test] +fn simple() { + let mut foo = || { + if false { + yield; + } + }; + + match foo.resume(()) { + State::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn return_capture() { + let a = String::from("foo"); + let mut foo = || { + if false { + yield; + } + a + }; + + match foo.resume(()) { + State::Complete(ref s) if *s == "foo" => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn simple_yield() { + let mut foo = || { + yield; + }; + + match foo.resume(()) { + State::Yielded(()) => {} + s => panic!("bad state: {:?}", s), + } + match foo.resume(()) { + State::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn yield_capture() { + let b = String::from("foo"); + let mut foo = || { + yield b; + }; + + match foo.resume(()) { + State::Yielded(ref s) if *s == "foo" => {} + s => panic!("bad state: {:?}", s), + } + match foo.resume(()) { + State::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn simple_yield_value() { + let mut foo = || { + yield String::from("bar"); + return String::from("foo") + }; + + match foo.resume(()) { + State::Yielded(ref s) if *s == "bar" => {} + s => panic!("bad state: {:?}", s), + } + match foo.resume(()) { + State::Complete(ref s) if *s == "foo" => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn return_after_yield() { + let a = String::from("foo"); + let mut foo = || { + yield; + return a + }; + + match foo.resume(()) { + State::Yielded(()) => {} + s => panic!("bad state: {:?}", s), + } + match foo.resume(()) { + State::Complete(ref s) if *s == "foo" => {} + s => panic!("bad state: {:?}", s), + } +} + +#[test] +fn send_and_sync() { + assert_send_sync(|| { + let _: () = gen arg; + yield + }); + assert_send_sync(|| { + let _: () = gen arg; + yield String::from("foo"); + }); + assert_send_sync(|| { + let _: () = gen arg; + yield; + return String::from("foo"); + }); + let a = 3; + assert_send_sync(|| { + let _: () = gen arg; + yield a; + return + }); + let a = 3; + assert_send_sync(move || { + let _: () = gen arg; + yield a; + return + }); + let a = String::from("a"); + assert_send_sync(|| { + let _: () = gen arg; + yield ; + drop(a); + return + }); + let a = String::from("a"); + assert_send_sync(move || { + let _: () = gen arg; + yield ; + drop(a); + return + }); + + fn assert_send_sync(_: T) {} +} + +#[test] +fn send_over_threads() { + let mut foo = || { yield }; + thread::spawn(move || { + match foo.resume(()) { + State::Yielded(()) => {} + s => panic!("bad state: {:?}", s), + } + match foo.resume(()) { + State::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } + }).join().unwrap(); + + let a = String::from("a"); + let mut foo = || { yield a }; + thread::spawn(move || { + match foo.resume(()) { + State::Yielded(ref s) if *s == "a" => {} + s => panic!("bad state: {:?}", s), + } + match foo.resume(()) { + State::Complete(()) => {} + s => panic!("bad state: {:?}", s), + } + }).join().unwrap(); +}