use itertools::Itertools; use json0_rs::error::{JsonError, Result}; use json0_rs::operation::Operation; use json0_rs::Json0; use log::{debug, info}; use serde_json::Value; use std::fmt::Display; use std::fs::File; use std::io::{self, BufRead}; use std::path::{Path, PathBuf}; use std::vec; use test_log::test; const COMMENT_PREFIX: char = '#'; fn read_lines

(filename: P) -> io::Result>> where P: AsRef, { let file = File::open(filename)?; Ok(io::BufReader::new(file).lines()) } fn read_json_value

(file_name: P) -> Result> where P: AsRef, { let mut out = vec![]; let mut line_number = 0; if let Ok(lines) = read_lines(file_name) { for line in lines.flatten() { line_number += 1; if !line.is_empty() && !line.starts_with(COMMENT_PREFIX) { let val = serde_json::from_str(&line).map_err(|e| { JsonError::UnexpectedError(format!("parse line: {} failed. {}", line, e)) })?; out.push((line_number, val)); } } } Ok(out) } trait Test { fn test(&self, executor: &E); } trait TestPattern, E> { fn load>(&self, input: &mut I) -> Result>; fn executor(&self) -> &E; fn test_input_path(&self) -> &str; } #[derive(Debug)] struct TransformTest { line: usize, input_left: Operation, input_right: Operation, result_left: Operation, result_right: Operation, } impl Test for TransformTest { fn test(&self, executor: &Json0) { info!( "execute test transform at line: {} left: {} right: {}", self.line, self.input_left, self.input_right ); let (l, r) = executor .transform(&self.input_left, &self.input_right) .unwrap(); assert_eq!(self.result_left, l, "left transform failed"); assert_eq!(self.result_right, r, "right transform failed"); } } impl Display for TransformTest { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( "left: {}\nright: {}\nrleft: {}\nrRight: {}", self.input_left, self.input_right, self.result_left, self.result_right )) } } struct TransformTestPattern<'a> { test_input_file_path: &'a str, transformer: Json0, } impl<'a> TransformTestPattern<'a> { fn new(p: &'a str) -> TransformTestPattern<'a> { TransformTestPattern { test_input_file_path: p, transformer: Json0::new(), } } } impl<'a> TestPattern for TransformTestPattern<'a> { fn load>( &self, input: &mut I, ) -> Result> { if let Some((line, i_l)) = input.next() { let ((_, i_r), (_, r_l), (_, r_r)) = input.next_tuple().ok_or( JsonError::UnexpectedError("not enough input values for test".into()), )?; let test = TransformTest { line, input_left: self.transformer.operation_factory().from_value(i_l)?, input_right: self.transformer.operation_factory().from_value(i_r)?, result_left: self.transformer.operation_factory().from_value(r_l)?, result_right: self.transformer.operation_factory().from_value(r_r)?, }; debug!("load test at line: {}\n{}", line, &test); return Ok(Some(test)); } Ok(None) } fn executor(&self) -> &Json0 { &self.transformer } fn test_input_path(&self) -> &str { self.test_input_file_path } } #[derive(Debug)] struct ApplyOperationTest { json: Value, operations: Vec, expect_result: Value, } struct ApplyOperationExecutor { json0: Json0, } impl ApplyOperationExecutor { fn apply(&self, json: &Value, operations: &[Operation]) -> Result { let mut out = json.clone(); self.json0.apply(&mut out, operations.to_owned())?; Ok(out) } } impl Test for ApplyOperationTest { fn test(&self, executor: &ApplyOperationExecutor) { let r = executor.apply(&self.json, &self.operations).unwrap(); assert_eq!(self.expect_result.clone(), r, "apply failed"); } } impl Display for ApplyOperationTest { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let ops_str = self.operations.iter().join(","); f.write_fmt(format_args!( "json: {}\noperations: [{:?}]\nexpect_result: {}", self.json, ops_str, self.expect_result )) } } struct ApplyOperationTestPattern<'a> { test_input_file_path: &'a str, executor: ApplyOperationExecutor, } impl<'a> ApplyOperationTestPattern<'a> { fn new(p: &'a str) -> ApplyOperationTestPattern<'a> { ApplyOperationTestPattern { test_input_file_path: p, executor: ApplyOperationExecutor { json0: Json0::new(), }, } } } impl<'a> TestPattern for ApplyOperationTestPattern<'a> { fn load>( &self, input: &mut I, ) -> Result> { if let Some((line, json)) = input.next() { let ((_, ops), (_, expect_result)) = input.next_tuple().ok_or( JsonError::UnexpectedError("not enough input values for test".into()), )?; let mut operations = vec![]; if let Value::Array(op_array) = ops { operations = op_array .into_iter() .map(|o| self.executor().json0.operation_factory().from_value(o)) .collect::>>()?; } let test = ApplyOperationTest { json, operations, expect_result, }; debug!("load test at line: {}\n{}", line, &test); return Ok(Some(test)); } Ok(None) } fn executor(&self) -> &ApplyOperationExecutor { &self.executor } fn test_input_path(&self) -> &str { self.test_input_file_path } } #[derive(Debug)] struct InvertOperationTest { origin_op: Operation, expect_invert_op: Operation, } struct InvertOperationExecutor { json0: Json0, } impl Test for InvertOperationTest { fn test(&self, _: &InvertOperationExecutor) { assert_eq!( *self.expect_invert_op.get(0).unwrap(), self.origin_op.get(0).unwrap().invert().unwrap(), "invert failed" ); } } impl Display for InvertOperationTest { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( "origin_op: {}\nexpect_invert_op: {}", self.origin_op, self.expect_invert_op )) } } struct InvertOperationTestPattern<'a> { test_input_file_path: &'a str, executor: InvertOperationExecutor, } impl<'a> InvertOperationTestPattern<'a> { fn new(p: &'a str) -> InvertOperationTestPattern<'a> { InvertOperationTestPattern { test_input_file_path: p, executor: InvertOperationExecutor { json0: Json0::new(), }, } } } impl<'a> TestPattern for InvertOperationTestPattern<'a> { fn load>( &self, input: &mut I, ) -> Result> { if let Some((line, origin_op)) = input.next() { let (_, expect_invert_op) = input.next().ok_or(JsonError::UnexpectedError( "not enough input values for test".into(), ))?; let test = InvertOperationTest { origin_op: self .executor() .json0 .operation_factory() .from_value(origin_op)?, expect_invert_op: self .executor() .json0 .operation_factory() .from_value(expect_invert_op)?, }; debug!("load test at line: {}\n{}", line, &test); return Ok(Some(test)); } Ok(None) } fn executor(&self) -> &InvertOperationExecutor { &self.executor } fn test_input_path(&self) -> &str { self.test_input_file_path } } #[derive(Debug)] struct MergeOperationTest { base_op: Operation, other_op: Operation, expect_op: Operation, } struct MergeOperationExecutor { json0: Json0, } impl Test for MergeOperationTest { fn test(&self, _: &MergeOperationExecutor) { let mut base = self.base_op.clone(); base.compose(self.other_op.clone()).unwrap(); assert_eq!(self.expect_op, base); } } impl Display for MergeOperationTest { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( "base_op: {}\nother_op: {}\nexpect_op: {}", self.base_op, self.other_op, self.expect_op )) } } struct MergeOperationTestPattern<'a> { test_input_file_path: &'a str, executor: MergeOperationExecutor, } impl<'a> MergeOperationTestPattern<'a> { fn new(p: &'a str) -> MergeOperationTestPattern<'a> { MergeOperationTestPattern { test_input_file_path: p, executor: MergeOperationExecutor { json0: Json0::new(), }, } } } impl<'a> TestPattern for MergeOperationTestPattern<'a> { fn load>( &self, input: &mut I, ) -> Result> { if let Some((line, base_op)) = input.next() { let ((_, other_op), (_, expect_result)) = input.next_tuple().ok_or( JsonError::UnexpectedError("not enough input values for test".into()), )?; let test = MergeOperationTest { base_op: self .executor() .json0 .operation_factory() .from_value(base_op)?, other_op: self .executor() .json0 .operation_factory() .from_value(other_op)?, expect_op: self .executor() .json0 .operation_factory() .from_value(expect_result)?, }; debug!("load test at line: {}\n{}", line, &test); return Ok(Some(test)); } Ok(None) } fn executor(&self) -> &MergeOperationExecutor { &self.executor } fn test_input_path(&self) -> &str { self.test_input_file_path } } fn run_test, E, P: Sized + TestPattern>(pattern: &P) -> Result<()> { let mut input_data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); input_data_path.push(pattern.test_input_path()); let json_values = read_json_value(&input_data_path)?; let executor = pattern.executor(); let mut iter = json_values.into_iter(); while let Some(test) = pattern.load(&mut iter)? { test.test(executor); } Ok(()) } #[test] fn test_json_apply() { let pattern = ApplyOperationTestPattern::new("tests/resources/apply_op_case.json"); run_test(&pattern).unwrap(); } #[test] fn test_invert() { let pattern = InvertOperationTestPattern::new("tests/resources/invert_op_case.json"); run_test(&pattern).unwrap(); } #[test] fn test_json_compose() { let pattern = MergeOperationTestPattern::new("tests/resources/compose_op_case.json"); run_test(&pattern).unwrap(); } #[test] fn test_transform_list() { let pattern = TransformTestPattern::new("tests/resources/transform_list_case.json"); run_test(&pattern).unwrap(); } #[test] fn test_transform_object() { let pattern = TransformTestPattern::new("tests/resources/transform_object_case.json"); run_test(&pattern).unwrap(); } #[test] fn test_other_transform_case() { let pattern = TransformTestPattern::new("tests/resources/other_transform_case.json"); run_test(&pattern).unwrap(); }