diff options
Diffstat (limited to 'third_party/rust/serial_test')
-rw-r--r-- | third_party/rust/serial_test/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/serial_test/Cargo.toml | 46 | ||||
-rw-r--r-- | third_party/rust/serial_test/LICENSE | 19 | ||||
-rw-r--r-- | third_party/rust/serial_test/README.md | 51 | ||||
-rw-r--r-- | third_party/rust/serial_test/src/code_lock.rs | 72 | ||||
-rw-r--r-- | third_party/rust/serial_test/src/file_lock.rs | 86 | ||||
-rw-r--r-- | third_party/rust/serial_test/src/lib.rs | 60 | ||||
-rw-r--r-- | third_party/rust/serial_test/src/rwlock.rs | 30 | ||||
-rw-r--r-- | third_party/rust/serial_test/tests/tests.rs | 8 |
9 files changed, 373 insertions, 0 deletions
diff --git a/third_party/rust/serial_test/.cargo-checksum.json b/third_party/rust/serial_test/.cargo-checksum.json new file mode 100644 index 0000000000..e02e1074db --- /dev/null +++ b/third_party/rust/serial_test/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"3eaed30d780757b28e3db22c8dc2ae302f94b8ef7be4c3452c171a3fa1f66c0b","LICENSE":"ac7e05bd11cc1cfc3f9452c1b9986a9b1d54e180fa88e44e69caf955f95dc8a6","README.md":"b6df6928aece5915ef3469e8384c3716a4fe58820fd1e8ab758c061bd5a499f0","src/code_lock.rs":"775bc592dc9f09aaef90d6455b0ed584fc571224611de4497f5ab0136abc4e78","src/file_lock.rs":"e4c954278a467674648ee43c1b10d0013201358d7c4bbc8cc3678b5af17b9848","src/lib.rs":"6462996f756ee44b47562462a1c5148672c90a6dfc1c093319e583b08b1d251a","src/rwlock.rs":"51c99268a9bb86c601373dbb84f549cd7694ffab151b72ee789d73c149bbf5c7","tests/tests.rs":"242fd98c91c8c42e39a0795a40f44fc11451be54b3196dc4487796f929322bc8"},"package":"e5bcc41d18f7a1d50525d080fd3e953be87c4f9f1a974f3c21798ca00d54ec15"}
\ No newline at end of file diff --git a/third_party/rust/serial_test/Cargo.toml b/third_party/rust/serial_test/Cargo.toml new file mode 100644 index 0000000000..1f46e3cc66 --- /dev/null +++ b/third_party/rust/serial_test/Cargo.toml @@ -0,0 +1,46 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "serial_test" +version = "0.6.0" +authors = ["Tom Parker-Shemilt <palfrey@tevp.net>"] +description = "Allows for the creation of serialised Rust tests" +readme = "README.md" +keywords = ["sequential"] +categories = ["development-tools::testing"] +license = "MIT" +repository = "https://github.com/palfrey/serial_test/" +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] +[dependencies.document-features] +version = "0.2" +optional = true + +[dependencies.fslock] +version = "0.2" +optional = true + +[dependencies.lazy_static] +version = "1.2" + +[dependencies.parking_lot] +version = ">= 0.10, < 0.12" + +[dependencies.serial_test_derive] +version = "~0.6.0" + +[features] +default = [] +docsrs = ["document-features"] +file_locks = ["fslock"] diff --git a/third_party/rust/serial_test/LICENSE b/third_party/rust/serial_test/LICENSE new file mode 100644 index 0000000000..5a8adc1d4d --- /dev/null +++ b/third_party/rust/serial_test/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 Tom Parker-Shemilt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.
\ No newline at end of file diff --git a/third_party/rust/serial_test/README.md b/third_party/rust/serial_test/README.md new file mode 100644 index 0000000000..d379a1d2fd --- /dev/null +++ b/third_party/rust/serial_test/README.md @@ -0,0 +1,51 @@ +# serial_test +[![Version](https://img.shields.io/crates/v/serial_test.svg)](https://crates.io/crates/serial_test) +[![Downloads](https://img.shields.io/crates/d/serial_test)](https://crates.io/crates/serial_test) +[![Docs](https://docs.rs/serial_test/badge.svg)](https://docs.rs/serial_test/) +[![MIT license](https://img.shields.io/crates/l/serial_test.svg)](./LICENSE) +[![Build Status](https://github.com/palfrey/serial_test/workflows/Continuous%20integration/badge.svg?branch=main)](https://github.com/palfrey/serial_test/actions) +[![MSRV: 1.39.0](https://flat.badgen.net/badge/MSRV/1.39.0/purple)](https://blog.rust-lang.org/2019/11/07/Rust-1.39.0.html) + +`serial_test` allows for the creation of serialised Rust tests using the `serial` attribute +e.g. +```rust +#[test] +#[serial] +fn test_serial_one() { + // Do things +} + +#[test] +#[serial] +fn test_serial_another() { + // Do things +} + +#[tokio::test] +#[serial] +async fn test_serial_another() { + // Do things asynchronously +} +``` +Multiple tests with the `serial` attribute are guaranteed to be executed in serial. Ordering of the tests is not guaranteed however. +Tests without the `serial` attribute may run at any time, including in parallel to tests marked as `serial`. Note that if you're using +an async test reactor attribute (e.g. `tokio::test` or `actix_rt::test`) then they should be listed *before* `serial`, otherwise we +don't get an async function and things break. There's now an error for this case to improve debugging. + +## Usage +We require at least Rust 1.39 for [async/await](https://blog.rust-lang.org/2019/11/07/Async-await-stable.html) support + +Add to your Cargo.toml +```toml +[dev-dependencies] +serial_test = "*" +``` + +plus `use serial_test::serial;` (for Rust 2018) or +```rust +#[macro_use] +extern crate serial_test; +``` +for earlier versions. + +You can then either add `#[serial]` or `#[serial(some_text)]` to tests as required. diff --git a/third_party/rust/serial_test/src/code_lock.rs b/third_party/rust/serial_test/src/code_lock.rs new file mode 100644 index 0000000000..a09b2f8d1e --- /dev/null +++ b/third_party/rust/serial_test/src/code_lock.rs @@ -0,0 +1,72 @@ +use lazy_static::lazy_static; +use parking_lot::ReentrantMutex; +use std::collections::HashMap; +use std::ops::{Deref, DerefMut}; +use std::sync::{Arc, RwLock}; + +lazy_static! { + static ref LOCKS: Arc<RwLock<HashMap<String, ReentrantMutex<()>>>> = + Arc::new(RwLock::new(HashMap::new())); +} + +fn check_new_key(name: &str) { + // Check if a new key is needed. Just need a read lock, which can be done in sync with everyone else + let new_key = { + let unlock = LOCKS.read().unwrap(); + !unlock.deref().contains_key(name) + }; + if new_key { + // This is the rare path, which avoids the multi-writer situation mostly + LOCKS + .write() + .unwrap() + .deref_mut() + .insert(name.to_string(), ReentrantMutex::new(())); + } +} + +#[doc(hidden)] +pub fn local_serial_core_with_return<E>( + name: &str, + function: fn() -> Result<(), E>, +) -> Result<(), E> { + check_new_key(name); + + let unlock = LOCKS.read().unwrap(); + // _guard needs to be named to avoid being instant dropped + let _guard = unlock.deref()[name].lock(); + function() +} + +#[doc(hidden)] +pub fn local_serial_core(name: &str, function: fn()) { + check_new_key(name); + + let unlock = LOCKS.read().unwrap(); + // _guard needs to be named to avoid being instant dropped + let _guard = unlock.deref()[name].lock(); + function(); +} + +#[doc(hidden)] +pub async fn local_async_serial_core_with_return<E>( + name: &str, + fut: impl std::future::Future<Output = Result<(), E>>, +) -> Result<(), E> { + check_new_key(name); + + let unlock = LOCKS.read().unwrap(); + // _guard needs to be named to avoid being instant dropped + let _guard = unlock.deref()[name].lock(); + fut.await +} + +#[doc(hidden)] +pub async fn local_async_serial_core(name: &str, fut: impl std::future::Future<Output = ()>) { + check_new_key(name); + + let unlock = LOCKS.read().unwrap(); + // _guard needs to be named to avoid being instant dropped + let _guard = unlock.deref()[name].lock(); + fut.await; +} diff --git a/third_party/rust/serial_test/src/file_lock.rs b/third_party/rust/serial_test/src/file_lock.rs new file mode 100644 index 0000000000..20cd99eddb --- /dev/null +++ b/third_party/rust/serial_test/src/file_lock.rs @@ -0,0 +1,86 @@ +use fslock::LockFile; +use std::{env, fs, path::Path}; + +struct Lock { + lockfile: LockFile, +} + +impl Lock { + fn unlock(self: &mut Lock) { + self.lockfile.unlock().unwrap(); + println!("Unlock"); + } +} + +fn do_lock(path: &str) -> Lock { + if !Path::new(path).exists() { + fs::write(path, "").unwrap_or_else(|_| panic!("Lock file path was {:?}", path)) + } + let mut lockfile = LockFile::open(path).unwrap(); + println!("Waiting on {:?}", path); + lockfile.lock().unwrap(); + println!("Locked for {:?}", path); + Lock { lockfile } +} + +fn path_for_name(name: &str) -> String { + let mut pathbuf = env::temp_dir(); + pathbuf.push(format!("serial-test-{}", name)); + pathbuf.into_os_string().into_string().unwrap() +} + +fn make_lock_for_name_and_path(name: &str, path: Option<&str>) -> Lock { + if let Some(opt_path) = path { + do_lock(opt_path) + } else { + let default_path = path_for_name(name); + do_lock(&default_path) + } +} + +#[doc(hidden)] +pub fn fs_serial_core(name: &str, path: Option<&str>, function: fn()) { + let mut lock = make_lock_for_name_and_path(name, path); + function(); + lock.unlock(); +} + +#[doc(hidden)] +pub fn fs_serial_core_with_return<E>( + name: &str, + path: Option<&str>, + function: fn() -> Result<(), E>, +) -> Result<(), E> { + let mut lock = make_lock_for_name_and_path(name, path); + let ret = function(); + lock.unlock(); + ret +} + +#[doc(hidden)] +pub async fn fs_async_serial_core_with_return<E>( + name: &str, + path: Option<&str>, + fut: impl std::future::Future<Output = Result<(), E>>, +) -> Result<(), E> { + let mut lock = make_lock_for_name_and_path(name, path); + let ret = fut.await; + lock.unlock(); + ret +} + +#[doc(hidden)] +pub async fn fs_async_serial_core( + name: &str, + path: Option<&str>, + fut: impl std::future::Future<Output = ()>, +) { + let mut lock = make_lock_for_name_and_path(name, path); + fut.await; + lock.unlock(); +} + +#[test] +fn test_serial() { + fs_serial_core("test", None, || {}); +} diff --git a/third_party/rust/serial_test/src/lib.rs b/third_party/rust/serial_test/src/lib.rs new file mode 100644 index 0000000000..e83eec042d --- /dev/null +++ b/third_party/rust/serial_test/src/lib.rs @@ -0,0 +1,60 @@ +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +//! # serial_test +//! `serial_test` allows for the creation of serialised Rust tests using the [serial](macro@serial) attribute +//! e.g. +//! ```` +//! #[test] +//! #[serial] +//! fn test_serial_one() { +//! // Do things +//! } +//! +//! #[test] +//! #[serial] +//! fn test_serial_another() { +//! // Do things +//! } +//! ```` +//! Multiple tests with the [serial](macro@serial) attribute are guaranteed to be executed in serial. Ordering +//! of the tests is not guaranteed however. +//! +//! For cases like doctests and integration tests where the tests are run as separate processes, we also support +//! [file_serial](macro@file_serial), with similar properties but based off file locking. Note that there are no +//! guarantees about one test with [serial](macro@serial) and another with [file_serial](macro@file_serial) +//! as they lock using different methods. +//! ```` +//! #[test] +//! #[file_serial] +//! fn test_serial_three() { +//! // Do things +//! } +//! ```` +//! +//! ## Feature flags +#![cfg_attr( + feature = "docsrs", + cfg_attr(doc, doc = ::document_features::document_features!()) +)] + +mod code_lock; +#[cfg(feature = "file_locks")] +mod file_lock; + +pub use code_lock::{ + local_async_serial_core, local_async_serial_core_with_return, local_serial_core, + local_serial_core_with_return, +}; + +#[cfg(feature = "file_locks")] +pub use file_lock::{ + fs_async_serial_core, fs_async_serial_core_with_return, fs_serial_core, + fs_serial_core_with_return, +}; + +// Re-export #[serial/file_serial]. +#[allow(unused_imports)] +pub use serial_test_derive::serial; + +#[cfg(feature = "file_locks")] +pub use serial_test_derive::file_serial; diff --git a/third_party/rust/serial_test/src/rwlock.rs b/third_party/rust/serial_test/src/rwlock.rs new file mode 100644 index 0000000000..42c1a99707 --- /dev/null +++ b/third_party/rust/serial_test/src/rwlock.rs @@ -0,0 +1,30 @@ +use std::sync::{ Arc, Mutex, Condvar}; + +// LockState can be in several possible states: + +// 1. 0 readers, false upgradeable_reader, false writer. No-one has any locks, anyone can acquire anything (initial state) +// 2. 1+ readers, false upgradeable_reader, false writer - readers. writer cannot be acquired, but upgradeable_reader can be +// 3. 1+ readers, true upgradeable_reader, false writer - bunch of readers, and one thread that could upgrade to writer, but not yet +// 4. 0 readers, true upgradeable_reader, false writer - upgradeable_reader thread can upgrade to writer +// 5. 0 readers, false upgradeable_reader, true writer - writer only. Nothing else can be acquired. + +struct LockState { + readers: u32, + upgradeable_reader: bool, + writer: bool +} + +struct Locks(Arc<(Mutex<LockState>, Condvar)>); + +impl Locks { + pub fn new() -> Locks { + Locks(Arc::new((Mutex::new( + LockState { + readers: 0, + upgradeable_reader: false, + writer: false + }), Condvar::new()))) + } + + pub fn read() -> +}
\ No newline at end of file diff --git a/third_party/rust/serial_test/tests/tests.rs b/third_party/rust/serial_test/tests/tests.rs new file mode 100644 index 0000000000..add8b10dec --- /dev/null +++ b/third_party/rust/serial_test/tests/tests.rs @@ -0,0 +1,8 @@ +use serial_test::local_serial_core; + +#[test] +fn test_empty_serial_call() { + local_serial_core("beta", || { + println!("Bar"); + }); +} |