// Protocol Buffers - Google's data interchange format // Copyright 2023 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd use enums_proto::{test_map_with_nested_enum, TestMapWithNestedEnum}; use googletest::prelude::*; use map_unittest_proto::{MapEnum, TestMap, TestMapWithMessages}; use paste::paste; use std::collections::HashMap; use unittest_proto::TestAllTypes; macro_rules! generate_map_primitives_tests { ( $(($k_type:ty, $v_type:ty, $k_field:ident, $v_field:ident, $k_nonzero:expr, $v_nonzero:expr $(,)?)),* $(,)? ) => { paste! { $( #[test] fn [< test_map_ $k_field _ $v_field >]() { let mut msg = TestMap::new(); assert_that!(msg.[< map_ $k_field _ $v_field >]().len(), eq(0)); assert_that!( msg.[< map_ $k_field _ $v_field >](), elements_are![] ); assert_that!( msg.[< map_ $k_field _ $v_field >]().keys().collect::>(), elements_are![] ); assert_that!( msg.[< map_ $k_field _ $v_field >]().values().collect::>(), elements_are![] ); let k = <$k_type>::default(); let v = <$v_type>::default(); assert_that!(msg.[< map_ $k_field _ $v_field _mut>]().insert(k, v), eq(true)); assert_that!(msg.[< map_ $k_field _ $v_field _mut>]().insert(k, v), eq(false)); assert_that!(msg.[< map_ $k_field _ $v_field >]().len(), eq(1)); assert_that!( msg.[< map_ $k_field _ $v_field >](), elements_are![eq((k, v))] ); assert_that!( msg.[< map_ $k_field _ $v_field >]().keys().collect::>(), elements_are![eq(&k)] ); assert_that!( msg.[< map_ $k_field _ $v_field >]().values().collect::>(), elements_are![eq(&v)] ); let k2: $k_type = $k_nonzero; let v2: $v_type = $v_nonzero; assert_that!(msg.[< map_ $k_field _ $v_field _mut>]().insert(k2, v2), eq(true)); assert_that!(msg.[< map_ $k_field _ $v_field >](), len(eq(2))); assert_that!( msg.[< map_ $k_field _ $v_field >](), unordered_elements_are![ eq((k, v)), eq((k2, v2)), ] ); assert_that!( msg.[< map_ $k_field _ $v_field >]().keys().collect::>(), unordered_elements_are![eq(&k), eq(&k2)] ); assert_that!( msg.[< map_ $k_field _ $v_field >]().values().collect::>(), unordered_elements_are![eq(&v), eq(&v2)] ); } )* } }; } generate_map_primitives_tests!( (i32, i32, int32, int32, 1, 1), (i64, i64, int64, int64, 1, 1), (u32, u32, uint32, uint32, 1, 1), (u64, u64, uint64, uint64, 1, 1), (i32, i32, sint32, sint32, 1, 1), (i64, i64, sint64, sint64, 1, 1), (u32, u32, fixed32, fixed32, 1, 1), (u64, u64, fixed64, fixed64, 1, 1), (i32, i32, sfixed32, sfixed32, 1, 1), (i64, i64, sfixed64, sfixed64, 1, 1), (i32, f32, int32, float, 1, 1.), (i32, f64, int32, double, 1, 1.), (bool, bool, bool, bool, true, true), (i32, &[u8], int32, bytes, 1, b"foo"), (i32, MapEnum, int32, enum, 1, MapEnum::Baz), ); #[test] fn collect_as_hashmap() { // Highlights conversion from protobuf map to hashmap. let mut msg = TestMap::new(); msg.map_string_string_mut().insert("hello", "world"); msg.map_string_string_mut().insert("fizz", "buzz"); msg.map_string_string_mut().insert("boo", "blah"); let hashmap: HashMap = msg.map_string_string().iter().map(|(k, v)| (k.to_string(), v.to_string())).collect(); assert_that!( hashmap, unordered_elements_are![ (eq("hello"), eq("world")), (eq("fizz"), eq("buzz")), (eq("boo"), eq("blah")), ] ); } #[test] fn test_string_maps() { let mut msg = TestMap::new(); msg.map_string_string_mut().insert("hello", "world"); msg.map_string_string_mut().insert("fizz", "buzz"); assert_that!(msg.map_string_string().len(), eq(2)); assert_that!(msg.map_string_string().get("fizz").unwrap(), eq("buzz")); assert_that!(msg.map_string_string().get("not found"), eq(None)); msg.map_string_string_mut().clear(); assert_that!(msg.map_string_string().len(), eq(0)); } #[test] fn test_nested_enum_maps() { // Verify that C++ thunks are generated and are with the right name for strings TestMapWithNestedEnum::new() .string_map_mut() .insert("foo", test_map_with_nested_enum::inner_nested::NestedEnum::Foo); } #[test] fn test_bytes_and_string_copied() { let mut msg = TestMap::new(); { // Ensure val is dropped after inserting into the map. let mut key = String::from("hello"); let mut val = String::from("world"); msg.map_string_string_mut().insert(key.as_str(), val.as_str()); msg.map_int32_bytes_mut().insert(1, val.as_bytes()); // Validate that map keys are copied by mutating the originals. key.replace_range(.., "ayo"); val.replace_range(.., "wOrld"); } assert_that!(msg.map_string_string_mut().get("hello").unwrap(), eq("world")); assert_that!(msg.map_string_string(), unordered_elements_are![(eq("hello"), eq("world"))]); assert_that!(msg.map_int32_bytes_mut().get(1).unwrap(), eq(b"world")); } #[test] fn test_map_setter() { let mut msg = TestMap::new(); msg.map_string_string_mut().insert("hello", "world"); msg.map_string_string_mut().insert("fizz", "buzz"); let mut msg2 = TestMap::new(); msg2.set_map_string_string(msg.map_string_string()); assert_that!( msg2.map_string_string(), unordered_elements_are![ eq(("hello".into(), "world".into())), eq(("fizz".into(), "buzz".into())) ] ); } macro_rules! generate_map_with_msg_values_tests { ( $(($k_field:ident, $k_nonzero:expr, $k_other:expr $(,)?)),* $(,)? ) => { paste! { $( #[test] fn [< test_map_ $k_field _all_types >]() { // We need to cover the following upb/c++ thunks: // TODO - b/323883851: Add test once Map::new is public. // * new // * free (covered implicitly by drop) // * clear, size, insert, get, remove, iter, iter_next (all covered below) let mut msg = TestMapWithMessages::new(); assert_that!(msg.[< map_ $k_field _all_types >]().len(), eq(0)); assert_that!(msg.[< map_ $k_field _all_types >]().get($k_nonzero), none()); // this block makes sure `insert` copies/moves, not borrows. { let mut msg_val = TestAllTypes::new(); msg_val.set_optional_int32(1001); assert_that!( msg .[< map_ $k_field _all_types_mut >]() .insert($k_nonzero, msg_val.as_view()), eq(true), "`insert` should return true when key was inserted." ); assert_that!( msg .[< map_ $k_field _all_types_mut >]() .insert($k_nonzero, msg_val.as_view()), eq(false), "`insert` should return false when key was already present." ); } assert_that!( msg.[< map_ $k_field _all_types >]().len(), eq(1), "`size` thunk should return correct len."); assert_that!( msg.[< map_ $k_field _all_types >]().get($k_nonzero), some(anything()), "`get` should return Some when key present."); assert_that!( msg.[< map_ $k_field _all_types >]().get($k_nonzero).unwrap().optional_int32(), eq(1001)); assert_that!( msg.[< map_ $k_field _all_types >]().get($k_other), none(), "`get` should return None when key missing."); msg.[< map_ $k_field _all_types_mut >]().clear(); assert_that!( msg.[< map_ $k_field _all_types >]().len(), eq(0), "`clear` should drop all elements."); assert_that!( msg.[< map_ $k_field _all_types_mut >]().insert($k_nonzero, TestAllTypes::new().as_view()), eq(true)); assert_that!( msg.[< map_ $k_field _all_types_mut >]().remove($k_nonzero), eq(true), "`remove` should return true when key was present."); assert_that!(msg.[< map_ $k_field _all_types >](), empty()); assert_that!( msg.[< map_ $k_field _all_types_mut >]().remove($k_nonzero), eq(false), "`remove` should return false when key was missing."); // empty iter // assert_that!( // msg.[< map_ $k_field _all_types_mut >]().iter().collect::>(), // elements_are![], // "`iter` should work when empty." // ); assert_that!( msg.[< map_ $k_field _all_types_mut >]().keys().count(), eq(0), "`iter` should work when empty." ); assert_that!( msg.[< map_ $k_field _all_types_mut >]().values().count(), eq(0), "`iter` should work when empty." ); // single element iter assert_that!( msg.[< map_ $k_field _all_types_mut >]().insert($k_nonzero, TestAllTypes::new().as_view()), eq(true)); // assert_that!( // msg.[< map_ $k_field _all_types >]().iter().collect::>(), // unordered_elements_are![ // eq(($k_nonzero, anything())), // ] // ); assert_that!( msg.[< map_ $k_field _all_types >]().keys().collect::>(), unordered_elements_are![eq(&$k_nonzero)] ); assert_that!( msg.[< map_ $k_field _all_types >]().values().count(), eq(1)); // 2 element iter assert_that!( msg .[< map_ $k_field _all_types_mut >]() .insert($k_other, TestAllTypes::new().as_view()), eq(true)); assert_that!( msg.[< map_ $k_field _all_types >](), len(eq(2)) ); assert_that!( msg.[< map_ $k_field _all_types >]().keys().collect::>(), unordered_elements_are![eq(&$k_nonzero), eq(&$k_other)] ); assert_that!( msg.[< map_ $k_field _all_types >]().values().count(), eq(2) ); } )* } } } generate_map_with_msg_values_tests!( (int32, 1i32, 2i32), (int64, 1i64, 2i64), (uint32, 1u32, 2u32), (uint64, 1u64, 2u64), (sint32, 1, 2), (sint64, 1, 2), (fixed32, 1u32, 2u32), (fixed64, 1u64, 2u64), (sfixed32, 1, 2), (sfixed64, 1, 2), (bool, true, false), (string, "foo", "bar"), );