From 1ef156ca34078a89d2955b52f5df18254bd9c095 Mon Sep 17 00:00:00 2001 From: Arnaud Bailly Date: Mon, 7 Oct 2024 09:59:03 +0200 Subject: Change delay on test success/failure --- rust/src/web.rs | 166 +++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 117 insertions(+), 49 deletions(-) (limited to 'rust/src/web.rs') diff --git a/rust/src/web.rs b/rust/src/web.rs index 5729083..62dea22 100644 --- a/rust/src/web.rs +++ b/rust/src/web.rs @@ -1,23 +1,12 @@ -use actix_web::{ - get, http::header::ContentType, middleware::Logger, post, web, App, HttpResponse, HttpServer, - Responder, -}; +use actix_web::{middleware::Logger, post, web, App, HttpResponse, HttpServer, Responder}; use clap::Parser; -use futures::try_join; -use futures::{future::join_all, lock::Mutex}; -use lambda::ast::Value; +use futures::lock::Mutex; use log::info; use proptest::test_runner::TestRunner; -use rand::rngs::SmallRng; -use rand::{Rng, SeedableRng}; +use rand::Rng; use serde::{Deserialize, Serialize}; -use std::{ - collections::HashMap, - env::args, - io::{stdin, stdout, IsTerminal}, - sync::Arc, -}; -use tokio; +use std::time::Duration; +use std::{collections::HashMap, sync::Arc}; use tokio::task; use uuid::Uuid; @@ -46,8 +35,11 @@ struct Client { grade: u8, runner: TestRunner, seed: u64, + results: Vec, + delay: std::time::Duration, } +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] enum TestResult { TestFailed(String), ErrorSendingTest(String), @@ -55,8 +47,20 @@ enum TestResult { } impl Client { - fn time_to_next_test(&self) -> std::time::Duration { - std::time::Duration::from_secs(1) + fn new(url: String) -> Self { + Self { + id: Uuid::new_v4(), + url, + grade: 1, + runner: TestRunner::deterministic(), + seed: 42, + results: Vec::new(), + delay: Duration::from_secs(10), + } + } + + fn time_to_next_test(&self) -> Duration { + self.delay } fn generate_expr(&mut self) -> (String, String) { @@ -67,26 +71,33 @@ impl Client { fn check_result( &mut self, - expected: String, - response: Result, + expected: &String, + response: &Result, ) -> TestResult { - match response { + let result = match response { Ok(expr) => { - let vals = parse(&expr); + let vals = parse(expr); let actual = eval_all(&vals) .iter() .map(|v| format!("{}", v)) .collect::>() .join("\n"); - if actual == expected { + if actual == *expected { self.grade += 1; + self.delay = Duration::from_secs_f64(self.delay.as_secs_f64() * 0.8); TestResult::TestSucceeded } else { + self.delay = Duration::from_secs_f64(self.delay.as_secs_f64() * 1.2); + if self.delay.as_secs() > 30 { + self.delay = Duration::from_secs(30); + } TestResult::TestFailed(actual) } } - Err(res) => res, - } + Err(res) => res.clone(), + }; + self.results.push(result.clone()); + result } } @@ -105,29 +116,22 @@ 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, - runner: TestRunner::deterministic(), - })); - let client_s = client.clone(); - self.clients.insert(registration.url.clone(), client); + let client = Client::new(registration.url.clone()); + let id = client.id.to_string(); + let client_ref = Arc::new(Mutex::new(client)); + let client_s = client_ref.clone(); + self.clients.insert(registration.url.clone(), client_ref); // let it run in the background // FIXME: should find a way to handle graceful termination task::spawn(async move { send_tests(client_s).await }); RegistrationResult::RegistrationSuccess { - id: id.to_string(), + id, url: registration.url.clone(), } } @@ -191,13 +195,12 @@ async fn main() -> std::io::Result<()> { async fn send_tests(client_m: Arc>) { loop { let mut client = client_m.lock().await; - let time_to_next_test = client.time_to_next_test(); - let (input, expected) = client.generate_expr(); + tokio::time::sleep(client.time_to_next_test()).await; - tokio::time::sleep(time_to_next_test).await; + let (input, expected) = client.generate_expr(); let response = send_test(&input, &client.url).await; - client.check_result(expected, response); + client.check_result(&expected, &response); } } @@ -228,6 +231,7 @@ mod app_tests { use actix_web::http::header::TryIntoHeaderValue; use actix_web::{body, http::header::ContentType, middleware::Logger, test, App}; + use lambda::ast::Value; use super::*; @@ -357,13 +361,7 @@ mod app_tests { } fn client() -> Client { - Client { - id: Uuid::new_v4(), - url: "http://1.2.3.4".to_string(), - grade: 1, - seed: 42, - runner: TestRunner::deterministic(), - } + Client::new("http://1.2.3.4".to_string()) } #[test] @@ -401,4 +399,74 @@ mod app_tests { _ => panic!("Expected symbol, got {:?}", parsed), } } + + #[test] + async fn client_increases_grade_on_successful_test() { + let mut client = client(); + let expected = "1".to_string(); + let response = Ok("1".to_string()); + + let result = client.check_result(&expected, &response); + + assert_eq!(TestResult::TestSucceeded, result); + assert_eq!(2, client.grade); + } + + #[test] + async fn client_does_not_increase_grade_on_failed_test() { + let mut client = client(); + let expected = "1".to_string(); + let response = Ok("2".to_string()); + + let result = client.check_result(&expected, &response); + + assert_eq!(TestResult::TestFailed("2".to_string()), result); + assert_eq!(1, client.grade); + } + + #[test] + async fn client_starts_delay_to_next_test_at_10s() { + let client = client(); + + let delay = client.time_to_next_test(); + + assert_eq!(std::time::Duration::from_secs(10), delay); + } + + #[test] + async fn client_increases_delay_to_next_upon_failed_test() { + let mut client = client(); + let expected = "1".to_string(); + let response = Ok("2".to_string()); + let delay_before = client.time_to_next_test(); + + client.check_result(&expected, &response); + + assert!(delay_before < client.time_to_next_test()); + } + + #[test] + async fn client_increases_delay_to_maximum_of_30s() { + let mut client = client(); + let expected = "1".to_string(); + let response = Ok("2".to_string()); + + for _ in 0..100 { + client.check_result(&expected, &response); + } + + assert_eq!(Duration::from_secs(30), client.time_to_next_test()); + } + + #[test] + async fn client_decreases_delay_to_next_upon_successful_test() { + let mut client = client(); + let expected = "1".to_string(); + let response = Ok("1".to_string()); + let delay_before = client.time_to_next_test(); + + client.check_result(&expected, &response); + + assert!(delay_before > client.time_to_next_test()); + } } -- cgit v1.2.3