From b933115607540a089046693e5583d4e17688c433 Mon Sep 17 00:00:00 2001 From: Olivier Desenfans Date: Mon, 20 Nov 2023 16:07:37 +0100 Subject: [PATCH] Internal: use rstest for complex tests (#10) Problem: we tend to repeat a lot of boilerplate in tests because normal Rust tests are limited (ex: no fixtures). Solution: use rstest. --- Cargo.toml | 1 + stone-prover/Cargo.toml | 1 + stone-prover/src/prover.rs | 190 ++++++++++++++++++++++++------------- 3 files changed, 124 insertions(+), 68 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1a14f8d..4ecc517 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,4 @@ serde_json = "1.0.108" tokio = { version = "1.34.0", features = ["macros", "process", "rt-multi-thread"] } tonic = "0.10.2" tonic-build = "0.10.2" +rstest = "0.18.2" diff --git a/stone-prover/Cargo.toml b/stone-prover/Cargo.toml index 088afdc..e07a908 100644 --- a/stone-prover/Cargo.toml +++ b/stone-prover/Cargo.toml @@ -12,4 +12,5 @@ thiserror = "1.0.50" tokio = { workspace = true } [dev-dependencies] +rstest = { workspace = true } test-toolkit = { path = "../test-toolkit" } \ No newline at end of file diff --git a/stone-prover/src/prover.rs b/stone-prover/src/prover.rs index a204a8e..7299b06 100644 --- a/stone-prover/src/prover.rs +++ b/stone-prover/src/prover.rs @@ -223,6 +223,7 @@ pub async fn run_prover_async( #[cfg(test)] mod test { + use rstest::{fixture, rstest}; use tempfile::NamedTempFile; use test_toolkit::get_fixture_path; @@ -238,28 +239,115 @@ mod test { proof } - /// Check that the Stone Prover command-line wrapper works. - #[test] - fn test_run_prover_from_command_line() { - let prover_config_file = get_fixture_path("fibonacci/cpu_air_prover_config.json"); - let parameter_file = get_fixture_path("fibonacci/cpu_air_params.json"); + /// All the files forming a complete prover test case. + struct ProverTestCase { + public_input_file: PathBuf, + prover_config_file: PathBuf, + prover_parameter_file: PathBuf, + memory_file: PathBuf, + trace_file: PathBuf, + proof_file: PathBuf, + } + + #[fixture] + fn fibonacci() -> ProverTestCase { let public_input_file = get_fixture_path("fibonacci/fibonacci_public_input.json"); + let prover_config_file = get_fixture_path("fibonacci/cpu_air_prover_config.json"); + let prover_parameter_file = get_fixture_path("fibonacci/cpu_air_params.json"); let memory_file = get_fixture_path("fibonacci/fibonacci_memory.bin"); let trace_file = get_fixture_path("fibonacci/fibonacci_trace.bin"); + let proof_file = get_fixture_path("fibonacci/fibonacci_proof.json"); + + ProverTestCase { + public_input_file, + prover_config_file, + prover_parameter_file, + memory_file, + trace_file, + proof_file, + } + } + /// Test case files adapted to match the prover command line arguments. + struct ProverCliTestCase { + public_input_file: PathBuf, + private_input_file: NamedTempFile, + prover_config_file: PathBuf, + prover_parameter_file: PathBuf, + proof: Proof, + } + + #[fixture] + fn prover_cli_test_case(#[from(fibonacci)] files: ProverTestCase) -> ProverCliTestCase { // Generate the private input in a temporary file let private_input_file = NamedTempFile::new().expect("Creating temporary private input file failed"); let private_input = PrivateInput { - memory_path: memory_file, - trace_path: trace_file, + memory_path: files.memory_file.clone(), + trace_path: files.trace_file.clone(), pedersen: vec![], range_check: vec![], ecdsa: vec![], }; + serde_json::to_writer(&private_input_file, &private_input) .expect("Writing private input file failed"); + let proof = read_proof_file(&files.proof_file); + + ProverCliTestCase { + public_input_file: files.public_input_file, + private_input_file, + prover_config_file: files.prover_config_file, + prover_parameter_file: files.prover_parameter_file, + proof, + } + } + + struct ParsedProverTestCase { + public_input: PublicInput, + memory: Vec, + trace: Vec, + prover_config: ProverConfig, + prover_parameters: ProverParameters, + proof: Proof, + } + + #[fixture] + fn parsed_prover_test_case(#[from(fibonacci)] files: ProverTestCase) -> ParsedProverTestCase { + let public_input: PublicInput = read_json_from_file(files.public_input_file).unwrap(); + let prover_config: ProverConfig = read_json_from_file(files.prover_config_file).unwrap(); + let prover_parameters: ProverParameters = + read_json_from_file(files.prover_parameter_file).unwrap(); + let memory = std::fs::read(files.memory_file).unwrap(); + let trace = std::fs::read(files.trace_file).unwrap(); + + let proof = read_proof_file(&files.proof_file); + + ParsedProverTestCase { + public_input, + memory, + trace, + prover_config, + prover_parameters, + proof, + } + } + + #[fixture] + fn prover_in_path() { + // Add build dir to path for the duration of the test + let path = std::env::var("PATH").unwrap_or_default(); + let build_dir = env!("OUT_DIR"); + std::env::set_var("PATH", format!("{build_dir}:{path}")); + } + + /// Check that the Stone Prover command-line wrapper works. + #[rstest] + fn test_run_prover_from_command_line( + prover_cli_test_case: ProverCliTestCase, + #[from(prover_in_path)] _path: (), + ) { // Add build dir to path for the duration of the test let path = std::env::var("PATH").unwrap_or_default(); let build_dir = env!("OUT_DIR"); @@ -267,85 +355,51 @@ mod test { let output_file = NamedTempFile::new().expect("Creating output file failed"); run_prover_from_command_line( - &public_input_file, - private_input_file.path(), - &prover_config_file, - ¶meter_file, + &prover_cli_test_case.public_input_file, + &prover_cli_test_case.private_input_file.path(), + &prover_cli_test_case.prover_config_file, + &prover_cli_test_case.prover_parameter_file, output_file.path(), ) .unwrap(); let proof = read_proof_file(output_file.path()); - let expected_proof_file = get_fixture_path("fibonacci/fibonacci_proof.json"); - let expected_proof = read_proof_file(expected_proof_file); - - assert_eq!(proof.proof_hex, expected_proof.proof_hex); + assert_eq!(proof.proof_hex, prover_cli_test_case.proof.proof_hex); } - #[test] - fn test_run_prover() { - let public_input_file = get_fixture_path("fibonacci/fibonacci_public_input.json"); - let prover_config_file = get_fixture_path("fibonacci/cpu_air_prover_config.json"); - let parameter_file = get_fixture_path("fibonacci/cpu_air_params.json"); - let memory_file = get_fixture_path("fibonacci/fibonacci_memory.bin"); - let trace_file = get_fixture_path("fibonacci/fibonacci_trace.bin"); - - let public_input: PublicInput = read_json_from_file(public_input_file).unwrap(); - let prover_config: ProverConfig = read_json_from_file(prover_config_file).unwrap(); - let prover_parameters: ProverParameters = read_json_from_file(parameter_file).unwrap(); - let memory = std::fs::read(memory_file).unwrap(); - let trace = std::fs::read(trace_file).unwrap(); - - // Add build dir to path for the duration of the test - let path = std::env::var("PATH").unwrap_or_default(); - let build_dir = env!("OUT_DIR"); - std::env::set_var("PATH", format!("{build_dir}:{path}")); - + #[rstest] + fn test_run_prover( + parsed_prover_test_case: ParsedProverTestCase, + #[from(prover_in_path)] _path: (), + ) { let proof = run_prover( - &public_input, - &memory, - &trace, - &prover_config, - &prover_parameters, + &parsed_prover_test_case.public_input, + &parsed_prover_test_case.memory, + &parsed_prover_test_case.trace, + &parsed_prover_test_case.prover_config, + &parsed_prover_test_case.prover_parameters, ) .unwrap(); - let expected_proof_file = get_fixture_path("fibonacci/fibonacci_proof.json"); - let expected_proof = read_proof_file(expected_proof_file); - assert_eq!(proof.proof_hex, expected_proof.proof_hex); + assert_eq!(proof.proof_hex, parsed_prover_test_case.proof.proof_hex); } + #[rstest] #[tokio::test] - async fn test_run_prover_async() { - let public_input_file = get_fixture_path("fibonacci/fibonacci_public_input.json"); - let prover_config_file = get_fixture_path("fibonacci/cpu_air_prover_config.json"); - let parameter_file = get_fixture_path("fibonacci/cpu_air_params.json"); - let memory_file = get_fixture_path("fibonacci/fibonacci_memory.bin"); - let trace_file = get_fixture_path("fibonacci/fibonacci_trace.bin"); - - let public_input: PublicInput = read_json_from_file(public_input_file).unwrap(); - let prover_config: ProverConfig = read_json_from_file(prover_config_file).unwrap(); - let prover_parameters: ProverParameters = read_json_from_file(parameter_file).unwrap(); - let memory = std::fs::read(memory_file).unwrap(); - let trace = std::fs::read(trace_file).unwrap(); - - // Add build dir to path for the duration of the test - let path = std::env::var("PATH").unwrap_or_default(); - let build_dir = env!("OUT_DIR"); - std::env::set_var("PATH", format!("{build_dir}:{path}")); - + async fn test_run_prover_async( + parsed_prover_test_case: ParsedProverTestCase, + #[from(prover_in_path)] _path: (), + ) { let proof = run_prover_async( - &public_input, - &memory, - &trace, - &prover_config, - &prover_parameters, + &parsed_prover_test_case.public_input, + &parsed_prover_test_case.memory, + &parsed_prover_test_case.trace, + &parsed_prover_test_case.prover_config, + &parsed_prover_test_case.prover_parameters, ) .await .unwrap(); - let expected_proof_file = get_fixture_path("fibonacci/fibonacci_proof.json"); - let expected_proof = read_proof_file(expected_proof_file); - assert_eq!(proof.proof_hex, expected_proof.proof_hex); + assert_eq!(proof.proof_hex, parsed_prover_test_case.proof.proof_hex); } }