| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! Tests parsing and deparsing of values. |
| //! |
| //! FOR_RELEASE: Fill this out. |
| //! |
| //! Note that this only focuses on testing the parsing/deparsing stage. We rely |
| //! on test_mojomparse.rs to verify that the translations to/from rust types |
| //! are accurate. |
| //! |
| //! The types in this file correspond to those defined in |
| //! //mojo/public/rust/test_mojom/parser_unittests.mojom |
| |
| chromium::import! { |
| "//mojo/public/rust/mojom_value_parser:mojom_value_parser_core"; |
| "//mojo/public/rust/mojom_value_parser:parser_unittests_rust"; |
| "//mojo/public/rust/mojom_value_parser:validation_parser"; |
| } |
| |
| use mojom_value_parser_core::*; |
| use ordered_float::OrderedFloat; |
| use parser_unittests_rust::parser_unittests::*; |
| use rust_gtest_interop::prelude::*; |
| |
| use crate::helpers::*; |
| |
| // Verify that `value` serializes to the binary data represented by `data`, |
| // which is a string in the mojom validation text format, as defined in |
| // //mojo/public/cpp/bindings/tests/validation_test_input_parser.h |
| fn validate_parsing<T>(value: T, data: &str) -> anyhow::Result<()> |
| where |
| T: MojomParse + PartialEq + std::fmt::Debug, |
| { |
| // We have to compute this eagerly since `value ` will get consumed by the test |
| let err_str = format!("\nRust value: {value:?}\nWire Data: {data}"); |
| |
| // Helper function so we can use the question mark operator, but also |
| // append context to it regardless of where we return. |
| let validate_parsing_internal = || -> anyhow::Result<()> { |
| let wire_data = validation_parser::parse(data) |
| .map_err(anyhow::Error::msg)? |
| // We currently don't do anything with handles, so only look at the data field |
| .data; |
| |
| // FOR_RELEASE: It would be nice to use the `verify_` macros from googletest |
| // that return a result, if we get access to them. |
| expect_eq!( |
| &value, |
| &parse_single_value_for_testing(wire_data.as_ref(), &mut [], T::wire_type())? |
| .try_into()? |
| ); |
| expect_eq!( |
| wire_data.as_ref(), |
| *deparse_single_value_for_testing(value.into(), T::wire_type())? |
| ); |
| Ok(()) |
| }; |
| |
| // We could also use anyhow's Context trait, but that overwrites the old context |
| // since we can't control the printing format gtest uses. |
| validate_parsing_internal().map_err(|err| anyhow::anyhow!("{err}{err_str}")) |
| } |
| |
| // Similar to `validate_parsing`, but for types with handles. It takes the |
| // number of handles the type should contain, and creates that many dummy |
| // handles for the deparsing process. It also does a slightly more relaxed check |
| // after parsing (comparing the parsed `MojomValue` instead of the parsed `T`). |
| fn validate_parsing_with_handles<T>(value: T, data: &str, num_handles: usize) -> anyhow::Result<()> |
| where |
| T: MojomParse + PartialEq + std::fmt::Debug, |
| { |
| // We have to compute this eagerly since `value ` will get consumed by the test |
| let err_str = format!("\nRust value: {value:?}\nWire Data: {data}"); |
| |
| // Helper function so we can use the question mark operator, but also |
| // append context to it regardless of where we return. |
| let validate_parsing_internal = || -> anyhow::Result<()> { |
| let wire_data = validation_parser::parse(data) |
| .map_err(anyhow::Error::msg)? |
| // We currently don't do anything with handles, so only look at the data field |
| .data; |
| |
| let mojom_value: MojomValue = value.into(); |
| let mut handles = (0..num_handles).map(|_| Some(dummy_handle())).collect::<Vec<_>>(); |
| |
| // FOR_RELEASE: It would be nice to use the `verify_` macros from googletest |
| // that return a result, if we get access to them. |
| expect_true!(equivalent_value( |
| &mojom_value, |
| &parse_single_value_for_testing(wire_data.as_ref(), &mut handles, T::wire_type())? |
| )); |
| // All the handles in `handles` should have been consumed during parsing |
| expect_true!(handles.into_iter().all(|opt| opt.is_none())); |
| expect_eq!( |
| wire_data.as_ref(), |
| *deparse_single_value_for_testing(mojom_value, T::wire_type())? |
| ); |
| Ok(()) |
| }; |
| |
| // We could also use anyhow's Context trait, but that overwrites the old context |
| // since we can't control the printing format gtest uses. |
| validate_parsing_internal().map_err(|err| anyhow::anyhow!("{err}{err_str}")) |
| } |
| |
| /// Check that we correctly fail to parse mismatching data. |
| fn validate_parsing_failure<T>(data: &str) -> anyhow::Result<()> |
| where |
| T: MojomParse + PartialEq + std::fmt::Debug, |
| { |
| validate_parsing_failure_with_handles::<T>(data, 0) |
| } |
| |
| /// Check that we correctly fail to parse mismatching data...with handles! |
| fn validate_parsing_failure_with_handles<T>(data: &str, num_handles: usize) -> anyhow::Result<()> |
| where |
| T: MojomParse + PartialEq + std::fmt::Debug, |
| { |
| let wire_data = validation_parser::parse(data).map_err(anyhow::Error::msg)?.data; |
| let mut handles = (0..num_handles).map(|_| Some(dummy_handle())).collect::<Vec<_>>(); |
| expect_true!( |
| parse_single_value_for_testing(wire_data.as_ref(), &mut handles, T::wire_type()).is_err() |
| ); |
| Ok(()) |
| } |
| |
| #[gtest(RustTestMojomParsing, TestPrimitiveParsing)] |
| fn test_primitives() -> anyhow::Result<()> { |
| validate_parsing(7u8, "[u1]7")?; |
| validate_parsing(0u8, "[u1]0")?; |
| validate_parsing(255u8, "[u1]0xFF")?; |
| validate_parsing(17u16, "[u2]17")?; |
| validate_parsing(0u16, "[u2]0")?; |
| validate_parsing(65535u16, "[u2]0xFFFF")?; |
| validate_parsing(1234567u32, "[u4]1234567")?; |
| validate_parsing(0u32, "[u4]0")?; |
| validate_parsing(4294967295u32, "[u4]0xFFFFFFFF")?; |
| validate_parsing(123456789101112u64, "[u8]123456789101112")?; |
| validate_parsing(0u64, "[u8]0")?; |
| validate_parsing(18446744073709551615u64, "[u8]0xFFFFFFFFFFFFFFFF")?; |
| |
| validate_parsing(-7i8, "[s1]-7")?; |
| validate_parsing(0i8, "[s1]0")?; |
| validate_parsing(127i8, "[s1]127")?; |
| validate_parsing(-128i8, "[s1]-128")?; |
| validate_parsing(-17i16, "[s2]-17")?; |
| validate_parsing(0i16, "[s2]0")?; |
| validate_parsing(32767i16, "[s2]32767")?; |
| validate_parsing(-32768i16, "[s2]-32768")?; |
| validate_parsing(-1234567i32, "[s4]-1234567")?; |
| validate_parsing(0i32, "[s4]0")?; |
| validate_parsing(2147483647i32, "[s4]2147483647")?; |
| validate_parsing(-2147483648i32, "[s4]-2147483648")?; |
| validate_parsing(-123456789101112i64, "[s8]-123456789101112")?; |
| validate_parsing(0i64, "[s8]0")?; |
| validate_parsing(9223372036854775807i64, "[s8]9223372036854775807")?; |
| validate_parsing(-9223372036854775808i64, "[s8]-9223372036854775808")?; |
| |
| validate_parsing(3.14f32, "[f]3.14")?; |
| validate_parsing(0.0f32, "[f]0.0")?; |
| validate_parsing(-1.0f32, "[f]-1.0")?; |
| |
| validate_parsing(2.71828f64, "[d]2.71828")?; |
| validate_parsing(0.0f64, "[d]0.0")?; |
| validate_parsing(-123.456f64, "[d]-123.456")?; |
| |
| // Also make sure we correctly fail to parse things. |
| // Since all bit patterns are valid, the only way for this to happen is if we |
| // don't have enough data. |
| validate_parsing_failure::<u16>("[u1]8")?; |
| validate_parsing_failure::<i64>("[s4]1006")?; |
| validate_parsing_failure::<f64>("[s4]1006")?; |
| |
| Ok(()) |
| } |
| |
| #[gtest(RustTestMojomParsing, TestStructParsing)] |
| fn test_structs() -> anyhow::Result<()> { |
| validate_parsing(Empty {}, "[u4]8 [u4]0")?; |
| |
| validate_parsing( |
| FourInts { a: 10, b: -20, c: 400, d: -8000 }, |
| "[u4]24 [u4]0 [s1]10 [u1]0 [s2]-20 [s4]400 [s8]-8000", |
| )?; |
| |
| validate_parsing( |
| FourIntsReversed { a: 10, b: -20, c: 400, d: -8000 }, |
| "[u4]24 [u4]0 [s8]-8000 [s4]400 [s2]-20 [s1]10 [u1]0", |
| )?; |
| |
| validate_parsing( |
| FourIntsIntermixed { a: 10, b: 400, c: -20, d: -5 }, |
| "[u4]16 [u4]0 [s1]10 [s1]-5 [s2]-20 [s4]400", |
| )?; |
| |
| let oncenested_val = OnceNested { |
| f1: FourInts { a: 1, b: 2, c: 3, d: 4 }, |
| a: 13, |
| b: 14, |
| f2: FourIntsReversed { a: 5, b: 6, c: 7, d: 8 }, |
| f3: FourIntsIntermixed { a: 9, b: 10, c: 11, d: 12 }, |
| c: 15, |
| }; |
| |
| let oncenested_wire = " |
| // OnceNested header |
| [dist4]OnceNested_size [u4]0 |
| // f1 |
| [dist8]f1_ptr |
| // a, b, c |
| [u4]13 [s1]14 [u1]0 [s2]15 |
| // f2 |
| [dist8]f2_ptr |
| // f3 |
| [dist8]f3_ptr |
| [anchr]OnceNested_size |
| |
| [anchr]f1_ptr |
| [u4]24 [u4]0 [s1]1 [u1]0 [s2]2 [s4]3 [s8]4 |
| |
| [anchr]f2_ptr |
| [u4]24 [u4]0 [s8]8 [s4]7 [s2]6 [s1]5 [u1]0 |
| |
| [anchr]f3_ptr |
| [u4]16 [u4]0 [s1]9 [s1]12 [s2]11 [s4]10 |
| "; |
| |
| validate_parsing(oncenested_val.clone(), oncenested_wire)?; |
| |
| let twicenested_val = TwiceNested { |
| o: oncenested_val.clone(), |
| a: 16, |
| f: FourInts { a: 17, b: 18, c: 19, d: 20 }, |
| b: 21, |
| c: 22, |
| }; |
| |
| let twicenested_wire = format!( |
| " |
| // TwiceNested header |
| [dist4]TwiceNested_size [u4]0 |
| // o |
| [dist8]o_ptr |
| // a, b |
| [s2]16 [s2]0 [s4]21 |
| // f |
| [dist8]f_ptr |
| // c |
| [s4]22 [s4]0 |
| |
| [anchr]TwiceNested_size |
| |
| [anchr]o_ptr |
| {oncenested_wire} |
| |
| [anchr]f_ptr |
| [u4]24 [u4]0 [s1]17 [u1]0 [s2]18 [s4]19 [s8]20 |
| " |
| ); |
| |
| validate_parsing(twicenested_val, &twicenested_wire)?; |
| |
| // Validate that struct parsing can fail in various ways |
| |
| // Wrong header size |
| validate_parsing_failure::<Empty>("[u4]4 [u4]0")?; |
| validate_parsing_failure::<Empty>("[u4]16 [u4]0")?; |
| // Not 8-byte aligned |
| validate_parsing_failure::<Empty>("[u4]9 [u4]0 [u1]0")?; |
| |
| // f1_ptr not 8-byte-aligned |
| validate_parsing_failure::<OnceNested>( |
| " |
| // OnceNested header |
| [dist4]OnceNested_size [u4]0 |
| // f1 |
| [dist8]f1_ptr |
| // a, b, c |
| [u4]13 [s1]14 [u1]0 [s2]15 |
| // f2 |
| [dist8]f2_ptr |
| // f3 |
| [dist8]f3_ptr |
| [anchr]OnceNested_size |
| |
| [u1]0 |
| [anchr]f1_ptr |
| [u4]24 [u4]0 [s1]1 [u1]0 [s2]2 [s4]3 [s8]4 |
| |
| [anchr]f2_ptr |
| [u4]24 [u4]0 [s8]8 [s4]7 [s2]6 [s1]5 [u1]0 |
| |
| [anchr]f3_ptr |
| [u4]16 [u4]0 [s1]9 [s1]12 [s2]11 [s4]10 |
| ", |
| )?; |
| |
| // f1_ptr invalidly null |
| validate_parsing_failure::<OnceNested>( |
| " |
| // OnceNested header |
| [dist4]OnceNested_size [u4]0 |
| // f1 |
| [u8]0 //f1_ptr |
| // a, b, c |
| [u4]13 [s1]14 [u1]0 [s2]15 |
| // f2 |
| [dist8]f2_ptr |
| // f3 |
| [dist8]f3_ptr |
| [anchr]OnceNested_size |
| |
| [u4]24 [u4]0 [s1]1 [u1]0 [s2]2 [s4]3 [s8]4 |
| |
| [anchr]f2_ptr |
| [u4]24 [u4]0 [s8]8 [s4]7 [s2]6 [s1]5 [u1]0 |
| |
| [anchr]f3_ptr |
| [u4]16 [u4]0 [s1]9 [s1]12 [s2]11 [s4]10 |
| ", |
| )?; |
| |
| // f1_ptr in wrong place |
| validate_parsing_failure::<OnceNested>( |
| " |
| // OnceNested header |
| [dist4]OnceNested_size [u4]0 |
| // f1 |
| [dist8]f1_ptr |
| // a, b, c |
| [u4]13 [s1]14 [u1]0 [s2]15 |
| // f2 |
| [dist8]f2_ptr |
| // f3 |
| [dist8]f3_ptr |
| [anchr]OnceNested_size |
| |
| [u4]24 [u4]0 [s1]1 [u1]0 [s2]2 [s4]3 [s8]4 |
| [anchr]f1_ptr |
| |
| [anchr]f2_ptr |
| [u4]24 [u4]0 [s8]8 [s4]7 [s2]6 [s1]5 [u1]0 |
| |
| [anchr]f3_ptr |
| [u4]16 [u4]0 [s1]9 [s1]12 [s2]11 [s4]10 |
| ", |
| )?; |
| |
| Ok(()) |
| } |
| |
| #[gtest(RustTestMojomParsing, TestBoolParsing)] |
| fn test_bools() -> anyhow::Result<()> { |
| validate_parsing( |
| TenBoolsAndAByte { |
| b0: true, |
| b1: true, |
| b2: false, |
| b3: false, |
| b4: false, |
| n1: 123, |
| b5: true, |
| b6: false, |
| b7: true, |
| // Byte boundary |
| b8: false, |
| b9: true, |
| }, |
| "[u4]16 [u4]0 [b]10100011 [u1]123 [b]00000010 [u1]0 [u4]0", |
| )?; |
| |
| validate_parsing( |
| TenBoolsAndTwoBytes { |
| b0: true, |
| b1: false, |
| b2: false, |
| b3: false, |
| b4: true, |
| n1: 12345, |
| b5: true, |
| b6: false, |
| b7: true, |
| // Byte boundary |
| b8: true, |
| b9: false, |
| }, |
| "[u4]16 [u4]0 [b]10110001 [b]00000001 [u2]12345 [u4]0", |
| )?; |
| |
| Ok(()) |
| } |
| |
| #[gtest(RustTestMojomParsing, TestEnumParsing)] |
| fn test_enums() -> anyhow::Result<()> { |
| validate_parsing( |
| SomeEnums { e1: TestEnum::Seven, n1: 98765, e2: TestEnum2::FourtyTwo }, |
| "[u4]24 [u4]0 [u4]7 [u4]42 [u8]98765", |
| )?; |
| |
| validate_parsing_failure::<SomeEnums>("[u4]24 [u4]0 [u4]5 [u4]42 [u8]98765")?; |
| validate_parsing_failure::<SomeEnums>("[u4]24 [u4]0 [u4]7 [u4]99 [u8]98765")?; |
| |
| validate_parsing(TestEnumWithNegativeDiscriminants::NegVal, "[s4]-1")?; |
| |
| validate_parsing(TestEnumWithNegativeDiscriminants::ZeroVal, "[u4]0")?; |
| |
| validate_parsing(TestEnumWithNegativeDiscriminants::PosVal, "[u4]1")?; |
| |
| Ok(()) |
| } |
| |
| #[gtest(RustTestMojomParsing, TestUnionParsing)] |
| fn test_unions() -> anyhow::Result<()> { |
| // BaseUnion |
| validate_parsing(BaseUnion::n1(10), "[u4]16 [u4]0 [u8]10")?; |
| validate_parsing(BaseUnion::u1(987654321), "[u4]16 [u4]1 [u8]987654321")?; |
| validate_parsing(BaseUnion::e1(TestEnum::Three), "[u4]16 [u4]2 [u8]3")?; |
| validate_parsing(BaseUnion::b1(false), "[u4]16 [u4]3 [u8]0")?; |
| validate_parsing(BaseUnion::b2(true), "[u4]16 [u4]4 [u8]1")?; |
| validate_parsing( |
| BaseUnion::em1(Empty {}), |
| "[u4]16 [u4]5 [dist8]em1_ptr [anchr]em1_ptr [u4]8 [u4]0", |
| )?; |
| validate_parsing( |
| BaseUnion::f1(FourInts { a: 5, b: 6, c: 7, d: 8 }), |
| concat!( |
| "[u4]16 [u4]6 [dist8]f1_ptr ", // BaseUnion |
| "[anchr]f1_ptr [u4]24 [u4]0 [s1]5 [u1]0 [s2]6 [s4]7 [s8]8", // FourInts |
| ), |
| )?; |
| validate_parsing(BaseUnion::fl(3.14f32), "[u4]16 [u4]7 [f]3.14 [u4]0")?; |
| |
| validate_parsing_failure::<BaseUnion>("[u4]16 [u4]99 [u8]0")?; |
| validate_parsing_failure::<BaseUnion>("[u4]16 [u4]6 [u8]0")?; |
| |
| // NestedUnion |
| validate_parsing(NestedUnion::n(99), "[u4]16 [u4]0 [u8]99")?; |
| validate_parsing( |
| NestedUnion::u(BaseUnion::e1(TestEnum::Three)), |
| "[u4]16 [u4]1 [dist8]u_ptr [anchr]u_ptr [u4]16 [u4]2 [u8]3", |
| )?; |
| validate_parsing( |
| NestedUnion::u(BaseUnion::f1(FourInts { a: 1, b: 2, c: 3, d: 4 })), |
| concat!( |
| "[u4]16 [u4]1 [dist8]u_ptr ", // NestedUnion |
| "[anchr]u_ptr [u4]16 [u4]6 [dist8]f1_ptr ", // BaseUnion |
| "[anchr]f1_ptr [u4]24 [u4]0 [s1]1 [u1]0 [s2]2 [s4]3 [s8]4", // FourInts |
| ), |
| )?; |
| |
| // Should be encoded as a pointer |
| validate_parsing_failure::<NestedUnion>("[u4]16 [u4]1 [u4]16 [u4]2 [u8]3")?; |
| |
| // WithNestedUnion |
| validate_parsing( |
| WithNestedUnion { n1: 17, u: NestedUnion::n(18), n2: 19 }, |
| concat!( |
| "[u4]40 [u4]0 [s8]17 ", // WithNestedUnion Header + n1 |
| "[u4]16 [u4]0 [u8]18 ", // NestedUnion |
| "[u4]19 [u4]0", // n2 |
| ), |
| )?; |
| validate_parsing( |
| WithNestedUnion { n1: 99, u: NestedUnion::u(BaseUnion::b1(true)), n2: 100 }, |
| concat!( |
| "[u4]40 [u4]0 [s8]99 ", // WithNestedUnion Header + n1 |
| "[u4]16 [u4]1 [dist8]u_ptr ", // NestedUnion |
| "[u4]100 [u4]0 ", // n2 |
| "[anchr]u_ptr [u4]16 [u4]3 [u8]1", // BaseUnion |
| ), |
| )?; |
| |
| // NestederUnion |
| validate_parsing(NestederUnion::b(false), "[u4]16 [u4]0 [u8]0")?; |
| validate_parsing(NestederUnion::n(-1), "[u4]16 [u4]1 [s1]-1 [u1]0 [u2]0 [u4]0")?; |
| validate_parsing( |
| NestederUnion::u(NestedUnion::n(123)), |
| "[u4]16 [u4]2 [dist8]u_ptr [anchr]u_ptr [u4]16 [u4]0 [u8]123", |
| )?; |
| validate_parsing( |
| NestederUnion::u(NestedUnion::u(BaseUnion::b2(true))), |
| concat!( |
| "[u4]16 [u4]2 [dist8]u_ptr ", // NestederUnion |
| "[anchr]u_ptr [u4]16 [u4]1 [dist8]u_ptr2 ", // NestedUnion |
| "[anchr]u_ptr2 [u4]16 [u4]4 [u8]1", // BaseUnion |
| ), |
| )?; |
| validate_parsing( |
| NestederUnion::w(WithNestedUnion { n1: 1000, u: NestedUnion::n(-50), n2: 200 }), |
| concat!( |
| "[u4]16 [u4]3 [dist8]w_ptr ", // NestederUnion |
| "[anchr]w_ptr [u4]40 [u4]0 [s8]1000 ", // WithNestedUnion Header + n1 |
| "[u4]16 [u4]0 [s4]-50 [u4]0 ", // NestedUnion |
| "[u4]200 [u4]0", // n2 |
| ), |
| )?; |
| validate_parsing( |
| NestederUnion::w(WithNestedUnion { |
| n1: 999, |
| u: NestedUnion::u(BaseUnion::f1(FourInts { a: 101, b: -102, c: 103, d: -104 })), |
| n2: -211, |
| }), |
| concat!( |
| "[u4]16 [u4]3 [dist8]w_ptr ", // NestederUnion |
| "[anchr]w_ptr [u4]40 [u4]0 [s8]999 ", // WithNestedUnion Header + n1 |
| "[u4]16 [u4]1 [dist8]u_ptr ", // NestedUnion |
| "[s4]-211 [u4]0 ", // n2 |
| "[anchr]u_ptr [u4]16 [u4]6 [dist8]f1_ptr ", // BaseUnion |
| "[anchr]f1_ptr [u4]24 [u4]0 [s1]101 [u1]0 [s2]-102 [s4]103 [s8]-104" // FourInts |
| ), |
| )?; |
| |
| // WithManyUnions |
| validate_parsing( |
| WithManyUnions { |
| u1: NestedUnion::n(50), |
| i1: 11, |
| u2: NestederUnion::b(false), |
| d1: 3.14159, |
| u3: BaseUnion::n1(55), |
| u4: NestederUnion::n(12), |
| i2: 33, |
| i3: 44, |
| }, |
| concat!( |
| "[u4]96 [u4]0 ", // WithManyUnions Header |
| "[u4]16 [u4]0 [u8]50 ", // u1 |
| "[s1]11 [u1]0 [u2]0 [s4]33 ", // i1, padding, i2 |
| "[u4]16 [u4]0 [u8]0 ", // u2 |
| "[d]3.14159 ", // d1 |
| "[u4]16 [u4]0 [u8]55 ", // u3 |
| "[u4]16 [u4]1 [u8]12 ", // u4 |
| "[s4]44 [u4]0", // i3, padding |
| ), |
| )?; |
| |
| // ALL the nesting! |
| validate_parsing( |
| WithManyUnions { |
| u1: NestedUnion::u(BaseUnion::b1(true)), |
| i1: -1, |
| u2: NestederUnion::u(NestedUnion::u(BaseUnion::e1(TestEnum::Seven))), |
| d1: 2.71828, |
| u3: BaseUnion::b2(false), |
| u4: NestederUnion::w(WithNestedUnion { |
| n1: 2000, |
| u: NestedUnion::u(BaseUnion::f1(FourInts { a: 12, b: 13, c: 14, d: 15 })), |
| n2: 3000, |
| }), |
| i2: -3, |
| i3: -4, |
| }, |
| concat!( |
| "[u4]96 [u4]0 ", // WithManyUnions Header |
| "[u4]16 [u4]1 [dist8]u1_ptr ", // NestedUnion::u |
| "[s1]-1 [u1]0 [u2]0 [s4]-3 ", // i1, padding, i2 |
| "[u4]16 [u4]2 [dist8]u2_ptr ", // NestederUnion::u |
| "[d]2.71828 ", // d1 |
| "[u4]16 [u4]4 [u8]0 ", // BaseUnion::b2(false) |
| "[u4]16 [u4]3 [dist8]u4_ptr ", // NestederUnion::w |
| "[s4]-4 [u4]0 ", // i3, padding |
| "[anchr]u1_ptr [u4]16 [u4]3 [u8]1 ", // BaseUnion::b1(true) - u1.u |
| "[anchr]u2_ptr [u4]16 [u4]1 [dist8]u2_u_ptr ", // NestedUnion::u - u2.u |
| "[anchr]u2_u_ptr [u4]16 [u4]2 [u8]7 ", // BaseUnion::e1(...) - u2.u.e1 |
| "[anchr]u4_ptr [u4]40 [u4]0 [s8]2000 ", // WithNestedUnion Header + n1 |
| "[u4]16 [u4]1 [dist8]u4_w_u_ptr ", // NestedUnion::u - u4.w.u |
| "[s4]3000 [u4]0 ", // WithNestedUnion.n2 |
| "[anchr]u4_w_u_ptr [u4]16 [u4]6 [dist8]u4_f1_ptr ", // BaseUnion::f1 - u4.w.u.u |
| "[anchr]u4_f1_ptr [u4]24 [u4]0 [s1]12 [u1]0 [s2]13 [s4]14 [s8]15" // FourInts |
| ), |
| )?; |
| |
| Ok(()) |
| } |
| |
| #[gtest(RustTestMojomParsing, TestArrayParsing)] |
| fn test_array_parsing() -> anyhow::Result<()> { |
| // array<int16> |
| validate_parsing::<Vec<i16>>( |
| vec![1, -2, 3, -4, 5], |
| "[u4]18 [u4]5 [s2]1 [s2]-2 [s2]3 [s2]-4 [s2]5 [u2]0 [u4]0", |
| )?; |
| |
| validate_parsing::<Vec<i16>>(vec![], "[u4]8 [u4]0")?; |
| |
| // array<uint64, 3> |
| validate_parsing::<[u64; 3]>([5, 6, 7], "[u4]32 [u4]3 [u8]5 [u8]6 [u8]7")?; |
| // Wrong number of elements |
| validate_parsing_failure::<[u64; 3]>("[u4]32 [u4]4 [u8]5 [u8]6 [u8]7 [u8]8")?; |
| |
| // array<bool> |
| validate_parsing::<Vec<bool>>( |
| vec![true, true, false, true, false, false, true, false, true], |
| "[u4]10 [u4]9 [b]01001011 [b]00000001 [u2]0 [u4]0", |
| )?; |
| |
| // array<bool, 20> |
| validate_parsing::<[bool; 20]>( |
| [ |
| false, true, true, false, true, true, false, true, true, false, true, true, false, |
| true, true, false, true, true, false, true, |
| ], |
| "[u4]11 [u4]20 [b]10110110 [b]01101101 [b]00001011 [u1]0 [u4]0", |
| )?; |
| |
| // array<float> |
| validate_parsing::<Vec<f32>>( |
| vec![1.0, -2.5, 3.14], |
| "[u4]20 [u4]3 [f]1.0 [f]-2.5 [f]3.14 [u4]0", |
| )?; |
| |
| // array<TestEnum> |
| validate_parsing::<Vec<TestEnum>>( |
| vec![TestEnum::Zero, TestEnum::Four, TestEnum::Seven], |
| "[u4]20 [u4]3 [u4]0 [u4]4 [u4]7 [u4]0", |
| )?; |
| |
| // Bad enum value |
| validate_parsing_failure::<Vec<TestEnum>>("[u4]24 [u4]3 [u4]0 [u4]99 [u4]4")?; |
| |
| // array<BaseUnion> |
| validate_parsing::<Vec<BaseUnion>>( |
| vec![BaseUnion::n1(10), BaseUnion::u1(20), BaseUnion::e1(TestEnum::Three)], |
| concat!( |
| "[u4]56 [u4]3 ", // Array header |
| "[u4]16 [u4]0 [u8]10 ", |
| "[u4]16 [u4]1 [u8]20 ", |
| "[u4]16 [u4]2 [u8]3", |
| ), |
| )?; |
| |
| // Bad union value in array |
| validate_parsing_failure::<Vec<BaseUnion>>(concat!( |
| "[u4]48 [u4]3 ", // Array header |
| "[u4]16 [u4]0 [u8]10 ", |
| "[u4]16 [u4]99 [u8]20 ", // Invalid discriminant |
| "[u4]16 [u4]2 [u8]3", |
| ))?; |
| |
| // array<FourInts> |
| validate_parsing::<Vec<FourInts>>( |
| vec![FourInts { a: 1, b: 2, c: 3, d: 4 }, FourInts { a: 5, b: 6, c: 7, d: 8 }], |
| concat!( |
| "[u4]24 [u4]2 ", // Array header |
| "[dist8]fourints_0_ptr ", |
| "[dist8]fourints_1_ptr ", |
| "[anchr]fourints_0_ptr ", |
| "[u4]24 [u4]0 [s1]1 [u1]0 [s2]2 [s4]3 [s8]4 ", // FourInts 0 |
| "[anchr]fourints_1_ptr ", |
| "[u4]24 [u4]0 [s1]5 [u1]0 [s2]6 [s4]7 [s8]8", // FourInts 1 |
| ), |
| )?; |
| |
| // array<array<uint8>> |
| validate_parsing::<Vec<Vec<u8>>>( |
| vec![vec![1, 2], vec![3, 4, 5]], |
| concat!( |
| "[u4]24 [u4]2 ", // Array header |
| "[dist8]nested_0_ptr ", |
| "[dist8]nested_1_ptr ", |
| "[anchr]nested_0_ptr ", |
| "[u4]10 [u4]2 [u1]1 [u1]2 [u2]0 [u4]0 ", // Inner array 0 |
| "[anchr]nested_1_ptr ", |
| "[u4]11 [u4]3 [u1]3 [u1]4 [u1]5 [u1]0 [u4]0", // Inner array 1 |
| ), |
| )?; |
| |
| // array<array<uint8, 2>, 3> |
| validate_parsing::<[[u8; 2]; 3]>( |
| [[6, 7], [8, 9], [10, 11]], |
| concat!( |
| "[u4]32 [u4]3 ", // Outer array header |
| "[dist8]nested_sized_0_ptr ", |
| "[dist8]nested_sized_1_ptr ", |
| "[dist8]nested_sized_2_ptr ", |
| "[anchr]nested_sized_0_ptr ", |
| "[u4]10 [u4]2 [u1]6 [u1]7 [u2]0 [u4]0 ", // Inner array 0 |
| "[anchr]nested_sized_1_ptr ", |
| "[u4]10 [u4]2 [u1]8 [u1]9 [u2]0 [u4]0 ", // Inner array 1 |
| "[anchr]nested_sized_2_ptr ", |
| "[u4]10 [u4]2 [u1]10 [u1]11 [u2]0 [u4]0", // Inner array 2 |
| ), |
| )?; |
| |
| // array<NestedUnion> |
| validate_parsing::<Vec<NestedUnion>>( |
| vec![NestedUnion::n(30), NestedUnion::u(BaseUnion::n1(40)), NestedUnion::n(50)], |
| concat!( |
| "[u4]56 [u4]3 ", // Array header |
| "[u4]16 [u4]0 [u8]30 ", |
| "[u4]16 [u4]1 [dist8]nested_union_1_ptr ", |
| "[u4]16 [u4]0 [u8]50 ", |
| "[anchr]nested_union_1_ptr ", |
| "[u4]16 [u4]0 [u8]40", |
| ), |
| )?; |
| |
| Ok(()) |
| } |
| |
| // Note that tests involving maps are especially tricky, because semantically |
| // the order of (key, value) pairs doesn't matter, but since we do exact |
| // comparisons it matters when we specify the wire data here. |
| // Our implementation uses a BTreeMap to guarantee that we always serialize in |
| // a sorted order. |
| #[gtest(RustTestMojomParsing, TestMapParsing)] |
| fn test_map_parsing() -> anyhow::Result<()> { |
| use std::collections::HashMap; |
| |
| validate_parsing::<HashMap<i8, i8>>( |
| [(1, 10), (3, 40), (8, 99)].into(), |
| concat!( |
| // Map header |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr ", |
| "[u4]11 [u4]3 ", // Keys header: size, num_elements |
| "[s1]1 [s1]3 [s1]8 [u1]0 [u4]0 ", |
| // values array |
| "[anchr]values_ptr ", |
| "[u4]11 [u4]3 ", // Values header: size, num_elements |
| "[s1]10 [s1]40 [s1]99 [u1]0 [u4]0 ", |
| ), |
| )?; |
| |
| // Empty maps are fine |
| validate_parsing::<HashMap<i8, i8>>( |
| [].into(), |
| concat!( |
| // Map struct |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr [u4]8 [u4]0 ", |
| // values array |
| "[anchr]values_ptr [u4]8 [u4]0 ", |
| ), |
| )?; |
| |
| // Mismatched sizes are not fine |
| validate_parsing_failure::<HashMap<i8, i8>>(concat!( |
| // Map struct |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr [u4]9 [u4]1 ", |
| "[s1]1 [s1]0 [s2]0 [s4]0 ", |
| // values array |
| "[anchr]values_ptr [u4]8 [u4]0 ", |
| ))?; |
| validate_parsing_failure::<HashMap<i8, i8>>(concat!( |
| // Map struct |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr [u4]9 [u4]1 ", |
| "[s1]1 [s1]0 [s2]0 [s4]0 ", |
| // values array |
| "[anchr]values_ptr [u4]10 [u4]2 ", |
| "[s1]2 [s1]3 [s2]0 [s4]0 ", |
| ))?; |
| |
| // Nor are duplicate keys |
| validate_parsing_failure::<HashMap<i8, i8>>(concat!( |
| // Map struct |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr [u4]10 [u4]2 ", |
| "[s1]1 [s1]1 [s2]0 [s4]0 ", |
| // values array |
| "[anchr]values_ptr [u4]10 [u4]2 ", |
| "[s1]2 [s1]3 [s2]0 [s4]0 ", |
| ))?; |
| validate_parsing_failure::<HashMap<OrderedFloat<f64>, i8>>(concat!( |
| // Map struct |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr [u4]24 [u4]2 ", |
| "[d]1.233e-4 [d]1.233e-4 ", |
| // values array |
| "[anchr]values_ptr [u4]10 [u4]2 ", |
| "[s1]2 [s1]3 [s2]0 [s4]0 ", |
| ))?; |
| |
| validate_parsing::<HashMap<bool, u16>>( |
| [(false, 1020), (true, 3040)].into(), |
| concat!( |
| // Map header |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr ", |
| "[u4]9 [u4]2 [b]00000010 [u1]0 [u2]0 [u4]0 ", |
| // values array |
| "[anchr]values_ptr ", |
| "[u4]12 [u4]2 [u2]1020 [u2]3040 [u4]0", |
| ), |
| )?; |
| |
| validate_parsing::<HashMap<OrderedFloat<f32>, i32>>( |
| [(1.1f32.into(), 10), (2.2f32.into(), 20)].into(), |
| concat!( |
| // Map header |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr ", |
| "[u4]16 [u4]2 [f]1.1 [f]2.2 ", |
| // values array |
| "[anchr]values_ptr ", |
| "[u4]16 [u4]2 [s4]10 [s4]20 ", |
| ), |
| )?; |
| |
| validate_parsing::<HashMap<TestEnum, i32>>( |
| [(TestEnum::Seven, -2), (TestEnum::Four, -3), (TestEnum::Zero, -1), (TestEnum::Three, -3)] |
| .into(), |
| concat!( |
| // Map header |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr ", |
| // Note that keys are sorted in ascending order |
| "[u4]24 [u4]4 [u4]0 [u4]3 [u4]4 [u4]7 ", |
| // values array |
| "[anchr]values_ptr ", |
| // And the values are sorted to match |
| "[u4]24 [u4]4 [s4]-1 [s4]-3 [s4]-3 [s4]-2 ", |
| ), |
| )?; |
| |
| validate_parsing::<HashMap<i8, FourInts>>( |
| [(1, FourInts { a: 1, b: 2, c: 3, d: 4 }), (5, FourInts { a: 5, b: 6, c: 7, d: 8 })].into(), |
| concat!( |
| // Map header |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr [u4]10 [u4]2 [s1]1 [s1]5 [u2]0 [u4]0 ", |
| // values array |
| "[anchr]values_ptr ", |
| "[u4]24 [u4]2 [dist8]v0 [dist8]v1 ", |
| "[anchr]v0 [u4]24 [u4]0 [s1]1 [u1]0 [s2]2 [s4]3 [s8]4 ", |
| "[anchr]v1 [u4]24 [u4]0 [s1]5 [u1]0 [s2]6 [s4]7 [s8]8 ", |
| ), |
| )?; |
| |
| validate_parsing::<HashMap<i8, NestedUnion>>( |
| [ |
| (-1, NestedUnion::n(10)), |
| (-2, NestedUnion::u(BaseUnion::e1(TestEnum::Seven))), |
| (-3, NestedUnion::n(23)), |
| ] |
| .into(), |
| concat!( |
| // Map header |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // keys array |
| "[anchr]keys_ptr [u4]11 [u4]3 [s1]-3 [s1]-2 [s1]-1 [s1]0 [u4]0 ", |
| // values array |
| "[anchr]values_ptr ", |
| "[u4]56 [u4]3 ", |
| "[u4]16 [u4]0 [u8]23 ", |
| "[u4]16 [u4]1 [dist8]v1 ", |
| "[u4]16 [u4]0 [u8]10 ", |
| "[anchr]v1 [u4]16 [u4]2 [u8]7", |
| ), |
| )?; |
| |
| validate_parsing::<HashMap<i8, HashMap<i16, u32>>>( |
| [(1, [(10, 100), (20, 200)].into()), (2, [(30, 300), (40, 400), (50, 500)].into())].into(), |
| concat!( |
| // Toplevel map header |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| // Toplevel keys array |
| "[anchr]keys_ptr [u4]10 [u4]2 [s1]1 [s1]2 [u2]0 [u4]0 ", |
| // Toplevel values array |
| "[anchr]values_ptr ", |
| "[u4]24 [u4]2 [dist8]v0 [dist8]v1 ", |
| // First nested map |
| "[anchr]v0 [u4]24 [u4]0 [dist8]v0_keys [dist8]v0_values ", |
| "[anchr]v0_keys [u4]12 [u4]2 [s2]10 [s2]20 [u4]0 ", |
| "[anchr]v0_values [u4]16 [u4]2 [u4]100 [u4]200 ", |
| // Second nested map |
| "[anchr]v1 [u4]24 [u4]0 [dist8]v1_keys [dist8]v1_values ", |
| "[anchr]v1_keys [u4]14 [u4]3 [s2]30 [s2]40 [s2]50 [u2]0 ", |
| "[anchr]v1_values [u4]20 [u4]3 [u4]300 [u4]400 [u4]500 [u4]0 ", |
| ), |
| )?; |
| |
| Ok(()) |
| } |
| |
| // Create the expected wire format representation of string. |
| fn str_wire_format(str: &str) -> String { |
| let size = 8 + str.len(); |
| let num_chars = str.len(); |
| let padding = (8 - (str.len() % 8)) % 8; |
| let body: String = |
| str.as_bytes().iter().fold("".to_string(), |acc, b| format!("{acc} [u1]{b}")); |
| let padding: String = std::iter::repeat_n("[u1]0 ", padding).collect(); |
| format!("[u4]{size} [u4]{num_chars} {body} {padding}") |
| } |
| |
| #[gtest(RustTestMojomParsing, TestStringParsing)] |
| fn test_string_parsing() -> anyhow::Result<()> { |
| use std::collections::HashMap; |
| |
| validate_parsing::<String>("hello".to_string(), &str_wire_format("hello"))?; |
| |
| validate_parsing::<Vec<String>>( |
| vec!["life".to_string(), "universe".to_string(), "everything".to_string()], |
| &format!( |
| "{} {} {} {} {} {} {} {}", |
| "[u4]32 [u4]3 ", |
| "[dist8]life_ptr [dist8]universe_ptr [dist8]everything_ptr", |
| "[anchr]life_ptr", |
| &str_wire_format("life"), |
| "[anchr]universe_ptr", |
| &str_wire_format("universe"), |
| "[anchr]everything_ptr", |
| &str_wire_format("everything"), |
| ), |
| )?; |
| |
| validate_parsing::<HashMap<u8, String>>( |
| [(10, "ten".to_string()), (20, "twenty".to_string())].into(), |
| &format!( |
| "{} {} {} {} {} {} {} {} {}", |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| "[anchr]keys_ptr ", |
| "[u4]10 [u4]2 [u1]10 [u1]20 [u2]0 [u4]0 ", |
| "[anchr]values_ptr ", |
| "[u4]24 [u4]2 [dist8]ten_ptr [dist8]twenty_ptr ", |
| "[anchr]ten_ptr ", |
| &str_wire_format("ten"), |
| "[anchr]twenty_ptr ", |
| &str_wire_format("twenty"), |
| ), |
| )?; |
| |
| validate_parsing::<HashMap<String, i16>>( |
| [("three".to_string(), 3), ("four".to_string(), 4)].into(), |
| &format!( |
| "{} {} {} {} {} {} {} {} {}", |
| "[u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| "[anchr]keys_ptr ", |
| "[u4]24 [u4]2 [dist8]four_ptr [dist8]three_ptr ", |
| "[anchr]four_ptr ", |
| &str_wire_format("four"), |
| "[anchr]three_ptr ", |
| &str_wire_format("three"), |
| "[anchr]values_ptr ", |
| "[u4]12 [u4]2 [s2]4 [s2]3 [u4]0 ", |
| ), |
| )?; |
| |
| // Non-UTF8 string (192 isn't a valid UTF-8 character) |
| validate_parsing_failure::<String>("[u4]10 [u4]2 [u1]72 [u1]192 [u2]0 [u4]0")?; |
| |
| // Extremely UTF-8 string, courtesy of the rust docs |
| validate_parsing("💖".to_string(), "[u4]12 [u4]4 [u1]240 [u1]159 [u1]146 [u1]150 [u4]0")?; |
| |
| Ok(()) |
| } |
| |
| #[gtest(RustTestMojomParsing, TestComplexUnionParsing)] |
| fn test_complex_union_parsing() -> anyhow::Result<()> { |
| // HoldsComplexTypes: string |
| validate_parsing::<HoldsComplexTypes>( |
| HoldsComplexTypes::str("union_string".to_string()), |
| &format!( |
| "[u4]16 [u4]0 [dist8]union_str_ptr [anchr]union_str_ptr {}", |
| &str_wire_format("union_string") |
| ), |
| )?; |
| |
| validate_parsing::<ComplexUnionHolder>( |
| ComplexUnionHolder { u: HoldsComplexTypes::str("union_string".to_string()) }, |
| &format!( |
| "[u4]24 [u4]0 [u4]16 [u4]0 [dist8]union_str_ptr [anchr]union_str_ptr {}", |
| &str_wire_format("union_string") |
| ), |
| )?; |
| |
| // HoldsComplexTypes: array<int16> |
| validate_parsing::<HoldsComplexTypes>( |
| HoldsComplexTypes::arr(vec![-10, -20, -30]), |
| concat!( |
| "[u4]16 [u4]1 [dist8]union_arr_ptr ", |
| "[anchr]union_arr_ptr [u4]14 [u4]3 [s2]-10 [s2]-20 [s2]-30 [u2]0" |
| ), |
| )?; |
| |
| // HoldsComplexTypes: map<uint8, uint8> |
| validate_parsing::<HoldsComplexTypes>( |
| HoldsComplexTypes::m([(100, 1), (101, 2)].into()), |
| concat!( |
| "[u4]16 [u4]2 [dist8]union_map_ptr ", |
| "[anchr]union_map_ptr [u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| "[anchr]keys_ptr [u4]10 [u4]2 [u1]100 [u1]101 [u2]0 [u4]0 ", |
| "[anchr]values_ptr [u4]10 [u4]2 [u1]1 [u1]2 [u2]0 [u4]0 ", |
| ), |
| )?; |
| Ok(()) |
| } |
| |
| #[gtest(RustTestMojomParsing, TestNullableParsing)] |
| fn test_nullable_parsing() -> anyhow::Result<()> { |
| validate_parsing::<NullableBasics>( |
| NullableBasics { |
| b: None, |
| n1: None, |
| n2: None, |
| empty: None, |
| e: None, |
| fourints: None, |
| f1: None, |
| f2: None, |
| }, |
| "[u4]48 [u4]0 [b]00000000 [u1]0 [u2]0 [u4]0 [u8]0 [u8]0 [f]0 [u4]0 [d]0", |
| )?; |
| |
| validate_parsing::<NullableBasics>( |
| NullableBasics { |
| b: Some(true), |
| n1: None, |
| n2: Some(12), |
| empty: None, |
| e: None, |
| fourints: Some(FourInts { a: 1, b: 2, c: 3, d: 4 }), |
| f1: None, |
| f2: Some(2.71828), |
| }, |
| concat!( |
| "[u4]48 [u4]0 [b]01001011 [u1]12 [u2]0 [u4]0 [u8]0 [dist8]fourints_ptr [f]0 [u4]0 [d]2.71828 ", |
| "[anchr]fourints_ptr [u4]24 [u4]0 [s1]1 [u1]0 [s2]2 [s4]3 [s8]4", |
| ), |
| )?; |
| |
| validate_parsing::<NullableBasics>( |
| NullableBasics { |
| b: None, |
| n1: Some(33), |
| n2: None, |
| empty: Some(Empty {}), |
| e: Some(TestEnum::Four), |
| fourints: None, |
| f1: Some(3.14), |
| f2: None, |
| }, |
| concat!( |
| "[u4]48 [u4]0 [b]00110100 [u1]0 [u2]33 [u4]4 [dist8]empty_ptr [u8]0 [f]3.14 [u4]0 [d]0 ", |
| "[anchr]empty_ptr [u4]8 [u4]0", |
| ), |
| )?; |
| |
| validate_parsing::<NullableBasics>( |
| NullableBasics { |
| b: Some(false), |
| n1: Some(44), |
| n2: Some(22), |
| empty: Some(Empty {}), |
| e: Some(TestEnum::Zero), |
| fourints: Some(FourInts { a: 1, b: 2, c: 3, d: 4 }), |
| f1: Some(1.23), |
| f2: Some(4.56), |
| }, |
| concat!( |
| "[u4]48 [u4]0 [b]01111101 [u1]22 [u2]44 [u4]0 [dist8]empty_ptr [dist8]fourints_ptr [f]1.23 [u4]0 [d]4.56 ", |
| "[anchr]empty_ptr [u4]8 [u4]0 ", |
| "[anchr]fourints_ptr [u4]24 [u4]0 [s1]1 [u1]0 [s2]2 [s4]3 [s8]4", |
| ), |
| )?; |
| |
| validate_parsing::<ArraysOfNullables>( |
| ArraysOfNullables { |
| bools: vec![ |
| Some(true), |
| None, |
| Some(false), |
| None, |
| None, |
| Some(true), |
| Some(true), |
| Some(false), |
| Some(true), |
| None, |
| None, |
| Some(false), |
| ], |
| empties: vec![None, Some(Empty {}), None, Some(Empty {})], |
| enums: vec![Some(TestEnum::Seven), None, Some(TestEnum::Zero), Some(TestEnum::Seven)], |
| unions: vec![Some(BaseUnion::n1(5)), None, Some(BaseUnion::b1(true))], |
| }, |
| concat!( |
| "[u4]40 [u4]0 [dist8]bools_ptr [dist8]empties_ptr [dist8]enums_ptr [dist8]unions_ptr ", |
| "[anchr]bools_ptr [u4]12 [u4]12 [b]11100101 [b]00001001 [b]01100001 [b]00000001 [u4]0 ", |
| "[anchr]empties_ptr [u4]40 [u4]4 [u8]0 [dist8]empty_ptr_1 [u8]0 [dist8]empty_ptr_2 ", |
| "[anchr]empty_ptr_1 [u4]8 [u4]0 ", |
| "[anchr]empty_ptr_2 [u4]8 [u4]0 ", |
| "[anchr]enums_ptr [u4]28 [u4]4 [b]00001101 [u1]0 [u2]0 [u4]7 [u4]0 [u4]0 [u4]7 [u4]0 ", |
| "[anchr]unions_ptr [u4]56 [u4]3 ", |
| "[u4]16 [u4]0 [u8]5 ", |
| "[u8]0 [u8]0 ", |
| "[u4]16 [u4]3 [u8]1" |
| ), |
| )?; |
| |
| validate_parsing::<NullableArrays>( |
| NullableArrays { null_arr: None, double_null_arr: None }, |
| "[u4]24 [u4]0 [u8]0 [u8]0", |
| )?; |
| |
| validate_parsing::<NullableArrays>( |
| NullableArrays { |
| null_arr: Some(vec![true, false, true]), |
| double_null_arr: Some(vec![Some(true), None, Some(false), Some(true)]), |
| }, |
| concat!( |
| "[u4]24 [u4]0 [dist8]null_arr_ptr [dist8]double_null_arr_ptr ", |
| "[anchr]null_arr_ptr [u4]9 [u4]3 [b]00000101 [u1]0 [u2]0 [u4]0 ", |
| "[anchr]double_null_arr_ptr [u4]10 [u4]4 [b]00001101 [b]00001001 [u2]0 [u4]0" |
| ), |
| )?; |
| |
| validate_parsing::<UnionWithNullables>(UnionWithNullables::e(None), "[u4]16 [u4]0 [u8]0")?; |
| validate_parsing::<UnionWithNullables>(UnionWithNullables::str(None), "[u4]16 [u4]1 [u8]0")?; |
| validate_parsing::<UnionWithNullables>(UnionWithNullables::u(None), "[u4]16 [u4]2 [u8]0")?; |
| |
| validate_parsing::<UnionWithNullables>( |
| UnionWithNullables::e(Some(Empty {})), |
| "[u4]16 [u4]0 [dist8]empty_ptr [anchr]empty_ptr [u4]8 [u4]0", |
| )?; |
| validate_parsing::<UnionWithNullables>( |
| UnionWithNullables::str(Some("union_string".to_string())), |
| &format!( |
| "[u4]16 [u4]1 [dist8]union_str_ptr [anchr]union_str_ptr {}", |
| &str_wire_format("union_string") |
| ), |
| )?; |
| validate_parsing::<UnionWithNullables>( |
| UnionWithNullables::u(Some(BaseUnion::n1(123))), |
| "[u4]16 [u4]2 [dist8]u_ptr [anchr]u_ptr [u4]16 [u4]0 [u8]123", |
| )?; |
| |
| validate_parsing::<NullableOthers>( |
| NullableOthers { u: None, m: None, str: None }, |
| "[u4]40 [u4]0 [u8]0 [u8]0 [u8]0 [u8]0", |
| )?; |
| validate_parsing::<NullableOthers>( |
| NullableOthers { |
| u: Some(UnionWithNullables::u(None)), |
| m: None, |
| str: Some("holla".to_string()), |
| }, |
| &format!( |
| "{} {} {}", |
| "[u4]40 [u4]0 [u4]16 [u4]2 [u8]0 [u8]0 [dist8]str_ptr ", |
| "[anchr]str_ptr ", |
| &str_wire_format("holla") |
| ), |
| )?; |
| validate_parsing::<NullableOthers>( |
| NullableOthers { |
| u: Some(UnionWithNullables::u(Some(BaseUnion::f1(FourInts { |
| a: 1, |
| b: 2, |
| c: 3, |
| d: 4, |
| })))), |
| m: None, |
| str: None, |
| }, |
| concat!( |
| "[u4]40 [u4]0 [u4]16 [u4]2 [dist8]u_inner_ptr [u8]0 [u8]0 ", |
| "[anchr]u_inner_ptr [u4]16 [u4]6 [dist8]f1_ptr ", |
| "[anchr]f1_ptr [u4]24 [u4]0 [s1]1 [u1]0 [s2]2 [s4]3 [s8]4" |
| ), |
| )?; |
| validate_parsing::<NullableOthers>( |
| NullableOthers { |
| u: Some(UnionWithNullables::u(Some(BaseUnion::n1(42)))), |
| m: Some([(1, 2), (3, 4)].into()), |
| str: Some("hello".to_string()), |
| }, |
| &format!( |
| "{} {} {} {} {} {} {}", |
| "[u4]40 [u4]0 [u4]16 [u4]2 [dist8]u_inner_ptr [dist8]m_ptr [dist8]str_ptr ", |
| "[anchr]u_inner_ptr [u4]16 [u4]0 [u8]42 ", |
| "[anchr]m_ptr [u4]24 [u4]0 [dist8]keys_ptr [dist8]values_ptr ", |
| "[anchr]keys_ptr [u4]10 [u4]2 [u1]1 [u1]3 [u2]0 [u4]0 ", |
| "[anchr]values_ptr [u4]10 [u4]2 [u1]2 [u1]4 [u2]0 [u4]0 ", |
| "[anchr]str_ptr ", |
| &str_wire_format("hello") |
| ), |
| )?; |
| |
| Ok(()) |
| } |
| |
| #[gtest(RustTestMojomParsing, TestHandleParsing)] |
| fn test_handle_parsing() -> anyhow::Result<()> { |
| // Note: [s4]-1 is 0xffffffff, the indicator for a `None` handle |
| validate_parsing_with_handles( |
| Handles { h1: dummy_handle(), h2: None, h3: dummy_handle().into(), h4: None }, |
| "[u4]24 [u4]0 [u4]0 [s4]-1 [u4]1 [s4]-1", |
| 2, |
| )?; |
| // Can't parse if we don't give enough handles |
| validate_parsing_failure_with_handles::<Handles>("[u4]24 [u4]0 [u4]0 [s4]-1 [u4]1 [s4]-1", 1)?; |
| |
| validate_parsing_with_handles( |
| Handles { |
| h1: dummy_handle(), |
| h2: Some(dummy_handle()), |
| h3: dummy_handle().into(), |
| h4: None, |
| }, |
| "[u4]24 [u4]0 [u4]0 [u4]1 [u4]2 [s4]-1", |
| 3, |
| )?; |
| |
| validate_parsing_with_handles( |
| Handles { |
| h1: dummy_handle(), |
| h2: None, |
| h3: dummy_handle().into(), |
| h4: Some(dummy_handle().into()), |
| }, |
| "[u4]24 [u4]0 [u4]0 [s4]-1 [u4]1 [u4]2", |
| 3, |
| )?; |
| |
| validate_parsing_with_handles( |
| Handles { |
| h1: dummy_handle(), |
| h2: Some(dummy_handle()), |
| h3: dummy_handle().into(), |
| h4: Some(dummy_handle().into()), |
| }, |
| "[u4]24 [u4]0 [u4]0 [u4]1 [u4]2 [u4]3", |
| 4, |
| )?; |
| |
| validate_parsing_with_handles(WithHandles::h1(None), "[u4]16 [u4]0 [s4]-1 [u4]0", 0)?; |
| validate_parsing_with_handles( |
| WithHandles::h1(Some(dummy_handle())), |
| "[u4]16 [u4]0 [u4]0 [u4]0", |
| 1, |
| )?; |
| validate_parsing_with_handles( |
| WithHandles::h2(dummy_handle().into()), |
| "[u4]16 [u4]1 [u4]0 [u4]0", |
| 1, |
| )?; |
| validate_parsing(WithHandles::n(42), "[u4]16 [u4]2 [u4]42 [u4]0")?; |
| |
| validate_parsing_with_handles( |
| NestedHandles { |
| h1: dummy_handle(), |
| arr: vec![Some(dummy_handle()), None, Some(dummy_handle()), None], |
| h2: dummy_handle(), |
| wh: WithHandles::h2(dummy_handle().into()), |
| h3: dummy_handle(), |
| }, |
| concat!( |
| "[u4]48 [u4]0 [u4]0 [u4]1 [dist8]arr_ptr ", |
| "[u4]16 [u4]1 [u4]2 [u4]0 ", // wh |
| "[u4]3 [u4]0 ", |
| "[anchr]arr_ptr [u4]24 [u4]4 [u4]4 [s4]-1 [u4]5 [s4]-1" // arr |
| ), |
| 6, |
| )?; |
| |
| Ok(()) |
| } |