├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── Cargo.toml ├── LICENSE ├── README.md ├── simulacrum ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples │ ├── macros_high.rs │ ├── macros_mid.rs │ └── manual.rs └── src │ └── lib.rs ├── simulacrum_auto ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples │ └── example.rs └── src │ └── lib.rs ├── simulacrum_macros ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src │ └── lib.rs ├── simulacrum_mock ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src │ ├── constraint │ ├── mod.rs │ ├── result.rs │ └── stock │ │ ├── always.rs │ │ ├── mod.rs │ │ ├── params.rs │ │ └── times.rs │ ├── expectation │ ├── mod.rs │ └── result.rs │ ├── lib.rs │ ├── method.rs │ ├── mock.rs │ └── store.rs ├── simulacrum_shared ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src │ ├── lib.rs │ └── validator.rs └── simulacrum_user ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── lib.rs ├── macros.rs └── validators ├── check.rs ├── compare.rs ├── deref.rs ├── mod.rs ├── trivial.rs └── tuple.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug", 11 | "program": "${workspaceRoot}/target/debug/examples/everything", 12 | "args": [], 13 | "cwd": "${workspaceRoot}" 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "cargo check", 8 | "type": "shell", 9 | "command": "cargo", 10 | "args": [ 11 | "check" 12 | ], 13 | "presentation": { 14 | "reveal": "always", 15 | "panel": "new", 16 | }, 17 | "problemMatcher": [ 18 | "$rustc" 19 | ], 20 | "group": { 21 | "kind": "build", 22 | "isDefault": true 23 | }, 24 | }, 25 | { 26 | "label": "cargo test", 27 | "type": "shell", 28 | "command": "cargo", 29 | "args": [ 30 | "test" 31 | ], 32 | "presentation": { 33 | "reveal": "always", 34 | "panel": "new", 35 | }, 36 | "problemMatcher": [ 37 | "$rustc" 38 | ], 39 | "group": { 40 | "kind": "test", 41 | "isDefault": true 42 | } 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "simulacrum", 4 | "simulacrum_auto", 5 | "simulacrum_macros", 6 | "simulacrum_mock", 7 | "simulacrum_shared", 8 | "simulacrum_user" 9 | ] 10 | 11 | # [patch.crates-io] 12 | # simulacrum = { path = 'simulacrum' } 13 | # simulacrum_macros = { path = 'simulacrum_macros' } 14 | # simulacrum_mock = { path = 'simulacrum_mock' } 15 | # simulacrum_shared = { path = 'simulacrum_shared' } 16 | # simulacrum_user = { path = 'simulacrum_user' } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Paracosm LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Simulacrum [![Docs](https://docs.rs/simulacrum/badge.svg)](https://docs.rs/simulacrum) [![Crates.io](https://img.shields.io/crates/v/simulacrum.svg)](https://crates.io/crates/simulacrum) 2 | ================================================================== 3 | 4 | A minimal library for creating mock objects by hand using stable Rust. 5 | 6 | To install, add this line to your Cargo.toml: 7 | 8 | ```toml 9 | [dependencies] 10 | simulacrum = "0.3.0" 11 | ``` 12 | 13 | Note that this crate has not yet reached version 1.0, so the API may change drastically between releases. 14 | 15 | ## Using Mock Objects 16 | 17 | Simulacrum mock objects provide a consistent interface that features call counts, parameter matching, return values, and modifying parameters by mutable reference. 18 | 19 | ```rust 20 | // Create a mock object 21 | let mut mock = CoolTraitMock::new(); 22 | 23 | // Set up expectations for it 24 | mock.expect_foo() 25 | .called_once(); 26 | mock.then() 27 | .expect_goop() 28 | .called_once() 29 | .with(true) 30 | .returning(|_| 5); 31 | 32 | // Execute test code 33 | m.foo(); 34 | assert_eq!(m.goop(true), 5); 35 | 36 | // When the mock object is dropped, its expectations will be evaluated 37 | ``` 38 | 39 | See [`macros_high.rs`](https://github.com/pcsm/simulacrum/blob/master/simulacrum/examples/macros_high.rs) for a full run-through of the mock object user API. 40 | 41 | ## Creating Mock Objects 42 | 43 | Simulacrum provides several APIs at different levels of abstraction, so you can create mock objects with the level of control you desire. All mock objects created with Simulacrum expose the same user API, no matter which API level is used to create them. 44 | 45 | The following examples all show how to mock this trait: 46 | 47 | ```rust 48 | trait CoolTrait { 49 | fn foo(&self); 50 | fn goop(&mut self, flag: bool) -> u32; 51 | fn store(&self, val: &i64); 52 | } 53 | ``` 54 | 55 | Note that the macro API only supports creating mocks from a trait, while the manual API allows you to create mock objects to stand in for structs as well. 56 | 57 | ### High-Level Macro Example 58 | 59 | The `create_mock!` macro creates a mock object from a trait. Just copy over the trait's interface and annotate it: 60 | 61 | ```rust 62 | extern crate simulacrum; 63 | 64 | use simulacrum::*; 65 | 66 | create_mock! { 67 | impl CoolTrait for CoolTraitMock (self) { 68 | expect_foo("foo"): 69 | fn foo(&self); 70 | 71 | expect_goop("goop"): 72 | fn goop(&mut self, flag: bool) -> u32; 73 | 74 | // & params are mocked as *const and &mut are mocked as *mut. 75 | expect_store("store"): 76 | fn store(&self, val: &i64); 77 | } 78 | } 79 | ``` 80 | 81 | See [`macros_high.rs`](https://github.com/pcsm/simulacrum/blob/master/simulacrum/examples/macros_high.rs) for more examples of how to mock out different types of methods with `create_mock!`. 82 | 83 | ### Mid-Level Macros Example 84 | 85 | If you need more control than the high-level macro offers, you can use the `create_mock_struct!` and `was_called!` macros. This is useful if you'd like to create mock objects with features that the high-level macro doesn't support, like generic methods. Note that you can mix-and-match these macros with the manual interface as well. 86 | 87 | ```rust 88 | extern crate simulacrum; 89 | 90 | use simulacrum::*; 91 | 92 | create_mock_struct! { 93 | struct CoolTraitMock: { 94 | expect_foo("foo"); 95 | expect_goop("goop") bool => u32; 96 | // Note that we've used *const instead of & for shared references. 97 | expect_store("store") *const i64; 98 | } 99 | } 100 | 101 | impl CoolTrait for CoolTraitMock { 102 | fn foo(&self) { 103 | was_called!(self, "foo") 104 | } 105 | 106 | fn goop(&mut self, flag: bool) -> u32 { 107 | was_called!(self, "goop", (flag: bool) -> u32) 108 | } 109 | 110 | fn store(&self, val: &i64) { 111 | // Again note the use of *const instead of & for shared references. 112 | was_called!(self, "store", (val: *const i64)) 113 | } 114 | } 115 | 116 | ``` 117 | 118 | See [`macros_mid.rs`](https://github.com/pcsm/simulacrum/blob/master/simulacrum/examples/macros_mid.rs) for more examples of how to mock out different types of methods with the Mid-Level Macros. 119 | 120 | ## Manual Example 121 | 122 | Create your mock objects manually for ultimate control. With this API, you can even create mocks to stand in for structs instead of traits. For a detailed example of the API, see the [`manual.rs`](https://github.com/pcsm/simulacrum/blob/master/simulacrum/examples/manual.rs) example. 123 | 124 | ```rust 125 | extern crate simulacrum; 126 | 127 | use simulacrum::*; 128 | 129 | trait CoolTrait { 130 | fn foo(&self); 131 | fn goop(&mut self, flag: bool) -> u32; 132 | } 133 | 134 | pub struct CoolTraitMock { 135 | e: Expectations 136 | } 137 | 138 | impl CoolTraitMock { 139 | pub fn new() -> Self { 140 | Self { 141 | e: Expectations::new() 142 | } 143 | } 144 | 145 | pub fn then(&mut self) -> &mut Self { 146 | self.e.then(); 147 | self 148 | } 149 | 150 | pub fn expect_foo(&mut self) -> Method<(), ()> { 151 | self.e.expect::<(), ()>("foo") 152 | } 153 | 154 | pub fn expect_goop(&mut self) -> Method { 155 | self.e.expect::("goop") 156 | } 157 | } 158 | 159 | impl CoolTrait for CoolTraitMock { 160 | fn foo(&self) { 161 | self.e.was_called::<(), ()>("foo", ()) 162 | } 163 | 164 | fn goop(&mut self, flag: bool) -> u32 { 165 | self.e.was_called_returning::("goop", flag) 166 | } 167 | } 168 | ``` -------------------------------------------------------------------------------- /simulacrum/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /simulacrum/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simulacrum" 3 | version = "0.3.1" 4 | authors = ["Jason Grlicky "] 5 | description = "Minimal library for creating mock objects by hand using stable Rust." 6 | keywords = ["mock", "mocking", "test", "testing", "TDD"] 7 | categories = ["development-tools::testing"] 8 | readme = "README.md" 9 | documentation = "https://docs.rs/simulacrum" 10 | repository = "https://github.com/pcsm/simulacrum/tree/master/simulacrum" 11 | license = "MIT" 12 | 13 | [badges] 14 | maintenance = { status = "actively-developed" } 15 | 16 | [dependencies] 17 | simulacrum_macros = "0.3.1" 18 | simulacrum_mock = "0.1.0" 19 | simulacrum_user = "0.1.0" -------------------------------------------------------------------------------- /simulacrum/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Paracosm LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /simulacrum/README.md: -------------------------------------------------------------------------------- 1 | Simulacrum [![Docs](https://docs.rs/simulacrum/badge.svg)](https://docs.rs/simulacrum) [![Crates.io](https://img.shields.io/crates/v/simulacrum.svg)](https://crates.io/crates/simulacrum) 2 | ================================================================== 3 | 4 | A minimal library for creating mock objects by hand using stable Rust. 5 | 6 | To install, add this line to your Cargo.toml: 7 | 8 | ```toml 9 | [dependencies] 10 | simulacrum = "0.3.0" 11 | ``` 12 | 13 | Note that this crate has not yet reached version 1.0, so the API may change drastically between releases. 14 | 15 | ## Using Mock Objects 16 | 17 | Simulacrum mock objects provide a consistent interface that features call counts, parameter matching, return values, and modifying parameters by mutable reference. 18 | 19 | ```rust 20 | // Create a mock object 21 | let mut mock = CoolTraitMock::new(); 22 | 23 | // Set up expectations for it 24 | mock.expect_foo() 25 | .called_once(); 26 | mock.then() 27 | .expect_goop() 28 | .called_once() 29 | .with(true) 30 | .returning(|_| 5); 31 | 32 | // Execute test code 33 | m.foo(); 34 | assert_eq!(m.goop(true), 5); 35 | 36 | // When the mock object is dropped, its expectations will be evaluated 37 | ``` 38 | 39 | See [`macros_high.rs`](https://github.com/pcsm/simulacrum/blob/master/simulacrum/examples/macros_high.rs) for a full run-through of the mock object user API. 40 | 41 | ## Creating Mock Objects 42 | 43 | Simulacrum provides several APIs at different levels of abstraction, so you can create mock objects with the level of control you desire. All mock objects created with Simulacrum expose the same user API, no matter which API level is used to create them. 44 | 45 | The following examples all show how to mock this trait: 46 | 47 | ```rust 48 | trait CoolTrait { 49 | fn foo(&self); 50 | fn goop(&mut self, flag: bool) -> u32; 51 | fn store(&self, val: &i64); 52 | } 53 | ``` 54 | 55 | Note that the macro API only supports creating mocks from a trait, while the manual API allows you to create mock objects to stand in for structs as well. 56 | 57 | ### High-Level Macro Example 58 | 59 | The `create_mock!` macro creates a mock object from a trait. Just copy over the trait's interface and annotate it: 60 | 61 | ```rust 62 | extern crate simulacrum; 63 | 64 | use simulacrum::*; 65 | 66 | create_mock! { 67 | impl CoolTrait for CoolTraitMock (self) { 68 | expect_foo("foo"): 69 | fn foo(&self); 70 | 71 | expect_goop("goop"): 72 | fn goop(&mut self, flag: bool) -> u32; 73 | 74 | // & params are mocked as *const and &mut are mocked as *mut. 75 | expect_store("store"): 76 | fn store(&self, val: &i64); 77 | } 78 | } 79 | ``` 80 | 81 | See [`macros_high.rs`](https://github.com/pcsm/simulacrum/blob/master/simulacrum/examples/macros_high.rs) for more examples of how to mock out different types of methods with `create_mock!`. 82 | 83 | ### Mid-Level Macros Example 84 | 85 | If you need more control than the high-level macro offers, you can use the `create_mock_struct!` and `was_called!` macros. This is useful if you'd like to create mock objects with features that the high-level macro doesn't support, like generic methods. Note that you can mix-and-match these macros with the manual interface as well. 86 | 87 | ```rust 88 | extern crate simulacrum; 89 | 90 | use simulacrum::*; 91 | 92 | create_mock_struct! { 93 | struct CoolTraitMock: { 94 | expect_foo("foo"); 95 | expect_goop("goop") bool => u32; 96 | // Note that we've used *const instead of & for shared references. 97 | expect_store("store") *const i64; 98 | } 99 | } 100 | 101 | impl CoolTrait for CoolTraitMock { 102 | fn foo(&self) { 103 | was_called!(self, "foo") 104 | } 105 | 106 | fn goop(&mut self, flag: bool) -> u32 { 107 | was_called!(self, "goop", (flag: bool) -> u32) 108 | } 109 | 110 | fn store(&self, val: &i64) { 111 | // Again note the use of *const instead of & for shared references. 112 | was_called!(self, "store", (val: *const i64)) 113 | } 114 | } 115 | 116 | ``` 117 | 118 | See [`macros_mid.rs`](https://github.com/pcsm/simulacrum/blob/master/simulacrum/examples/macros_mid.rs) for more examples of how to mock out different types of methods with the Mid-Level Macros. 119 | 120 | ## Manual Example 121 | 122 | Create your mock objects manually for ultimate control. With this API, you can even create mocks to stand in for structs instead of traits. For a detailed example of the API, see the [`manual.rs`](https://github.com/pcsm/simulacrum/blob/master/simulacrum/examples/manual.rs) example. 123 | 124 | ```rust 125 | extern crate simulacrum; 126 | 127 | use simulacrum::*; 128 | 129 | trait CoolTrait { 130 | fn foo(&self); 131 | fn goop(&mut self, flag: bool) -> u32; 132 | } 133 | 134 | pub struct CoolTraitMock { 135 | e: Expectations 136 | } 137 | 138 | impl CoolTraitMock { 139 | pub fn new() -> Self { 140 | Self { 141 | e: Expectations::new() 142 | } 143 | } 144 | 145 | pub fn then(&mut self) -> &mut Self { 146 | self.e.then(); 147 | self 148 | } 149 | 150 | pub fn expect_foo(&mut self) -> Method<(), ()> { 151 | self.e.expect::<(), ()>("foo") 152 | } 153 | 154 | pub fn expect_goop(&mut self) -> Method { 155 | self.e.expect::("goop") 156 | } 157 | } 158 | 159 | impl CoolTrait for CoolTraitMock { 160 | fn foo(&self) { 161 | self.e.was_called::<(), ()>("foo", ()) 162 | } 163 | 164 | fn goop(&mut self, flag: bool) -> u32 { 165 | self.e.was_called_returning::("goop", flag) 166 | } 167 | } 168 | ``` -------------------------------------------------------------------------------- /simulacrum/examples/macros_high.rs: -------------------------------------------------------------------------------- 1 | // This is the highest-level macro available in stable Simulacrum. 2 | // 3 | // It creates a Mock struct and impls a Trait - all you have to do is copy over 4 | // the trait interface and annotate it. 5 | // 6 | // Note that if you want additional control, like not mocking certain parameters, 7 | // you should use the mid-level macros shown in the `macros_mid.rs` example. For 8 | // even more control, you can use the `simulacrum` crate directly. 9 | 10 | extern crate simulacrum; 11 | 12 | use simulacrum::*; 13 | 14 | trait CoolTrait { 15 | // Shared self 16 | fn foo(&self); 17 | 18 | // Mutable self 19 | fn bar(&mut self); 20 | 21 | // One parameter and returning a value 22 | fn goop(&mut self, flag: bool) -> u32; 23 | 24 | // Multiple parameters 25 | fn zing(&self, first: i32, second: bool); 26 | 27 | // Static reference 28 | fn boop(&self, name: &'static str); 29 | 30 | // Shared reference 31 | fn store(&self, val: &i64); 32 | 33 | // Mutable reference 34 | fn toggle(&self, bit: &mut bool); 35 | 36 | // Unsafe 37 | unsafe fn ohno(&self); 38 | } 39 | 40 | create_mock! { 41 | impl CoolTrait for CoolTraitMock (self) { 42 | expect_foo("foo"): 43 | fn foo(&self); 44 | 45 | expect_bar("bar"): 46 | fn bar(&mut self); 47 | 48 | expect_goop("goop"): 49 | fn goop(&mut self, flag: bool) -> u32; 50 | 51 | expect_zing("zing"): 52 | fn zing(&self, first: i32, second: bool); 53 | 54 | // &'static params are a special case - other lifetimes can't be mocked. 55 | expect_boop("boop"): 56 | fn boop(&self, name: &'static str); 57 | 58 | // & params are mocked as *const and &mut are mocked as *mut. 59 | expect_store("store"): 60 | fn store(&self, val: &i64); 61 | 62 | expect_toggle("toggle"): 63 | fn toggle(&self, bit: &mut bool); 64 | 65 | expect_ohno("ohno"): 66 | unsafe fn ohno(&self); 67 | } 68 | } 69 | 70 | fn main() { 71 | // Create a mock object 72 | let mut m = CoolTraitMock::new(); 73 | 74 | // Set up expectations for it 75 | m.expect_bar().called_never(); 76 | m.expect_foo().called_once(); 77 | m.then().expect_goop().called_once().with(true).returning(|_| 5); 78 | m.then().expect_zing().called_once().with(params!(13, false)); 79 | m.expect_boop().called_times(2); 80 | m.expect_store().called_once().with(deref(777)); 81 | m.expect_toggle().called_once().with(deref(true)) 82 | .modifying(|&mut arg| { unsafe { *arg = false } }); 83 | m.expect_ohno().called_once(); 84 | 85 | // Execute test code 86 | m.foo(); 87 | assert_eq!(m.goop(true), 5); 88 | m.zing(13, false); 89 | m.boop("hey"); 90 | m.boop("yo"); 91 | m.store(&777); 92 | let mut b = true; 93 | m.toggle(&mut b); 94 | assert_eq!(b, false); 95 | unsafe { 96 | m.ohno(); 97 | } 98 | 99 | // When the mock object is dropped, its expectations will be evaluated 100 | } -------------------------------------------------------------------------------- /simulacrum/examples/macros_mid.rs: -------------------------------------------------------------------------------- 1 | // Here we'll use the mid-level macros available in Simulacrum. 2 | // 3 | // There is one to create the Mock struct itself, and one to facilitate 4 | // marking methods as called when implementing the trait for the Mock struct. 5 | // 6 | // Note that if you want more control over your Mock object, you should look into 7 | // the low-level API available in the `simulacrum` crate. 8 | // 9 | // You can see that & and &mut parameters are mocked as *const and *mut. Also note 10 | // that the *mut parameter uses `was_called!()` with a `()` return type and 11 | // `.returning()` to have its return behavior specified. 12 | 13 | extern crate simulacrum; 14 | 15 | use simulacrum::*; 16 | 17 | trait CoolTrait { 18 | // Shared self 19 | fn foo(&self); 20 | 21 | // Mutable self 22 | fn bar(&mut self); 23 | 24 | // One parameter and returning a value 25 | fn goop(&mut self, flag: bool) -> u32; 26 | 27 | // Multiple parameters 28 | fn zing(&self, first: i32, second: bool); 29 | 30 | // Static reference 31 | fn boop(&self, name: &'static str); 32 | 33 | // Shared reference 34 | fn store(&self, val: &i64); 35 | 36 | // Mutable reference 37 | fn toggle(&self, bit: &mut bool); 38 | } 39 | 40 | create_mock_struct! { 41 | struct CoolTraitMock: { 42 | expect_foo("foo"); 43 | expect_bar("bar"); 44 | expect_goop("goop") bool => u32; 45 | expect_zing("zing") (i32, bool); 46 | expect_boop("boop") &'static str; 47 | expect_store("store") *const i64; 48 | expect_toggle("toggle") *mut bool; 49 | } 50 | } 51 | 52 | impl CoolTrait for CoolTraitMock { 53 | fn foo(&self) { 54 | was_called!(self, "foo") 55 | } 56 | 57 | fn bar(&mut self) { 58 | was_called!(self, "bar") 59 | } 60 | 61 | fn goop(&mut self, flag: bool) -> u32 { 62 | was_called!(self, "goop", (flag: bool) -> u32) 63 | } 64 | 65 | fn zing(&self, first: i32, second: bool) { 66 | was_called!(self, "zing", (first: i32, second: bool)) 67 | } 68 | 69 | fn boop(&self, name: &'static str) { 70 | was_called!(self, "boop", (name: &'static str)) 71 | } 72 | 73 | fn store(&self, val: &i64) { 74 | was_called!(self, "store", (val: *const i64)) 75 | } 76 | 77 | fn toggle(&self, bit: &mut bool) { 78 | was_called!(self, "toggle", (bit: *mut bool)) 79 | } 80 | } 81 | 82 | fn main() { 83 | // Create a mock object 84 | let mut m = CoolTraitMock::new(); 85 | 86 | // Set up expectations for it 87 | m.expect_bar().called_never(); 88 | m.expect_foo().called_once(); 89 | m.then().expect_goop().called_once().with(true).returning(|_| 5); 90 | m.then().expect_zing().called_once().with(params!(13, false)); 91 | m.expect_boop().called_times(2); 92 | m.expect_store().called_once().with(deref(777)); 93 | m.expect_toggle().called_once().with(deref(true)) 94 | .modifying(|&mut arg| { unsafe { *arg = false } }); 95 | 96 | // Execute test code 97 | m.foo(); 98 | assert_eq!(m.goop(true), 5); 99 | m.zing(13, false); 100 | m.boop("hey"); 101 | m.boop("yo"); 102 | m.store(&777); 103 | let mut b = true; 104 | m.toggle(&mut b); 105 | assert_eq!(b, false); 106 | 107 | // When the mock object is dropped, its expectations will be evaluated 108 | } -------------------------------------------------------------------------------- /simulacrum/examples/manual.rs: -------------------------------------------------------------------------------- 1 | // This example demonstrates everything that can be done with Simulacrum at the 2 | // at the lowest level API. 3 | 4 | extern crate simulacrum; 5 | 6 | use simulacrum::*; 7 | 8 | trait CoolTrait { 9 | // Shared self 10 | fn foo(&self); 11 | 12 | // Mutable self 13 | fn bar(&mut self); 14 | 15 | // One parameter and returning a value 16 | fn goop(&mut self, flag: bool) -> u32; 17 | 18 | // Multiple parameters 19 | fn zing(&self, first: i32, second: bool); 20 | 21 | // Static reference 22 | fn boop(&self, name: &'static str); 23 | 24 | // Shared reference 25 | fn store(&self, val: &i64); 26 | 27 | // Mutable reference 28 | fn toggle(&self, bit: &mut bool); 29 | } 30 | 31 | pub struct CoolTraitMock { 32 | e: Expectations 33 | } 34 | 35 | impl CoolTraitMock { 36 | pub fn new() -> Self { 37 | Self { 38 | e: Expectations::new() 39 | } 40 | } 41 | 42 | pub fn then(&mut self) -> &mut Self { 43 | self.e.then(); 44 | self 45 | } 46 | 47 | pub fn expect_foo(&mut self) -> Method<(), ()> { 48 | self.e.expect::<(), ()>("foo") 49 | } 50 | 51 | pub fn expect_bar(&mut self) -> Method<(), ()> { 52 | self.e.expect::<(), ()>("bar") 53 | } 54 | 55 | pub fn expect_goop(&mut self) -> Method { 56 | self.e.expect::("goop") 57 | } 58 | 59 | pub fn expect_zing(&mut self) -> Method<(i32, bool), ()> { 60 | self.e.expect::<(i32, bool), ()>("zing") 61 | } 62 | 63 | pub fn expect_boop(&mut self) -> Method<&'static str, ()> { 64 | self.e.expect::<&'static str, ()>("boop") 65 | } 66 | 67 | pub fn expect_store(&mut self) -> Method<*const i64, ()> { 68 | self.e.expect::<*const i64, ()>("store") 69 | } 70 | 71 | pub fn expect_toggle(&mut self) -> Method<*mut bool, ()> { 72 | self.e.expect::<*mut bool, ()>("toggle") 73 | } 74 | } 75 | 76 | impl CoolTrait for CoolTraitMock { 77 | fn foo(&self) { 78 | self.e.was_called::<(), ()>("foo", ()) 79 | } 80 | 81 | fn bar(&mut self) { 82 | self.e.was_called::<(), ()>("bar", ()) 83 | } 84 | 85 | fn goop(&mut self, flag: bool) -> u32 { 86 | self.e.was_called_returning::("goop", flag) 87 | } 88 | 89 | fn zing(&self, first: i32, second: bool) { 90 | self.e.was_called::<(i32, bool), ()>("zing", (first, second)) 91 | } 92 | 93 | fn boop(&self, name: &'static str) { 94 | self.e.was_called::<&'static str, ()>("boop", name) 95 | } 96 | 97 | fn store(&self, val: &i64) { 98 | self.e.was_called::<*const i64, ()>("store", val) 99 | } 100 | 101 | fn toggle(&self, bit: &mut bool) { 102 | self.e.was_called::<*mut bool, ()>("toggle", bit) 103 | } 104 | } 105 | 106 | fn main() { 107 | // Create a mock object 108 | let mut m = CoolTraitMock::new(); 109 | 110 | // Set up expectations for it 111 | m.expect_bar().called_never(); 112 | m.expect_foo().called_once(); 113 | m.then().expect_goop().called_once().with(true).returning(|_| 5); 114 | m.then().expect_zing().called_once().with(params!(13, false)); 115 | m.expect_boop().called_times(2); 116 | m.expect_store().called_once().with(deref(777)); 117 | m.expect_toggle().called_once().with(deref(true)) 118 | .modifying(|&mut arg| { unsafe { *arg = false } }); 119 | 120 | // Execute test code 121 | m.foo(); 122 | assert_eq!(m.goop(true), 5); 123 | m.zing(13, false); 124 | m.boop("hey"); 125 | m.boop("yo"); 126 | m.store(&777); 127 | let mut b = true; 128 | m.toggle(&mut b); 129 | assert_eq!(b, false); 130 | 131 | // When the mock object is dropped, its expectations will be evaluated 132 | } -------------------------------------------------------------------------------- /simulacrum/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Minimal library for creating mock objects by hand using stable Rust. 2 | //! 3 | //! This crate is a facade that just re-exports any crates necessary to both 4 | //! create and use mock objects in Simulacrum. 5 | 6 | extern crate simulacrum_macros; 7 | extern crate simulacrum_mock; 8 | extern crate simulacrum_user; 9 | 10 | // pub use * from the crate root re-exports macros since Rust 1.15 11 | pub use simulacrum_macros::*; 12 | pub use simulacrum_mock::*; 13 | pub use simulacrum_user::*; -------------------------------------------------------------------------------- /simulacrum_auto/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /simulacrum_auto/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simulacrum_auto" 3 | version = "0.1.0" 4 | authors = ["Jason Grlicky "] 5 | description = "Procedural macro to automatically create mock objects from traits." 6 | keywords = ["mock", "mocking", "test", "testing", "TDD"] 7 | categories = ["development-tools::testing"] 8 | readme = "README.md" 9 | documentation = "https://docs.rs/simulacrum_auto" 10 | repository = "https://github.com/pcsm/simulacrum/tree/master/simulacrum_auto" 11 | license = "MIT" 12 | 13 | [badges] 14 | maintenance = { status = "experimental" } 15 | 16 | [dependencies] 17 | quote = "0.3" 18 | simulacrum = "0.3.0" 19 | syn = { version = "0.11.11", features = ["full"] } 20 | 21 | [lib] 22 | proc-macro = true -------------------------------------------------------------------------------- /simulacrum_auto/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Paracosm LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /simulacrum_auto/README.md: -------------------------------------------------------------------------------- 1 | Simulacrum Auto 2 | ================================================================== 3 | 4 | This library is not yet fit for public consumption. -------------------------------------------------------------------------------- /simulacrum_auto/examples/example.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro)] 2 | 3 | extern crate simulacrum; 4 | extern crate simulacrum_auto; 5 | 6 | use simulacrum::*; 7 | use simulacrum_auto::simulacrum; 8 | 9 | #[simulacrum] 10 | trait CoolTrait { 11 | /// Shared self - Also demonstrates doc comments 12 | fn foo(&self); 13 | 14 | // Mutable self 15 | fn bar(&mut self); 16 | 17 | // One parameter and returning a value 18 | fn goop(&mut self, flag: bool) -> u32; 19 | 20 | // Multiple parameters 21 | fn zing(&self, first: i32, second: bool); 22 | 23 | // Static reference 24 | fn boop(&self, name: &'static str); 25 | 26 | // Shared reference 27 | fn store(&self, val: &i64); 28 | 29 | // Mutable reference 30 | fn toggle(&self, bit: &mut bool); 31 | } 32 | 33 | fn main() { 34 | // Set up expectations 35 | let mut m = CoolTraitMock::new(); 36 | m.expect_bar().called_never(); 37 | m.expect_foo().called_once(); 38 | m.then().expect_goop().called_once().with(true).returning(|_| 5); 39 | m.then().expect_zing().called_once().with(params!(13, false)); 40 | m.expect_boop().called_times(2); 41 | m.expect_store().called_once().with(deref(777)); 42 | m.expect_toggle().called_once().with(deref(true)) 43 | .modifying(|&mut arg| { unsafe { *arg = false } }); 44 | 45 | // Execute test code 46 | m.foo(); 47 | assert_eq!(m.goop(true), 5); 48 | m.zing(13, false); 49 | m.boop("hey"); 50 | m.boop("yo"); 51 | m.store(&777); 52 | let mut b = true; 53 | m.toggle(&mut b); 54 | assert_eq!(b, false); 55 | 56 | // When the Expectations struct is dropped, each of its expectations will be evaluated 57 | } -------------------------------------------------------------------------------- /simulacrum_auto/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro)] 2 | #![recursion_limit="128"] 3 | 4 | extern crate proc_macro; 5 | #[macro_use] extern crate quote; 6 | extern crate simulacrum; 7 | extern crate syn; 8 | 9 | use proc_macro::TokenStream; 10 | use quote::ToTokens; 11 | 12 | use std::str::FromStr; 13 | 14 | struct Method { 15 | ident: syn::Ident, 16 | original_item: syn::TraitItem, 17 | sig: syn::MethodSig 18 | } 19 | 20 | #[proc_macro_attribute] 21 | pub fn simulacrum(_args: TokenStream, input: TokenStream) -> TokenStream { 22 | // Generate the Rust code string to use as the output 23 | let output = simulacrum_internal(&input.to_string()); 24 | 25 | // Turn that Rust back into a token stream 26 | TokenStream::from_str(output.as_str()).unwrap() 27 | } 28 | 29 | fn simulacrum_internal(input: &str) -> quote::Tokens { 30 | // Generate the AST from the token stream we were given 31 | let item = syn::parse_item(&input.to_string()).unwrap(); 32 | 33 | // Generate Mock struct name 34 | let ident = &item.ident; 35 | let name = quote! { #ident }; 36 | let name = syn::Ident::new(format!("{}Mock", name.as_str())); 37 | 38 | // Get method information 39 | let trait_items = get_trait_items(&item); 40 | let methods = gather_trait_methods(&trait_items); 41 | 42 | // Generate what is required for the create_mock! macro 43 | let annotations = generate_annotations(&methods); 44 | let original_methods = gather_original_methods(&methods); 45 | 46 | // Generate fn expect_blah() -> Method methods 47 | // let expects = generate_expects(&methods); 48 | 49 | // Generate blah() stub methods 50 | // let stubs = generate_stubs(&methods); 51 | 52 | let output = quote! { 53 | #item 54 | 55 | create_mock! { 56 | impl #ident for #name (self) { 57 | #( 58 | #annotations 59 | #original_methods 60 | )* 61 | } 62 | } 63 | }; 64 | 65 | output 66 | } 67 | 68 | fn get_trait_items(item: &syn::Item) -> Vec { 69 | match item.node { 70 | syn::ItemKind::Trait(_unsafety, ref _generics, ref _ty_param_bound, ref items) => { 71 | items.clone() 72 | }, 73 | _ => vec![].clone() 74 | } 75 | } 76 | 77 | fn gather_trait_methods(trait_items: &Vec) -> Vec { 78 | let mut result = Vec::new(); 79 | for item in trait_items { 80 | match item.node { 81 | syn::TraitItemKind::Method(ref sig, _) => { 82 | let m = Method { 83 | ident: item.ident.clone(), 84 | original_item: item.clone(), 85 | sig: sig.clone() 86 | }; 87 | result.push(m); 88 | }, 89 | _ => { } 90 | } 91 | } 92 | result 93 | } 94 | 95 | fn generate_annotations(methods: &Vec) -> Vec { 96 | let mut result = Vec::new(); 97 | for method in methods { 98 | let ident = &method.ident; 99 | let ident_tokens = quote!{ #ident }; 100 | let ident_str = ident_tokens.as_str(); 101 | let name = expectify_method_name(ident); 102 | // let otype = generate_output_type(&method.sig.decl.output); 103 | // let ituple = generate_input_tuple(&method.sig.decl.inputs); 104 | let annotation = quote! { 105 | #name(#ident_str): 106 | }; 107 | result.push(annotation); 108 | } 109 | result 110 | } 111 | 112 | // fn generate_expects(methods: &Vec) -> quote::Tokens { 113 | // let mut result = quote::Tokens::new(); 114 | // for method in methods { 115 | // let ident = &method.ident; 116 | // let ident_tokens = quote!{ #ident }; 117 | // let ident_str = ident_tokens.as_str(); 118 | // let name = expectify_method_name(ident); 119 | // let otype = generate_output_type(&method.sig.decl.output); 120 | // let ituple = generate_input_tuple(&method.sig.decl.inputs); 121 | // let expect_method = quote! { 122 | // pub fn #name(&mut self) -> Method<#ituple, #otype> { 123 | // self.e.expect::<#ituple, #otype>(#ident_str) 124 | // } 125 | // }; 126 | // result.append(expect_method); 127 | // } 128 | // result 129 | // } 130 | 131 | fn expectify_method_name(ident: &syn::Ident) -> syn::Ident { 132 | syn::Ident::new(format!("expect_{}", ident)) 133 | } 134 | 135 | // fn generate_output_type(output: &syn::FunctionRetTy) -> quote::Tokens { 136 | // match output { 137 | // &syn::FunctionRetTy::Default => quote! { () }, 138 | // &syn::FunctionRetTy::Ty(ref ty) => quote! { #ty } 139 | // } 140 | // } 141 | 142 | // fn generate_input_tuple(input: &Vec) -> quote::Tokens { 143 | // let types = gather_captured_arg_types(input); 144 | 145 | // let mut result = quote::Tokens::new(); 146 | // let num_types = types.len(); 147 | // match num_types { 148 | // 1 => { 149 | // let first = types.first().unwrap(); 150 | // first.to_tokens(&mut result); 151 | // }, 152 | // _ => { 153 | // result.append("("); 154 | // let mut num_added = 0; 155 | // for ty in types { 156 | // ty.to_tokens(&mut result); 157 | // num_added += 1; 158 | 159 | // if num_added < num_types { 160 | // result.append(","); 161 | // } 162 | // } 163 | // result.append(")"); 164 | // } 165 | // } 166 | // result 167 | // } 168 | 169 | // fn gather_captured_arg_types(input: &Vec) -> Vec { 170 | // let mut result = Vec::new(); 171 | // for arg in input { 172 | // match arg { 173 | // &syn::FnArg::Captured(ref _pattern, ref ty) => { 174 | // result.push(ty.clone()); 175 | // }, 176 | // _ => { } 177 | // } 178 | // } 179 | // result 180 | // } 181 | 182 | fn gather_original_methods(methods: &Vec) -> Vec { 183 | let mut result = Vec::new(); 184 | for method in methods { 185 | let to_output = &method.original_item; 186 | let tokens = quote! { 187 | #to_output 188 | }; 189 | 190 | // Push the tokens onto our result Vec 191 | result.push(tokens); 192 | } 193 | result 194 | } 195 | 196 | // fn generate_stubs(methods: &Vec) -> quote::Tokens { 197 | // let mut result = quote::Tokens::new(); 198 | // for method in methods { 199 | // let ident = &method.ident; 200 | // let ident_tokens = quote!{ #ident }; 201 | // let ident_str = ident_tokens.as_str(); 202 | // let otype = generate_output_type(&method.sig.decl.output); 203 | // let ituple = generate_input_tuple(&method.sig.decl.inputs); 204 | // let itypes = generate_input_types(&method.sig.decl.inputs); 205 | // // TODO: generate method sig tokens 206 | // let mut method_sig_tokens = quote::Tokens::new(); 207 | // // TODO: generate method sig tokens 208 | // let mut body_tokens = quote::Tokens::new(); 209 | // let stub_method = quote! { 210 | // #method_sig_tokens { 211 | // #body_tokens 212 | // } 213 | // }; 214 | // result.append(stub_method); 215 | // } 216 | // result 217 | // } 218 | 219 | // fn generate_input_types(input: &Vec) -> quote::Tokens { 220 | // let mut result = quote::Tokens::new(); 221 | // // for arg in input { 222 | // // match arg { 223 | // // &syn::FnArg::Captured(ref _pattern, ref ty) => { 224 | // // result.push(ty.clone()); 225 | // // }, 226 | // // _ => { } 227 | // // } 228 | // // } 229 | // result 230 | // } 231 | 232 | #[cfg(test)] 233 | mod tests { 234 | use super::*; 235 | 236 | // #[test] 237 | // // Test for fn blah() 238 | // fn test_generate_input_tuple_none() { 239 | // let input = Vec::new(); 240 | 241 | // let expected = quote! { () }; 242 | 243 | // let result = generate_input_tuple(&input); 244 | 245 | // assert_eq!(expected, result); 246 | // } 247 | 248 | // #[test] 249 | // // Test for fn blah(arg: i32) 250 | // fn test_generate_input_tuple_one_captured() { 251 | // let mut input = Vec::new(); 252 | // // arg: i32 253 | // let binding_mode = syn::BindingMode::ByValue(syn::Mutability::Immutable); 254 | // let ident = syn::parse_ident("arg").unwrap(); 255 | // let pattern = syn::Pat::Ident(binding_mode, ident, None); 256 | // let ty = syn::parse_type("i32").unwrap(); 257 | // let arg = syn::FnArg::Captured(pattern, ty); 258 | // input.push(arg); 259 | 260 | // let expected = quote! { i32 }; 261 | 262 | // let result = generate_input_tuple(&input); 263 | 264 | // assert_eq!(expected, result); 265 | // } 266 | 267 | // #[test] 268 | // // Test for fn blah(&self) 269 | // fn test_generate_input_tuple_self_ref() { 270 | // let mut input = Vec::new(); 271 | // // &self 272 | // let arg = syn::FnArg::SelfRef(None, syn::Mutability::Immutable); 273 | // input.push(arg); 274 | 275 | // let expected = quote! { () }; 276 | 277 | // let result = generate_input_tuple(&input); 278 | 279 | // assert_eq!(expected, result); 280 | // } 281 | 282 | // #[test] 283 | // // Test for fn blah(&self, arg: i32) 284 | // fn test_generate_input_tuple_self_ref_one_captured() { 285 | // let mut input = Vec::new(); 286 | // // &self 287 | // let arg = syn::FnArg::SelfRef(None, syn::Mutability::Immutable); 288 | // input.push(arg); 289 | // // arg: i32 290 | // let binding_mode = syn::BindingMode::ByValue(syn::Mutability::Immutable); 291 | // let ident = syn::parse_ident("arg").unwrap(); 292 | // let pattern = syn::Pat::Ident(binding_mode, ident, None); 293 | // let ty = syn::parse_type("i32").unwrap(); 294 | // let arg = syn::FnArg::Captured(pattern, ty); 295 | // input.push(arg); 296 | 297 | // let expected = quote! { i32 }; 298 | 299 | // let result = generate_input_tuple(&input); 300 | 301 | // assert_eq!(expected, result); 302 | // } 303 | 304 | // #[test] 305 | // // Test for fn blah(&self, arg1: i32, arg2: bool) 306 | // fn test_generate_input_tuple_self_ref_two_captured() { 307 | // let mut input = Vec::new(); 308 | // // &self 309 | // let arg = syn::FnArg::SelfRef(None, syn::Mutability::Immutable); 310 | // input.push(arg); 311 | // // arg1: i32 312 | // let binding_mode = syn::BindingMode::ByValue(syn::Mutability::Immutable); 313 | // let ident = syn::parse_ident("arg1").unwrap(); 314 | // let pattern = syn::Pat::Ident(binding_mode, ident, None); 315 | // let ty = syn::parse_type("i32").unwrap(); 316 | // let arg = syn::FnArg::Captured(pattern, ty); 317 | // input.push(arg); 318 | // // arg2: bool 319 | // let binding_mode = syn::BindingMode::ByValue(syn::Mutability::Immutable); 320 | // let ident = syn::parse_ident("arg2").unwrap(); 321 | // let pattern = syn::Pat::Ident(binding_mode, ident, None); 322 | // let ty = syn::parse_type("bool").unwrap(); 323 | // let arg = syn::FnArg::Captured(pattern, ty); 324 | // input.push(arg); 325 | 326 | // let expected = quote! { ( i32, bool ) }; 327 | 328 | // let result = generate_input_tuple(&input); 329 | 330 | // assert_eq!(expected, result); 331 | // } 332 | 333 | #[test] 334 | fn it_works() { 335 | let input = quote! { 336 | pub trait CoolTrait { 337 | /// Shared self - Also, Doc comments 338 | fn foo(&self); 339 | 340 | // Mutable self 341 | fn bar(&mut self); 342 | 343 | // One parameter and returning a value 344 | fn goop(&mut self, flag: bool) -> u32; 345 | 346 | // Multiple parameters 347 | fn zing(&self, first: i32, second: &mut bool); 348 | 349 | // Note: It doesn't work with references yet! 350 | // fn boop(&self, name: &'static str) 351 | } 352 | }; 353 | 354 | let expected = quote! { 355 | pub trait CoolTrait { 356 | /// Shared self - Also, Doc comments 357 | fn foo(&self); 358 | fn bar(&mut self); 359 | fn goop(&mut self, flag: bool) -> u32; 360 | fn zing(&self, first: i32, second: &mut bool); 361 | } 362 | 363 | create_mock! { 364 | impl CoolTrait for CoolTraitMock (self) { 365 | expect_foo("foo"): 366 | fn foo(&self); 367 | 368 | expect_bar("bar"): 369 | fn bar(&mut self); 370 | 371 | expect_goop("goop"): 372 | fn goop(&mut self, flag: bool) -> u32; 373 | 374 | expect_zing("zing"): 375 | fn zing(&self, first: i32, second: &mut bool); 376 | } 377 | } 378 | }; 379 | 380 | let result = simulacrum_internal(input.as_str()); 381 | 382 | assert_eq!(expected, result); 383 | } 384 | 385 | // #[test] 386 | // #[ignore] 387 | // fn it_works() { 388 | // let input = quote! { 389 | // pub trait CoolTrait { 390 | // // Shared self 391 | // fn foo(&self); 392 | 393 | // // Mutable self 394 | // fn bar(&mut self); 395 | 396 | // // One parameter and returning a value 397 | // fn goop(&mut self, flag: bool) -> u32; 398 | 399 | // // Multiple parameters 400 | // fn zing(&self, first: i32, second: bool); 401 | 402 | // // Note: It doesn't work with references yet! 403 | // // fn boop(&self, name: &'static str) 404 | // } 405 | // }; 406 | 407 | // let expected = quote! { 408 | // pub trait CoolTrait { 409 | // fn foo(&self); 410 | // fn bar(&mut self); 411 | // fn goop(&mut self, flag: bool) -> u32; 412 | // fn zing(&self, first: i32, second: bool); 413 | // } 414 | 415 | // pub struct CoolTraitMock { 416 | // e: Expectations 417 | // } 418 | 419 | // impl CoolTraitMock { 420 | // pub fn new() -> Self { 421 | // Self { 422 | // e: Expectations::new() 423 | // } 424 | // } 425 | 426 | // pub fn then(&mut self) -> &mut Self { 427 | // self.e.then(); 428 | // self 429 | // } 430 | 431 | // pub fn expect_foo(&mut self) -> Method<(), ()> { 432 | // self.e.expect::<(), ()>("foo") 433 | // } 434 | 435 | // pub fn expect_bar(&mut self) -> Method<(), ()> { 436 | // self.e.expect::<(), ()>("bar") 437 | // } 438 | 439 | // pub fn expect_goop(&mut self) -> Method { 440 | // self.e.expect::("goop") 441 | // } 442 | 443 | // pub fn expect_zing(&mut self) -> Method<(i32, bool), ()> { 444 | // self.e.expect::<(i32, bool), ()>("zing") 445 | // } 446 | // } 447 | 448 | // impl CoolTrait for CoolTraitMock { 449 | // fn foo(&self) { 450 | // self.e.was_called::<(), ()>("foo", ()) 451 | // } 452 | 453 | // fn bar(&mut self) { 454 | // self.e.was_called::<(), ()>("bar", ()) 455 | // } 456 | 457 | // fn goop(&mut self, flag: bool) -> u32 { 458 | // self.e.was_called_returning::("goop", flag) 459 | // } 460 | 461 | // fn zing(&self, first: i32, second: bool) { 462 | // self.e.was_called::<(i32, bool), ()>("zing", (first, second)) 463 | // } 464 | // } 465 | // }; 466 | 467 | // let result = simulacrum_internal(input.as_str()); 468 | 469 | // assert_eq!(expected, result); 470 | // } 471 | } 472 | -------------------------------------------------------------------------------- /simulacrum_macros/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /simulacrum_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simulacrum_macros" 3 | version = "0.3.1" 4 | authors = ["Jason Grlicky "] 5 | description = "Macros to simplify creating mock objects with Simulacrum." 6 | keywords = ["mock", "mocking", "test", "testing", "TDD"] 7 | categories = ["development-tools::testing"] 8 | readme = "README.md" 9 | documentation = "https://docs.rs/simulacrum_macros" 10 | repository = "https://github.com/pcsm/simulacrum/tree/master/simulacrum_macros" 11 | license = "MIT" 12 | 13 | [badges] 14 | maintenance = { status = "actively-developed" } 15 | 16 | [dependencies] 17 | simulacrum_mock = "0.1.0" 18 | -------------------------------------------------------------------------------- /simulacrum_macros/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Paracosm LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /simulacrum_macros/README.md: -------------------------------------------------------------------------------- 1 | Simulacrum Macros [![Docs](https://docs.rs/simulacrum_macros/badge.svg)](https://docs.rs/simulacrum_macros) [![Crates.io](https://img.shields.io/crates/v/simulacrum_macros.svg)](https://crates.io/crates/simulacrum_macros) 2 | ================================================================== 3 | 4 | These macros ease the creation of mock objects using [`simulacrum`](https://github.com/pcsm/simulacrum/tree/master/simulacrum). See that crate's README for details. -------------------------------------------------------------------------------- /simulacrum_macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate simulacrum_mock; 2 | 3 | pub use simulacrum_mock::{Expectations, Method}; 4 | 5 | /// Use this macro to create an `.expect_METHOD_NAME()` method. 6 | #[macro_export] 7 | macro_rules! create_expect_method { 8 | ($name:ident($key:expr) $inputs:ty => $output:ty) => { 9 | #[allow(non_snake_case)] 10 | pub fn $name(&mut self) -> $crate::Method<$inputs, $output> { 11 | self.e.expect::<$inputs, $output>($key) 12 | } 13 | }; 14 | ($name:ident($key:expr) $inputs:ty) => { 15 | create_expect_method!($name($key) $inputs => ()); 16 | }; 17 | ($name:ident($key:expr)) => { 18 | create_expect_method!($name($key) () => ()); 19 | }; 20 | } 21 | 22 | /// Use this macro to do a `self.e.was_called` `self.e.was_called_returning` with 23 | /// a shorter interface. 24 | #[macro_export] 25 | macro_rules! was_called { 26 | ($self_:ident, $key:expr, $sig:tt -> $output:ty) => { 27 | #[allow(unused_parens)] 28 | $self_.e.was_called_returning:: ()), $output>($key, simulacrum_tuplefy!(name $sig -> ())) 29 | }; 30 | ($self_:ident, $key:expr, $sig:tt) => { 31 | #[allow(unused_parens)] 32 | $self_.e.was_called:: ()), ()>($key, simulacrum_tuplefy!(name $sig -> ())) 33 | }; 34 | ($self_:ident, $key:expr) => { 35 | #[allow(unused_parens)] 36 | $self_.e.was_called::<(), ()>($key, ()) 37 | }; 38 | } 39 | 40 | // Create an input tuple from a method signature tt. 41 | // Uses push-down accumulation pattern: 42 | // see https://danielkeep.github.io/tlborm/book/pat-push-down-accumulation.html 43 | #[macro_export] 44 | macro_rules! simulacrum_tuplefy { 45 | // Coerce a capture into a particular kind. 46 | // See https://danielkeep.github.io/tlborm/book/blk-ast-coercion.html 47 | (@as_ty $token:ty) => { $token }; 48 | (@as_expr $token:expr) => { $token }; 49 | 50 | // main - Strip off parentheses 51 | ($mode:tt ($($param:tt)*) -> ($($result:tt)*)) => { 52 | simulacrum_tuplefy!(@inner $mode ($($param)*) -> ()) 53 | }; 54 | 55 | // simulacrum_tuplefy - For each param, get the type. Ignore &self and &mut self. 56 | 57 | // If there are no params left, coerce the final result to a type with 58 | // parentheses around it. 59 | (@inner kind () -> ($($result:tt)*)) => { 60 | simulacrum_tuplefy!(@as_ty ( $($result)* )) 61 | }; 62 | (@inner name () -> ($($result:tt)*)) => { 63 | simulacrum_tuplefy!(@as_expr ( $($result)* )) 64 | }; 65 | 66 | // Ignore &self and &mut self. 67 | (@inner $mode:tt (& self) -> ($($result:tt)*)) => { 68 | simulacrum_tuplefy!( @inner $mode () -> ($($result)*) ) 69 | }; 70 | (@inner $mode:tt (& mut self) -> ($($result:tt)*)) => { 71 | simulacrum_tuplefy!( @inner $mode () -> ($($result)*) ) 72 | }; 73 | (@inner $mode:tt (& self, $($tail:tt)*) -> ($($result:tt)*)) => { 74 | simulacrum_tuplefy!( @inner $mode ($($tail)*) -> ($($result)*) ) 75 | }; 76 | (@inner $mode:tt (& mut self, $($tail:tt)*) -> ($($result:tt)*)) => { 77 | simulacrum_tuplefy!( @inner $mode ($($tail)*) -> ($($result)*) ) 78 | }; 79 | 80 | // Accept &'static params. 81 | (@inner kind ($name:ident: &'static $kind:ty) -> ($($result:tt)*)) => { 82 | simulacrum_tuplefy!( @inner kind () -> ($($result)* &'static $kind) ) 83 | }; 84 | (@inner kind ($name:ident: &'static $kind:ty, $($tail:tt)*) -> ($($result:tt)*)) => { 85 | simulacrum_tuplefy!( @inner kind ($($tail)*) -> ($($result)* &'static $kind,) ) 86 | }; 87 | 88 | // Convert & and &mut params to *const and *mut. 89 | (@inner kind ($name:ident: & $kind:ty) -> ($($result:tt)*)) => { 90 | simulacrum_tuplefy!( @inner kind () -> ($($result)* *const $kind) ) 91 | }; 92 | (@inner kind ($name:ident: & mut $kind:ty) -> ($($result:tt)*)) => { 93 | simulacrum_tuplefy!( @inner kind () -> ($($result)* *mut $kind) ) 94 | }; 95 | (@inner kind ($name:ident: & $kind:ty, $($tail:tt)*) -> ($($result:tt)*)) => { 96 | simulacrum_tuplefy!( @inner kind ($($tail)*) -> ($($result)* *const $kind,) ) 97 | }; 98 | (@inner kind ($name:ident: & mut $kind:ty, $($tail:tt)*) -> ($($result:tt)*)) => { 99 | simulacrum_tuplefy!( @inner kind ($($tail)*) -> ($($result)* *mut $kind,) ) 100 | }; 101 | 102 | // Get the type of the parameter and move on. 103 | (@inner kind ($name:ident: $kind:ty, $($tail:tt)*) -> ($($result:tt)*)) => { 104 | simulacrum_tuplefy!( @inner kind ($($tail)*) -> ($($result)* $kind,) ) 105 | }; 106 | (@inner kind ($name:ident: $kind:ty) -> ($($result:tt)*)) => { 107 | simulacrum_tuplefy!( @inner kind () -> ($($result)* $kind) ) 108 | }; 109 | 110 | // Get the name of the parameter and move on. 111 | (@inner name ($name:ident: $kind:ty, $($tail:tt)*) -> ($($result:tt)*)) => { 112 | simulacrum_tuplefy!( @inner name ($($tail)*) -> ($($result)* $name,) ) 113 | }; 114 | (@inner name ($name:ident: $kind:ty) -> ($($result:tt)*)) => { 115 | simulacrum_tuplefy!( @inner name () -> ($($result)* $name) ) 116 | }; 117 | } 118 | 119 | #[macro_export] 120 | macro_rules! create_mock_struct { 121 | (@create_expect_methods) => {}; 122 | (@create_expect_methods $name:ident($key:expr) $inputs:ty => $output:ty; $($tail:tt)*) => { 123 | create_expect_method!($name($key) $inputs => $output); 124 | create_mock_struct!(@create_expect_methods $($tail)*); 125 | }; 126 | (@create_expect_methods $name:ident($key:expr) $inputs:ty; $($tail:tt)*) => { 127 | create_expect_method!($name($key) $inputs); 128 | create_mock_struct!(@create_expect_methods $($tail)*); 129 | }; 130 | (@create_expect_methods $name:ident($key:expr); $($tail:tt)*) => { 131 | create_expect_method!($name($key)); 132 | create_mock_struct!(@create_expect_methods $($tail)*); 133 | }; 134 | (struct $name:ident: { 135 | $($methods:tt)* 136 | }) => { 137 | #[allow(non_snake_case)] 138 | pub struct $name { 139 | e: $crate::Expectations 140 | } 141 | 142 | #[allow(dead_code)] 143 | #[allow(non_snake_case)] 144 | impl $name { 145 | pub fn new() -> Self { 146 | Self { 147 | e: $crate::Expectations::new() 148 | } 149 | } 150 | 151 | pub fn then(&mut self) -> &mut Self { 152 | self.e.then(); 153 | self 154 | } 155 | 156 | create_mock_struct!(@create_expect_methods $($methods)*); 157 | } 158 | 159 | impl Default for $name { 160 | fn default() -> Self { 161 | Self::new() 162 | } 163 | } 164 | }; 165 | } 166 | 167 | #[macro_export] 168 | macro_rules! create_mock { 169 | // create_mock_struct 170 | (@create_mock_struct($mock_name:ident, ()) -> ($($result:tt)*)) => { 171 | create_mock_struct! { 172 | struct $mock_name: { 173 | $($result)* 174 | } 175 | } 176 | }; 177 | (@create_mock_struct 178 | ($mock_name:ident, ( 179 | $expect_name:ident($key:expr): 180 | fn $method_name:ident $sig:tt; 181 | $($tail:tt)* 182 | )) -> ($($result:tt)*) 183 | ) => { 184 | create_mock!(@create_mock_struct ($mock_name, ($($tail)*)) -> ( 185 | $($result)* 186 | $expect_name($key) simulacrum_tuplefy!(kind $sig -> ()); 187 | )); 188 | }; 189 | (@create_mock_struct 190 | ($mock_name:ident, ( 191 | $expect_name:ident($key:expr): 192 | fn $method_name:ident $sig:tt -> $output:ty; 193 | $($tail:tt)* 194 | )) -> ($($result:tt)*) 195 | ) => { 196 | create_mock!(@create_mock_struct ($mock_name, ($($tail)*)) -> ( 197 | $($result)* 198 | $expect_name($key) simulacrum_tuplefy!(kind $sig -> ()) => $output; 199 | )); 200 | }; 201 | (@create_mock_struct 202 | ($mock_name:ident, ( 203 | $expect_name:ident($key:expr): 204 | unsafe fn $method_name:ident $sig:tt; 205 | $($tail:tt)* 206 | )) -> ($($result:tt)*) 207 | ) => { 208 | create_mock!(@create_mock_struct ($mock_name, ($($tail)*)) -> ( 209 | $($result)* 210 | $expect_name($key) simulacrum_tuplefy!(kind $sig -> ()); 211 | )); 212 | }; 213 | (@create_mock_struct 214 | ($mock_name:ident, ( 215 | $expect_name:ident($key:expr): 216 | unsafe fn $method_name:ident $sig:tt -> $output:ty; 217 | $($tail:tt)* 218 | )) -> ($($result:tt)*) 219 | ) => { 220 | create_mock!(@create_mock_struct ($mock_name, ($($tail)*)) -> ( 221 | $($result)* 222 | $expect_name($key) simulacrum_tuplefy!(kind $sig -> ()) => $output; 223 | )); 224 | }; 225 | 226 | // create_stub_methods 227 | (@create_stub_methods ($self_:ident)) => {}; 228 | (@create_stub_methods ($self_:ident) 229 | $expect_name:ident($key:expr): 230 | fn $method_name:ident $sig:tt; 231 | $($tail:tt)* 232 | ) => { 233 | #[allow(warnings)] 234 | fn $method_name $sig { 235 | was_called!($self_, $key, $sig) 236 | } 237 | create_mock!(@create_stub_methods ($self_) $($tail)*); 238 | }; 239 | (@create_stub_methods ($self_:ident) 240 | $expect_name:ident($key:expr): 241 | fn $method_name:ident $sig:tt -> $output:ty; 242 | $($tail:tt)* 243 | ) => { 244 | #[allow(warnings)] 245 | fn $method_name $sig -> $output { 246 | was_called!($self_, $key, $sig -> $output) 247 | } 248 | create_mock!(@create_stub_methods ($self_) $($tail)*); 249 | }; 250 | (@create_stub_methods ($self_:ident) 251 | $expect_name:ident($key:expr): 252 | unsafe fn $method_name:ident $sig:tt; 253 | $($tail:tt)* 254 | ) => { 255 | #[allow(warnings)] 256 | unsafe fn $method_name $sig { 257 | was_called!($self_, $key, $sig) 258 | } 259 | create_mock!(@create_stub_methods ($self_) $($tail)*); 260 | }; 261 | (@create_stub_methods ($self_:ident) 262 | $expect_name:ident($key:expr): 263 | unsafe fn $method_name:ident $sig:tt -> $output:ty; 264 | $($tail:tt)* 265 | ) => { 266 | #[allow(warnings)] 267 | unsafe fn $method_name $sig -> $output { 268 | was_called!($self_, $key, $sig -> $output) 269 | } 270 | create_mock!(@create_stub_methods ($self_) $($tail)*); 271 | }; 272 | 273 | // main 274 | (impl $trait_name:ident for $mock_name:ident ($self_:ident) { 275 | $($method_info:tt)* 276 | }) => { 277 | create_mock!(@create_mock_struct ($mock_name, ($($method_info)*)) -> ()); 278 | 279 | impl $trait_name for $mock_name { 280 | create_mock!(@create_stub_methods ($self_) $($method_info)*); 281 | } 282 | }; 283 | } 284 | -------------------------------------------------------------------------------- /simulacrum_mock/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /simulacrum_mock/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simulacrum_mock" 3 | version = "0.1.0" 4 | authors = ["Jason Grlicky "] 5 | description = "Core functionality for creating mock objects with Simulacrum." 6 | keywords = ["mock", "mocking", "test", "testing", "TDD"] 7 | categories = ["development-tools::testing"] 8 | readme = "README.md" 9 | documentation = "https://docs.rs/simulacrum_mock" 10 | repository = "https://github.com/pcsm/simulacrum/tree/master/simulacrum_mock" 11 | license = "MIT" 12 | 13 | [badges] 14 | maintenance = { status = "actively-developed" } 15 | 16 | [dependencies] 17 | debugit = "0.1.0" 18 | handlebox = "0.3.0" 19 | simulacrum_shared = "0.1.0" 20 | 21 | [dev-dependencies] 22 | simulacrum_user = "0.1.0" -------------------------------------------------------------------------------- /simulacrum_mock/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Paracosm LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /simulacrum_mock/README.md: -------------------------------------------------------------------------------- 1 | Simulacrum Mock [![Docs](https://docs.rs/simulacrum_mock/badge.svg)](https://docs.rs/simulacrum_mock) [![Crates.io](https://img.shields.io/crates/v/simulacrum_mock.svg)](https://crates.io/crates/simulacrum_mock) 2 | ================================================================== 3 | 4 | Core functionality for creating mock objects with [`simulacrum`](https://github.com/pcsm/simulacrum/tree/master/simulacrum). See that crate's README for details. -------------------------------------------------------------------------------- /simulacrum_mock/src/constraint/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod result; 2 | pub mod stock; 3 | 4 | pub use self::result::*; 5 | 6 | /// A `Constraint` is a type that can be added to an `Expectation`. 7 | /// 8 | /// All `Constraint`s added to an `Expectation` must all pass in order for the 9 | /// `Expectation` to pass. 10 | pub trait Constraint { 11 | /// This constraint has been called with the given parameters. Update the 12 | /// 13 | /// Constraint state so that when `verify()` is called, it will return the 14 | /// correct result. 15 | #[allow(unused_variables)] 16 | fn handle_call(&mut self, params: &I) { } 17 | 18 | /// At the end of the test, see if the Constraint passed or failed. 19 | fn verify(&self) -> ConstraintResult; 20 | } 21 | 22 | // lol, it would be handy to have simulacrum here 23 | pub struct ConstraintMock { 24 | handle_call_expected: bool, 25 | handle_call_called: bool 26 | } 27 | 28 | impl ConstraintMock { 29 | pub fn new() -> Self { 30 | Self { 31 | handle_call_expected: false, 32 | handle_call_called: false 33 | } 34 | } 35 | 36 | pub fn expect_handle_call(&mut self) { 37 | self.handle_call_expected = true 38 | } 39 | } 40 | 41 | impl Constraint for ConstraintMock { 42 | fn handle_call(&mut self, _params: &I) { 43 | self.handle_call_called = true 44 | } 45 | 46 | fn verify(&self) -> ConstraintResult { 47 | Ok(()) 48 | } 49 | } 50 | 51 | impl Drop for ConstraintMock { 52 | /// All expectations will be verified when the mock object is dropped. 53 | fn drop(&mut self) { 54 | if self.handle_call_expected && !self.handle_call_called { 55 | panic!("handle_call was not called"); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /simulacrum_mock/src/constraint/result.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// The Error type is a message to be printed to the user. 4 | pub type ConstraintResult = Result<(), ConstraintError>; 5 | 6 | #[derive(Clone, Debug, PartialEq)] 7 | pub enum ConstraintError { 8 | AlwaysFail, 9 | CalledTooFewTimes(i64), 10 | CalledTooManyTimes(i64), 11 | CallNotExpected, 12 | Custom(String), // For custom constraints from users 13 | MismatchedParams(String, String), // Expected Message, Received Message 14 | } 15 | 16 | impl fmt::Display for ConstraintError { 17 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 18 | match self { 19 | &ConstraintError::AlwaysFail => { 20 | write!(f, "Expectation will always fail.") 21 | }, 22 | &ConstraintError::CalledTooFewTimes(times) => { 23 | write!(f, "Called {} times fewer than expected.", times) 24 | }, 25 | &ConstraintError::CalledTooManyTimes(times) => { 26 | write!(f, "Called {} times more than expected.", times) 27 | }, 28 | &ConstraintError::CallNotExpected => { 29 | write!(f, "Called when not expected.") 30 | }, 31 | &ConstraintError::Custom(ref msg) => { 32 | write!(f, "{}", msg) 33 | }, 34 | &ConstraintError::MismatchedParams(ref expected_msg, ref received_msg) => { 35 | write!(f, "Called with unexpected parameters:\n Expected: {}\n Received: {}", expected_msg, received_msg) 36 | }, 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /simulacrum_mock/src/constraint/stock/always.rs: -------------------------------------------------------------------------------- 1 | //! `Constraint`s that always pass or fail, for testing. 2 | 3 | use constraint::{Constraint, ConstraintError, ConstraintResult}; 4 | 5 | /// A `Constraint` that always passes. 6 | pub struct AlwaysPass; 7 | 8 | impl Constraint for AlwaysPass { 9 | fn verify(&self) -> ConstraintResult { 10 | Ok(()) 11 | } 12 | } 13 | 14 | /// A `Constraint` that always fails. 15 | pub struct AlwaysFail; 16 | 17 | impl Constraint for AlwaysFail { 18 | fn verify(&self) -> ConstraintResult { 19 | Err(ConstraintError::AlwaysFail) 20 | } 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn test_pass() { 29 | let c: AlwaysPass = AlwaysPass; 30 | 31 | let r = >::verify(&c); 32 | 33 | assert!(r.is_ok(), "Constraint should always pass"); 34 | } 35 | 36 | #[test] 37 | fn test_fail() { 38 | let c = AlwaysFail; 39 | 40 | let r = >::verify(&c); 41 | 42 | assert!(r.is_err(), "Constraint should always fail"); 43 | assert_eq!(r.unwrap_err(), ConstraintError::AlwaysFail, "Constraint should return the correct error"); 44 | } 45 | } -------------------------------------------------------------------------------- /simulacrum_mock/src/constraint/stock/mod.rs: -------------------------------------------------------------------------------- 1 | //! Types that impl `Constraint` that are included with Simulacrum. 2 | 3 | pub mod always; 4 | pub mod params; 5 | pub mod times; -------------------------------------------------------------------------------- /simulacrum_mock/src/constraint/stock/params.rs: -------------------------------------------------------------------------------- 1 | use debugit::DebugIt; 2 | use simulacrum_shared::Validator; 3 | 4 | use constraint::{Constraint, ConstraintError, ConstraintResult}; 5 | 6 | /// A method must be called with parameters that meet certain requirements. 7 | pub struct Params { 8 | /// Should be `true` if the method has been called with valid parameters every time. 9 | is_valid: bool, 10 | received_param_msg: String, 11 | /// A closure that will be called with the parameters to validate that they 12 | /// conform to the requirements. 13 | validator: Box> 14 | } 15 | 16 | impl Params { 17 | pub fn new(validator: V) -> Self where 18 | V: Validator + 'static 19 | { 20 | Params { 21 | is_valid: true, 22 | received_param_msg: "".to_owned(), 23 | validator: Box::new(validator) 24 | } 25 | } 26 | } 27 | 28 | impl Constraint for Params { 29 | fn handle_call(&mut self, params: &I) { 30 | if self.is_valid { 31 | self.is_valid = self.validator.validate(params); 32 | if !self.is_valid { 33 | self.received_param_msg = format!("{:?}", DebugIt(params)); 34 | } 35 | } 36 | } 37 | 38 | fn verify(&self) -> ConstraintResult { 39 | if self.is_valid { 40 | Ok(()) 41 | } else { 42 | let expected_msg = self.validator.print(); 43 | let received_msg = self.received_param_msg.clone(); 44 | Err(ConstraintError::MismatchedParams(expected_msg, received_msg)) 45 | } 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use simulacrum_user::*; 52 | 53 | use super::*; 54 | 55 | #[test] 56 | fn test_new() { 57 | let c = Params::new(none()); 58 | 59 | let r = >::verify(&c); 60 | 61 | assert!(r.is_ok(), "Constraint should pass after being created"); 62 | } 63 | 64 | #[test] 65 | fn test_handle_call_pass() { 66 | // Validator approves of any input 67 | let mut c = Params::new(any()); 68 | 69 | c.handle_call(&()); 70 | let r = >::verify(&c); 71 | 72 | assert!(r.is_ok(), "Constraint should pass"); 73 | } 74 | 75 | #[test] 76 | fn test_handle_call_fail() { 77 | // Validator closure disapproves of any input 78 | let mut c = Params::new(none()); 79 | 80 | c.handle_call(&()); 81 | let r = >::verify(&c); 82 | 83 | assert!(r.is_err(), "Constraint should fail"); 84 | } 85 | 86 | #[test] 87 | fn test_handle_call_good_then_bad() { 88 | // Validator closure approves input over 5 89 | let mut c = Params::new(passes(|arg| *arg > 5)); 90 | 91 | c.handle_call(&10); // Good 92 | c.handle_call(&3); // Bad 93 | let r = >::verify(&c); 94 | 95 | assert!(r.is_err(), "Constraint should fail"); 96 | } 97 | 98 | #[test] 99 | fn test_handle_call_bad_then_good() { 100 | // Validator closure approves input over 5 101 | let mut c = Params::new(passes(|arg| *arg > 5)); 102 | 103 | c.handle_call(&3); // Bad 104 | c.handle_call(&10); // Good 105 | let r = >::verify(&c); 106 | 107 | assert!(r.is_err(), "Constraint should fail"); 108 | } 109 | } -------------------------------------------------------------------------------- /simulacrum_mock/src/constraint/stock/times.rs: -------------------------------------------------------------------------------- 1 | use constraint::{Constraint, ConstraintError, ConstraintResult}; 2 | 3 | /// A method must be called a certain number of times 4 | pub struct Times(i64); 5 | 6 | impl Times { 7 | pub fn new(expected_calls: i64) -> Self { 8 | Times(expected_calls) 9 | } 10 | } 11 | 12 | impl Constraint for Times { 13 | fn handle_call(&mut self, _params: &I) { 14 | self.0 -= 1; 15 | } 16 | 17 | fn verify(&self) -> ConstraintResult { 18 | match self.0 { 19 | x if x < 0 => Err(ConstraintError::CalledTooManyTimes(x.abs())), 20 | x if x > 0 => Err(ConstraintError::CalledTooFewTimes(x)), 21 | _ => Ok(()) 22 | } 23 | } 24 | } 25 | 26 | #[cfg(test)] 27 | mod tests { 28 | use super::*; 29 | 30 | #[test] 31 | fn test_pass() { 32 | let c = Times::new(0); 33 | 34 | let r = >::verify(&c); 35 | 36 | assert!(r.is_ok()); 37 | } 38 | 39 | #[test] 40 | fn test_fail_called_fewer() { 41 | let c = Times::new(1); 42 | 43 | let r = >::verify(&c); 44 | 45 | assert!(r.is_err(), "Constraint should fail"); 46 | assert_eq!(r.unwrap_err(), ConstraintError::CalledTooFewTimes(1), "Constraint should return the correct error"); 47 | } 48 | 49 | #[test] 50 | fn test_fail_called_more() { 51 | let c = Times::new(-1); 52 | 53 | let r = >::verify(&c); 54 | 55 | assert!(r.is_err(), "Constraint should fail"); 56 | assert_eq!(r.unwrap_err(), ConstraintError::CalledTooManyTimes(1), "Constraint should return the correct error"); 57 | } 58 | 59 | #[test] 60 | fn test_handle_call() { 61 | let mut c = Times::new(2); 62 | 63 | // Called twice 64 | c.handle_call(&()); 65 | c.handle_call(&()); 66 | let r = >::verify(&c); 67 | 68 | assert!(r.is_ok()); 69 | } 70 | } -------------------------------------------------------------------------------- /simulacrum_mock/src/expectation/mod.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | use std::cell::RefCell; 3 | use std::ops::{Deref, DerefMut}; 4 | 5 | use super::MethodName; 6 | 7 | pub mod result; 8 | 9 | pub use super::constraint::Constraint; 10 | pub use self::result::{ExpectationError, ExpectationResult}; 11 | 12 | /// An expectation that a method must be called. Also includes an optional 13 | /// closure to produce return values, if necessary. 14 | pub struct Expectation where 15 | I: 'static 16 | { 17 | name: MethodName, 18 | constraints: Vec>>, 19 | modification_fn: Option>, 20 | return_fn: Option O>> 21 | } 22 | 23 | impl Expectation where 24 | I: 'static, 25 | O: 'static 26 | { 27 | pub fn new(name: S) -> Self { 28 | Expectation { 29 | name: name.to_string(), 30 | constraints: Vec::new(), 31 | modification_fn: None, 32 | return_fn: None 33 | } 34 | } 35 | 36 | pub fn handle_call(&mut self, params_cell: &RefCell) { 37 | self.constraints_handle_call(params_cell); 38 | self.run_modification_behavior(params_cell); 39 | } 40 | 41 | fn constraints_handle_call(&mut self, params_cell: &RefCell) { 42 | for constraint in self.constraints.iter_mut() { 43 | let params = params_cell.borrow(); 44 | constraint.handle_call(params.deref()); 45 | } 46 | } 47 | 48 | fn run_modification_behavior(&mut self, params_cell: &RefCell) { 49 | if self.modification_fn.is_some() { 50 | let mut params = params_cell.borrow_mut(); 51 | (self.modification_fn.as_mut().unwrap())(params.deref_mut()) 52 | } 53 | } 54 | 55 | pub fn return_value_for(&mut self, params_cell: RefCell) -> O { 56 | if self.return_fn.is_some() { 57 | (self.return_fn.as_mut().unwrap())(params_cell.into_inner()) 58 | } else { 59 | panic!("No return closure specified for `{}`, which should return.", self.name); 60 | } 61 | } 62 | 63 | pub(crate) fn constrain(&mut self, constraint: C) where 64 | C: Constraint + 'static 65 | { 66 | self.constraints.push(Box::new(constraint)); 67 | } 68 | 69 | pub(crate) fn set_modification(&mut self, modification_behavior: F) where 70 | F: 'static + FnMut(&mut I) 71 | { 72 | self.modification_fn = Some(Box::new(modification_behavior)); 73 | } 74 | 75 | pub(crate) fn set_return(&mut self, return_behavior: F) where 76 | F: 'static + FnMut(I) -> O 77 | { 78 | self.return_fn = Some(Box::new(return_behavior)); 79 | } 80 | } 81 | 82 | pub trait ExpectationT { 83 | fn as_any(&mut self) -> &mut Any; 84 | 85 | fn verify(&self) -> ExpectationResult; 86 | 87 | fn name(&self) -> &MethodName; 88 | } 89 | 90 | impl ExpectationT for Expectation where 91 | I: 'static, 92 | O: 'static 93 | { 94 | fn as_any(&mut self) -> &mut Any { 95 | self 96 | } 97 | 98 | fn verify(&self) -> ExpectationResult { 99 | for constraint in self.constraints.iter() { 100 | if let Err(constraint_err) = constraint.verify() { 101 | return Err(ExpectationError { 102 | constraint_err, 103 | method_name: self.name.clone() 104 | }) 105 | } 106 | } 107 | Ok(()) 108 | } 109 | 110 | fn name(&self) -> &MethodName { 111 | &self.name 112 | } 113 | } 114 | 115 | #[cfg(test)] 116 | mod tests { 117 | use super::*; 118 | use constraint::{ConstraintError, ConstraintMock}; 119 | use constraint::stock::always::{AlwaysFail, AlwaysPass}; 120 | use std::{sync::Arc, cell::RefCell}; 121 | 122 | #[test] 123 | fn test_new() { 124 | let e: Expectation<(), ()> = Expectation::new("foo"); 125 | 126 | assert_eq!(e.name, "foo", "Name of Constraint should be `foo`"); 127 | assert_eq!(e.constraints.len(), 0, "Number of Constraints should be 0"); 128 | assert!(e.return_fn.is_none(), "Return Behavior Should Not Exist"); 129 | assert!(e.modification_fn.is_none(), "Modification Behavior Should Not Exist"); 130 | } 131 | 132 | #[test] 133 | fn test_handle_call() { 134 | let mut e: Expectation<(), ()> = Expectation::new("foo"); 135 | let mut m = ConstraintMock::new(); 136 | m.expect_handle_call(); 137 | e.constrain(m); 138 | 139 | e.handle_call(&RefCell::new(())); 140 | 141 | // ConstraintMock verifies on Drop 142 | } 143 | 144 | #[test] 145 | fn test_constrain() { 146 | let mut e: Expectation<(), ()> = Expectation::new("test"); 147 | 148 | e.constrain(AlwaysPass); 149 | 150 | assert_eq!(e.constraints.len(), 1, "Number of Constraints should be 1"); 151 | } 152 | 153 | #[test] 154 | fn test_return() { 155 | let mut e: Expectation<(), i32> = Expectation::new("yaz"); 156 | 157 | e.set_return(|_| 5); 158 | 159 | assert!(e.return_fn.is_some(), "Return Closure Should Exist"); 160 | assert_eq!(e.return_value_for(RefCell::new(())), 5, "Return Closure Should return 5"); 161 | } 162 | 163 | #[test] 164 | fn test_return_consuming() { 165 | // Does not implement Clone or Copy 166 | struct UniquelyOwned(u32); 167 | 168 | let mut e: Expectation = Expectation::new("foo"); 169 | 170 | let dest: Arc>> = 171 | Arc::new(RefCell::new(None)); 172 | let dest2 = dest.clone(); 173 | e.set_return(move |x| { 174 | dest2.replace(Some(x)); 175 | }); 176 | e.return_value_for(RefCell::new(UniquelyOwned(42))); 177 | 178 | assert!(dest.borrow().is_some()); 179 | } 180 | 181 | #[test] 182 | fn test_set_modification() { 183 | let mut e: Expectation<(), ()> = Expectation::new("bing"); 184 | 185 | e.set_modification(|_| ()); 186 | 187 | assert!(e.modification_fn.is_some(), "Modification Closure Should Exist"); 188 | } 189 | 190 | #[test] 191 | #[should_panic] 192 | fn test_return_no_closure_given() { 193 | let mut e: Expectation<(), i32> = Expectation::new("yaz"); 194 | 195 | // Did not set the return here 196 | 197 | // Panic: .returning() was not called, so we don't know what to return 198 | e.return_value_for(RefCell::new(())); 199 | } 200 | 201 | #[test] 202 | fn test_verify_pass() { 203 | let mut e: Expectation<(), ()> = Expectation::new("zonk"); 204 | 205 | e.constrain(AlwaysPass); 206 | e.constrain(AlwaysPass); 207 | let r = e.verify(); 208 | 209 | assert!(r.is_ok(), "Expectation should pass"); 210 | } 211 | 212 | #[test] 213 | fn test_verify_fail() { 214 | let mut e: Expectation<(), ()> = Expectation::new("boop"); 215 | 216 | e.constrain(AlwaysPass); 217 | e.constrain(AlwaysFail); // Will cause Expectation to fail 218 | let r = e.verify(); 219 | 220 | assert!(r.is_err(), "Expectation should fail"); 221 | let r = r.unwrap_err(); 222 | assert_eq!(r.method_name, "boop", "Expectation error should have the correct method name"); 223 | assert_eq!(r.constraint_err, ConstraintError::AlwaysFail, "Expectation error should contain the correct Constraint error"); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /simulacrum_mock/src/expectation/result.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use MethodName; 4 | use constraint::ConstraintError; 5 | 6 | pub type ExpectationResult = Result<(), ExpectationError>; 7 | 8 | #[derive(Clone, Debug, PartialEq)] 9 | pub struct ExpectationError { 10 | pub constraint_err: ConstraintError, 11 | pub method_name: MethodName 12 | } 13 | 14 | impl fmt::Display for ExpectationError { 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | write!(f, "{}: {}", self.method_name, self.constraint_err) 17 | } 18 | } -------------------------------------------------------------------------------- /simulacrum_mock/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Core functionality for creating mock objects with Simulacrum. 2 | 3 | extern crate debugit; 4 | extern crate handlebox; 5 | extern crate simulacrum_shared; 6 | 7 | #[cfg(test)] 8 | extern crate simulacrum_user; 9 | 10 | pub mod constraint; 11 | pub mod expectation; 12 | pub mod method; 13 | pub mod mock; 14 | mod store; 15 | 16 | pub type MethodName = String; 17 | 18 | pub use handlebox::Handle as ExpectationId; 19 | 20 | pub use self::mock::Expectations; 21 | pub use self::method::Method; -------------------------------------------------------------------------------- /simulacrum_mock/src/method.rs: -------------------------------------------------------------------------------- 1 | //! This is the API that you'll call in your tests when using your Mock objects. 2 | 3 | use simulacrum_shared::Validator; 4 | 5 | use std::marker::PhantomData; 6 | 7 | use super::{ExpectationId, MethodName}; 8 | use super::expectation::Expectation; 9 | use super::constraint::stock::times::Times; 10 | use super::constraint::stock::params::Params; 11 | use super::store::ExpectationStore; 12 | 13 | // I is a tuple of args for this method excluding self. 14 | // O is the return value or () if there is no return value. 15 | pub(crate) struct MethodTypes { 16 | pub(crate) input: PhantomData, 17 | pub(crate) output: PhantomData 18 | } 19 | 20 | impl MethodTypes { 21 | pub(crate) fn new() -> Self { 22 | MethodTypes { 23 | input: PhantomData, 24 | output: PhantomData 25 | } 26 | } 27 | } 28 | 29 | pub(crate) struct MethodSig { 30 | pub(crate) name: MethodName, 31 | pub(crate) _types: MethodTypes 32 | } 33 | 34 | /// What you get from calling `.expect_METHOD_NAME()` on a Mock. 35 | /// 36 | /// From here, use this struct's methods to set the number of calls expected. 37 | #[must_use] 38 | pub struct Method<'a, I, O> { 39 | store: &'a mut ExpectationStore, 40 | sig: MethodSig, 41 | } 42 | 43 | impl<'a, I, O> Method<'a, I, O> where 44 | I: 'static, 45 | O: 'static 46 | { 47 | pub(crate) fn new(store: &'a mut ExpectationStore, name: S) -> Self { 48 | let name = name.to_string(); 49 | // Bail if there's already an expectation for a method with this name in the current era 50 | if store.has_expectation_for_method_in_current_era(&name) { 51 | panic!("Only one expectation can be set for method '{}' per era - use .then() to create a new era before adding another expectation.", name); 52 | } 53 | 54 | let types = MethodTypes { 55 | input: PhantomData, 56 | output: PhantomData 57 | }; 58 | let sig = MethodSig { 59 | name, 60 | _types: types 61 | }; 62 | Self { 63 | store, 64 | sig 65 | } 66 | } 67 | 68 | /// You expect this method to be called zero times. 69 | pub fn called_never(self) -> TrackedMethod<'a, I, O> { 70 | self.called_times(0) 71 | } 72 | 73 | /// You expect this method to be called only once. 74 | pub fn called_once(self) -> TrackedMethod<'a, I, O> { 75 | self.called_times(1) 76 | } 77 | 78 | /// You expect this method to be called `calls` number of times. 79 | pub fn called_times(self, calls: i64) -> TrackedMethod<'a, I, O> { 80 | // Create an expectation that counts a certain number of calls. 81 | let mut exp: Expectation = Expectation::new(&self.sig.name); 82 | exp.constrain(Times::new(calls)); 83 | 84 | // Add the expectation to the store. 85 | let id = self.store.add(exp); 86 | 87 | TrackedMethod { 88 | id, 89 | method: self 90 | } 91 | } 92 | 93 | /// This method can be called any number of times, including zero. 94 | pub fn called_any(self) -> TrackedMethod<'a, I, O> { 95 | // Create an empty expectation 96 | let exp: Expectation = Expectation::new(&self.sig.name); 97 | 98 | // Add the expectation to the store. 99 | let id = self.store.add(exp); 100 | 101 | TrackedMethod { 102 | id, 103 | method: self 104 | } 105 | } 106 | } 107 | 108 | /// Once you've specified the number of times you expect a method to be called, 109 | /// you can specify additional behaviors and expectations through this object's 110 | /// methods. 111 | pub struct TrackedMethod<'a, I, O> { 112 | id: ExpectationId, 113 | method: Method<'a, I, O> 114 | } 115 | 116 | impl<'a, I, O> TrackedMethod<'a, I, O> where 117 | I: 'static, 118 | O: 'static 119 | { 120 | /// Specify a `Validator` object that will validate parameters when called. 121 | /// 122 | /// If it returns `false` when its `validate()` method is called, the 123 | /// expectation will be invalidated. 124 | pub fn with(self, validator: V) -> Self where 125 | V: Validator + 'static 126 | { 127 | let constraint = Params::new(validator); 128 | self.method.store.get_mut::(self.id).constrain(constraint); 129 | self 130 | } 131 | 132 | /// Specify a behavior to be executed as a side-effect when the method is called. 133 | /// 134 | /// The primary use for this is to modify parameters passed as mutable references. 135 | pub fn modifying(self, modification_behavior: F) -> Self where 136 | F: 'static + FnMut(&mut I) 137 | { 138 | self.method.store.get_mut::(self.id).set_modification(modification_behavior); 139 | self 140 | } 141 | 142 | pub fn returning(self, result_behavior: F) -> Self where 143 | F: 'static + FnMut(I) -> O 144 | { 145 | self.method.store.get_mut::(self.id).set_return(result_behavior); 146 | self 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /simulacrum_mock/src/mock.rs: -------------------------------------------------------------------------------- 1 | //! Mock object internals. You can use this API to construct mock objects manually. 2 | 3 | use std::thread; 4 | 5 | use super::method::Method; 6 | use super::store::ExpectationStore; 7 | 8 | #[derive(Default)] 9 | pub struct Expectations { 10 | store: ExpectationStore 11 | } 12 | 13 | impl Expectations { 14 | /// Create a new `Expectations` instance. Call this when your mock object is created. 15 | pub fn new() -> Self { 16 | Expectations { 17 | store: ExpectationStore::new() 18 | } 19 | } 20 | 21 | /// Returns a `Method` struct which you can use to add expectations for the 22 | /// method with the given name. 23 | pub fn expect(&mut self, name: &str) -> Method where 24 | I: 'static, 25 | O: 'static 26 | { 27 | Method::new(&mut self.store, name) 28 | } 29 | 30 | /// Begin a new Era. Expectations in one Era must be met before expectations 31 | /// in future eras will be evaluated. 32 | /// 33 | /// Note that Eras are evaluated eagerly. This means that Eras may advance more 34 | /// quickly than you'd intuitively expect in certain situations. For example, 35 | /// `called_any()` is marked as complete after the first call is received. 36 | /// This menas that, for the purposes of telling if an Era should be advanced or 37 | /// not, `called_any()` and `called_once()` are the same. 38 | pub fn then(&mut self) -> &mut Self { 39 | self.store.new_era(); 40 | self 41 | } 42 | 43 | /// When a tracked method is called on the mock object, call this with the method's name 44 | /// in order to tell the `Expectations` that the method was called. 45 | /// 46 | /// Unlike `was_called_returning`, this method does not return a value. 47 | pub fn was_called(&self, name: &str, params: I) where 48 | I: 'static, 49 | O: 'static 50 | { 51 | self.store 52 | .matcher_for::(name) 53 | .was_called(params); 54 | } 55 | 56 | /// Same as the `was_called` method, but also returns the result. 57 | pub fn was_called_returning(&self, name: &str, params: I) -> O where 58 | I: 'static, 59 | O: 'static 60 | { 61 | self.store 62 | .matcher_for::(name) 63 | .was_called_returning(params) 64 | } 65 | 66 | fn verify(&self) { 67 | if let Err(e) = self.store.verify() { 68 | panic!("{}", e); 69 | } 70 | } 71 | } 72 | 73 | impl Drop for Expectations { 74 | /// All expectations will be verified when the mock object is dropped, 75 | /// panicking if any of them are unmet. 76 | /// 77 | /// In the case where the Expectations object is being dropped because the 78 | /// thread is _already_ panicking, the Expectations object is not verified. 79 | fn drop(&mut self) { 80 | if !thread::panicking() { 81 | self.verify(); 82 | } 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod tests { 88 | use simulacrum_user::*; 89 | 90 | use std::panic; 91 | 92 | use super::*; 93 | 94 | #[test] 95 | fn test_called_once() { 96 | let mut e = Expectations::new(); 97 | e.expect::<(), ()>("spoo").called_once(); 98 | 99 | e.was_called::<(), ()>("spoo", ()); 100 | 101 | // Verified on drop 102 | } 103 | 104 | #[test] 105 | #[should_panic] 106 | fn test_called_once_fail() { 107 | let mut e = Expectations::new(); 108 | 109 | // Panic: "spoo" should have been called once, but was never called 110 | e.expect::<(), ()>("spoo").called_once(); 111 | } 112 | 113 | #[test] 114 | fn test_called_twice() { 115 | let mut e = Expectations::new(); 116 | e.expect::<(), ()>("nom").called_times(2); 117 | 118 | e.was_called::<(), ()>("nom", ()); 119 | e.was_called::<(), ()>("nom", ()); 120 | } 121 | 122 | #[test] 123 | #[should_panic] 124 | fn test_called_twice_fail() { 125 | let mut e = Expectations::new(); 126 | e.expect::<(), ()>("nom").called_times(2); 127 | 128 | // Panic: "nom" should have been called twice, but was only called once 129 | e.was_called::<(), ()>("nom", ()); 130 | } 131 | 132 | #[test] 133 | fn test_called_never_pass() { 134 | let mut e = Expectations::new(); 135 | e.expect::<(), ()>("blitz").called_never(); 136 | } 137 | 138 | #[test] 139 | #[should_panic] 140 | fn test_called_never_fail() { 141 | let mut e = Expectations::new(); 142 | e.expect::<(), ()>("blitz").called_never(); 143 | 144 | // Panic: "blitz" should have never been called 145 | e.was_called::<(), ()>("blitz", ()); 146 | } 147 | 148 | #[test] 149 | fn test_called_any_zero() { 150 | let mut e = Expectations::new(); 151 | e.expect::<(), ()>("mega").called_any(); 152 | } 153 | 154 | #[test] 155 | fn test_called_any_two() { 156 | let mut e = Expectations::new(); 157 | e.expect::<(), ()>("mega").called_any(); 158 | 159 | e.was_called::<(), ()>("mega", ()); 160 | e.was_called::<(), ()>("mega", ()); 161 | } 162 | 163 | #[test] 164 | fn test_param() { 165 | let mut e = Expectations::new(); 166 | e.expect::("doog").called_once().with(gt(5)); 167 | 168 | e.was_called::("doog", 10); 169 | } 170 | 171 | #[test] 172 | #[should_panic] 173 | fn test_param_fail() { 174 | let mut e = Expectations::new(); 175 | e.expect::("doog").called_once().with(gt(5)); 176 | 177 | // Panic: "doog"'s parameter was not > 5 178 | e.was_called::("doog", 1); 179 | } 180 | 181 | #[test] 182 | fn test_returning() { 183 | let mut e = Expectations::new(); 184 | e.expect::<(), i32>("boye").called_any().returning(|_| 5); 185 | 186 | let r = e.was_called_returning::<(), i32>("boye", ()); 187 | 188 | assert_eq!(r, 5); 189 | } 190 | 191 | #[test] 192 | #[should_panic] 193 | fn test_returning_no_matches() { 194 | let e = Expectations::new(); 195 | 196 | // Not expecting "boye" here, so when it's called, we should panic 197 | 198 | // Panic: No expectation matches, so we can't return a value 199 | e.was_called_returning::<(), i32>("boye", ()); 200 | } 201 | 202 | #[test] 203 | fn test_modifications() { 204 | let mut e = Expectations::new(); 205 | e.expect::<*mut i32, ()>("dawg") 206 | .called_any() 207 | .modifying(|&mut arg| { 208 | unsafe { 209 | *arg = 3; 210 | } 211 | }); 212 | 213 | let mut i = 2; 214 | e.was_called::<*mut i32, ()>("dawg", &mut i); 215 | 216 | assert_eq!(i, 3); 217 | } 218 | 219 | #[test] 220 | fn test_then() { 221 | let mut e = Expectations::new(); 222 | e.expect::("fren").called_once().with(gt(5)); 223 | e.then().expect::("fren").called_once().with(lt(3)); 224 | 225 | e.was_called::("fren", 10); // Matches first era, completing it 226 | e.was_called::("fren", 1); // Matches second era, completing it 227 | } 228 | 229 | #[test] 230 | fn test_then_never() { 231 | // Test to see that `called_never()` is only enforced until the era is completed 232 | let mut e = Expectations::new(); 233 | e.expect::<(), ()>("bruh").called_never(); 234 | e.expect::<(), ()>("fren").called_once(); 235 | e.then().expect::<(), ()>("bruh").called_once(); 236 | 237 | e.was_called::<(), ()>("fren", ()); // Matches first era, completing it 238 | e.was_called::<(), ()>("bruh", ()); // Matches second era, completing it 239 | } 240 | 241 | #[test] 242 | #[should_panic] 243 | fn test_then_partial_fail() { 244 | let mut e = Expectations::new(); 245 | e.expect::("fren").called_once().with(gt(5)); 246 | e.then().expect::("fren").called_times(2).with(lt(3)); 247 | 248 | e.was_called::("fren", 10); // Matches first era, completing it 249 | e.was_called::("fren", 1); // Matches second era, but still incomplete 250 | 251 | // Panic: "fren" was expected to be called twice in the second era 252 | } 253 | 254 | #[test] 255 | fn test_then_multi_call() { 256 | let mut e = Expectations::new(); 257 | 258 | // These expectations can be called in any order 259 | e.expect::<(), ()>("eh").called_once(); 260 | e.expect::<(), ()>("donk").called_times(2); 261 | 262 | // These expectations are called afterwards in any order 263 | e.then().expect::<(), ()>("calxx").called_times(3); 264 | e.expect::<(), ()>("mer").called_once(); 265 | 266 | e.was_called::<(), ()>("donk", ()); 267 | e.was_called::<(), ()>("eh", ()); 268 | e.was_called::<(), ()>("donk", ()); // Completes first era 269 | e.was_called::<(), ()>("calxx", ()); 270 | e.was_called::<(), ()>("mer", ()); 271 | e.was_called::<(), ()>("calxx", ()); 272 | e.was_called::<(), ()>("calxx", ()); // Completes second era 273 | } 274 | 275 | #[test] 276 | #[should_panic] 277 | fn test_then_wrong_order_fail() { 278 | let mut e = Expectations::new(); 279 | 280 | // These expectations can be called in any order 281 | e.expect::<(), ()>("eh").called_once(); 282 | e.expect::<(), ()>("donk").called_once(); 283 | 284 | // These expectations are called afterwards in any order 285 | e.then().expect::<(), ()>("calxx").called_once(); 286 | e.expect::<(), ()>("mer").called_once(); 287 | 288 | e.was_called::<(), ()>("mer", ()); // No matching expectations 289 | e.was_called::<(), ()>("calxx", ()); // No matching expectations 290 | e.was_called::<(), ()>("donk", ()); 291 | e.was_called::<(), ()>("eh", ()); // Completes first era 292 | 293 | // Panic: Second era was never completed 294 | } 295 | 296 | #[test] 297 | fn test_then_specific() { 298 | let mut e = Expectations::new(); 299 | 300 | e.expect::<(), ()>("eh").called_once(); 301 | e.then().expect::<(), ()>("donk").called_once(); 302 | e.then().expect::<(), ()>("mer").called_once(); 303 | e.expect::<(), ()>("eh").called_once(); 304 | 305 | e.was_called::<(), ()>("eh", ()); // Completes first era 306 | e.was_called::<(), ()>("donk", ()); // Completes second era 307 | e.was_called::<(), ()>("eh", ()); 308 | e.was_called::<(), ()>("mer", ()); // Completes third era 309 | } 310 | 311 | #[test] 312 | fn test_empty_era_leading() { 313 | let mut e = Expectations::new(); 314 | 315 | // Create an empty era at the start 316 | e.then(); 317 | e.expect::<(), ()>("eh").called_once(); 318 | 319 | e.was_called::<(), ()>("eh", ()); // Completes first and second eras 320 | } 321 | 322 | #[test] 323 | fn test_empty_era_middle() { 324 | let mut e = Expectations::new(); 325 | 326 | // Create an empty era in the middle 327 | e.expect::<(), ()>("eh").called_once(); 328 | e.then(); 329 | e.then(); 330 | e.expect::<(), ()>("eh").called_once(); 331 | 332 | e.was_called::<(), ()>("eh", ()); // Completes first and second eras 333 | e.was_called::<(), ()>("eh", ()); // Completes third era 334 | } 335 | 336 | #[test] 337 | fn test_empty_era_end() { 338 | let mut e = Expectations::new(); 339 | 340 | e.expect::<(), ()>("eh").called_once(); 341 | // Create an empty era at the end 342 | e.then(); 343 | 344 | e.was_called::<(), ()>("eh", ()); // Completes first and second eras 345 | } 346 | 347 | #[test] 348 | fn test_one_expectation_per_era() { 349 | let mut e = Expectations::new(); 350 | 351 | e.expect::<(), ()>("eh").called_never(); 352 | let result = panic::catch_unwind( 353 | panic::AssertUnwindSafe(|| { 354 | e.expect::<(), ()>("eh").called_never(); // Panic: only one expectation should be registered for "eh" in a given era 355 | }) 356 | ); 357 | assert!(result.is_err()); 358 | } 359 | 360 | #[test] 361 | fn test_eras_complete_eagerly() { 362 | let mut e = Expectations::new(); 363 | 364 | // Expectations 365 | e.expect::<(), ()>("c").called_any(); 366 | e.then(); 367 | e.expect::<(), ()>("d").called_once(); 368 | 369 | // Calls 370 | e.was_called::<(), ()>("c", ()); // Completes first era 371 | e.was_called::<(), ()>("d", ()); // Completes second era 372 | } 373 | 374 | #[test] 375 | fn test_calls_ignored_after_final_era_completes() { 376 | let mut e = Expectations::new(); 377 | 378 | // Expectations 379 | e.expect::<(), ()>("c").called_once(); 380 | e.then(); 381 | 382 | // Calls 383 | e.was_called::<(), ()>("c", ()); // Completes first era 384 | e.was_called::<(), ()>("c", ()); // Doesn't matter, all eras are complete already 385 | } 386 | 387 | // Note: this test is a WIP 388 | // Test for a bug where in the first era, if a param mismatch occurs, the 389 | // second era isn't evaluated. 390 | // #[test] 391 | // fn test_bug_multi_era_param_mismatch() { 392 | // let result = panic::catch_unwind(|| { 393 | // // Expectations 394 | // let mut e = Expectations::new(); 395 | // e.expect::<(), ()>("b") 396 | // .called_once(); 397 | // e.expect::("a") 398 | // .called_once() 399 | // .with(5); 400 | // e.then(); 401 | // e.expect::<(), ()>("b") 402 | // .called_once(); 403 | // e.expect::("a") 404 | // .called_once() 405 | // .with(6); 406 | 407 | // // Calls 408 | // e.was_called::<(), ()>("b", ()); 409 | // e.was_called::("a", 3); 410 | // // Should panic with mismatched parameters 411 | // }); 412 | 413 | // let expected_panic = "blah"; 414 | // match result { 415 | // Ok(_) => panic!("Mock did not panic when it should have"), 416 | // Err(e) => { 417 | // if let Some(e) = e.downcast_ref::<&'static str>() { 418 | // assert_eq!(e, &expected_panic, "Mismatched parameters should have been caught"); 419 | // } else { 420 | // panic!("Mock panicked with an unknown error") 421 | // } 422 | // } 423 | // } 424 | // } 425 | } 426 | -------------------------------------------------------------------------------- /simulacrum_mock/src/store.rs: -------------------------------------------------------------------------------- 1 | use handlebox::HandleBox; 2 | 3 | use std::cell::RefCell; 4 | use std::sync::Mutex; 5 | 6 | use super::{ExpectationId, MethodName}; 7 | use super::expectation::{Constraint, Expectation, ExpectationT, ExpectationResult}; 8 | use super::method::{MethodSig, MethodTypes}; 9 | 10 | // A thread-safe store for `Box`s, including the order that they should be 11 | // evaluated in (Eras). 12 | pub(crate) struct ExpectationStore(Mutex); 13 | 14 | struct Inner { 15 | current_unverified_era: usize, 16 | eras: Vec, 17 | expectations: HandleBox> 18 | } 19 | 20 | type Era = Vec; 21 | 22 | impl ExpectationStore { 23 | pub fn new() -> Self { 24 | let eras = vec![Era::new()]; 25 | ExpectationStore(Mutex::new(Inner { 26 | current_unverified_era: 0, 27 | eras, 28 | expectations: HandleBox::new() 29 | })) 30 | } 31 | 32 | pub fn get_mut(&self, id: ExpectationId) -> ExpectationEditor { 33 | ExpectationEditor { 34 | id, 35 | store: &self, 36 | _types: MethodTypes::new() 37 | } 38 | } 39 | 40 | pub(crate) fn has_expectation_for_method_in_current_era(&self, name: &MethodName) -> bool { 41 | // If the current era is complete, move on to the next incomplete one. 42 | self.advance_era(); 43 | 44 | // Lock our inner mutex 45 | let inner = self.0.lock().unwrap(); 46 | 47 | // Get the current era and see if there's an expectation with this name in it 48 | for id in inner.eras.last().unwrap() { 49 | if inner.expectations.get(&id).unwrap().name() == name { 50 | return true; 51 | } 52 | } 53 | 54 | false 55 | } 56 | 57 | pub fn matcher_for(&self, name: &str) -> ExpectationMatcher where 58 | I: 'static, 59 | O: 'static 60 | { 61 | let sig = MethodSig { 62 | name: name.to_string(), 63 | _types: MethodTypes::new() 64 | }; 65 | 66 | // If the current era is complete, move on to the next incomplete one. 67 | self.advance_era(); 68 | 69 | // Lock our inner mutex 70 | let inner = self.0.lock().unwrap(); 71 | 72 | // Only return ids if we have unverified Eras remaining 73 | if inner.current_unverified_era < inner.eras.len() { 74 | // Gather up ids for expectations that match this one in the current Era 75 | let mut ids = inner.eras.get(inner.current_unverified_era).unwrap().clone(); 76 | ids.retain(|&id| { 77 | inner.expectations.get(&id).unwrap().name() == name 78 | }); 79 | 80 | ExpectationMatcher { 81 | ids, 82 | sig, 83 | store: &self 84 | } 85 | } else { 86 | ExpectationMatcher { 87 | ids: Vec::new(), 88 | sig, 89 | store: &self 90 | } 91 | } 92 | } 93 | 94 | // If the current era is complete, move on to the next incomplete one 95 | #[allow(unused_must_use)] 96 | pub fn advance_era(&self) { 97 | // We don't care about the result, we're just doing it to advance the era if necessary 98 | self.verify(); 99 | } 100 | 101 | // Add a new Expectation under the current Era and return its id. 102 | pub fn add(&self, expectation: E) -> ExpectationId where 103 | E: ExpectationT + 'static 104 | { 105 | // Lock our inner mutex 106 | let mut inner = self.0.lock().unwrap(); 107 | 108 | // Add a new expectation 109 | let id = inner.expectations.add(Box::new(expectation)); 110 | 111 | // Add that new expectation to the current Era 112 | inner.eras.last_mut().unwrap().push(id); 113 | 114 | id 115 | } 116 | 117 | // Begin a new Era and make it the current one. 118 | pub fn new_era(&self) { 119 | // Lock our inner mutex 120 | let mut inner = self.0.lock().unwrap(); 121 | 122 | inner.eras.push(Vec::new()); 123 | } 124 | 125 | /// Verify all expectations in this store. 126 | pub fn verify(&self) -> ExpectationResult { 127 | let mut status = Ok(()); 128 | 129 | // Lock our inner mutex 130 | let mut inner = self.0.lock().unwrap(); 131 | 132 | let original_unverified_era = inner.current_unverified_era; 133 | 134 | 'eras: for era_index in original_unverified_era .. inner.eras.len() { 135 | // Update our current unverified era to be the current Era index 136 | inner.current_unverified_era = era_index; 137 | 138 | // If we have any not-yet-verified Expectations in this Era, do not 139 | // mark it as complete 140 | let era = &inner.eras[era_index]; 141 | for id in era.iter() { 142 | let expectation = inner.expectations.get(id).unwrap(); 143 | let r = expectation.verify(); 144 | if r.is_err() { 145 | // Note the error in this Era 146 | status = r; 147 | // Stop processing Eras since this one is still incomplete 148 | break 'eras; 149 | } 150 | } 151 | } 152 | 153 | status 154 | } 155 | 156 | /// (For testing) Get the number of total Expectations in the store. 157 | #[allow(dead_code)] 158 | fn exp_count(&self) -> usize { 159 | self.0.lock().unwrap().expectations.map.len() 160 | } 161 | 162 | /// (For testing) Get the number of total Eras in the store. 163 | #[allow(dead_code)] 164 | fn era_count(&self) -> usize { 165 | self.0.lock().unwrap().eras.len() 166 | } 167 | } 168 | 169 | impl Default for ExpectationStore { 170 | fn default() -> Self { 171 | Self::new() 172 | } 173 | } 174 | 175 | // Used internally to mutably access an `ExpectationStore`. 176 | pub struct ExpectationEditor<'a, I, O> { 177 | id: ExpectationId, 178 | store: &'a ExpectationStore, 179 | _types: MethodTypes 180 | } 181 | 182 | impl<'a, I, O> ExpectationEditor<'a, I, O> where 183 | I: 'static, 184 | O: 'static 185 | { 186 | pub(crate) fn constrain(&self, constraint: C) where 187 | C: Constraint + 'static 188 | { 189 | self.store.0.lock().unwrap().expectations.get_mut(&self.id).unwrap().as_any().downcast_mut::>().unwrap().constrain(constraint); 190 | } 191 | 192 | pub(crate) fn set_modification(&mut self, modification_behavior: F) where 193 | F: 'static + FnMut(&mut I) 194 | { 195 | self.store.0.lock().unwrap().expectations.get_mut(&self.id).unwrap().as_any().downcast_mut::>().unwrap().set_modification(modification_behavior); 196 | } 197 | 198 | pub(crate) fn set_return(&mut self, return_behavior: F) where 199 | F: 'static + FnMut(I) -> O 200 | { 201 | self.store.0.lock().unwrap().expectations.get_mut(&self.id).unwrap().as_any().downcast_mut::>().unwrap().set_return(return_behavior); 202 | } 203 | 204 | #[allow(dead_code)] 205 | fn verify(&self) -> ExpectationResult { 206 | self.store.0.lock().unwrap().expectations.get_mut(&self.id).unwrap().verify() 207 | } 208 | } 209 | 210 | // Used internally to mutably access an `ExpectationStore` when trying to apply 211 | // a method to a set of matched expectations. 212 | // 213 | // I is a tuple of args for this method excluding self. 214 | // O is the return value or () if there is no return value. 215 | pub(crate) struct ExpectationMatcher<'a, I, O> { 216 | ids: Vec, 217 | sig: MethodSig, 218 | store: &'a ExpectationStore 219 | } 220 | 221 | impl<'a, I, O> ExpectationMatcher<'a, I, O> where 222 | I: 'static, 223 | O: 'static 224 | { 225 | /// Tell each matched Expectation that this method was called. 226 | #[allow(unused_must_use)] 227 | pub fn was_called(self, params: I) -> Self { 228 | let cell = RefCell::new(params); 229 | for id in self.ids.iter() { 230 | self.store.0.lock().unwrap().expectations.get_mut(&id).unwrap().as_any().downcast_mut::>().unwrap().handle_call(&cell); 231 | } 232 | self 233 | } 234 | 235 | /// Same as `was_called()`, but returns a value as well. 236 | /// 237 | /// Returns the result of the closure the user provided with `TrackedMethod.returning()`. 238 | /// 239 | /// If multiple Expectations are matched, the last one matched is used. 240 | /// 241 | /// If no closure was specified or no expectations matched, this method panics. 242 | #[allow(unused_must_use)] 243 | pub fn was_called_returning(mut self, params: I) -> O { 244 | let cell = RefCell::new(params); 245 | if let Some(id) = self.ids.pop() { 246 | self.store.0.lock().unwrap().expectations.get_mut(&id).unwrap().as_any().downcast_mut::>().unwrap().handle_call(&cell); 247 | let result = self.store.0.lock().unwrap().expectations.get_mut(&id).unwrap().as_any().downcast_mut::>().unwrap().return_value_for(cell); 248 | result 249 | } else { 250 | panic!("Can't return a value for method `{}` with no matching expectations.", self.sig.name); 251 | } 252 | } 253 | 254 | // For Testing 255 | #[allow(dead_code)] 256 | fn id_count(&self) -> usize { 257 | self.ids.len() 258 | } 259 | } 260 | 261 | #[cfg(test)] 262 | mod store_tests { 263 | use super::*; 264 | use constraint::ConstraintError; 265 | use constraint::stock::always::{AlwaysFail, AlwaysPass}; 266 | 267 | #[test] 268 | fn test_new() { 269 | let s = ExpectationStore::new(); 270 | 271 | assert!(s.verify().is_ok(), "Store should be Ok after creation"); 272 | assert_eq!(s.era_count(), 1, "Store should have one Era after creation"); 273 | assert_eq!(s.exp_count(), 0, "Store should have no Expectations after creation"); 274 | } 275 | 276 | #[test] 277 | fn test_add() { 278 | let s = ExpectationStore::new(); 279 | let e: Expectation<(), ()> = Expectation::new("foo"); 280 | 281 | s.add(e); 282 | 283 | assert_eq!(s.era_count(), 1, "Number of Eras"); 284 | assert_eq!(s.exp_count(), 1, "Number of Expectations"); 285 | } 286 | 287 | #[test] 288 | fn test_new_era() { 289 | let s = ExpectationStore::new(); 290 | 291 | s.new_era(); 292 | 293 | assert_eq!(s.era_count(), 2, "Number of Eras after creating a new one"); 294 | } 295 | 296 | #[test] 297 | fn test_verify_no_expectations() { 298 | let s = ExpectationStore::new(); 299 | 300 | let r = s.verify(); 301 | 302 | assert!(r.is_ok()); 303 | } 304 | 305 | #[test] 306 | fn test_verify_simple_pass() { 307 | let s = ExpectationStore::new(); 308 | let mut e: Expectation<(), ()> = Expectation::new("squig"); 309 | e.constrain(AlwaysPass); 310 | s.add(e); 311 | 312 | let r = s.verify(); 313 | 314 | assert!(r.is_ok(), "Store should pass"); 315 | } 316 | 317 | #[test] 318 | fn test_verify_simple_fail() { 319 | let s = ExpectationStore::new(); 320 | let mut e: Expectation<(), ()> = Expectation::new("zooks"); 321 | e.constrain(AlwaysFail); 322 | s.add(e); 323 | 324 | let r = s.verify(); 325 | 326 | assert!(r.is_err(), "Store should fail"); 327 | let r = r.unwrap_err(); 328 | assert_eq!(r.method_name, "zooks", "Store error should have the correct method name"); 329 | assert_eq!(r.constraint_err, ConstraintError::AlwaysFail, "Store error should contain the correct Constraint error"); 330 | } 331 | 332 | #[test] 333 | fn test_match() { 334 | let s = ExpectationStore::new(); 335 | let mut e: Expectation<(), ()> = Expectation::new("frolf"); 336 | e.constrain(AlwaysPass); 337 | s.add(e); 338 | let mut e: Expectation<(), ()> = Expectation::new("star"); 339 | e.constrain(AlwaysPass); 340 | s.add(e); 341 | 342 | let m = s.matcher_for::<(), ()>("star"); 343 | 344 | assert_eq!(m.id_count(), 1, "Ids matched should be 1"); 345 | } 346 | 347 | #[test] 348 | fn test_match_current_era() { 349 | let s = ExpectationStore::new(); 350 | let mut e: Expectation<(), ()> = Expectation::new("frob"); 351 | e.constrain(AlwaysFail); 352 | s.add(e); 353 | 354 | s.new_era(); 355 | 356 | // Add the same method twice! It doesn't make sense in practice, but we want 357 | // to only return one Id, signifying that the first Era was matched against. 358 | let mut e: Expectation<(), ()> = Expectation::new("frob"); 359 | e.constrain(AlwaysFail); 360 | s.add(e); 361 | let mut e: Expectation<(), ()> = Expectation::new("frob"); 362 | e.constrain(AlwaysFail); 363 | s.add(e); 364 | 365 | let m = s.matcher_for::<(), ()>("frob"); 366 | 367 | assert_eq!(m.id_count(), 1, "Ids matched should be 1"); 368 | } 369 | 370 | #[test] 371 | fn test_match_current_era_passed() { 372 | let s = ExpectationStore::new(); 373 | let mut e: Expectation<(), ()> = Expectation::new("buzz"); 374 | e.constrain(AlwaysPass); 375 | s.add(e); 376 | 377 | // Cheat and bump up the current unverified era to the end 378 | s.0.lock().unwrap().current_unverified_era = 1; 379 | 380 | let m = s.matcher_for::<(), ()>("buzz"); 381 | 382 | assert_eq!(m.id_count(), 0, "Ids matched should be 0"); 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /simulacrum_shared/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /simulacrum_shared/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simulacrum_shared" 3 | version = "0.1.0" 4 | authors = ["Jason Grlicky "] 5 | description = "Types used when both creating and using mock objects with Simulacrum." 6 | keywords = ["mock", "mocking", "test", "testing", "TDD"] 7 | categories = ["development-tools::testing"] 8 | readme = "README.md" 9 | documentation = "https://docs.rs/simulacrum_shared" 10 | repository = "https://github.com/pcsm/simulacrum/tree/master/simulacrum_shared" 11 | license = "MIT" 12 | 13 | [badges] 14 | maintenance = { status = "actively-developed" } 15 | 16 | [dependencies] 17 | debugit = "0.1.0" -------------------------------------------------------------------------------- /simulacrum_shared/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Paracosm LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /simulacrum_shared/README.md: -------------------------------------------------------------------------------- 1 | Simulacrum Shared [![Docs](https://docs.rs/simulacrum_shared/badge.svg)](https://docs.rs/simulacrum_shared) [![Crates.io](https://img.shields.io/crates/v/simulacrum_shared.svg)](https://crates.io/crates/simulacrum_shared) 2 | ================================================================== 3 | 4 | Types used when both creating and using mock objects with [`simulacrum`](https://github.com/pcsm/simulacrum/tree/master/simulacrum). See that crate's README for details. -------------------------------------------------------------------------------- /simulacrum_shared/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Types used when both creating and using mock objects with Simulacrum. 2 | 3 | extern crate debugit; 4 | 5 | pub mod validator; 6 | 7 | pub use validator::Validator; -------------------------------------------------------------------------------- /simulacrum_shared/src/validator.rs: -------------------------------------------------------------------------------- 1 | use debugit::DebugIt; 2 | 3 | /// A `Validator` is an object that knows how to validate method parameters. 4 | /// 5 | /// To use these, you typically pass them to the `.with()` method for use with 6 | /// the `Params` Constraint. 7 | pub trait Validator { 8 | /// This object has been called with the given parameters. Return `true` 9 | /// if they are acceptable, and `false` if they are not. 10 | fn validate(&mut self, param: &I) -> bool; 11 | 12 | // Method to create a string representing the pass condition of this Validator. 13 | fn print(&self) -> String; 14 | } 15 | 16 | /// `Validator` is automatically implemented for types that implement `PartialEq`. 17 | impl Validator for I { 18 | fn validate(&mut self, param: &I) -> bool { 19 | &*param == self 20 | } 21 | 22 | fn print(&self) -> String { 23 | format!("{:?}", DebugIt(self)).to_owned() 24 | } 25 | } 26 | 27 | #[cfg(test)] 28 | mod tests { 29 | use super::*; 30 | 31 | #[test] 32 | fn test_partialeq_validate() { 33 | let v = 555; 34 | assert!(555.validate(&v)); 35 | assert!(!666.validate(&v)); 36 | } 37 | } -------------------------------------------------------------------------------- /simulacrum_user/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /simulacrum_user/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simulacrum_user" 3 | version = "0.1.0" 4 | authors = ["Jason Grlicky "] 5 | description = "Functionality that is helpful when using mock objects created with Simulacrum." 6 | keywords = ["mock", "mocking", "test", "testing", "TDD"] 7 | categories = ["development-tools::testing"] 8 | readme = "README.md" 9 | documentation = "https://docs.rs/simulacrum_user" 10 | repository = "https://github.com/pcsm/simulacrum/tree/master/simulacrum_user" 11 | license = "MIT" 12 | 13 | [badges] 14 | maintenance = { status = "actively-developed" } 15 | 16 | [dependencies] 17 | debugit = "0.1.0" 18 | simulacrum_shared = "0.1.0" -------------------------------------------------------------------------------- /simulacrum_user/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Paracosm LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /simulacrum_user/README.md: -------------------------------------------------------------------------------- 1 | Simulacrum User [![Docs](https://docs.rs/simulacrum_user/badge.svg)](https://docs.rs/simulacrum_user) [![Crates.io](https://img.shields.io/crates/v/simulacrum_user.svg)](https://crates.io/crates/simulacrum_user) 2 | ================================================================== 3 | 4 | Functionality that is helpful when using mock objects created with [`simulacrum`](https://github.com/pcsm/simulacrum/tree/master/simulacrum). See that crate's README for details. -------------------------------------------------------------------------------- /simulacrum_user/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Functionality that is helpful when using mock objects created with Simulacrum. 2 | 3 | extern crate debugit; 4 | extern crate simulacrum_shared; 5 | 6 | #[macro_use] 7 | pub mod macros; 8 | pub mod validators; 9 | 10 | pub use self::validators::*; -------------------------------------------------------------------------------- /simulacrum_user/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Use this macro to create a `Validator` that works for methods with 2-9 parameters. 2 | #[macro_export] 3 | macro_rules! params { 4 | ($a:expr, $b:expr, $c:expr, $d:expr, $e: expr, $f: expr, $g: expr, $h: expr, $i: expr) => { 5 | $crate::Tuple9(Box::new($a), Box::new($b), Box::new($c), Box::new($d), Box::new($e), Box::new($f), Box::new($g), Box::new($h), Box::new($i)); 6 | }; 7 | ($a:expr, $b:expr, $c:expr, $d:expr, $e: expr, $f: expr, $g: expr, $h: expr) => { 8 | $crate::Tuple8(Box::new($a), Box::new($b), Box::new($c), Box::new($d), Box::new($e), Box::new($f), Box::new($g), Box::new($h)); 9 | }; 10 | ($a:expr, $b:expr, $c:expr, $d:expr, $e: expr, $f: expr, $g: expr) => { 11 | $crate::Tuple7(Box::new($a), Box::new($b), Box::new($c), Box::new($d), Box::new($e), Box::new($f), Box::new($g)); 12 | }; 13 | ($a:expr, $b:expr, $c:expr, $d:expr, $e: expr, $f: expr) => { 14 | $crate::Tuple6(Box::new($a), Box::new($b), Box::new($c), Box::new($d), Box::new($e), Box::new($f)); 15 | }; 16 | ($a:expr, $b:expr, $c:expr, $d:expr, $e: expr) => { 17 | $crate::Tuple5(Box::new($a), Box::new($b), Box::new($c), Box::new($d), Box::new($e)); 18 | }; 19 | ($a:expr, $b:expr, $c:expr, $d:expr) => { 20 | $crate::Tuple4(Box::new($a), Box::new($b), Box::new($c), Box::new($d)); 21 | }; 22 | ($a:expr, $b:expr, $c:expr) => { 23 | $crate::Tuple3(Box::new($a), Box::new($b), Box::new($c)); 24 | }; 25 | ($a:expr, $b:expr) => { 26 | $crate::Tuple2(Box::new($a), Box::new($b)) 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /simulacrum_user/src/validators/check.rs: -------------------------------------------------------------------------------- 1 | use simulacrum_shared::Validator; 2 | 3 | /// A closure that will be called with the parameters to validate that they 4 | /// conform to the requirements. 5 | pub struct Check(Box bool>); 6 | 7 | pub fn passes(closure: F) -> Check where 8 | F: FnMut(&I) -> bool + 'static 9 | { 10 | Check(Box::new(closure)) 11 | } 12 | 13 | impl Validator for Check { 14 | fn validate(&mut self, param: &I) -> bool { 15 | (self.0)(param) 16 | } 17 | 18 | fn print(&self) -> String { 19 | "".to_owned() 20 | } 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn test_validate() { 29 | let mut c = passes(|arg: &i32| *arg == 555); 30 | let v: i32 = 555; 31 | assert!(c.validate(&v)); 32 | } 33 | } -------------------------------------------------------------------------------- /simulacrum_user/src/validators/compare.rs: -------------------------------------------------------------------------------- 1 | use debugit::DebugIt; 2 | use simulacrum_shared::Validator; 3 | 4 | pub struct GreaterThan(I); 5 | 6 | /// Parameter(s) must be > the provided value. 7 | pub fn gt(other: I) -> GreaterThan { 8 | GreaterThan(other) 9 | } 10 | 11 | impl Validator for GreaterThan { 12 | fn validate(&mut self, param: &I) -> bool { 13 | *param > self.0 14 | } 15 | 16 | fn print(&self) -> String { 17 | format!("> {:?}", DebugIt(&self.0)).to_owned() 18 | } 19 | } 20 | 21 | pub struct LessThan(I); 22 | 23 | /// Parameter(s) must be < the provided value. 24 | pub fn lt(other: I) -> LessThan { 25 | LessThan(other) 26 | } 27 | 28 | impl Validator for LessThan { 29 | fn validate(&mut self, param: &I) -> bool { 30 | *param < self.0 31 | } 32 | 33 | fn print(&self) -> String { 34 | format!("< {:?}", DebugIt(&self.0)).to_owned() 35 | } 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use super::*; 41 | 42 | #[test] 43 | fn test_gt() { 44 | let mut c = gt(888); 45 | let v = 999; 46 | assert!(c.validate(&v)); 47 | } 48 | 49 | #[test] 50 | fn test_gt_fail() { 51 | let mut c = gt(555); 52 | let v = 1; 53 | assert!(!c.validate(&v)); 54 | } 55 | 56 | #[test] 57 | fn test_lt() { 58 | let mut c = lt(10); 59 | let v = 1; 60 | assert!(c.validate(&v)); 61 | } 62 | 63 | #[test] 64 | fn test_lt_fail() { 65 | let mut c = lt(10); 66 | let v = 25; 67 | assert!(!c.validate(&v)); 68 | } 69 | } -------------------------------------------------------------------------------- /simulacrum_user/src/validators/deref.rs: -------------------------------------------------------------------------------- 1 | use simulacrum_shared::Validator; 2 | 3 | use std::marker::PhantomData; 4 | 5 | /// Parameter(s) must equal the provided value. 6 | pub struct Deref(V, PhantomData) where V: Validator; 7 | 8 | pub fn deref(validator: V) -> Deref where 9 | V: Validator 10 | { 11 | Deref(validator, PhantomData) 12 | } 13 | 14 | impl Validator<*mut I> for Deref where 15 | V: Validator 16 | { 17 | fn validate(&mut self, param: &*mut I) -> bool { 18 | unsafe { 19 | self.0.validate(&*param.as_mut().unwrap()) 20 | } 21 | } 22 | 23 | fn print(&self) -> String { 24 | format!("&mut {}", self.0.print()).to_owned() 25 | } 26 | } 27 | 28 | impl Validator<*const I> for Deref where 29 | V: Validator 30 | { 31 | fn validate(&mut self, param: &*const I) -> bool { 32 | unsafe { 33 | self.0.validate(&*param.as_ref().unwrap()) 34 | } 35 | } 36 | 37 | fn print(&self) -> String { 38 | format!("& {}", self.0.print()).to_owned() 39 | } 40 | } 41 | 42 | #[cfg(test)] 43 | mod tests { 44 | use super::*; 45 | 46 | #[test] 47 | fn test_validate_const() { 48 | let mut c = deref(888); 49 | let v = &888 as *const i32; 50 | assert!(c.validate(&v)); 51 | } 52 | 53 | #[test] 54 | #[should_panic] 55 | fn test_validate_const_fail() { 56 | let mut c = deref(555); 57 | let v = &888 as *const i32; 58 | assert!(c.validate(&v)); 59 | } 60 | 61 | #[test] 62 | fn test_validate_mut() { 63 | let mut c = deref(888); 64 | let v = &mut 888 as *mut i32; 65 | assert!(c.validate(&v)); 66 | } 67 | 68 | #[test] 69 | #[should_panic] 70 | fn test_validate_mut_fail() { 71 | let mut c = deref(555); 72 | let v = &mut 888 as *mut i32; 73 | assert!(c.validate(&v)); 74 | } 75 | } -------------------------------------------------------------------------------- /simulacrum_user/src/validators/mod.rs: -------------------------------------------------------------------------------- 1 | //! Types that impl `Validator`. 2 | 3 | pub mod check; 4 | pub mod compare; 5 | pub mod deref; 6 | pub mod trivial; 7 | pub mod tuple; 8 | 9 | pub use self::check::passes; 10 | pub use self::compare::{gt, lt}; 11 | pub use self::deref::deref; 12 | pub use self::trivial::{any, none}; 13 | pub use self::tuple::*; -------------------------------------------------------------------------------- /simulacrum_user/src/validators/trivial.rs: -------------------------------------------------------------------------------- 1 | use simulacrum_shared::Validator; 2 | 3 | /// A `Validator` that either always passes or fails. 4 | pub struct Trivial(bool); 5 | 6 | pub fn any() -> Trivial { 7 | Trivial(true) 8 | } 9 | 10 | pub fn none() -> Trivial { 11 | Trivial(false) 12 | } 13 | 14 | impl Validator for Trivial { 15 | fn validate(&mut self, _param: &I) -> bool { 16 | self.0 17 | } 18 | 19 | fn print(&self) -> String { 20 | if self.0 { 21 | "".to_owned() 22 | } else { 23 | "".to_owned() 24 | } 25 | } 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | 32 | #[test] 33 | fn test_any() { 34 | let mut c = any(); 35 | assert!(c.validate(&())); 36 | } 37 | 38 | #[test] 39 | fn test_none() { 40 | let mut c = none(); 41 | assert!(!c.validate(&())); 42 | } 43 | } -------------------------------------------------------------------------------- /simulacrum_user/src/validators/tuple.rs: -------------------------------------------------------------------------------- 1 | //! A family of `Validators` that splits tuples into their own validators. 2 | use simulacrum_shared::Validator; 3 | 4 | macro_rules! create_tuple_validator { 5 | ($name:ident: $(($index:tt, $generic:ident)),*) => { 6 | pub struct $name<$($generic),*>( 7 | $(pub Box>),* 8 | ); 9 | 10 | impl<$($generic),*> Validator<($($generic),*)> for $name<$($generic),*> { 11 | fn validate(&mut self, param: &($($generic),*)) -> bool { 12 | $(self.$index.validate(¶m.$index) &&)* 13 | true 14 | } 15 | 16 | fn print(&self) -> String { 17 | let inner = [ 18 | $( 19 | self.$index.print(), 20 | )* 21 | ].join(", "); 22 | format!("({})", inner) 23 | } 24 | } 25 | }; 26 | } 27 | 28 | create_tuple_validator!(Tuple2: (0, A), (1, B)); 29 | create_tuple_validator!(Tuple3: (0, A), (1, B), (2, C)); 30 | create_tuple_validator!(Tuple4: (0, A), (1, B), (2, C), (3, D)); 31 | create_tuple_validator!(Tuple5: (0, A), (1, B), (2, C), (3, D), (4, E)); 32 | create_tuple_validator!(Tuple6: (0, A), (1, B), (2, C), (3, D), (4, E), (5, F)); 33 | create_tuple_validator!(Tuple7: (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G)); 34 | create_tuple_validator!(Tuple8: (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H)); 35 | create_tuple_validator!(Tuple9: (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I)); 36 | 37 | #[cfg(test)] 38 | mod tests { 39 | use super::*; 40 | use super::super::trivial::*; 41 | 42 | #[test] 43 | fn test_2_both() { 44 | let mut c = params!(any(), any()); 45 | assert!(c.validate(&((), ()))); 46 | } 47 | 48 | #[test] 49 | fn test_2_first() { 50 | let mut c = params!(any(), none()); 51 | assert!(!c.validate(&((), ()))); 52 | } 53 | 54 | #[test] 55 | fn test_2_second() { 56 | let mut c = params!(none(), any()); 57 | assert!(!c.validate(&((), ()))); 58 | } 59 | 60 | #[test] 61 | fn test_2_neither() { 62 | let mut c = params!(none(), none()); 63 | assert!(!c.validate(&((), ()))); 64 | } 65 | 66 | #[test] 67 | fn test_3_all() { 68 | let mut c = params!(any(), any(), any()); 69 | assert!(c.validate(&((), (), ()))); 70 | } 71 | 72 | #[test] 73 | fn test_3_fail() { 74 | let mut c = params!(none(), any(), none()); 75 | assert!(!c.validate(&((), (), ()))); 76 | } 77 | 78 | #[test] 79 | fn test_4_all() { 80 | let mut c = params!(any(), any(), any(), any()); 81 | assert!(c.validate(&((), (), (), ()))); 82 | } 83 | 84 | #[test] 85 | fn test_4_fail() { 86 | let mut c = params!(any(), none(), any(), none()); 87 | assert!(!c.validate(&((), (), (), ()))); 88 | } 89 | } --------------------------------------------------------------------------------