rust-embedded / wg

Coordination repository of the embedded devices Working Group
1.93k stars 98 forks source link

Mocking of embedded-hal #70

Closed dbrgn closed 5 years ago

dbrgn commented 6 years ago

What's the best way to test embedded-hal drivers without actual hardware access?

I think a "mocked" implementation of the embedded-hal traits would be very useful. I started with I2C and Delay:

https://github.com/dbrgn/embedded-hal-mock

The goal is to provide functions to mock certain outcomes:

Currently I only provide implementations for I2C and a no-op implementation of the Delay trait.

Is this a good approach? If yes, contributions are welcome. (I'm also open to moving the crate/repo to another organization where more people can contribute.)

jamesmunns commented 6 years ago

@dbrgn awesome! We were actually just talking about this in the wg. I'll take a more detailed look later!

CC @japaric and @jcsoo

dbrgn commented 6 years ago

What I noticed when trying to write a few tests is that it's not possible to reconfigure the mock if you pass it into a driver by-value.

This probably ties into https://github.com/japaric/embedded-hal/issues/35

Right now I'm simply creating a new driver instance every time I want to change the return value:

https://github.com/dbrgn/sgp30-rs/blob/0b8d54fd34bbee2ac79258982b3bfb61c919ec6b/src/lib.rs#L180-L202

jamesmunns commented 6 years ago

@dbrgn - generally whenever I worked with this in C, I would have some kind of global that would handle this, or provide access. I haven't tried to tackle this in Rust before.

A couple ideas I have off the top of my head:

therealprof commented 6 years ago

@jamesmunns I was thinking of using channels for the shared driver problem discussed in japaric/embedded-hal#35, but AFAIK there's nothing like this available in #[no_std] at the moment?

jamesmunns commented 6 years ago

@therealprof Can you just extern crate std; in the test mod? Or maybe #cfg[test] extern crate std;?

I'll try to make a test example

jamesmunns commented 6 years ago

This is a tiny impl, I'm going to take it further to try out what a mock impl would look like:

➜  nostdtest git:(master) ✗ cat src/lib.rs 
#![no_std]
#![allow(unused_variables, dead_code)]

fn foo() -> () {
    ()
}

#[cfg(test)]
mod tests {
    extern crate std;

    #[test]
    fn it_works() {
        use tests::std::vec::Vec;
        let x: Vec<u8> = Vec::new();
        assert_eq!(2 + 2, 4);
    }
}
➜  nostdtest git:(master) ✗ cargo check
   Compiling nostdtest v0.1.0 (file:///home/james/personal/nostdtest)
    Finished dev [unoptimized + debuginfo] target(s) in 0.10 secs
➜  nostdtest git:(master) ✗ cargo test
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running target/debug/deps/nostdtest-f266c74e06fc6874

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests nostdtest

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

➜  nostdtest git:(master) ✗ xargo check --target thumbv6m-none-eabi
   Compiling nostdtest v0.1.0 (file:///home/james/personal/nostdtest)
    Finished dev [unoptimized + debuginfo] target(s) in 0.10 secs
therealprof commented 6 years ago

@jamesmunns Well, my question was related to using channels on #[no_std] drivers so the answer is: no, I can't.

jamesmunns commented 6 years ago

Hey @therealprof, sorry, what I was trying to illustrate was that the channels would only be used by the test and embedded-hal-mock, but not in the underlying driver crate you are trying to develop/test. I'll get back to you later today with an experiment of what I meant, and if that was not helpful (or not what you had in mind, or doesn't actually work), then thats okay :)

therealprof commented 6 years ago

@jamesmunns I just had a glimpse of hope that someone had an idea that would help to solve one of the most limiting problems we face on embedded rust today. If that's not you, that's okay. ;)

dbrgn commented 6 years ago

My intent was definitely to facilitate testing of drivers, so while channels are a great idea that probably won't work with no_std...

Nemo157 commented 6 years ago

Your mock doesn't need to be no_std, you can definitely use standard library channels inside mock implementations of the embedded-hal traits and pass those into no_std drivers.

dbrgn commented 6 years ago

@Nemo157 ah of course, you are right! :+1:

susu commented 6 years ago

I did something nasty:

https://github.com/susu/sx1278/blob/master/tests/test_radio_settings.rs

Because it is a simulator, it is not really reusable. Maybe you can use the "pattern".

jamesmunns commented 5 years ago

Hey @dbrgn, do you feel that this issue should stay open? Or would discussions on this topic be better suited to live in the embedded-hal-mock space?

dbrgn commented 5 years ago

I think embedded-hal-mock has developed quite nicely and has already received a few contributions. I think this can be closed.