microsoft / microsoft/injectorppforrust

Ability to pass closure in async

Open
#100 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Rust
Stars
404
Forks
19
Avg merge
23h 57m
Merged PRs (30d)
1

Description

Hey Folks,

I was trying out the injectorpp to mock async calls. I wanted a way to map input to output. e.g

{
  "hostname": "IamInLinux",
  "ls": "remotefile1 remotefile2"
}

For sync functions I was able to use will_execute_raw .

Is there a way to pass async closures or fake_functions in async ?

I tried to implement the above behaviour using multiple calls to when_called_async in test_multicall, but it fails.

use tokio::process::Command;

#[tokio::main]
async fn main() {
    run().await;
    // run_double();
}

async fn run() {
    let output = Command::new("ls")
        .output()
        .await
        .expect("Failed to execute command");

    if output.status.success() {
        let stdout = String::from_utf8_lossy(&output.stdout);
        println!("Command output:\n{}", stdout);
    } else {
        let stderr = String::from_utf8_lossy(&output.stderr);
        eprintln!("Command failed:\n{}", stderr);
    }
}

async fn run_double() {
    let output = Command::new("ls")
        .output()
        .await
        .expect("Failed to execute command");

    if output.status.success() {
        let stdout = String::from_utf8_lossy(&output.stdout);
        println!("Command output:\n{}", stdout);
    } else {
        let stderr = String::from_utf8_lossy(&output.stderr);
        eprintln!("Command failed:\n{}", stderr);
    }

    let output = Command::new("hostname")
        .output()
        .await
        .expect("Failed to execute command");

    if output.status.success() {
        let stdout = String::from_utf8_lossy(&output.stdout);
        println!("Command output:\n{}", stdout);
    } else {
        let stderr = String::from_utf8_lossy(&output.stderr);
        eprintln!("Command failed:\n{}", stderr);
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use injectorpp::interface::injector::*;
    use tokio::process::Command;

    use std::process::{ExitStatus, Output};

    #[tokio::test]
    async fn test_simple() {
        let mut c = Command::new("ls");
        let mut injector = InjectorPP::new();
        injector
            .when_called_async(injectorpp::async_func!(
                Command::output(&mut c),
                std::io::Result<Output>
            ))
            .will_return_async(injectorpp::async_return!(
                Ok(Output {
                    status: ExitStatus::default(),
                    stdout: b"1,2".to_vec(),
                    stderr: b"".to_vec()
                }),
                std::io::Result<Output>
            ));

        run().await;
    }

    async fn fake_cmd_run(cmd: &mut Command) -> std::io::Result<Output> {
        if cmd.as_std().get_program() == "ls" {
            Ok(Output {
                status: ExitStatus::default(),
                stdout: b"fakefile1,fakefile2".to_vec(),
                stderr: b"".to_vec(),
            })
        } else if cmd.as_std().get_program() == "hostname" {
            Ok(Output {
                status: ExitStatus::default(),
                stdout: b"IamLinux".to_vec(),
                stderr: b"".to_vec(),
            })
        } else {
            Err(std::io::Error::other("Did not work"))
        }
    }

    #[tokio::test]
    async fn test_multicall() {
        let mut c = Command::new("ls");

        let mut injector = InjectorPP::new();
        injector
            .when_called_async(injectorpp::async_func!(
                Command::output(&mut c),
                std::io::Result<Output>
            ))
            .will_return_async(injectorpp::async_return!(
                Ok(Output {
                    status: ExitStatus::default(),
                    stdout: b"1,2".to_vec(),
                    stderr: b"".to_vec()
                }),
                std::io::Result<Output>
            ));

        let mut z = Command::new("hostname");

        injector
            .when_called_async(injectorpp::async_func!(
                Command::output(&mut z),
                std::io::Result<Output>
            ))
            .will_return_async(injectorpp::async_return!(
                Ok(Output {
                    status: ExitStatus::default(),
                    stdout: b"IamLinux".to_vec(),
                    stderr: b"".to_vec()
                }),
                std::io::Result<Output>
            ));

        run_double().await;
    }
}

cargo test output

---- test::test_simple stdout ----
Command output:
1,2

---- test::test_multicall stdout ----
Command output:
IamLinux
Command output:
IamLinux

Expected output:

---- test::test_multicall stdout ----
Command output:
1,2
Command output:
IamLinux

Cargo.toml

[package]
name = "injector-example"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { version = "1", features = ["full"] }
tokio-test = "0.4"

[dev-dependencies]
injectorpp = { branch = "main", git = "https://github.com/microsoft/injectorppforrust.git", default-features = false }

I am using Mac M2 Pro to run the test and latest main branch (aa3c7f464c9f6e6f9fa1e6d4d9337c4bd9d53b89) of injectorpp repository.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the async mocking APIs used in the reported test: when_called_async, async_func!, will_return_async, and test_multicall. Reproduce the failure with the provided tokio tests, then trace how successive async calls are matched and determine how closure-based results should be supported. Done means test_multicall returns 1,2 for ls and IamLinux for hostname, with a regression test covering the behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
testing
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.