diff --git a/.gitignore b/.gitignore index e2769d9d2..b707be5e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ *.log coverage +conjure_oxide/tests/**/*.generated.* + ## Rust debug/ target/ diff --git a/Cargo.lock b/Cargo.lock index 573ed9a22..695f2eeb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,14 +27,10 @@ dependencies = [ ] [[package]] -name = "assert-json-diff" -version = "2.0.2" +name = "anyhow" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" -dependencies = [ - "serde", - "serde_json", -] +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "autocfg" @@ -48,13 +44,6 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - - [[package]] name = "bindgen" version = "0.64.0" @@ -179,12 +168,12 @@ dependencies = [ name = "conjure_oxide" version = "0.0.1" dependencies = [ - "assert-json-diff", "minion_rs", "serde", "serde_json", "serde_with", "thiserror", + "walkdir", ] [[package]] @@ -592,6 +581,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "serde" version = "1.0.192" @@ -741,6 +739,16 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasm-bindgen" version = "0.2.88" @@ -823,6 +831,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/conjure_oxide/Cargo.toml b/conjure_oxide/Cargo.toml index e4ea59526..5d53553b9 100644 --- a/conjure_oxide/Cargo.toml +++ b/conjure_oxide/Cargo.toml @@ -4,8 +4,11 @@ version = "0.0.1" edition = "2021" default-run = "conjure_oxide" +# these are available inside build.rs +[build-dependencies] +walkdir = "2.4.0" + [dependencies] -assert-json-diff = "2.0.2" serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.108" serde_with = "3.4.0" diff --git a/conjure_oxide/build.rs b/conjure_oxide/build.rs index 350f42380..7a160cba8 100644 --- a/conjure_oxide/build.rs +++ b/conjure_oxide/build.rs @@ -1,30 +1,55 @@ -use std::{ - env::var, - fs::File, - fs::{read_dir, DirEntry}, - io::Write, - path::Path, -}; +use std::env::var; +use std::fs::{read_dir, File}; +use std::io::{self, Write}; +use std::path::Path; +use walkdir::WalkDir; -fn main() { - let out_dir = var("OUT_DIR").unwrap(); +fn main() -> io::Result<()> { + let out_dir = var("OUT_DIR").map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; // wrapping in a std::io::Error to match main's error type let dest = Path::new(&out_dir).join("gen_tests.rs"); - let mut f = File::create(&dest).unwrap(); + let mut f = File::create(&dest)?; let test_dir = "tests/integration"; - for dir in read_dir(test_dir).unwrap() { - write_test(&mut f, &dir.unwrap()); + + for subdir in WalkDir::new(test_dir) { + let subdir = subdir?; + if subdir.file_type().is_dir() { + let essence_files: Vec = read_dir(subdir.path())? + .filter_map(Result::ok) + .filter(|entry| { + entry + .path() + .extension() + .map_or(false, |ext| ext == "essence") + }) + .filter_map(|entry| { + entry + .path() + .file_stem() + .and_then(|stem| stem.to_str()) + .map(|s| s.to_owned()) + }) + .collect(); + + write_test(&mut f, subdir.path().display().to_string(), essence_files)?; + } } + + Ok(()) } -fn write_test(file: &mut File, dir: &DirEntry) { - let binding = dir.path(); - let path = binding.to_str().unwrap(); - write!( - file, - include_str!("./tests/gen_test_template"), - name = path.replace("./", "").replace("/", "_"), - path = path - ) - .unwrap(); +fn write_test(file: &mut File, path: String, essence_files: Vec) -> io::Result<()> { + // TODO: Consider supporting multiple Essence files? + if essence_files.len() == 1 { + write!( + file, + include_str!("./tests/gen_test_template"), + // TODO: better sanitisation of paths to function names + test_name = path.replace("./", "").replace("/", "_").replace("-", "_"), + test_dir = path, + essence_file = essence_files[0] + ) + } else { + Ok(()) + } } diff --git a/conjure_oxide/src/main.rs b/conjure_oxide/src/main.rs index 9d2b7d103..cc3937147 100644 --- a/conjure_oxide/src/main.rs +++ b/conjure_oxide/src/main.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fs::File, io::Read}; +use std::collections::HashMap; use conjure_oxide::ast::*; diff --git a/conjure_oxide/tests/gen_test_template b/conjure_oxide/tests/gen_test_template index 337c98a2d..2c3dd27ff 100644 --- a/conjure_oxide/tests/gen_test_template +++ b/conjure_oxide/tests/gen_test_template @@ -1,4 +1,4 @@ #[test] -fn {name}() -> Result<(), Box> {{ - integration_test("{path}") +fn {test_name}() -> Result<(), Box> {{ + integration_test("{test_dir}", "{essence_file}") }} diff --git a/conjure_oxide/tests/generated_tests.rs b/conjure_oxide/tests/generated_tests.rs index 56895cff6..03d86a8c8 100644 --- a/conjure_oxide/tests/generated_tests.rs +++ b/conjure_oxide/tests/generated_tests.rs @@ -1,34 +1,66 @@ -use assert_json_diff::assert_json_eq; +use std::env; use std::error::Error; use std::fs::File; use std::io::prelude::*; use conjure_oxide::ast::Model; -fn integration_test(path: &str) -> Result<(), Box> { - println!("Integration test for: {}", path); +use std::path::Path; + +fn main() { + let file_path = Path::new("/path/to/your/file.txt"); + let base_name = file_path.file_stem().and_then(|stem| stem.to_str()); + + match base_name { + Some(name) => println!("Base name: {}", name), + None => println!("Could not extract the base name"), + } +} + +fn integration_test(path: &str, essence_base: &str) -> Result<(), Box> { + // -------------------------------------------------------------------------------- + // -- parsing the essence file + + // calling conjure to convert Essence to astjson let mut cmd = std::process::Command::new("conjure"); let output = cmd .arg("pretty") .arg("--output-format=astjson") - .arg(format!("{path}/input.essence", path = path)) + .arg(format!("{path}/{essence_base}.essence")) .output()?; + assert!( + String::from_utf8(output.stderr)?.is_empty(), + "conjure's stderr is not empty" + ); let astjson = String::from_utf8(output.stdout)?; + + // "parsing" astjson as Model let generated_mdl = Model::from_json(&astjson)?; - let mut expected_str = String::new(); - let mut f = File::open(format!( - "{path}/input.serialised.expected.json", - path = path - ))?; - f.read_to_string(&mut expected_str)?; + // serialise to file + let generated_json = serde_json::to_string_pretty(&generated_mdl)?; + File::create(format!("{path}/{essence_base}.generated.serialised.json"))? + .write_all(generated_json.as_bytes())?; + + if std::env::var("ACCEPT").map_or(false, |v| v == "true") { + std::fs::copy( + format!("{path}/{essence_base}.generated.serialised.json"), + format!("{path}/{essence_base}.expected.serialised.json"), + )?; + } + + // -------------------------------------------------------------------------------- + // -- reading the expected version from the filesystem + + let expected_str = + std::fs::read_to_string(format!("{path}/{essence_base}.expected.serialised.json"))?; + let mut expected_mdl: Model = serde_json::from_str(&expected_str)?; expected_mdl.constraints = Vec::new(); // TODO - remove this line once we parse constraints - println!("Expected {:#?}", expected_mdl); - println!("\nActual {:#?}", generated_mdl); + // -------------------------------------------------------------------------------- + // assert that they are the same model - // assert_json_eq!(serde_json::to_string_pretty(&generated_mdl)?, expected_str); // TODO - replace line once we parse constraints assert_eq!(generated_mdl, expected_mdl); Ok(()) diff --git a/conjure_oxide/tests/integration/basic/bool-01/bool-01.essence b/conjure_oxide/tests/integration/basic/bool-01/bool-01.essence new file mode 100644 index 000000000..67256f5f7 --- /dev/null +++ b/conjure_oxide/tests/integration/basic/bool-01/bool-01.essence @@ -0,0 +1 @@ +find x : bool diff --git a/conjure_oxide/tests/integration/basic/bool-01/bool-01.expected.serialised.json b/conjure_oxide/tests/integration/basic/bool-01/bool-01.expected.serialised.json new file mode 100644 index 000000000..06dbb91f2 --- /dev/null +++ b/conjure_oxide/tests/integration/basic/bool-01/bool-01.expected.serialised.json @@ -0,0 +1,13 @@ +{ + "variables": [ + [ + { + "UserName": "x" + }, + { + "domain": "BoolDomain" + } + ] + ], + "constraints": [] +} \ No newline at end of file diff --git a/conjure_oxide/tests/integration/basic/bool-02/bool-02.essence b/conjure_oxide/tests/integration/basic/bool-02/bool-02.essence new file mode 100644 index 000000000..38ac0068f --- /dev/null +++ b/conjure_oxide/tests/integration/basic/bool-02/bool-02.essence @@ -0,0 +1 @@ +find x, y : bool \ No newline at end of file diff --git a/conjure_oxide/tests/integration/basic/bool-02/bool-02.expected.serialised.json b/conjure_oxide/tests/integration/basic/bool-02/bool-02.expected.serialised.json new file mode 100644 index 000000000..d5883b4ff --- /dev/null +++ b/conjure_oxide/tests/integration/basic/bool-02/bool-02.expected.serialised.json @@ -0,0 +1,21 @@ +{ + "variables": [ + [ + { + "UserName": "y" + }, + { + "domain": "BoolDomain" + } + ], + [ + { + "UserName": "x" + }, + { + "domain": "BoolDomain" + } + ] + ], + "constraints": [] +} \ No newline at end of file diff --git a/conjure_oxide/tests/integration/basic/bool-03/bool-03.essence b/conjure_oxide/tests/integration/basic/bool-03/bool-03.essence new file mode 100644 index 000000000..99a6dc38a --- /dev/null +++ b/conjure_oxide/tests/integration/basic/bool-03/bool-03.essence @@ -0,0 +1,2 @@ +find x, y : bool +such that x != y diff --git a/conjure_oxide/tests/integration/basic/bool-03/bool-03.expected.serialised.json b/conjure_oxide/tests/integration/basic/bool-03/bool-03.expected.serialised.json new file mode 100644 index 000000000..2ef697aed --- /dev/null +++ b/conjure_oxide/tests/integration/basic/bool-03/bool-03.expected.serialised.json @@ -0,0 +1,21 @@ +{ + "variables": [ + [ + { + "UserName": "x" + }, + { + "domain": "BoolDomain" + } + ], + [ + { + "UserName": "y" + }, + { + "domain": "BoolDomain" + } + ] + ], + "constraints": [] +} \ No newline at end of file diff --git a/conjure_oxide/tests/integration/xyz/input.serialised.expected.json b/conjure_oxide/tests/integration/xyz/input.expected.serialised.json similarity index 52% rename from conjure_oxide/tests/integration/xyz/input.serialised.expected.json rename to conjure_oxide/tests/integration/xyz/input.expected.serialised.json index 905e956aa..f7a71e68e 100644 --- a/conjure_oxide/tests/integration/xyz/input.serialised.expected.json +++ b/conjure_oxide/tests/integration/xyz/input.expected.serialised.json @@ -2,7 +2,7 @@ "variables": [ [ { - "UserName": "b" + "UserName": "a" }, { "domain": { @@ -19,7 +19,7 @@ ], [ { - "UserName": "a" + "UserName": "b" }, { "domain": { @@ -52,46 +52,5 @@ } ] ], - "constraints": [ - { - "Eq": [ - { - "Sum": [ - { - "Reference": { - "UserName": "a" - } - }, - { - "Reference": { - "UserName": "b" - } - }, - { - "Reference": { - "UserName": "c" - } - } - ] - }, - { - "ConstantInt": 4 - } - ] - }, - { - "Geq": [ - { - "Reference": { - "UserName": "a" - } - }, - { - "Reference": { - "UserName": "b" - } - } - ] - } - ] -} + "constraints": [] +} \ No newline at end of file