ntest-0.8.1/.cargo_vcs_info.json0000644000000001430000000000100121750ustar { "git": { "sha1": "a858d8a9683504cb98fd2408e44987c53ae367dd" }, "path_in_vcs": "ntest" }ntest-0.8.1/Cargo.toml0000644000000025340000000000100102010ustar # 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 = "ntest" version = "0.8.1" authors = ["Armin Becher "] description = "Testing framework for rust which enhances the built-in library with some useful features." documentation = "https://docs.rs/ntest" readme = "README.md" keywords = [ "test", "tests", "unit", "testing", ] categories = [ "development-tools", "development-tools::testing", ] license = "MIT" repository = "https://github.com/becheran/ntest" [profile.dev] panic = "unwind" [lib] path = "src/lib.rs" [dependencies.ntest_proc_macro_helper] version = "0.8.0" [dependencies.ntest_test_cases] version = "0.8.0" [dependencies.ntest_timeout] version = "0.8.1" [dev-dependencies.tokio] version = "1.6.1" features = [ "rt", "macros", ] [badges.gitlab] branch = "master" repository = "becheran/ntest_ci" [badges.maintenance] status = "actively-developed" ntest-0.8.1/Cargo.toml.orig000064400000000000000000000017540072674642500137150ustar 00000000000000[package] name = "ntest" version = "0.8.1" authors = [ "Armin Becher ",] edition = "2018" description = "Testing framework for rust which enhances the built-in library with some useful features." keywords = [ "test", "tests", "unit", "testing",] categories = [ "development-tools", "development-tools::testing",] readme = "README.md" license = "MIT" repository = "https://github.com/becheran/ntest" documentation = "https://docs.rs/ntest" [lib] path = "src/lib.rs" [dependencies.ntest_test_cases] version = "0.8.0" path = "../ntest_test_cases" [dependencies.ntest_timeout] version = "0.8.1" path = "../ntest_timeout" [dependencies.ntest_proc_macro_helper] version = "0.8.0" path = "../ntest_proc_macro_helper" [badges.gitlab] repository = "becheran/ntest_ci" branch = "master" [badges.maintenance] status = "actively-developed" [profile.dev] panic = "unwind" [dev-dependencies.tokio] version = "1.6.1" features = [ "rt", "macros",] ntest-0.8.1/LICENSE000064400000000000000000000021000072674642500120150ustar 00000000000000MIT License Copyright (c) 2019 Armin Becher 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.ntest-0.8.1/README.md000064400000000000000000000051440072674642500123020ustar 00000000000000# NTest [![docs](https://docs.rs/ntest/badge.svg)](https://docs.rs/ntest) [![crates](https://img.shields.io/badge/crates.io-ntest-orange)](https://crates.io/crates/ntest) [![downloads](https://badgen.net/crates/d/ntest)](https://crates.io/crates/ntest) [![build status](https://gitlab.com/becheran/ntest_ci/badges/master/pipeline.svg)](https://gitlab.com/becheran/ntest_ci/pipelines) [![license](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) Testing framework for rust which enhances the built-in library with some useful features. Inspired by the *.Net* unit-testing framework [NUnit](https://github.com/nunit/nunit). - [documentation](https://docs.rs/ntest/) - [library on crates.io](https://crates.io/crates/ntest) ## Getting Started Some functions of *NTest* use [procedural macros](https://doc.rust-lang.org/reference/procedural-macros.html) which are stable for rust edition 2018. If you use the library make sure that you are using the *2018 version* of rust. Update the *Cargo.toml* file: ```toml [package] edition = "2018" # .. ``` Add the *NTest library* to your developer dependencies in the *Cargo.toml* file: ```toml [dev-dependencies] ntest = "*" ``` ## Content - `#[timeout()]` Attribute used for timeouts in tests. - `#[test_case()]` Attribute used to define multiple test cases for a test function. - `assert_about_equal!()` Compare two floating point values or vectors for equality. - `assert_false!()` Expects false argument for test case. - `assert_true!()` Expects true argument for test case. - `assert_panics!()` Expects block to panic. Otherwise the test fails. For more information read the [documentation](https://docs.rs/ntest/). ## Examples ### Create test cases ```rust use ntest::test_case; #[test_case("https://doc.rust-lang.org.html")] #[test_case("http://www.website.php", name="important_test")] fn test_http_link_types(link: &str) { test_link(link, &LinkType::HTTP); } ``` ### Timeout for long running functions ```rust use ntest::timeout; #[test] #[timeout(10)] #[should_panic] fn timeout() { loop {}; } ``` ### Combine attributes ```rust use std::{thread, time}; use ntest::timeout; use ntest::test_case; #[test_case(200)] #[timeout(100)] #[should_panic] #[test_case(10)] #[timeout(100)] fn test_function(i : u32) { let sleep_time = time::Duration::from_millis(i); thread::sleep(sleep_time); } ``` ## License This project is licensed under the MIT License - see the [LICENSE](https://github.com/becheran/ntest/blob/master/LICENSE) file for details. ntest-0.8.1/src/lib.rs000064400000000000000000000154570072674642500127360ustar 00000000000000//! The ntest lib enhances the rust test framework with some useful functions. // Reexport procedural macros pub extern crate ntest_proc_macro_helper; extern crate ntest_test_cases; extern crate ntest_timeout; #[doc(hidden)] pub use ntest_proc_macro_helper::*; #[doc(inline)] pub use ntest_test_cases::test_case; #[doc(inline)] pub use ntest_timeout::timeout; use std::sync::mpsc; use std::thread; use std::time::Duration; // Reexport traits mod traits; #[doc(inline)] pub use crate::traits::MaxDifference; #[doc(hidden)] /// Timeout helper for proc macro timeout pub fn execute_with_timeout( code: &'static (dyn Fn() -> T + Sync + 'static), timeout_ms: u64, ) -> Option { let (sender, receiver) = mpsc::channel(); thread::spawn(move || if let Ok(()) = sender.send(code()) {}); match receiver.recv_timeout(Duration::from_millis(timeout_ms)) { Ok(t) => Some(t), Err(_) => None, } } #[doc(hidden)] /// Difference helper for proc macro about equal pub fn about_eq(a: T, b: T, eps: f64) -> bool { a.max_diff(b) < eps } /// Compare floating point values or vectors of floating points wether they are approximately equal. /// The default value for epsilon is `1.0e-6`. /// /// # Examples /// /// Compare two floating point values which are about equal: /// ``` /// # use ntest::assert_about_eq; /// # fn main() { /// assert_about_eq!(42.00000001f32, 42.0f32); /// # } /// ``` /// /// Explicitly set an epsilon value. This test should fail: /// ``` should_panic /// # use ntest::assert_about_eq; /// # fn main() { /// assert_about_eq!(42.001f32, 42.0f32, 1.0e-4); /// # } /// ``` /// /// Compare two vectors or arrays of floats which are about equal: /// ``` /// # use ntest::assert_about_eq; /// # fn main() { /// assert_about_eq!(vec![1.100000001, 2.1], vec![1.1, 2.1], 0.001f64); /// # } /// ``` /// /// Arrays can be compared to a length of up to `32`. See the [MaxDifference](trait.MaxDifference.html) implementation for more details: /// ``` /// # use ntest::assert_about_eq; /// # fn main() { ///# // Test double usage ///# assert_about_eq!([1.100000001, 2.1], [1.1, 2.1], 0.001f64); /// assert_about_eq!([1.100000001, 2.1], [1.1, 2.1], 0.001f64); /// # } /// ``` #[macro_export] macro_rules! assert_about_eq { ($a:expr, $b:expr, $eps:expr) => { let eps = $eps; assert!( $crate::about_eq($a, $b, eps), "assertion failed: `(left !== right)` \ (left: `{:?}`, right: `{:?}`, epsilon: `{:?}`)", $a, $b, eps ); }; ($a:expr, $b:expr,$eps:expr,) => { assert_about_eq!($a, $b, $eps); }; ($a:expr, $b:expr) => { assert_about_eq!($a, $b, 1.0e-6); }; ($a:expr, $b:expr,) => { assert_about_eq!($a, $b, 1.0e-6); }; } /// Expects a true expression. Otherwise panics. /// /// Is an alias for the [assert! macro](https://doc.rust-lang.org/std/macro.assert.html). /// /// # Examples /// /// This call won't panic. /// ```rust /// # use ntest::assert_true; /// # fn main() { /// assert_true!(true); /// # } ///``` /// /// This call will panic. /// ```should_panic /// # use ntest::assert_true; /// # fn main() { /// assert_true!(false); /// # } /// ``` #[macro_export] macro_rules! assert_true { ($x:expr) => { if !$x { panic!("assertion failed: Expected 'true', but was 'false'"); } }; ($x:expr,) => { assert_true!($x); }; } /// Expects a false expression. Otherwise panics. /// /// # Examples /// /// This call won't panic. /// ```rust /// # use ntest::assert_false; /// # fn main() { /// assert_false!(false); /// # } /// ``` /// /// This call will panic. /// ```should_panic /// # use ntest::assert_false; /// # fn main() { /// assert_false!(true); /// # } /// ``` #[macro_export] macro_rules! assert_false { ($x:expr) => {{ if $x { panic!("assertion failed: Expected 'false', but was 'true'"); } }}; ($x:expr,) => {{ assert_false!($x); }}; } /// A panic in Rust is not always implemented via unwinding, but can be implemented by aborting the /// process as well. This function only catches unwinding panics, not those that abort the process. /// See the catch unwind [documentation](https://doc.rust-lang.org/std/panic/fn.catch_unwind.html) /// for more information. /// /// # Examples /// /// This call won't panic. /// ```rust /// # use ntest::assert_panics; /// # fn main() { /// // Other panics can happen before this call. /// assert_panics!({panic!("I am panicing")}); /// # } /// ``` /// /// This call will panic. /// ```should_panic /// # use ntest::assert_panics; /// # fn main() { /// assert_panics!({println!("I am not panicing")}); /// # } /// ``` #[macro_export] macro_rules! assert_panics { ($x:block) => {{ let result = std::panic::catch_unwind(|| $x); if !result.is_err() { panic!("assertion failed: code in block did not panic"); } }}; ($x:block,) => {{ assert_panics!($x); }}; } #[cfg(test)] mod tests { use super::*; #[test] fn assert_true() { assert_true!(true); } #[test] #[should_panic] fn assert_true_fails() { assert_true!(false); } #[test] fn assert_true_trailing_comma() { assert_true!(true,); } #[test] fn assert_false() { assert_false!(false); } #[test] #[should_panic] fn assert_false_fails() { assert_false!(true); } #[test] fn assert_false_trailing_comma() { assert_false!(false,); } #[test] fn assert_panics() { assert_panics!({ panic!("I am panicing!") },); } #[test] #[should_panic] fn assert_panics_fails() { assert_panics!({ println!("I am not panicing!") },); } #[test] fn assert_panics_trailing_comma() { assert_panics!({ panic!("I am panicing!") },); } #[test] fn vector() { assert_about_eq!(vec![1.1, 2.1], vec![1.1, 2.1]); } #[test] #[should_panic] fn vector_fails() { assert_about_eq!(vec![1.2, 2.1], vec![1.1, 2.1]); } #[test] fn vector_trailing_comma() { assert_about_eq!(vec![1.2, 2.1], vec![1.2, 2.1],); } #[test] fn vector_trailing_comma_with_epsilon() { assert_about_eq!(vec![1.100000001, 2.1], vec![1.1, 2.1], 0.001f64,); } #[test] fn it_should_not_panic_if_values_are_approx_equal() { assert_about_eq!(64f32.sqrt(), 8f32); } #[test] fn about_equal_f32() { assert_about_eq!(3f32, 3f32, 1f64); } #[test] fn about_equal_f64() { assert_about_eq!(3f64, 3f64); } #[test] fn compare_with_epsilon() { assert_about_eq!(42f64, 43f64, 2f64); } #[test] #[should_panic] fn fail_with_epsilon() { assert_about_eq!(3f64, 4f64, 1e-8f64); } } ntest-0.8.1/src/traits.rs000064400000000000000000000057070072674642500134730ustar 00000000000000/// Helper trait for `assert_about_equal` macro. Returns the max difference between /// two vectors of floats. Can also be used for single floats. /// /// # Examples /// /// Compare two floating numbers: /// ``` /// # use ntest::MaxDifference; /// # fn main() { /// assert!((0.1f64 - 42.1f32.max_diff(42.0f32)) < 1.0e-4f64); /// # } /// ``` /// /// Compare two vectors. Returns the maximum difference in the vectors. In this case *~0.1*.: /// ``` /// # use ntest::MaxDifference; /// # fn main() { /// assert!(0.1f64 - vec![42.0, 42.0f32, 1.001f32].max_diff(vec![42.0, 42.1f32, 1.0f32]) < 1.0e-4f64); /// # } /// ``` /// Compare two arrays. Trait implemented for arrays of length `0-32`: /// ``` /// # use ntest::MaxDifference; /// # fn main() { /// assert!(0.1f64 - [42.0, 42.0f32, 1.001f32].max_diff([42.0, 42.1f32, 1.0f32]) < 1.0e-4f64); /// # } /// ``` pub trait MaxDifference { fn max_diff(self, other: Self) -> f64; } impl MaxDifference for f32 { fn max_diff(self, other: Self) -> f64 { f64::from((self - other).abs()) } } impl MaxDifference for f64 { fn max_diff(self, other: Self) -> f64 { (self - other).abs() } } impl MaxDifference for Vec { fn max_diff(self, other: Self) -> f64 { let mut max: f64 = 0.0; for (a, b) in self.iter().zip(other.iter()) { let diff = f64::from((*a - *b).abs()); if diff > max { max = diff; } } max } } impl MaxDifference for Vec { fn max_diff(self, other: Self) -> f64 { let mut max: f64 = 0.0; for (a, b) in self.iter().zip(other.iter()) { let diff = (*a - *b).abs(); if diff > max { max = diff; } } max } } macro_rules! array_impls { ($($N:literal)+) => { $( impl MaxDifference for [f64; $N] { fn max_diff(self, other: Self) -> f64 { let mut max: f64 = 0.0; for (a, b) in self.iter().zip(other.iter()) { let diff = (*a - *b).abs(); if diff > max { max = diff; } } max } } impl MaxDifference for [f32; $N] { fn max_diff(self, other: Self) -> f64 { let mut max: f64 = 0.0; for (a, b) in self.iter().zip(other.iter()) { let diff = f64::from((*a - *b).abs()); if diff > max { max = diff; } } max } } )+ } } array_impls! { 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 } ntest-0.8.1/tests/integration.rs000064400000000000000000000024100072674642500150470ustar 00000000000000use ntest::test_case; use ntest::timeout; use std::{thread, time}; #[test_case(200)] #[timeout(100)] #[should_panic] #[test_case(10)] #[timeout(100)] fn test_function(i: u32) { let sleep_time = time::Duration::from_millis(i); thread::sleep(sleep_time); } #[test] #[timeout(100)] fn no_timeout() { let fifty_millis = time::Duration::from_millis(50); thread::sleep(fifty_millis); } #[test] #[timeout(10)] #[should_panic] fn timeout() { let fifty_millis = time::Duration::from_millis(50); thread::sleep(fifty_millis); } #[test] #[timeout(1)] #[should_panic] fn timeout_inf_loop() { let ten_millis = time::Duration::from_millis(10); loop{ thread::sleep(ten_millis); } } #[test] #[timeout(100)] fn timeout_with_result() -> Result<(), String> { let ten_millis = time::Duration::from_millis(10); thread::sleep(ten_millis); Ok(()) } #[tokio::test] #[timeout(100)] async fn tokio_timeout() { let ten_millis = time::Duration::from_millis(10); thread::sleep(ten_millis); } #[tokio::test] #[timeout(1)] #[should_panic] async fn tokio_should_panic_timeout() { let ten_millis = time::Duration::from_millis(10); loop{ thread::sleep(ten_millis); } }