pcsm / simulacrum

A small library for creating mock objects in Rust.
MIT License
26 stars 3 forks source link

Can't mock a Send Trait #56

Closed asomers closed 5 years ago

asomers commented 6 years ago

Simulacrum's ExpectationT objects are not Send, which means that Simulacrum cannot mock a trait that's Send. Example:

    fn send() {
        pub trait A : Send {
            fn foo(&self);
        }

        create_mock! {
            impl A for AMock (self) {
                expect_foo("foo"):
                fn foo(&self);
            }
        }

        let mut mock = AMock::new();
        mock.expect_foo().called_any();
        mock.foo();
    }

This results in the error:

error[E0277]: `(dyn simulacrum::expectation::ExpectationT + 'static)` cannot be sent between threads safely
   --> src/t_simulacrum.rs:549:9
    |
549 | /         create_mock! {
550 | |             impl A for AMock (self) {
551 | |                 expect_foo("foo"):
552 | |                 fn foo(&self);
553 | |             }
554 | |         }
    | |_________^ `(dyn simulacrum::expectation::ExpectationT + 'static)` cannot be sent between threads safely                                                   
    |
    = help: the trait `std::marker::Send` is not implemented for `(dyn simulacrum::expectation::ExpectationT + 'static)`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<(dyn simulacrum::expectation::ExpectationT + 'static)>`
    = note: required because it appears within the type `std::boxed::Box<(dyn simulacrum::expectation::ExpectationT + 'static)>`
    = note: required because of the requirements on the impl of `std::marker::Send` for `alloc::collections::btree::node::Root<u32, std::boxed::Box<(dyn simulacrum::expectation::ExpectationT + 'static)>>`
    = note: required because it appears within the type `std::collections::BTreeMap<u32, std::boxed::Box<(dyn simulacrum::expectation::ExpectationT + 'static)>>`
    = note: required because it appears within the type `handlebox::HandleBox<std::boxed::Box<(dyn simulacrum::expectation::ExpectationT + 'static)>>`
    = note: required because it appears within the type `simulacrum_mock::store::Inner`
    = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<simulacrum_mock::store::Inner>`
    = note: required because it appears within the type `simulacrum_mock::store::ExpectationStore`
    = note: required because it appears within the type `simulacrum::Expectations`
    = note: required because it appears within the type `<t_simulacrum::t::Simulacrum as TestSuite>::send::AMock`
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
jasongrlicky commented 6 years ago

Hey @asomers, thanks so much for bringing this to my attention. I haven't had very much time to work on Simulacrum recently, but this strikes me as an important thing to be able to have work.

asomers commented 6 years ago

Thanks for taking an interest! BTW, it isn't just Simulacrum. There literally isn't a single mock library for Rust that whose mocks are Send.

ramosbugs commented 5 years ago

I was able to make ExpectationT implement Send (https://github.com/ramosbugs/simulacrum/commit/1be72ba2e0e0113e48756525f8342441670a6b49), but it would constitute a breaking change. I'm not sure how many users would be impacted by that. Thoughts?

asomers commented 5 years ago

I think it's not insignificant that you place a Send bound on I. That would prevent using Simulacrum to mock a function that takes an Rc<T> argument, for example. The solution I came up with (but haven't published yet) is to use Fragile for non-Send types.

ramosbugs commented 5 years ago

interesting! I think for my use case, Fragile wouldn't work because I need another thread to actually read the return value of a mocked function, and from my understanding of Fragile that would fail since it's not the original thread. But, I agree that requiring Send is a big ask. I'll probably just restructure my code to mock out the thread creation to sidestep the issue.

jasongrlicky commented 5 years ago

Fixed in 6b3066a434f5c0e53252040cc11e1c73630a2875. Thank you @asomers for the suggestion to look into the fragile crate!