Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mocking of embedded-hal #70

Closed
dbrgn opened this issue Mar 25, 2018 · 15 comments
Closed

Mocking of embedded-hal #70

dbrgn opened this issue Mar 25, 2018 · 15 comments
Labels
feb-2019-cleanup These issues are proposed to be closed, as part of a cleanup of issues in February 2019

Comments

@dbrgn
Copy link

dbrgn commented Mar 25, 2018

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:

  • Set the data that will be read by an I2C device in advance
  • Set an error that will be returned when writing to an I2C device in advance
  • etc...

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
Copy link
Member

@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
Copy link
Author

dbrgn commented Mar 25, 2018

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 rust-embedded/embedded-hal#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
Copy link
Member

@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:

  • Use a channel internally, so you can keep sending data or configuration commands, or maybe a command/data abstraction built on top of channels, so you can keep cloning the sender, and you can probably just keep moving the receiver
  • Use some kind of mutable global key/value store

@therealprof
Copy link
Contributor

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

@jamesmunns
Copy link
Member

@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
Copy link
Member

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
Copy link
Contributor

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

@jamesmunns
Copy link
Member

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
Copy link
Contributor

@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
Copy link
Author

dbrgn commented Mar 30, 2018

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
Copy link

Nemo157 commented Mar 30, 2018

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
Copy link
Author

dbrgn commented Mar 30, 2018

@Nemo157 ah of course, you are right! 👍

@susu
Copy link

susu commented Apr 4, 2018

I did something nasty:

  • integration test (tests directory, next to src), so I could use std
  • by using an Rc<RefCell<X>> I wrote a "simulator" of my SX1278 chip (I called it mock, but it simulates the SPI traffic)

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
Copy link
Member

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?

@jamesmunns jamesmunns added the feb-2019-cleanup These issues are proposed to be closed, as part of a cleanup of issues in February 2019 label Feb 4, 2019
@dbrgn
Copy link
Author

dbrgn commented Feb 4, 2019

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

@dbrgn dbrgn closed this as completed Feb 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feb-2019-cleanup These issues are proposed to be closed, as part of a cleanup of issues in February 2019
Projects
None yet
Development

No branches or pull requests

5 participants