mercredi 31 juillet 2019

How to handle passing a mutably borrowed struct to function closure?

I am using the proptest crate to run some property tests in a no_std environment doing bare metal development on ARM. proptest's default test_runner::TestRunner::run() impl takes some input (as a Strategy or ValueTree object defined in proptest) and a function closure as parameters so you can run however many tests you want with the values in the value tree or strategy.

My problem is that I need to test a function call that takes as an argument a mutably borrowed struct. This wont compile, and the compilation error that results is:

error[E0596]: cannot borrow `*<obj>` as mutable, as it is a captured variable in a 'Fn' closure

where obj is the struct passed to the test_runner, passed via a function call one level up as a mutably borrowed object.

Digging into the src code for proptest I believe the error is due to the function signature for test_runner::TestRunner::run(), as the test parameter needs to be implemented as FnMut so that captured variables can be mutable in the function call. Here is the function signature for test_runner::TestRunner::run()

pub fn run<S: Strategy>(
        &mut self,
        strategy: &S,
        test: impl Fn(S::Value) -> TestCaseResult,
    ) -> TestRunResult<S> {

As I mentioned above Im doing bare metal development with no_std as prereq. So far I have found that I am unable to get custom test frameworks to work correctly, possibly because I have a main() func defined, but ultimately the main() that is supposedly generated by the feature never is set up and none of the test functions are ever collected. This may be partly because I am using xargo to compile, as it adequately handles the complex linking needs I have which I was unable to get to work using cargo xbuild (at a higher level it seems cargo build is ignoring my linker flag arguments even if i add them to a .cargo/config file)

It is also probably worth noting that I am not able to clone the object because it included metadata with different lifetimes and also objects defined in a third party library that do not implement cloning.

Also relevant background I have only been working with rust for ~6 months and so I know I am probably missing a lot here.

Here is the function passed from a conditionally compiled run func:

fn test_mod(runner: &mut TestRunner, obj: &mut MyObjStruct) -> Result<(), TestError(u32)>>{
    runner.run(&(0x400_0000u32..0xe00_0000u32), |addr| {
        if mod::test::test_page_map(addr, obj).is_ok() {
            Ok(())
        } else {
            Err(TestCaseError::fail(Reason::from("Failed to map address")))
        }
    })
}

Where mod::test::test_page_map takes the obj, uses relevant metadata in the struct, updates that data accordingly, and maps a page to the input address. It returns a result based on whether or not the map succeeded.

Does anyone see a way around this problem that does not involve simply not using proptest? Is there some mechanism available in no_std land like cell or something that I can wrap the mutable object with so it has interior mutability but can be passed as a captured variable to a 'Fn' closure? Or is this something I should implement and submit a PR to the proptest folks for? I suppose I am not completely stuck with proptest, so am also open to any suggestions on other testing frameworks that work in no_std land

Aucun commentaire:

Enregistrer un commentaire