summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnaud Bailly <arnaud.bailly@iohk.io>2024-10-06 16:05:32 +0200
committerArnaud Bailly <arnaud.bailly@iohk.io>2024-10-06 16:05:32 +0200
commitaaaee7bdc476f5f0631dc0d2f367c54bdfe03d12 (patch)
tree6cfcc377df1a25ab351748b32439fbc2fc6d3f44
parent8e2942b353cff3af8b20a040dca260a4420dd76e (diff)
downloadlambda-nantes-aaaee7bdc476f5f0631dc0d2f367c54bdfe03d12.tar.gz
Restructure Client to contain all test execution logic
-rw-r--r--rust/Cargo.toml2
-rw-r--r--rust/src/lambda.rs12
-rw-r--r--rust/src/web.rs135
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(&registration.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);
+ }
}