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

standardised format of json rule output #569

Merged
merged 17 commits into from
Jan 19, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
96 changes: 67 additions & 29 deletions conjure_oxide/src/utils/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::io::Write;
use std::sync::{Arc, RwLock};

use conjure_core::context::Context;
use serde_json::{json, Error as JsonError, Value as JsonValue};
use serde_json::{json, Error as JsonError, Map, Value as JsonValue};

use conjure_core::error::Error;

Expand Down Expand Up @@ -191,35 +191,15 @@ pub fn read_rule_trace(
test_name: &str,
prefix: &str,
accept: bool,
) -> Result<Vec<String>, std::io::Error> {
) -> Result<JsonValue, anyhow::Error> {
let filename = format!("{path}/{test_name}-{prefix}-rule-trace.json");
let mut rules_trace: Vec<String> = read_to_string(&filename)
.unwrap()
.lines()
.map(String::from)
.collect();

//only count the number of rule in generated file (assumming the expected version already has that line and it is correct)
if prefix == "generated" {
let rule_count = rules_trace.len();

let count_message = json!({
"message": " Number of rules applied",
"count": rule_count
});

// Append the count message to the vector
let count_message_string = serde_json::to_string(&count_message)?;
rules_trace.push(count_message_string.clone());

// Write the updated rules trace back to the file
let mut file = OpenOptions::new()
.write(true)
.truncate(true) // Overwrite the file with updated content
.open(&filename)?;

writeln!(file, "{}", rules_trace.join("\n"))?;
}
let rule_traces = if prefix == "generated" {
count_and_sort_rules(&filename)?
} else {
let file_contents = std::fs::read_to_string(filename)?;
sort_json_object(&serde_json::from_str(&file_contents)?, false)
};

if accept {
std::fs::copy(
Expand All @@ -228,7 +208,65 @@ pub fn read_rule_trace(
)?;
}

Ok(rules_trace)
Ok(rule_traces)
}

pub fn count_and_sort_rules(filename: &str) -> Result<JsonValue, anyhow::Error> {
let file_contents = read_to_string(filename)?;

let sorted_json_rules = if file_contents.trim().is_empty() {
let rule_count_message = json!({
"Number of rules applied": 0,
});
sort_json_object(&rule_count_message, true) // Sort the rule count message
} else {
let rule_count = file_contents.lines().count();
let mut sorted_json_rules = sort_json_rules(&file_contents);

let rule_count_message = json!({
"Number of rules applied": rule_count,
});

if let Some(array) = sorted_json_rules.as_array_mut() {
array.push(rule_count_message);
} else {
return Err(anyhow::anyhow!("Expected JSON array"));
}
sorted_json_rules
};

let generated_sorted_json_rules = serde_json::to_string_pretty(&sorted_json_rules)?;

let mut file = OpenOptions::new()
.write(true)
.truncate(true)
.open(filename)?;

file.write_all(generated_sorted_json_rules.as_bytes())?;

Ok(sorted_json_rules)
}

fn sort_json_rules(json_rule_traces: &str) -> JsonValue {
let mut sorted_rule_traces = Vec::new();

for line in json_rule_traces.lines() {
let mut map = Map::new();

let parts = line.split("; ");

for part in parts {
if let Some((key, value)) = part.split_once(": ") {
map.insert(
key.trim().to_string(),
json!(value.trim().trim_end_matches("\"}}")),
);
}
}

sorted_rule_traces.push(JsonValue::Object(map));
}
JsonValue::Array(sorted_rule_traces)
}

pub fn read_human_rule_trace(
Expand Down
24 changes: 15 additions & 9 deletions conjure_oxide/tests/generated_tests.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use conjure_core::rule_engine::rewrite_naive;
use conjure_core::Model;
use conjure_oxide::utils::essence_parser::parse_essence_file_native;
use conjure_oxide::utils::testing::read_human_rule_trace;
use conjure_oxide::utils::testing::{read_human_rule_trace, read_rule_trace};
use glob::glob;
use itertools::Itertools;
use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::fs;
use std::fs::File;
use tracing::{span, Level};
use tracing::{span, Level, Metadata as OtherMetadata};
use tracing_subscriber::{
filter::EnvFilter, filter::FilterFn, fmt, layer::SubscriberExt, Layer, Registry,
};
Expand Down Expand Up @@ -230,6 +230,11 @@ fn integration_test_inner(

//Stage 3: Check that the generated rules match with the expected in terms if type, order and count

let generated_json_rule_trace = read_rule_trace(path, essence_base, "generated", accept)?;
let expected_json_rule_trace = read_rule_trace(path, essence_base, "expected", accept)?;

assert_eq!(expected_json_rule_trace, generated_json_rule_trace);

let generated_rule_trace_human =
read_human_rule_trace(path, essence_base, "generated", accept)?;
let expected_rule_trace_human = read_human_rule_trace(path, essence_base, "expected", accept)?;
Expand Down Expand Up @@ -380,16 +385,16 @@ pub fn create_scoped_subscriber(
impl tracing::Subscriber + Send + Sync,
Vec<tracing_appender::non_blocking::WorkerGuard>,
) {
//let (target1_layer, guard1) = create_file_layer_json(path, test_name);
let (target1_layer, guard1) = create_file_layer_json(path, test_name);
let (target2_layer, guard2) = create_file_layer_human(path, test_name);
let layered = target2_layer;
let layered = target1_layer.and_then(target2_layer);

let subscriber = Arc::new(tracing_subscriber::registry().with(layered))
as Arc<dyn tracing::Subscriber + Send + Sync>;
// setting this subscriber as the default
let _default = tracing::subscriber::set_default(subscriber.clone());

(subscriber, vec![guard2])
(subscriber, vec![guard1, guard2])
}

fn create_file_layer_json(
Expand All @@ -401,13 +406,14 @@ fn create_file_layer_json(
let (non_blocking, guard1) = tracing_appender::non_blocking(file);

let layer1 = fmt::layer()
.with_writer(non_blocking)
.json()
.with_writer(non_blocking)
.with_level(false)
.without_time()
.with_target(false)
.with_filter(EnvFilter::new("rule_engine=trace"))
.with_filter(FilterFn::new(|meta| meta.target() == "rule_engine"));
.without_time()
.with_filter(FilterFn::new(|meta: &OtherMetadata| {
meta.target() == "rule_engine"
}));

(layer1, guard1)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Number of rules applied": 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Number of rules applied": 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Number of rules applied": 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Number of rules applied": 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[
{
"rule_applied": "div_to_bubble ([(\\\"Bubble\\\", 6000)])",
"initial_expression": "UnsafeDiv(a, b)",
"tranformed_expression": "{SafeDiv(a, b) @ (b != 0)}",
"new_top": "[]"
},
{
"rule_applied": "bubble_up ([(\\\"Bubble\\\", 8900)])",
"initial_expression": "({SafeDiv(a, b) @ (b != 0)} = 1)",
"tranformed_expression": "{(SafeDiv(a, b) = 1) @ And([(b != 0)])}",
"new_top": "[]"
},
{
"rule_applied": "expand_bubble ([(\\\"Bubble\\\", 8900)])",
"initial_expression": "{(SafeDiv(a, b) = 1) @ And([(b != 0)])}",
"tranformed_expression": "And([(SafeDiv(a, b) = 1), And([(b != 0)])])",
"new_top": "[]"
},
{
"rule_applied": "remove_unit_vector_and ([(\\\"Base\\\", 8800)])",
"initial_expression": "And([(b != 0)])",
"tranformed_expression": "(b != 0)",
"new_top": "[]"
},
{
"rule_applied": "introduce_diveq ([(\\\"Minion\\\", 4200)])",
"initial_expression": "(SafeDiv(a, b) = 1)",
"tranformed_expression": "DivEq(a, b, 1)",
"new_top": "[]"
},
{
"Number of rules applied": 5
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{
"rule_applied": "apply_eval_constant ([(\\\"Constant\\\", 9001)])",
"initial_expression": "UnsafeDiv(4, 2)",
"tranformed_expression": "2",
"new_top": "[]"
},
{
"rule_applied": "geq_to_ineq ([(\\\"Minion\\\", 4100)])",
"initial_expression": "(a >= 2)",
"tranformed_expression": "Ineq(2, a, 0)",
"new_top": "[]"
},
{
"Number of rules applied": 2
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[
{
"rule_applied": "div_to_bubble ([(\\\"Bubble\\\", 6000)])",
"initial_expression": "UnsafeDiv(8, a)",
"tranformed_expression": "{SafeDiv(8, a) @ (a != 0)}",
"new_top": "[]"
},
{
"rule_applied": "bubble_up ([(\\\"Bubble\\\", 8900)])",
"initial_expression": "(2 = {SafeDiv(8, a) @ (a != 0)})",
"tranformed_expression": "{(2 = SafeDiv(8, a)) @ And([(a != 0)])}",
"new_top": "[]"
},
{
"rule_applied": "expand_bubble ([(\\\"Bubble\\\", 8900)])",
"initial_expression": "{(2 = SafeDiv(8, a)) @ And([(a != 0)])}",
"tranformed_expression": "And([(2 = SafeDiv(8, a)), And([(a != 0)])])",
"new_top": "[]"
},
{
"rule_applied": "remove_unit_vector_and ([(\\\"Base\\\", 8800)])",
"initial_expression": "And([(a != 0)])",
"tranformed_expression": "(a != 0)",
"new_top": "[]"
},
{
"rule_applied": "introduce_diveq ([(\\\"Minion\\\", 4200)])",
"initial_expression": "(2 = SafeDiv(8, a))",
"tranformed_expression": "DivEq(8, a, 2)",
"new_top": "[]"
},
{
"Number of rules applied": 5
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[
{
"rule_applied": "div_to_bubble ([(\\\"Bubble\\\", 6000)])",
"initial_expression": "UnsafeDiv(b, c)",
"tranformed_expression": "{SafeDiv(b, c) @ (c != 0)}",
"new_top": "[]"
},
{
"rule_applied": "bubble_up ([(\\\"Bubble\\\", 8900)])",
"initial_expression": "(a = {SafeDiv(b, c) @ (c != 0)})",
"tranformed_expression": "{(a = SafeDiv(b, c)) @ And([(c != 0)])}",
"new_top": "[]"
},
{
"rule_applied": "expand_bubble ([(\\\"Bubble\\\", 8900)])",
"initial_expression": "{(a = SafeDiv(b, c)) @ And([(c != 0)])}",
"tranformed_expression": "And([(a = SafeDiv(b, c)), And([(c != 0)])])",
"new_top": "[]"
},
{
"rule_applied": "remove_unit_vector_and ([(\\\"Base\\\", 8800)])",
"initial_expression": "And([(c != 0)])",
"tranformed_expression": "(c != 0)",
"new_top": "[]"
},
{
"rule_applied": "distribute_not_over_and ([(\\\"Base\\\", 8400)])",
"initial_expression": "Not(And([(a = SafeDiv(b, c)), (c != 0)]))",
"tranformed_expression": "Or([Not((a = SafeDiv(b, c))), Not((c != 0))])",
"new_top": "[]"
},
{
"rule_applied": "negated_eq_to_neq ([(\\\"Base\\\", 8800)])",
"initial_expression": "Not((a = SafeDiv(b, c)))",
"tranformed_expression": "(a != SafeDiv(b, c))",
"new_top": "[]"
},
{
"rule_applied": "negated_neq_to_eq ([(\\\"Base\\\", 8800)])",
"initial_expression": "Not((c != 0))",
"tranformed_expression": "(c = 0)",
"new_top": "[]"
},
{
"rule_applied": "flatten_binop ([(\\\"Minion\\\", 4400)])",
"initial_expression": "(a != SafeDiv(b, c))",
"tranformed_expression": "(a != __0)",
"new_top": "[__0 =aux SafeDiv(b, c)]"
},
{
"rule_applied": "introduce_diveq ([(\\\"Minion\\\", 4200)])",
"initial_expression": "__0 =aux SafeDiv(b, c)",
"tranformed_expression": "DivEq(b, c, __0)",
"new_top": "[]"
},
{
"Number of rules applied": 9
}
]
Loading
Loading