diff options
| author | Arnaud Bailly <arnaud.bailly@iohk.io> | 2024-10-06 16:05:32 +0200 |
|---|---|---|
| committer | Arnaud Bailly <arnaud.bailly@iohk.io> | 2024-10-06 16:05:32 +0200 |
| commit | aaaee7bdc476f5f0631dc0d2f367c54bdfe03d12 (patch) | |
| tree | 6cfcc377df1a25ab351748b32439fbc2fc6d3f44 | |
| parent | 8e2942b353cff3af8b20a040dca260a4420dd76e (diff) | |
| download | lambda-nantes-aaaee7bdc476f5f0631dc0d2f367c54bdfe03d12.tar.gz | |
Restructure Client to contain all test execution logic
| -rw-r--r-- | rust/Cargo.toml | 2 | ||||
| -rw-r--r-- | rust/src/lambda.rs | 12 | ||||
| -rw-r--r-- | rust/src/web.rs | 135 |
3 files changed, 108 insertions, 41 deletions
diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 01a9aa0..887bc41 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -rand = "0.8.5" +rand = { version = "0.8.5", features = ["small_rng"] } serde = { version = "1.0", features = ["derive"] } chrono = { version= "0.4.38", features = ["serde"]} serde_json = "1.0.128" diff --git a/rust/src/lambda.rs b/rust/src/lambda.rs index 7c8d9be..d1ab5b5 100644 --- a/rust/src/lambda.rs +++ b/rust/src/lambda.rs @@ -1,4 +1,4 @@ -use rand::Rng; +use rand::{rngs::SmallRng, Rng, RngCore, SeedableRng}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -98,8 +98,14 @@ fn gensym() -> String { format!("x_{}", n1) } -pub fn generate_expr(size: u32) -> Value { - Value::Num(3) +pub fn generate_expr<R: Rng>(size: u32, rng: &mut R) -> Value { + match size { + 0 | 1 => { + let n: u16 = rng.gen(); + Value::Num(n.into()) + } + _ => todo!(), + } } #[cfg(test)] diff --git a/rust/src/web.rs b/rust/src/web.rs index e805da7..6bc892b 100644 --- a/rust/src/web.rs +++ b/rust/src/web.rs @@ -5,7 +5,10 @@ use actix_web::{ use clap::Parser; use futures::try_join; use futures::{future::join_all, lock::Mutex}; +use lambda::ast::Value; use log::info; +use rand::rngs::SmallRng; +use rand::{Rng, SeedableRng}; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, @@ -17,7 +20,7 @@ use tokio; use tokio::task; use uuid::Uuid; -use lambda::lambda::{eval_whnf, generate_expr, Environment}; +use lambda::lambda::{eval_all, eval_whnf, generate_expr, Environment}; use lambda::parser::parse; #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] @@ -35,11 +38,55 @@ enum RegistrationResult { UrlAlreadyRegistered { url: String }, } -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +#[derive(Debug, PartialEq, Clone)] struct Client { id: Uuid, url: String, grade: u8, + rng: SmallRng, + seed: u64, +} + +enum TestResult { + TestFailed(String), + ErrorSendingTest(String), + TestSucceeded, +} + +impl Client { + fn time_to_next_test(&self) -> std::time::Duration { + std::time::Duration::from_secs(1) + } + + fn generate_expr(&mut self) -> (String, String) { + let input = generate_expr(self.grade.into(), &mut self.rng); + let expected = eval_whnf(&input, &mut Environment::new()); + (input.to_string(), expected.to_string()) + } + + fn check_result( + &mut self, + expected: String, + response: Result<String, TestResult>, + ) -> TestResult { + match response { + Ok(expr) => { + let vals = parse(&expr); + let actual = eval_all(&vals) + .iter() + .map(|v| format!("{}", v)) + .collect::<Vec<_>>() + .join("\n"); + if actual == expected { + self.grade += 1; + TestResult::TestSucceeded + } else { + TestResult::TestFailed(actual) + } + } + Err(res) => res, + } + } } #[derive(Debug)] @@ -57,16 +104,20 @@ impl State { impl AppState for State { fn register(&mut self, registration: &Registration) -> RegistrationResult { + let mut rng = rand::thread_rng(); if self.clients.contains_key(®istration.url) { RegistrationResult::UrlAlreadyRegistered { url: registration.url.clone(), } } else { let id = Uuid::new_v4(); + let seed = rng.gen(); let client = Arc::new(Mutex::new(Client { id, url: registration.url.clone(), grade: 1, + seed, + rng: SmallRng::seed_from_u64(seed), })); let client_s = client.clone(); self.clients.insert(registration.url.clone(), client); @@ -138,49 +189,34 @@ async fn main() -> std::io::Result<()> { async fn send_tests(client_m: Arc<Mutex<Client>>) { loop { - tokio::time::sleep(std::time::Duration::from_secs(1)).await; let mut client = client_m.lock().await; - let result = send_test(client.grade, &client.url).await; - match result { - Ok(_) => { - client.grade += 1; - } - Err(_) => { - // FIXME - } - } + let time_to_next_test = client.time_to_next_test(); + let (input, expected) = client.generate_expr(); + + tokio::time::sleep(time_to_next_test).await; + let response = send_test(&input, &client.url).await; + + client.check_result(expected, response); } } -async fn send_test(grade: u8, url: &String) -> Result<(), String> { - let input = generate_expr(grade.into()); - let mut env = Environment::new(); - let output = eval_whnf(&input, &mut env); +async fn send_test(input: &String, url: &String) -> Result<String, TestResult> { info!("Sending {} to {}", input, url); + let body = input.clone(); let response = reqwest::Client::new() .post(url) .header("content-type", "text/plain") - .body(format!("{}", input)) + .body(body) .send() .await; match response { Ok(response) => { let body = response.text().await.unwrap(); - let vals = parse(&body); - // FIXME: should be able to handle multiple values - let result = eval_whnf(vals.first().unwrap(), &mut env); - info!("Received {} from {}", body, url); - if result == output { - info!("Test passed {}", url); - Ok(()) - } else { - info!("Test failed {}", url); - Err("Test failed".to_string()) - } + Ok(body) } Err(e) => { info!("Error sending test: {}", e); - Err("Error sending test".to_string()) + Err(TestResult::ErrorSendingTest(e.to_string())) } } } @@ -319,12 +355,37 @@ mod app_tests { ); } - // #[test] - // async fn tester_repeatedly_callback_registered_client() { - // let app_state = Arc::new(Mutex::new(State::new())); - // let send_state = app_state.clone(); - // let client = Client { - // id: Uuid::new_v4(), - // url: "http:// - // } + #[test] + async fn client_generates_constant_at_level_1() { + let mut client = Client { + id: Uuid::new_v4(), + url: "http://1.2.3.4".to_string(), + grade: 1, + seed: 42, + rng: SmallRng::seed_from_u64(42), + }; + + let (input, _) = client.generate_expr(); + + match parse(&input)[..] { + [Value::Num(_)] => (), + _ => panic!("Expected constant 3"), + } + } + + #[test] + async fn client_generates_different_inputs_on_each_call() { + let mut client = Client { + id: Uuid::new_v4(), + url: "http://1.2.3.4".to_string(), + grade: 1, + seed: 42, + rng: SmallRng::seed_from_u64(42), + }; + + let (input1, _) = client.generate_expr(); + let (input2, _) = client.generate_expr(); + + assert_ne!(input1, input2); + } } |
