Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model example library for testing #157

Merged
merged 18 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e2af1ec
Eliminated example/ dir to integrate with conjure_oxide/tests/integra…
PedroGGBM Jan 29, 2024
7c79347
init attempt for generate_custom.rs for example custom string input
PedroGGBM Jan 31, 2024
1c208e3
Added comprehension and enumerate essence file examples
PedroGGBM Jan 31, 2024
a73702a
generate_custom.rs temporary fix for walkdir filter filename [DOESN'T…
PedroGGBM Jan 31, 2024
1261604
Merge branch 'conjure-cp:main' into pedro-dev
Jan 31, 2024
fc9bfee
added finite given [set] tests
PedroGGBM Feb 7, 2024
f28ce0a
Merge branch 'conjure-cp:main' into pedro-dev
Feb 11, 2024
846d7b3
fix for generate_custom.rs get_example_model: only filename and corre…
PedroGGBM Feb 11, 2024
4fdfd09
added 'interesting tests' to integration test dir
PedroGGBM Feb 12, 2024
0dc195e
fix dependencies in main.rs for custom_example
PedroGGBM Feb 12, 2024
cdb5453
(ignore) playing around with code coverage sample yml file
PedroGGBM Feb 16, 2024
27bb092
final changes custom return model function
PedroGGBM Feb 19, 2024
d27dfe3
Merge branch 'main' into pedro-dev
PedroGGBM Feb 19, 2024
0ec5946
changed main.rs cfg tests to xyz instead of bool (no current support)
PedroGGBM Feb 19, 2024
c362a79
eliminated yaml code-coverage file for PR test passing
PedroGGBM Feb 20, 2024
828809d
Merge branch 'main' into pedro-dev
PedroGGBM Feb 20, 2024
f7e48e2
debug boolean 01/02/03 integration test with cargo test
PedroGGBM Feb 21, 2024
37ad20e
removed conjure-output for basic/01 bool tests folder
PedroGGBM Feb 21, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion conjure_oxide/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ clap = { version = "4.5.1", features = ["derive"] }
strum_macros = "0.26.1"
strum = "0.26.1"
versions = "6.1.0"
linkme = "0.3.23"
linkme = "0.3.22"
walkdir = "2.4.0"

[features]

Expand Down
160 changes: 160 additions & 0 deletions conjure_oxide/src/generate_custom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// generate_custom.rs with get_example_model function

// dependencies
use crate::parse::model_from_json;
use conjure_core::ast::Model;
use std::env;
use std::error::Error;
use std::fs::{copy, read_to_string, File};
use std::io::Write;
use std::path::PathBuf;
use walkdir::WalkDir;

use serde_json::Value;

/// Searches recursively in `../tests/integration` folder for an `.essence` file matching the given filename,
/// then uses conjure to process it into astjson, and returns the parsed model.
///
/// # Arguments
///
/// * `filename` - A string slice that holds filename without extension
///
/// # Returns
///
/// Function returns a `Result<Value, Box<dyn Error>>`, where `Value` is the parsed model
pub fn get_example_model(filename: &str) -> Result<Model, Box<dyn Error>> {
// define relative path -> integration tests dir
let base_dir = "tests/integration";
let mut essence_path = PathBuf::new();

// walk through directory tree recursively starting at base
for entry in WalkDir::new(base_dir).into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path.is_file()
&& path.extension().map_or(false, |e| e == "essence")
&& path.file_stem() == Some(std::ffi::OsStr::new(filename))
{
essence_path = path.to_path_buf();
break;
}
}

println!("PATH TO FILE: {}", essence_path.display());

// return error if file not found
if essence_path.as_os_str().is_empty() {
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
"ERROR: File not found in any subdirectory",
)));
}

// let path = PathBuf::from(format!("../tests/integration/basic/comprehension{}.essence", filename));
let mut cmd = std::process::Command::new("conjure");
let output = cmd
.arg("pretty")
.arg("--output-format=astjson")
.arg(essence_path)
.output()?;

// convert Conjure's stdout from bytes to string
let astjson = String::from_utf8(output.stdout)?;

println!("ASTJSON: {}", astjson);

// parse AST JSON from desired Model format
let generated_mdl = model_from_json(&astjson)?;

Ok(generated_mdl)
}

/// Searches for an `.essence` file at the given filepath,
/// then uses conjure to process it into astjson, and returns the parsed model.
///
/// # Arguments
///
/// * `filepath` - A string slice that holds the full file path
///
/// # Returns
///
/// Function returns a `Result<Value, Box<dyn Error>>`, where `Value` is the parsed model
pub fn get_example_model_by_path(filepath: &str) -> Result<Model, Box<dyn Error>> {
let essence_path = PathBuf::from(filepath);

// return error if file not found
if essence_path.as_os_str().is_empty() {
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
"ERROR: File not found in any subdirectory",
)));
}

println!("PATH TO FILE: {}", essence_path.display());

// Command execution using 'conjure' CLI tool with provided path
let mut cmd = std::process::Command::new("conjure");
let output = cmd
.arg("pretty")
.arg("--output-format=astjson")
.arg(&essence_path)
.output()?;

// convert Conjure's stdout from bytes to string
let astjson = String::from_utf8(output.stdout)?;

println!("ASTJSON: {}", astjson);

// parse AST JSON into the desired Model format
let generated_model = model_from_json(&astjson)?;

Ok(generated_model)
}

/// Recursively sorts the keys of all JSON objects within the provided JSON value.
///
/// serde_json will output JSON objects in an arbitrary key order.
/// this is normally fine, except in our use case we wouldn't want to update the expected output again and again.
/// so a consistent (sorted) ordering of the keys is desirable.
fn sort_json_object(value: &Value) -> Value {
match value {
Value::Object(obj) => {
let mut ordered: Vec<(String, Value)> = obj
.iter()
.map(|(k, v)| {
if k == "variables" {
(k.clone(), sort_json_variables(v))
} else {
(k.clone(), sort_json_object(v))
}
})
// .map(|(k, v)| (k.clone(), sort_json_object(v)))
.collect();
ordered.sort_by(|a, b| a.0.cmp(&b.0));

Value::Object(ordered.into_iter().collect())
}
Value::Array(arr) => Value::Array(arr.iter().map(sort_json_object).collect()),
_ => value.clone(),
}
}

/// Sort the "variables" field by name.
/// We have to do this separately becasue that field is not a JSON object, instead it's an array of tuples.
fn sort_json_variables(value: &Value) -> Value {
match value {
Value::Array(vars) => {
let mut vars_sorted = vars.clone();
vars_sorted.sort_by(|a, b| {
let a_obj = &a.as_array().unwrap()[0];
let a_name: crate::ast::Name = serde_json::from_value(a_obj.clone()).unwrap();

let b_obj = &b.as_array().unwrap()[0];
let b_name: crate::ast::Name = serde_json::from_value(b_obj.clone()).unwrap();

a_name.cmp(&b_name)
});
Value::Array(vars_sorted)
}
_ => value.clone(),
}
}
1 change: 1 addition & 0 deletions conjure_oxide/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

pub mod error;
pub mod find_conjure;
pub mod generate_custom;
pub mod parse;
pub mod rewrite;
mod rules;
Expand Down
30 changes: 30 additions & 0 deletions conjure_oxide/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::sync::Mutex;
use anyhow::Result as AnyhowResult;
use clap::{arg, command, Parser};
use conjure_oxide::find_conjure::conjure_executable;
use conjure_oxide::generate_custom::{get_example_model, get_example_model_by_path};
use conjure_oxide::parse::model_from_json;
use conjure_oxide::rewrite::rewrite_model;
use conjure_oxide::solvers::FromConjureModel;
Expand Down Expand Up @@ -100,3 +101,32 @@ pub fn main() -> AnyhowResult<()> {

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_get_example_model_success() {
let filename = "input";
get_example_model(filename).unwrap();
}

#[test]
fn test_get_example_model_by_filepath() {
let filepath = "tests/integration/xyz/input.essence";
get_example_model_by_path(filepath).unwrap();
}

#[test]
fn test_get_example_model_fail_empty_filename() {
let filename = "";
get_example_model(filename).unwrap_err();
}

#[test]
fn test_get_example_model_fail_empty_filepath() {
let filepath = "";
get_example_model_by_path(filepath).unwrap_err();
}
}
2 changes: 1 addition & 1 deletion conjure_oxide/tests/generated_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn main() {
}
}

fn integration_test(path: &str, essence_base: &str) -> Result<(), Box<dyn Error>> {
pub fn integration_test(path: &str, essence_base: &str) -> Result<(), Box<dyn Error>> {
// --------------------------------------------------------------------------------
// -- parsing the essence file

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
language Essence 1.3

find x : int(0..1000)
such that x = sum([ 1 | i : set (size 2) of int(7..9) ])
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
language Essence 1.3

find x : int(0..1000)
find y : int(7,8)
such that x = sum([ 1 | i : set (size 2) of int(7..9)
, y in i
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
language Essence 1.3

find x : int(0..1000)
such that x = sum([ j | i : set (minSize 1, maxSize 2) of int(7..8), j <- i ])
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
language Essence 1.3

find x : int(0..1000)
find y : int(7,8)
such that x = sum([ j | i : set (minSize 1, maxSize 2) of int(7..8)
, y in i
, j <- i
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
language Essence 1.3

find x : int(0..1000)
such that x = sum([ 1 | i : set (minSize 1, maxSize 2) of int(7..9) ])
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
language Essence 1.3

find x : int(0..1000)
find y : int(7,8)
such that x = sum([ 1 | i : set (minSize 1, maxSize 2) of int(7..9)
, y in i
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
language Essence 1.3

find x : int(0..1000)
such that x = sum([ 1 | i : set (minSize 1, maxSize 2) of (int(7..9), bool) ])
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
language Essence 1.3

find x : int(0..1000)
find y : int(7,8)
find z : bool
such that x = sum([ 1 | i : set (minSize 1, maxSize 2) of (int(7..9), bool)
, (y,z) in i
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
language Essence 1.3

given E1 new type enum
find x : E1

letting E2 be new type enum {a,b,c,d}
find y : E2
find z : E2(a..c)
find t : E2(b,d)

such that y = z, z = t
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
language Essence 1.3

given E1 new type enum
letting E2 be new type enum {a,b,c,d}

find x : (E1, E2)

such that x[2] = a
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
language Essence 1.3

given E1 new type enum
find x : E1
such that forAll i : E1 . x <= i

letting E2 be new type enum {a,b,c,d}
find y : E2
such that forAll j : E2 . y <= j
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
language Essence 1.3

given a : set of int
find x,y,z : int(0..100)
such that x = |a|
such that y = min(a)
such that z = max(a)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
language Essence 1.3

given a : set of int
find x : int(|a|)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
language Essence 1.3

given a : set of int
find x,y,z,t : int(0..100)
such that x = |a|
such that y = min(a)
such that z = max(a)
such that allDiff([x,y,z,t])
such that t in a $ refer to a
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
language Essence 1.3

given a : set of set of int
find x : int(-1000..1000)
such that x = max([ max(i) | i <- a ])
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
language Essence 1.3

given s: set of set (minSize 0) of int(2..5, 4)
find x : int(0..9)
such that exists s' in s . x in s'
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
language Essence 1.3

letting n be 5
letting g be relation
( (1,2)
, (2,1)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
language Essence 1.3

letting n be 5
letting g be relation
( (1,2)
, (2,3)
, (3,1)
)
Loading
Loading