// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <stddef.h> #include <stdint.h> #include <utility> #include <vector> #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "mojo/public/cpp/bindings/array.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/lib/array_internal.h" #include "mojo/public/cpp/bindings/lib/fixed_buffer.h" #include "mojo/public/cpp/bindings/lib/serialization.h" #include "mojo/public/cpp/bindings/lib/validation_context.h" #include "mojo/public/cpp/bindings/lib/validation_errors.h" #include "mojo/public/cpp/bindings/string.h" #include "mojo/public/cpp/test_support/test_utils.h" #include "mojo/public/interfaces/bindings/tests/test_structs.mojom.h" #include "mojo/public/interfaces/bindings/tests/test_unions.mojom.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { namespace test { TEST(UnionTest, PlainOldDataGetterSetter) { PodUnionPtr pod(PodUnion::New()); pod->set_f_int8(10); EXPECT_EQ(10, pod->get_f_int8()); EXPECT_TRUE(pod->is_f_int8()); EXPECT_FALSE(pod->is_f_int8_other()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT8); pod->set_f_uint8(11); EXPECT_EQ(11, pod->get_f_uint8()); EXPECT_TRUE(pod->is_f_uint8()); EXPECT_FALSE(pod->is_f_int8()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT8); pod->set_f_int16(12); EXPECT_EQ(12, pod->get_f_int16()); EXPECT_TRUE(pod->is_f_int16()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT16); pod->set_f_uint16(13); EXPECT_EQ(13, pod->get_f_uint16()); EXPECT_TRUE(pod->is_f_uint16()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT16); pod->set_f_int32(14); EXPECT_EQ(14, pod->get_f_int32()); EXPECT_TRUE(pod->is_f_int32()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT32); pod->set_f_uint32(static_cast<uint32_t>(15)); EXPECT_EQ(static_cast<uint32_t>(15), pod->get_f_uint32()); EXPECT_TRUE(pod->is_f_uint32()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT32); pod->set_f_int64(16); EXPECT_EQ(16, pod->get_f_int64()); EXPECT_TRUE(pod->is_f_int64()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_INT64); pod->set_f_uint64(static_cast<uint64_t>(17)); EXPECT_EQ(static_cast<uint64_t>(17), pod->get_f_uint64()); EXPECT_TRUE(pod->is_f_uint64()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_UINT64); pod->set_f_float(1.5); EXPECT_EQ(1.5, pod->get_f_float()); EXPECT_TRUE(pod->is_f_float()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_FLOAT); pod->set_f_double(1.9); EXPECT_EQ(1.9, pod->get_f_double()); EXPECT_TRUE(pod->is_f_double()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_DOUBLE); pod->set_f_bool(true); EXPECT_TRUE(pod->get_f_bool()); pod->set_f_bool(false); EXPECT_FALSE(pod->get_f_bool()); EXPECT_TRUE(pod->is_f_bool()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_BOOL); pod->set_f_enum(AnEnum::SECOND); EXPECT_EQ(AnEnum::SECOND, pod->get_f_enum()); EXPECT_TRUE(pod->is_f_enum()); EXPECT_EQ(pod->which(), PodUnion::Tag::F_ENUM); } TEST(UnionTest, PodEquals) { PodUnionPtr pod1(PodUnion::New()); PodUnionPtr pod2(PodUnion::New()); pod1->set_f_int8(10); pod2->set_f_int8(10); EXPECT_TRUE(pod1.Equals(pod2)); pod2->set_f_int8(11); EXPECT_FALSE(pod1.Equals(pod2)); pod2->set_f_int8_other(10); EXPECT_FALSE(pod1.Equals(pod2)); } TEST(UnionTest, PodClone) { PodUnionPtr pod(PodUnion::New()); pod->set_f_int8(10); PodUnionPtr pod_clone = pod.Clone(); EXPECT_EQ(10, pod_clone->get_f_int8()); EXPECT_TRUE(pod_clone->is_f_int8()); EXPECT_EQ(pod_clone->which(), PodUnion::Tag::F_INT8); } TEST(UnionTest, PodSerialization) { PodUnionPtr pod1(PodUnion::New()); pod1->set_f_int8(10); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<PodUnionPtr>(pod1, false, &context); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = nullptr; mojo::internal::Serialize<PodUnionPtr>(pod1, &buf, &data, false, &context); PodUnionPtr pod2; mojo::internal::Deserialize<PodUnionPtr>(data, &pod2, &context); EXPECT_EQ(10, pod2->get_f_int8()); EXPECT_TRUE(pod2->is_f_int8()); EXPECT_EQ(pod2->which(), PodUnion::Tag::F_INT8); } TEST(UnionTest, EnumSerialization) { PodUnionPtr pod1(PodUnion::New()); pod1->set_f_enum(AnEnum::SECOND); size_t size = mojo::internal::PrepareToSerialize<PodUnionPtr>(pod1, false, nullptr); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = nullptr; mojo::internal::Serialize<PodUnionPtr>(pod1, &buf, &data, false, nullptr); PodUnionPtr pod2; mojo::internal::Deserialize<PodUnionPtr>(data, &pod2, nullptr); EXPECT_EQ(AnEnum::SECOND, pod2->get_f_enum()); EXPECT_TRUE(pod2->is_f_enum()); EXPECT_EQ(pod2->which(), PodUnion::Tag::F_ENUM); } TEST(UnionTest, PodValidation) { PodUnionPtr pod(PodUnion::New()); pod->set_f_int8(10); size_t size = mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = nullptr; mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_TRUE( internal::PodUnion_Data::Validate(raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, SerializeNotNull) { PodUnionPtr pod(PodUnion::New()); pod->set_f_int8(0); size_t size = mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr); mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = nullptr; mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr); EXPECT_FALSE(data->is_null()); } TEST(UnionTest, SerializeIsNullInlined) { PodUnionPtr pod; size_t size = mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); // Check that dirty output buffers are handled correctly by serialization. data->size = 16U; data->tag = PodUnion::Tag::F_UINT16; data->data.f_f_int16 = 20; mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, true, nullptr); EXPECT_TRUE(data->is_null()); PodUnionPtr pod2; mojo::internal::Deserialize<PodUnionPtr>(data, &pod2, nullptr); EXPECT_TRUE(pod2.is_null()); } TEST(UnionTest, SerializeIsNullNotInlined) { PodUnionPtr pod; size_t size = mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = nullptr; mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr); EXPECT_EQ(nullptr, data); } TEST(UnionTest, NullValidation) { void* buf = nullptr; mojo::internal::ValidationContext validation_context(buf, 0, 0); EXPECT_TRUE(internal::PodUnion_Data::Validate( buf, &validation_context, false)); } TEST(UnionTest, OutOfAlignmentValidation) { size_t size = sizeof(internal::PodUnion_Data); // Get an aligned object and shift the alignment. mojo::internal::FixedBufferForTesting aligned_buf(size + 1); void* raw_buf = aligned_buf.Leak(); char* buf = reinterpret_cast<char*>(raw_buf) + 1; internal::PodUnion_Data* data = reinterpret_cast<internal::PodUnion_Data*>(buf); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_FALSE(internal::PodUnion_Data::Validate( buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, OOBValidation) { size_t size = sizeof(internal::PodUnion_Data) - 1; mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); EXPECT_FALSE( internal::PodUnion_Data::Validate(raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, UnknownTagValidation) { size_t size = sizeof(internal::PodUnion_Data); mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = internal::PodUnion_Data::New(&buf); data->tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(0xFFFFFF); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); EXPECT_FALSE( internal::PodUnion_Data::Validate(raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, UnknownEnumValueValidation) { PodUnionPtr pod(PodUnion::New()); pod->set_f_enum(static_cast<AnEnum>(0xFFFF)); size_t size = mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = nullptr; mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_FALSE( internal::PodUnion_Data::Validate(raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, UnknownExtensibleEnumValueValidation) { PodUnionPtr pod(PodUnion::New()); pod->set_f_extensible_enum(static_cast<AnExtensibleEnum>(0xFFFF)); size_t size = mojo::internal::PrepareToSerialize<PodUnionPtr>(pod, false, nullptr); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::PodUnion_Data* data = nullptr; mojo::internal::Serialize<PodUnionPtr>(pod, &buf, &data, false, nullptr); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_TRUE( internal::PodUnion_Data::Validate(raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, StringGetterSetter) { ObjectUnionPtr pod(ObjectUnion::New()); String hello("hello world"); pod->set_f_string(hello); EXPECT_EQ(hello, pod->get_f_string()); EXPECT_TRUE(pod->is_f_string()); EXPECT_EQ(pod->which(), ObjectUnion::Tag::F_STRING); } TEST(UnionTest, StringEquals) { ObjectUnionPtr pod1(ObjectUnion::New()); ObjectUnionPtr pod2(ObjectUnion::New()); pod1->set_f_string("hello world"); pod2->set_f_string("hello world"); EXPECT_TRUE(pod1.Equals(pod2)); pod2->set_f_string("hello universe"); EXPECT_FALSE(pod1.Equals(pod2)); } TEST(UnionTest, StringClone) { ObjectUnionPtr pod(ObjectUnion::New()); String hello("hello world"); pod->set_f_string(hello); ObjectUnionPtr pod_clone = pod.Clone(); EXPECT_EQ(hello, pod_clone->get_f_string()); EXPECT_TRUE(pod_clone->is_f_string()); EXPECT_EQ(pod_clone->which(), ObjectUnion::Tag::F_STRING); } TEST(UnionTest, StringSerialization) { ObjectUnionPtr pod1(ObjectUnion::New()); String hello("hello world"); pod1->set_f_string(hello); size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(pod1, false, nullptr); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(pod1, &buf, &data, false, nullptr); ObjectUnionPtr pod2; mojo::internal::Deserialize<ObjectUnionPtr>(data, &pod2, nullptr); EXPECT_EQ(hello, pod2->get_f_string()); EXPECT_TRUE(pod2->is_f_string()); EXPECT_EQ(pod2->which(), ObjectUnion::Tag::F_STRING); } TEST(UnionTest, NullStringValidation) { size_t size = sizeof(internal::ObjectUnion_Data); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf); data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING; data->data.unknown = 0x0; mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); EXPECT_FALSE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, StringPointerOverflowValidation) { size_t size = sizeof(internal::ObjectUnion_Data); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf); data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING; data->data.unknown = 0xFFFFFFFFFFFFFFFF; mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); void* raw_buf = buf.Leak(); EXPECT_FALSE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, StringValidateOOB) { size_t size = 32; mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = internal::ObjectUnion_Data::New(&buf); data->tag = internal::ObjectUnion_Data::ObjectUnion_Tag::F_STRING; data->data.f_f_string.offset = 8; char* ptr = reinterpret_cast<char*>(&data->data.f_f_string); mojo::internal::ArrayHeader* array_header = reinterpret_cast<mojo::internal::ArrayHeader*>(ptr + *ptr); array_header->num_bytes = 20; // This should go out of bounds. array_header->num_elements = 20; mojo::internal::ValidationContext validation_context(data, 32, 0); void* raw_buf = buf.Leak(); EXPECT_FALSE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } // TODO(azani): Move back in array_unittest.cc when possible. // Array tests TEST(UnionTest, PodUnionInArray) { SmallStructPtr small_struct(SmallStruct::New()); small_struct->pod_union_array.emplace(2); small_struct->pod_union_array.value()[0] = PodUnion::New(); small_struct->pod_union_array.value()[1] = PodUnion::New(); small_struct->pod_union_array.value()[0]->set_f_int8(10); small_struct->pod_union_array.value()[1]->set_f_int16(12); EXPECT_EQ(10, small_struct->pod_union_array.value()[0]->get_f_int8()); EXPECT_EQ(12, small_struct->pod_union_array.value()[1]->get_f_int16()); } TEST(UnionTest, PodUnionInArraySerialization) { Array<PodUnionPtr> array(2); array[0] = PodUnion::New(); array[1] = PodUnion::New(); array[0]->set_f_int8(10); array[1]->set_f_int16(12); EXPECT_EQ(2U, array.size()); size_t size = mojo::internal::PrepareToSerialize<Array<PodUnionPtr>>(array, nullptr); EXPECT_EQ(40U, size); mojo::internal::FixedBufferForTesting buf(size); mojo::internal::Array_Data<internal::PodUnion_Data>* data; mojo::internal::ContainerValidateParams validate_params(0, false, nullptr); mojo::internal::Serialize<Array<PodUnionPtr>>(array, &buf, &data, &validate_params, nullptr); Array<PodUnionPtr> array2; mojo::internal::Deserialize<Array<PodUnionPtr>>(data, &array2, nullptr); EXPECT_EQ(2U, array2.size()); EXPECT_EQ(10, array2[0]->get_f_int8()); EXPECT_EQ(12, array2[1]->get_f_int16()); } TEST(UnionTest, PodUnionInArraySerializationWithNull) { Array<PodUnionPtr> array(2); array[0] = PodUnion::New(); array[0]->set_f_int8(10); EXPECT_EQ(2U, array.size()); size_t size = mojo::internal::PrepareToSerialize<Array<PodUnionPtr>>(array, nullptr); EXPECT_EQ(40U, size); mojo::internal::FixedBufferForTesting buf(size); mojo::internal::Array_Data<internal::PodUnion_Data>* data; mojo::internal::ContainerValidateParams validate_params(0, true, nullptr); mojo::internal::Serialize<Array<PodUnionPtr>>(array, &buf, &data, &validate_params, nullptr); Array<PodUnionPtr> array2; mojo::internal::Deserialize<Array<PodUnionPtr>>(data, &array2, nullptr); EXPECT_EQ(2U, array2.size()); EXPECT_EQ(10, array2[0]->get_f_int8()); EXPECT_TRUE(array2[1].is_null()); } TEST(UnionTest, ObjectUnionInArraySerialization) { Array<ObjectUnionPtr> array(2); array[0] = ObjectUnion::New(); array[1] = ObjectUnion::New(); array[0]->set_f_string("hello"); array[1]->set_f_string("world"); EXPECT_EQ(2U, array.size()); size_t size = mojo::internal::PrepareToSerialize<Array<ObjectUnionPtr>>(array, nullptr); EXPECT_EQ(72U, size); mojo::internal::FixedBufferForTesting buf(size); mojo::internal::Array_Data<internal::ObjectUnion_Data>* data; mojo::internal::ContainerValidateParams validate_params(0, false, nullptr); mojo::internal::Serialize<Array<ObjectUnionPtr>>(array, &buf, &data, &validate_params, nullptr); std::vector<char> new_buf; new_buf.resize(size); void* raw_buf = buf.Leak(); memcpy(new_buf.data(), raw_buf, size); free(raw_buf); data = reinterpret_cast<mojo::internal::Array_Data<internal::ObjectUnion_Data>*>( new_buf.data()); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); ASSERT_TRUE(mojo::internal::Array_Data<internal::ObjectUnion_Data>::Validate( data, &validation_context, &validate_params)); Array<ObjectUnionPtr> array2; mojo::internal::Deserialize<Array<ObjectUnionPtr>>(data, &array2, nullptr); EXPECT_EQ(2U, array2.size()); EXPECT_EQ(String("hello"), array2[0]->get_f_string()); EXPECT_EQ(String("world"), array2[1]->get_f_string()); } // TODO(azani): Move back in struct_unittest.cc when possible. // Struct tests TEST(UnionTest, Clone_Union) { SmallStructPtr small_struct(SmallStruct::New()); small_struct->pod_union = PodUnion::New(); small_struct->pod_union->set_f_int8(10); SmallStructPtr clone = small_struct.Clone(); EXPECT_EQ(10, clone->pod_union->get_f_int8()); } // Serialization test of a struct with a union of plain old data. TEST(UnionTest, Serialization_UnionOfPods) { SmallStructPtr small_struct(SmallStruct::New()); small_struct->pod_union = PodUnion::New(); small_struct->pod_union->set_f_int32(10); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct, &context); mojo::internal::FixedBufferForTesting buf(size); internal::SmallStruct_Data* data = nullptr; mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data, &context); SmallStructPtr deserialized; mojo::internal::Deserialize<SmallStructPtr>(data, &deserialized, &context); EXPECT_EQ(10, deserialized->pod_union->get_f_int32()); } // Serialization test of a struct with a union of structs. TEST(UnionTest, Serialization_UnionOfObjects) { SmallObjStructPtr obj_struct(SmallObjStruct::New()); obj_struct->obj_union = ObjectUnion::New(); String hello("hello world"); obj_struct->obj_union->set_f_string(hello); size_t size = mojo::internal::PrepareToSerialize<SmallObjStructPtr>( obj_struct, nullptr); mojo::internal::FixedBufferForTesting buf(size); internal::SmallObjStruct_Data* data = nullptr; mojo::internal::Serialize<SmallObjStructPtr>(obj_struct, &buf, &data, nullptr); SmallObjStructPtr deserialized; mojo::internal::Deserialize<SmallObjStructPtr>(data, &deserialized, nullptr); EXPECT_EQ(hello, deserialized->obj_union->get_f_string()); } // Validation test of a struct with a union. TEST(UnionTest, Validation_UnionsInStruct) { SmallStructPtr small_struct(SmallStruct::New()); small_struct->pod_union = PodUnion::New(); small_struct->pod_union->set_f_int32(10); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct, &context); mojo::internal::FixedBufferForTesting buf(size); internal::SmallStruct_Data* data = nullptr; mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data, &context); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_TRUE(internal::SmallStruct_Data::Validate( raw_buf, &validation_context)); free(raw_buf); } // Validation test of a struct union fails due to unknown union tag. TEST(UnionTest, Validation_PodUnionInStruct_Failure) { SmallStructPtr small_struct(SmallStruct::New()); small_struct->pod_union = PodUnion::New(); small_struct->pod_union->set_f_int32(10); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct, &context); mojo::internal::FixedBufferForTesting buf(size); internal::SmallStruct_Data* data = nullptr; mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data, &context); data->pod_union.tag = static_cast<internal::PodUnion_Data::PodUnion_Tag>(100); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_FALSE(internal::SmallStruct_Data::Validate( raw_buf, &validation_context)); free(raw_buf); } // Validation fails due to non-nullable null union in struct. TEST(UnionTest, Validation_NullUnion_Failure) { SmallStructNonNullableUnionPtr small_struct( SmallStructNonNullableUnion::New()); size_t size = mojo::internal::PrepareToSerialize<SmallStructNonNullableUnionPtr>( small_struct, nullptr); mojo::internal::FixedBufferForTesting buf(size); internal::SmallStructNonNullableUnion_Data* data = internal::SmallStructNonNullableUnion_Data::New(&buf); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_FALSE(internal::SmallStructNonNullableUnion_Data::Validate( raw_buf, &validation_context)); free(raw_buf); } // Validation passes with nullable null union. TEST(UnionTest, Validation_NullableUnion) { SmallStructPtr small_struct(SmallStruct::New()); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<SmallStructPtr>(small_struct, &context); mojo::internal::FixedBufferForTesting buf(size); internal::SmallStruct_Data* data = nullptr; mojo::internal::Serialize<SmallStructPtr>(small_struct, &buf, &data, &context); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_TRUE(internal::SmallStruct_Data::Validate( raw_buf, &validation_context)); free(raw_buf); } // TODO(azani): Move back in map_unittest.cc when possible. // Map Tests TEST(UnionTest, PodUnionInMap) { SmallStructPtr small_struct(SmallStruct::New()); small_struct->pod_union_map.emplace(); small_struct->pod_union_map.value()["one"] = PodUnion::New(); small_struct->pod_union_map.value()["two"] = PodUnion::New(); small_struct->pod_union_map.value()["one"]->set_f_int8(8); small_struct->pod_union_map.value()["two"]->set_f_int16(16); EXPECT_EQ(8, small_struct->pod_union_map.value()["one"]->get_f_int8()); EXPECT_EQ(16, small_struct->pod_union_map.value()["two"]->get_f_int16()); } TEST(UnionTest, PodUnionInMapSerialization) { Map<String, PodUnionPtr> map; map.insert("one", PodUnion::New()); map.insert("two", PodUnion::New()); map["one"]->set_f_int8(8); map["two"]->set_f_int16(16); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<Map<String, PodUnionPtr>>( map, &context); EXPECT_EQ(120U, size); mojo::internal::FixedBufferForTesting buf(size); typename mojo::internal::MojomTypeTraits<Map<String, PodUnionPtr>>::Data* data; mojo::internal::ContainerValidateParams validate_params( new mojo::internal::ContainerValidateParams(0, false, nullptr), new mojo::internal::ContainerValidateParams(0, false, nullptr)); mojo::internal::Serialize<Map<String, PodUnionPtr>>( map, &buf, &data, &validate_params, &context); Map<String, PodUnionPtr> map2; mojo::internal::Deserialize<Map<String, PodUnionPtr>>(data, &map2, &context); EXPECT_EQ(8, map2["one"]->get_f_int8()); EXPECT_EQ(16, map2["two"]->get_f_int16()); } TEST(UnionTest, PodUnionInMapSerializationWithNull) { Map<String, PodUnionPtr> map; map.insert("one", PodUnion::New()); map.insert("two", nullptr); map["one"]->set_f_int8(8); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<Map<String, PodUnionPtr>>( map, &context); EXPECT_EQ(120U, size); mojo::internal::FixedBufferForTesting buf(size); typename mojo::internal::MojomTypeTraits<Map<String, PodUnionPtr>>::Data* data; mojo::internal::ContainerValidateParams validate_params( new mojo::internal::ContainerValidateParams(0, false, nullptr), new mojo::internal::ContainerValidateParams(0, true, nullptr)); mojo::internal::Serialize<Map<String, PodUnionPtr>>( map, &buf, &data, &validate_params, &context); Map<String, PodUnionPtr> map2; mojo::internal::Deserialize<Map<String, PodUnionPtr>>(data, &map2, &context); EXPECT_EQ(8, map2["one"]->get_f_int8()); EXPECT_TRUE(map2["two"].is_null()); } TEST(UnionTest, StructInUnionGetterSetterPasser) { DummyStructPtr dummy(DummyStruct::New()); dummy->f_int8 = 8; ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_dummy(std::move(dummy)); EXPECT_EQ(8, obj->get_f_dummy()->f_int8); } TEST(UnionTest, StructInUnionSerialization) { DummyStructPtr dummy(DummyStruct::New()); dummy->f_int8 = 8; ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_dummy(std::move(dummy)); size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr); EXPECT_EQ(32U, size); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr); ObjectUnionPtr obj2; mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, nullptr); EXPECT_EQ(8, obj2->get_f_dummy()->f_int8); } TEST(UnionTest, StructInUnionValidation) { DummyStructPtr dummy(DummyStruct::New()); dummy->f_int8 = 8; ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_dummy(std::move(dummy)); size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_TRUE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, StructInUnionValidationNonNullable) { mojo::internal::SerializationWarningObserverForTesting suppress_warning; DummyStructPtr dummy(nullptr); ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_dummy(std::move(dummy)); size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_FALSE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, StructInUnionValidationNullable) { DummyStructPtr dummy(nullptr); ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_nullable(std::move(dummy)); size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_TRUE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, ArrayInUnionGetterSetter) { Array<int8_t> array(2); array[0] = 8; array[1] = 9; ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_array_int8(std::move(array)); EXPECT_EQ(8, obj->get_f_array_int8()[0]); EXPECT_EQ(9, obj->get_f_array_int8()[1]); } TEST(UnionTest, ArrayInUnionSerialization) { Array<int8_t> array(2); array[0] = 8; array[1] = 9; ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_array_int8(std::move(array)); size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr); EXPECT_EQ(32U, size); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr); ObjectUnionPtr obj2; mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, nullptr); EXPECT_EQ(8, obj2->get_f_array_int8()[0]); EXPECT_EQ(9, obj2->get_f_array_int8()[1]); } TEST(UnionTest, ArrayInUnionValidation) { Array<int8_t> array(2); array[0] = 8; array[1] = 9; ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_array_int8(std::move(array)); size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_TRUE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, MapInUnionGetterSetter) { std::unordered_map<std::string, int8_t> map; map.insert({"one", 1}); map.insert({"two", 2}); ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_map_int8(std::move(map)); EXPECT_EQ(1, obj->get_f_map_int8()["one"]); EXPECT_EQ(2, obj->get_f_map_int8()["two"]); } TEST(UnionTest, MapInUnionSerialization) { std::unordered_map<std::string, int8_t> map; map.insert({"one", 1}); map.insert({"two", 2}); ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_map_int8(std::move(map)); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, &context); EXPECT_EQ(112U, size); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, &context); ObjectUnionPtr obj2; mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, &context); EXPECT_EQ(1, obj2->get_f_map_int8()["one"]); EXPECT_EQ(2, obj2->get_f_map_int8()["two"]); } TEST(UnionTest, MapInUnionValidation) { std::unordered_map<std::string, int8_t> map; map.insert({"one", 1}); map.insert({"two", 2}); ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_map_int8(std::move(map)); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, &context); EXPECT_EQ(112U, size); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, &context); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_TRUE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, UnionInUnionGetterSetter) { PodUnionPtr pod(PodUnion::New()); pod->set_f_int8(10); ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_pod_union(std::move(pod)); EXPECT_EQ(10, obj->get_f_pod_union()->get_f_int8()); } TEST(UnionTest, UnionInUnionSerialization) { PodUnionPtr pod(PodUnion::New()); pod->set_f_int8(10); ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_pod_union(std::move(pod)); size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr); EXPECT_EQ(32U, size); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr); ObjectUnionPtr obj2; mojo::internal::Deserialize<ObjectUnionPtr>(data, &obj2, nullptr); EXPECT_EQ(10, obj2->get_f_pod_union()->get_f_int8()); } TEST(UnionTest, UnionInUnionValidation) { PodUnionPtr pod(PodUnion::New()); pod->set_f_int8(10); ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_pod_union(std::move(pod)); size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr); EXPECT_EQ(32U, size); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_TRUE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, UnionInUnionValidationNonNullable) { mojo::internal::SerializationWarningObserverForTesting suppress_warning; PodUnionPtr pod(nullptr); ObjectUnionPtr obj(ObjectUnion::New()); obj->set_f_pod_union(std::move(pod)); size_t size = mojo::internal::PrepareToSerialize<ObjectUnionPtr>(obj, false, nullptr); mojo::internal::FixedBufferForTesting buf(size); internal::ObjectUnion_Data* data = nullptr; mojo::internal::Serialize<ObjectUnionPtr>(obj, &buf, &data, false, nullptr); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 0); EXPECT_FALSE(internal::ObjectUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, HandleInUnionGetterSetter) { ScopedMessagePipeHandle pipe0; ScopedMessagePipeHandle pipe1; CreateMessagePipe(nullptr, &pipe0, &pipe1); HandleUnionPtr handle(HandleUnion::New()); handle->set_f_message_pipe(std::move(pipe1)); std::string golden("hello world"); WriteTextMessage(pipe0.get(), golden); std::string actual; ReadTextMessage(handle->get_f_message_pipe().get(), &actual); EXPECT_EQ(golden, actual); } TEST(UnionTest, HandleInUnionSerialization) { ScopedMessagePipeHandle pipe0; ScopedMessagePipeHandle pipe1; CreateMessagePipe(nullptr, &pipe0, &pipe1); HandleUnionPtr handle(HandleUnion::New()); handle->set_f_message_pipe(std::move(pipe1)); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>( handle, false, &context); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::HandleUnion_Data* data = nullptr; mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false, &context); EXPECT_EQ(1U, context.handles.size()); HandleUnionPtr handle2(HandleUnion::New()); mojo::internal::Deserialize<HandleUnionPtr>(data, &handle2, &context); std::string golden("hello world"); WriteTextMessage(pipe0.get(), golden); std::string actual; ReadTextMessage(handle2->get_f_message_pipe().get(), &actual); EXPECT_EQ(golden, actual); } TEST(UnionTest, HandleInUnionValidation) { ScopedMessagePipeHandle pipe0; ScopedMessagePipeHandle pipe1; CreateMessagePipe(nullptr, &pipe0, &pipe1); HandleUnionPtr handle(HandleUnion::New()); handle->set_f_message_pipe(std::move(pipe1)); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>( handle, false, &context); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::HandleUnion_Data* data = nullptr; mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false, &context); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 1); EXPECT_TRUE(internal::HandleUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } TEST(UnionTest, HandleInUnionValidationNull) { mojo::internal::SerializationWarningObserverForTesting suppress_warning; ScopedMessagePipeHandle pipe; HandleUnionPtr handle(HandleUnion::New()); handle->set_f_message_pipe(std::move(pipe)); mojo::internal::SerializationContext context; size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>( handle, false, &context); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::HandleUnion_Data* data = nullptr; mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false, &context); void* raw_buf = buf.Leak(); mojo::internal::ValidationContext validation_context( data, static_cast<uint32_t>(size), 1); EXPECT_FALSE(internal::HandleUnion_Data::Validate( raw_buf, &validation_context, false)); free(raw_buf); } class SmallCacheImpl : public SmallCache { public: explicit SmallCacheImpl(const base::Closure& closure) : int_value_(0), closure_(closure) {} ~SmallCacheImpl() override {} int64_t int_value() const { return int_value_; } private: void SetIntValue(int64_t int_value) override { int_value_ = int_value; closure_.Run(); } void GetIntValue(const GetIntValueCallback& callback) override { callback.Run(int_value_); } int64_t int_value_; base::Closure closure_; }; TEST(UnionTest, InterfaceInUnion) { base::MessageLoop message_loop; base::RunLoop run_loop; SmallCacheImpl impl(run_loop.QuitClosure()); SmallCachePtr ptr; Binding<SmallCache> bindings(&impl, GetProxy(&ptr)); HandleUnionPtr handle(HandleUnion::New()); handle->set_f_small_cache(std::move(ptr)); handle->get_f_small_cache()->SetIntValue(10); run_loop.Run(); EXPECT_EQ(10, impl.int_value()); } TEST(UnionTest, InterfaceInUnionSerialization) { base::MessageLoop message_loop; base::RunLoop run_loop; SmallCacheImpl impl(run_loop.QuitClosure()); SmallCachePtr ptr; Binding<SmallCache> bindings(&impl, GetProxy(&ptr)); mojo::internal::SerializationContext context; HandleUnionPtr handle(HandleUnion::New()); handle->set_f_small_cache(std::move(ptr)); size_t size = mojo::internal::PrepareToSerialize<HandleUnionPtr>( handle, false, &context); EXPECT_EQ(16U, size); mojo::internal::FixedBufferForTesting buf(size); internal::HandleUnion_Data* data = nullptr; mojo::internal::Serialize<HandleUnionPtr>(handle, &buf, &data, false, &context); EXPECT_EQ(1U, context.handles.size()); HandleUnionPtr handle2(HandleUnion::New()); mojo::internal::Deserialize<HandleUnionPtr>(data, &handle2, &context); handle2->get_f_small_cache()->SetIntValue(10); run_loop.Run(); EXPECT_EQ(10, impl.int_value()); } class UnionInterfaceImpl : public UnionInterface { public: UnionInterfaceImpl() {} ~UnionInterfaceImpl() override {} private: void Echo(PodUnionPtr in, const EchoCallback& callback) override { callback.Run(std::move(in)); } }; void ExpectInt16(int16_t value, PodUnionPtr out) { EXPECT_EQ(value, out->get_f_int16()); } TEST(UnionTest, UnionInInterface) { base::MessageLoop message_loop; UnionInterfaceImpl impl; UnionInterfacePtr ptr; Binding<UnionInterface> bindings(&impl, GetProxy(&ptr)); PodUnionPtr pod(PodUnion::New()); pod->set_f_int16(16); ptr->Echo(std::move(pod), base::Bind(&ExpectInt16, 16)); base::RunLoop().RunUntilIdle(); } } // namespace test } // namespace mojo