#[cfg(test)] mod tests { use std::io::Read; use crate::parser::*; use nom::error::VerboseError; use nom::Err; #[test] fn test_parse_array() { // Test case 1: Valid input let input = r#"[ "value1", "value2", "value3" ]"#; let expected_output = Ok(("", vec!["value1".into(), "value2".into(), "value3".into()])); assert_eq!(parse_array(input), expected_output); // Test case 2: Empty array let input = r#"[]"#; let expected_output = Ok(("", vec![])); assert_eq!(parse_array(input), expected_output); // Test case 3: Array with whitespace let input = r#"[ "value1" , "value2" , "value3" ]"#; let expected_output = Ok(("", vec!["value1".into(), "value2".into(), "value3".into()])); assert_eq!(parse_array(input), expected_output); // Test case 4: Array with empty values let input = r#"[ "", "", "" ]"#; let expected_output = Ok(("", vec!["".into(), "".into(), "".into()])); assert_eq!(parse_array(input), expected_output); // Test case 5: Invalid input - missing closing bracket let input = r#"[ "value1", "value2", "value3""#; assert!(parse_array(input).is_err()); // Test case 5: Invalid input - missing opening bracket let input = r#""value1", "value2", "value3" ]"#; assert!(parse_array(input).is_err()); // Test case 6: Array with trailing comma is not an error let input = r#"[ "value1", "value2", "value3", ]"#; let expected_output = Ok(("", vec!["value1".into(), "value2".into(), "value3".into()])); assert_eq!(parse_array(input), expected_output); } #[test] fn test_parse_entry() { // Test case 1: Valid input let input = r#"key: "value""#; let expected_output = Ok(("", ("key".to_string(), Value::String("value".to_string())))); assert_eq!(parse_module_entry(input), expected_output); // Test case 2: Valid input with whitespace let input = r#" key : "value" "#; let expected_output = Ok(("", ("key".to_string(), Value::String("value".to_string())))); assert_eq!(parse_module_entry(input), expected_output); // Test case 3: Valid input with array value let input = r#"key: [ "value1", "value2", "value3" ]"#; let expected_output = Ok(( "", ( "key".to_string(), Value::Array(vec!["value1".into(), "value2".into(), "value3".into()]), ), )); assert_eq!(parse_module_entry(input), expected_output); // Test case 4: Invalid input - missing colon let input = r#"key "value""#; assert!(parse_module_entry(input).is_err()); // Test case 5: Invalid input - missing value let input = r#"key:"#; assert!(parse_module_entry(input).is_err()); // Test case 6: Invalid input - missing key let input = r#":"value""#; assert!(parse_module_entry(input).is_err()); // Test case 7: Invalid input - missing key and value let input = r#":"#; assert!(parse_module_entry(input).is_err()); } #[test] fn test_parse_module() { let input = r#" module_name { key1: "value1", key2: true, key3: [ "value2", "value3" ], } "#; let expected_output = Module { typ: "module_name".to_string(), entries: vec![ ("key1".to_string(), Value::String("value1".to_string())), ("key2".to_string(), Value::Boolean(true)), ( "key3".to_string(), Value::Array(vec!["value2".into(), "value3".into()]), ), ] .into_iter() .collect(), }; assert_eq!(parse_module(input), Ok(("", expected_output))); } #[test] fn test_parse_blueprint() { let input = r#" module_name { key1: "value1", key2: true, key3: [ "value2", "value3" ], } module_name2 { key1: "value1", key2: true, key3: [ "value2", "value3" ], }"#; let output = BluePrint::parse(input).unwrap(); assert_eq!(output.modules.len(), 2); assert_eq!(output.modules[0].typ, "module_name"); assert_eq!(output.modules[1].typ, "module_name2"); let mut keys = output.modules[0] .entries .keys() .map(|x| x.to_owned()) .collect::>(); keys.sort(); assert_eq!( keys, vec!["key1".to_string(), "key2".to_string(), "key3".to_string()] ); } #[test] fn test_nested_dict() { let input = r#" rust_test_host { name: "ss", srcs: ["src/ss.rs"], test_options: { unit_test: true, }, } "#; let output: Result<(&str, Module), Err>> = parse_module(input); assert!(output.is_ok()); } #[test] fn test_comment() { let input = r#" rust_test_host { // name: "ss", // srcs: ["src/ss.rs"], test_options: { unit_test: true, }, } "#; let output = parse_module(input); display_error(input, &output); assert!(output.is_ok()); } #[test] fn test_all_comment() { let input = r#"/* rust_test_host { // name: "ss", // srcs: ["src/ss.rs"], test_options: { unit_test: true, }, } */"#; let output = BluePrint::parse(input); if output.is_err() { println!("Error: {}", output.unwrap_err()); panic!("Error in parsing"); } } #[test] fn test_issue_1() { let input = r#" aidl_interface { name: "android.hardware.tetheroffload", vendor_available: true, srcs: ["android/hardware/tetheroffload/*.aidl"], stability: "vintf", backend: { cpp: { enabled: false, }, java: { sdk_version: "module_current", apex_available: [ "com.android.tethering", ], min_sdk_version: "30", enabled: true, }, ndk: { apps_enabled: false, }, }, versions_with_info: [ { version: "1", imports: [], }, ], frozen: true, } "#; let output = parse_module(input); display_error(input, &output); assert!(output.is_ok()); } #[test] fn test_issue_2() { let input = r#" aidl_interface { name: "android.hardw\"are.tetheroffload", } "#; let output = parse_module(input); display_error(input, &output); assert!(output.is_ok()); } #[test] fn test_module_second_form() { let input = r#" aidl_interface(name = "android.hardware.tetheroffload") "#; let output = parse_module(input); display_error(input, &output); assert!(output.is_ok()); } fn display_error(input: &str, output: &Result<(&str, T), Err>>) -> () { if let Err(e) = output { println!("Error: {}", format_err(input, e.clone())); } } #[test] fn test_expr() { let input = r#""abc" + "def""#; let output = parse_expr(input); display_error(input, &output); assert!(output.is_ok()); assert!(output.as_ref().unwrap().0.is_empty()); assert!(output.unwrap().1 == Value::String("abcdef".to_string())); } #[test] fn test_expr_array() { let input = r#"["abc", "def"] + [ "ghi" ]"#; let output = parse_expr(input); display_error(input, &output); assert!(output.is_ok()); assert!(output.as_ref().unwrap().0.is_empty()); assert!(output.unwrap().1 == Value::Array(vec!["abc".into(), "def".into(), "ghi".into()])); } #[test] fn test_expr_ident() { let input = r#"ident + [ "ghi" ]"#; let output = parse_expr(input); display_error(input, &output); assert!(output.is_ok()); assert!(output.as_ref().unwrap().0.is_empty()); assert!( output.unwrap().1 == Value::ConcatExpr([ Value::Ident("ident".to_string()), Value::Array(["ghi".into()].into()) ].into()) ); } #[test] fn test_expr_value() { let input = r#"123"#; let output = parse_expr(input); display_error(input, &output); assert!(output.is_ok()); assert!(output.as_ref().unwrap().0.is_empty()); assert!( output.unwrap().1 == Value::Integer(123)); } // found in platform_testing/tests/health/scenarios/tests/Android.bp #[test] fn test_complicated_concat() { let input = r#""out_dir=$$(dirname $(out)) && assets_dir=\"assets\" " + "&& mkdir -p $$out_dir/$$assets_dir && src_protos=($(locations assets/*.textpb)) " + "&& for file in $${src_protos[@]} ; do fname=$$(basename $$file) " + "&& if ! ($(location aprotoc) --encode=longevity.profile.Configuration " + "$(location :profile-proto-def) < $$file > " + "$$out_dir/$$assets_dir/$${fname//.textpb/.pb}) ; then " + "echo \"\x1b[0;31mFailed to parse profile $$file. See above for errors.\x1b[0m\" " + "&& exit 1 ; fi ; done && jar cf $(out) -C $$(dirname $(out)) $$assets_dir""#; let output = parse_expr(input); display_error(input, &output); assert!(output.is_ok()); assert!(output.as_ref().unwrap().0.is_empty()); } #[test] fn test_linecomment_wo_eol() { let input = r#"// foo"#; let output = BluePrint::parse(input); assert!(output.is_ok()); } #[test] fn test_defines_extends(){ let input = r#" var = ["a", "b"] var2 = 12 var += ["c"] var2 += 1 var3 = "abc" var3 += "def" "#; let output = BluePrint::parse(input); assert!(output.is_ok()); let bp = output.unwrap(); assert_eq!(bp.variables.get("var").unwrap(), &Value::Array(vec!["a".into(), "b".into(), "c".into()])); assert_eq!(bp.variables.get("var2").unwrap(), &Value::Integer(13)); assert_eq!(bp.variables.get("var3").unwrap(), &Value::String("abcdef".to_string())); } #[test] fn test_defines_extends_error(){ let input = r#" var = ["a", "b"] var2 = 12 var += 1 var2 += "a" "#; let output = BluePrint::parse(input); println!("Error: {}", output.unwrap_err()); // assert!(output.is_err()); } #[test] fn test_function() { let input = r#"method("ss")"#; let output = parse_expr(input); display_error(input, &output); assert!(output.is_ok()); assert_eq!(output.unwrap().1, Value::Function(Function { name: "method".to_string(), args: vec![Value::String("ss".to_string())] })); } #[test] fn test_aosp_db() { // generate tarball from aosp tree // fd -g Android.bp | tar cJf ../rs-bp/src/test_db.tar.xz -T - let data = include_bytes!("test_db.tar.xz"); let mut archive = tar::Archive::new(liblzma::read::XzDecoder::new(&data[..])); let mut count = 0; let mut bytes = 0; let mut num_errors = 0; let mut all_bp = Vec::new(); // first decompress in memory to avoid disk IO for measuring performance for entry in archive.entries().unwrap() { let entry = entry.unwrap(); let mut entry_data = std::io::BufReader::new(entry); let mut contents = String::new(); entry_data.read_to_string(&mut contents).unwrap(); bytes += contents.len(); all_bp.push((format!("{:?}", entry_data.into_inner().path().unwrap()), contents)); } let now = std::time::Instant::now(); for (path, contents) in all_bp { let output = BluePrint::parse(&contents); if output.is_err() { println!("Error for file: {:?}", path); println!("File content: {}", contents); println!("Error: {}", output.unwrap_err()); num_errors += 1; } count += 1; } let elapsed = now.elapsed().as_secs_f32(); println!("{} files ({} bytes) parsed in {:.3}s {}MB/s", count, bytes, elapsed, bytes as f32 / elapsed / 1024.0 / 1024.0); assert_eq!(num_errors, 0); } }