| #[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::<Vec<_>>(); |
| 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<VerboseError<&str>>> = 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<T>(input: &str, output: &Result<(&str, T), Err<VerboseError<&str>>>) -> () { |
| 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); |
| } |
| } |