diff --git a/README.md b/README.md index 8a574c6..3eca1c2 100644 --- a/README.md +++ b/README.md @@ -58,184 +58,10 @@ test tests::multiplication_tests::when_operands_are_swapped ... ok test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ``` -## Advanced use +## Documentation -For `#[test_case(body)]` the body is built as follows: +Most up to date documentation is available in our [wiki](https://github.com/frondeus/test-case/wiki). -`body` := `$arguments ($expected_result)? ($description)?` - -### Arguments - -`arguments` := `$expr(,$expr)*(,)?` - -Comma separated list of one or more [expressions][1], eg.: -```rust -#[test_case(a, b, c,)] -#[test_case(())] -#[test_case(a_method_that_produces_arg(1, 2, 3), "a string")] -``` - -### Expected result - -`expected_result` := `=> ($modifier)* $validator` - -Optional part that provides assertions to instantiated tests. - -When using `expected_result` version of `test_case` tested function **must** return a type -that can be matched with validator. Each validator description states how to ensure -that the type returned by function can be matched. - -#### Modifiers - -`modifier` := `ignore | inconclusive` - -Both `ignore` and `inconclusive` keywords indicate that test case should be skipped. This is equivalent to using -`#[ignore]` attribute on normal test. Eg.: - -```rust -#[test_case(0.0 => ignore 0.0)] // not yet implemented -``` - -#### Validator - -There are numerous validators provided by `test_case`: - -`validator` := `$simple|$matching|$panicking|$with|$using|$complex` - -##### Simple - -`simple` := `$expr` - -Accepts any [expression][1] that evaluates to function return type and -compares it against whatever tested block returns via `assert_eq`. Eg.: - -```rust -#[test_case("2.0" => 2.0)] -fn parses_a_string(arg_in: &str) -> f64 { - body omitted... -} -``` -##### Matching - -`matching` := `matches $pattern` - -A [pattern][3] following keyword `matches`. -Result of a function is compared to `pattern` via [MatchExpression][2]. Eg.: - -```rust -#[test_case("2.0" => matches Ok(_))] -#[test_case("1.0" => matches Ok(v) if v == 1.0f64)] -#[test_case("abc" => matches Err(_))] -``` - -##### Panicking - -`panicking` := `panics ($expr)?` - -Indicates that test instance should panic. Works identical to `#[should_panic]` test attribute. -Optional expression after the keyword is treated like `expected` in [should_panic][4]. Eg.: - -```rust -#[test_case(0 => panics "division by zero")] -``` - -##### With - -`with` := `with $closure` - -Allows manual assertions of the result of testing function. -Closure must indicate argument type and it has to be implicitly convertible from type returned by testing function. -Eg.: - -```rust -#[test_case(2.0 => 0.0)] -#[test_case(0.0 => with |i: f64| assert!(i.is_nan()))] -fn test_division(i: f64) -> f64 { - 0.0 / i -} -``` - -##### Using - -`using` := `using $path` - -Work similar to `with` attribute, with the difference being that instead of a closure -it accepts path to a function that should validate result of the testing function. Eg.: - -```rust -fn is_power_of_two(input: u64) { - assert!(input.is_power_of_two()) -} - -#[test_case(1 => using self::is_power_of_two)] -fn some_test(input: u64) -> u64 { - "body omitted..." -} -``` - -##### Complex - -`complex` := `(it|is) $complex_expression` - -`complex_expression` := `not $complex_expression_inner | $complex_expression_inner (and $complex_expression_inner)* | $complex_expression_inner (or $complex_expression_inner)*` - -`complex_expression_inner` := `$cmp_assertion|$path_assertion|$collection_assertion|\($complex_expression\)` - -`cmp_assertion` := `$ord_assertion|$almost_eq_assertion` -`path_assertion` := `existing_path|file|dir|directory` -`collection_assertion` := `contains $expr|contains_in_order $expr` -`ord_assertion` := `(eq|equal_to|lt|less_than|gt|greater_than|leq|less_or_equal_than|geq|greater_or_equal_than) $expr` -`almost_eq_assertion` := `(almost_equal_to|almost) $expr precision $expr` - -Complex assertions are created as an extension to `test_case` allowing for more flexibility in comparisons. Eg.: - -```rust -#[test_case(args => is lt 2*3.14)] -fn take_piece_of_circle(...) -> f64 { - "body omitted..." -} - -#[test_case(args => is existing_path)] -fn installation_created_path(...) -> PathBuf { - "body omitted..." -} - -#[test_case(args => is almost_equal_to 2.0 precision 0.00001)] -fn some_volatile_computation(...) -> f64 { - "body omitted..." -} - -#[test_case(args => it contains "Jack")] -fn list_of_users(...) -> Vec { - "body omitted..." -} - -#[test_case(args => it contains_in_order [1, 2, 3])] -fn sorts_asc(...) -> Vec { - "body omitted..." -} -``` - -`it` and `is` have equivalent interpretation. Both variants exist in order to make test cases easier to read. - -> complex assertions are WIP content, use at own discretion. - -## Notes about async & additional attributes - -If `test_case` is used with `async` tests, eg. `#[tokio::test]`, or user wants to pass other attributes to each -test instance then additional attributes have to be added past first occurrence of `#[test_case]`. Eg.: - -```rust -#[test_case(...)] -#[tokio::test] -#[allow(clippy::non_camel_case_types)] -async fn xyz() { } -``` - -[1]: https://doc.rust-lang.org/reference/expressions.html -[2]: https://doc.rust-lang.org/reference/expressions/match-expr.html -[3]: https://doc.rust-lang.org/reference/patterns.html -[4]: https://doc.rust-lang.org/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic # License diff --git a/src/lib.rs b/src/lib.rs index 0ccca5d..36c7360 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,184 +48,10 @@ //! test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out //! ``` //! -//! # Advanced use +//! # Documentation //! -//! For `#[test_case(body)]` the body is built as follows: +//! Most up to date documentation is available in our [wiki](https://github.com/frondeus/test-case/wiki). //! -//! `body` := `$arguments ($expected_result)? ($description)?` -//! -//! ## Arguments -//! -//! `arguments` := `$expr(,$expr)*(,)?` -//! -//! Comma separated list of one or more [expressions][1], eg.: -//! ```rust -//! #[test_case(a, b, c,)] -//! #[test_case(())] -//! #[test_case(a_method_that_produces_arg(1, 2, 3), "a string")] -//! ``` -//! -//! ## Expected result -//! -//! `expected_result` := `=> ($modifier)* $validator` -//! -//! Optional part that provides assertions to instantiated tests. -//! -//! When using `expected_result` version of `test_case` tested function **must** return a type -//! that can be matched with validator. Each validator description states how to ensure -//! that the type returned by function can be matched. -//! -//! ### Modifiers -//! -//! `modifier` := `ignore | inconclusive` -//! -//! Both `ignore` and `inconclusive` keywords indicate that test case should be skipped. This is equivalent to using -//! `#[ignore]` attribute on normal test. Eg.: -//! -//! ```rust -//! #[test_case(0.0 => ignore 0.0)] // not yet implemented -//! ``` -//! -//! ### Validator -//! -//! There are numerous validators provided by `test_case`: -//! -//! `validator` := `$simple|$matching|$panicking|$with|$using|$complex` -//! -//! #### Simple -//! -//! `simple` := `$expr` -//! -//! Accepts any [expression][1] that evaluates to function return type and -//! compares it against whatever tested block returns via `assert_eq`. Eg.: -//! -//! ```rust -//! #[test_case("2.0" => 2.0)] -//! fn parses_a_string(arg_in: &str) -> f64 { -//! body omitted... -//! } -//! ``` -//! #### Matching -//! -//! `matching` := `matches $pattern` -//! -//! A [pattern][3] following keyword `matches`. -//! Result of a function is compared to `pattern` via [MatchExpression][2]. Eg.: -//! -//! ```rust -//! #[test_case("2.0" => matches Ok(_))] -//! #[test_case("1.0" => matches Ok(v) if v == 1.0f64)] -//! #[test_case("abc" => matches Err(_))] -//! ``` -//! -//! #### Panicking -//! -//! `panicking` := `panics ($expr)?` -//! -//! Indicates that test instance should panic. Works identical to `#[should_panic]` test attribute. -//! Optional expression after the keyword is treated like `expected` in [should_panic][4]. Eg.: -//! -//! ```rust -//! #[test_case(0 => panics "division by zero")] -//! ``` -//! -//! #### With -//! -//! `with` := `with $closure` -//! -//! Allows manual assertions of the result of testing function. -//! Closure must indicate argument type and it has to be implicitly convertible from type returned by testing function. -//! Eg.: -//! -//! ```rust -//! #[test_case(2.0 => 0.0)] -//! #[test_case(0.0 => with |i: f64| assert!(i.is_nan()))] -//! fn test_division(i: f64) -> f64 { -//! 0.0 / i -//! } -//! ``` -//! -//! #### Using -//! -//! `using` := `using $path` -//! -//! Work similar to `with` attribute, with the difference being that instead of a closure -//! it accepts path to a function that should validate result of the testing function. Eg.: -//! -//! ```rust -//! fn is_power_of_two(input: u64) { -//! assert!(input.is_power_of_two()) -//! } -//! -//! #[test_case(1 => using self::is_power_of_two)] -//! fn some_test(input: u64) -> u64 { -//! "body omitted..." -//! } -//! ``` -//! -//! #### Complex -//! -//! `complex` := `(it|is) $complex_expression` -//! -//! `complex_expression` := `not $complex_expression_inner | $complex_expression_inner (and $complex_expression_inner)* | $complex_expression_inner (or $complex_expression_inner)*` -//! -//! `complex_expression_inner` := `$cmp_assertion|$path_assertion|$collection_assertion|\($complex_expression\)` -//! -//! `cmp_assertion` := `$ord_assertion|$almost_eq_assertion` -//! `path_assertion` := `existing_path|file|dir|directory` -//! `collection_assertion` := `contains $expr|contains_in_order $expr` -//! `ord_assertion` := `(eq|equal_to|lt|less_than|gt|greater_than|leq|less_or_equal_than|geq|greater_or_equal_than) $expr` -//! `almost_eq_assertion` := `(almost_equal_to|almost) $expr precision $expr` -//! -//! Complex assertions are created as an extension to `test_case` allowing for more flexibility in comparisons. Eg.: -//! -//! ```rust -//! #[test_case(args => is lt 2*3.14)] -//! fn take_piece_of_circle(...) -> f64 { -//! "body omitted..." -//! } -//! -//! #[test_case(args => is existing_path)] -//! fn installation_created_path(...) -> PathBuf { -//! "body omitted..." -//! } -//! -//! #[test_case(args => is almost_equal_to 2.0 precision 0.00001)] -//! fn some_volatile_computation(...) -> f64 { -//! "body omitted..." -//! } -//! -//! #[test_case(args => it contains "Jack")] -//! fn list_of_users(...) -> Vec { -//! "body omitted..." -//! } -//! -//! #[test_case(args => it contains_in_order [1, 2, 3])] -//! fn sorts_asc(...) -> Vec { -//! "body omitted..." -//! } -//! ``` -//! -//! `it` and `is` have equivalent interpretation. Both variants exist in order to make test cases easier to read. -//! -//! > complex assertions are WIP content, use at own discretion. -//! -//! # Notes about async & additional attributes -//! -//! If `test_case` is used with `async` tests, eg. `#[tokio::test]`, or user wants to pass other attributes to each -//! test instance then additional attributes have to be added past first occurrence of `#[test_case]`. Eg.: -//! -//! ```rust -//! #[test_case(...)] -//! #[tokio::test] -//! #[allow(clippy::non_camel_case_types)] -//! async fn xyz() { } -//! ``` -//! -//! [1]: https://doc.rust-lang.org/reference/expressions.html -//! [2]: https://doc.rust-lang.org/reference/expressions/match-expr.html -//! [3]: https://doc.rust-lang.org/reference/patterns.html -//! [4]: https://doc.rust-lang.org/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic extern crate proc_macro; @@ -256,54 +82,7 @@ mod utils; /// 4. _(Required)_ Test body /// /// When _expected result_ is provided, it is compared against the actual value generated with _test body_ using `assert_eq!`. -/// _Test cases_ that don't provide _expected result_ should contain custom assertions within _test body_. -/// -/// # Examples -/// -/// - Without result and name -/// -/// ```rust -/// # use test_case::test_case; -/// #[test_case(5)] -/// #[test_case(10)] -/// fn is_positive(x: i8) { -/// assert!(x > 0) -/// } -/// ``` -/// -/// - With description, without result -/// -/// ```rust -/// # use test_case::test_case; -/// #[test_case(1 ; "little number")] -/// #[test_case(100 ; "big number")] -/// #[test_case(5)] // some tests may use default name generated from arguments list -/// fn is_positive(x: i8) { -/// assert!(x > 0) -/// } -/// ``` -/// -/// - With result, without description -/// -/// ```rust -/// # use test_case::test_case; -/// #[test_case(1, 2 => 3)] -/// #[test_case(-1, -2 => -3)] -/// fn addition(x: i8, y: i8) -> i8 { -/// x + y -/// } -/// ``` -/// -/// - With result and name -/// -/// ```rust -/// # use test_case::test_case; -/// #[test_case(1, 2 => 3 ; "both numbers possitive")] -/// #[test_case(-1, -2 => -3 ; "both numbers negative")] -/// fn addition(x: i8, y: i8) -> i8 { -/// x + y -/// } -/// ``` +/// _Test cases_ that don't provide _expected result_ should contain custom assertions within _test body_ or return `Result` similar to `#[test]` macro. #[proc_macro_attribute] #[proc_macro_error::proc_macro_error] pub fn test_case(args: TokenStream, input: TokenStream) -> TokenStream {