From aaaee7bdc476f5f0631dc0d2f367c54bdfe03d12 Mon Sep 17 00:00:00 2001 From: Arnaud Bailly Date: Sun, 6 Oct 2024 16:05:32 +0200 Subject: Restructure Client to contain all test execution logic --- rust/src/web.rs | 135 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 37 deletions(-) (limited to 'rust/src/web.rs') 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, + ) -> TestResult { + match response { + Ok(expr) => { + let vals = parse(&expr); + let actual = eval_all(&vals) + .iter() + .map(|v| format!("{}", v)) + .collect::>() + .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>) { 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 { 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); + } } -- cgit v1.2.3