| // Copyright 2019 The Fuchsia 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 "src/lib/fidl_codec/library_loader.h" |
| |
| #include <iostream> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/lib/fidl_codec/library_loader_test_data.h" |
| #include "src/lib/fidl_codec/list_test_data.h" |
| |
| namespace fidl_codec { |
| |
| // Check that we can load all the FIDL description files without errors (and without crash). |
| TEST(LibraryLoader, CheckAll) { |
| fidl_codec_test::SdkExamples sdk_examples; |
| fidl_codec_test::FidlcodecExamples other_examples; |
| |
| LibraryReadError err; |
| LibraryLoader library_loader; |
| // Test all the files in sdk/core.fidl_json.txt. |
| for (const auto& element : sdk_examples.map()) { |
| library_loader.AddContent(element.second, &err); |
| ASSERT_EQ(err.value, LibraryReadError::kOk); |
| } |
| // Test all the fidl_codec files. |
| for (const auto& element : other_examples.map()) { |
| library_loader.AddContent(element.second, &err); |
| ASSERT_EQ(err.value, LibraryReadError::kOk); |
| } |
| // Do the tests. |
| ASSERT_TRUE(library_loader.DecodeAll()); |
| } |
| |
| TEST(LibraryLoader, LoadSimple) { |
| fidl_codec_test::FidlcodecExamples examples; |
| LibraryReadError err; |
| LibraryLoader loader; |
| for (const auto& element : examples.map()) { |
| loader.AddContent(element.second, &err); |
| } |
| ASSERT_EQ(LibraryReadError::kOk, err.value); |
| |
| Library* library_ptr = loader.GetLibraryFromName("fidl.test.frobinator"); |
| ASSERT_NE(library_ptr, nullptr); |
| |
| std::string kDesiredInterfaceName = "fidl.test.frobinator/Frobinator"; |
| Interface* found_interface = nullptr; |
| ASSERT_TRUE(library_ptr->GetInterfaceByName(kDesiredInterfaceName, &found_interface)); |
| |
| ASSERT_NE(found_interface, nullptr) << "Could not find interface " << kDesiredInterfaceName; |
| |
| std::string kDesiredFullMethodName = "fidl.test.frobinator/Frobinator.Frob"; |
| const InterfaceMethod* found_method = nullptr; |
| found_interface->GetMethodByFullName(kDesiredFullMethodName, &found_method); |
| |
| ASSERT_NE(found_method, nullptr) << "Could not find method " << kDesiredFullMethodName; |
| } |
| |
| // Makes sure that loading works when you load one IR at a time, instead of in a bunch. |
| TEST(LibraryLoader, LoadSimpleOneAtATime) { |
| fidl_codec_test::FidlcodecExamples examples; |
| LibraryLoader loader; |
| LibraryReadError err; |
| for (const auto& element : examples.map()) { |
| loader.AddContent(element.second, &err); |
| ASSERT_EQ(LibraryReadError::kOk, err.value); |
| } |
| |
| Library* library_ptr = loader.GetLibraryFromName("fidl.test.frobinator"); |
| ASSERT_NE(library_ptr, nullptr); |
| |
| std::string kDesiredInterfaceName = "fidl.test.frobinator/Frobinator"; |
| Interface* found_interface = nullptr; |
| ASSERT_TRUE(library_ptr->GetInterfaceByName(kDesiredInterfaceName, &found_interface)); |
| |
| ASSERT_NE(found_interface, nullptr) << "Could not find interface " << kDesiredInterfaceName; |
| |
| std::string kDesiredFullMethodName = "fidl.test.frobinator/Frobinator.Frob"; |
| const InterfaceMethod* found_method = nullptr; |
| found_interface->GetMethodByFullName(kDesiredFullMethodName, &found_method); |
| |
| ASSERT_NE(found_method, nullptr) << "Could not find method " << kDesiredFullMethodName; |
| } |
| |
| // Ensure that, if you load two libraries with the same name, the first one loaded wins. |
| // LoadAll calls AddContent using the last item in the list. That means that, for LoadAll, the |
| // last one wins. |
| TEST(LibraryLoader, FirstContentWins) { |
| fidl_codec_test::FidlcodecExamples examples; |
| std::string frobinator_value; |
| const std::string file_to_replace = "frobinator.fidl.json"; |
| for (const auto& element : examples.map()) { |
| if (element.first.compare(element.first.length() - file_to_replace.length(), |
| file_to_replace.length(), file_to_replace) == 0) { |
| frobinator_value = element.second; |
| } |
| } |
| ASSERT_NE(frobinator_value, "") << "Frobinator library not found"; |
| |
| // Duplicate the frobinator entry, replacing the Frob method with a Frog method |
| const std::string old_method = "\"Frob\""; |
| const std::string new_method = "\"Frog\""; |
| size_t pos = frobinator_value.find(old_method); |
| while (pos != std::string::npos) { |
| frobinator_value.replace(pos, old_method.size(), new_method); |
| pos = frobinator_value.find(old_method, pos + new_method.size()); |
| } |
| |
| LibraryReadError err; |
| LibraryLoader loader; |
| |
| // Add the modified version. It will forbid the initial version to load. |
| loader.AddContent(frobinator_value, &err); |
| |
| // Adds all the standard versions. |
| for (const auto& element : examples.map()) { |
| loader.AddContent(element.second, &err); |
| ASSERT_EQ(LibraryReadError::kOk, err.value); |
| } |
| |
| Library* library_ptr = loader.GetLibraryFromName("fidl.test.frobinator"); |
| ASSERT_NE(library_ptr, nullptr); |
| |
| std::string kDesiredInterfaceName = "fidl.test.frobinator/Frobinator"; |
| Interface* found_interface = nullptr; |
| ASSERT_TRUE(library_ptr->GetInterfaceByName(kDesiredInterfaceName, &found_interface)); |
| |
| ASSERT_NE(found_interface, nullptr) << "Could not find interface " << kDesiredInterfaceName; |
| |
| // We should find Frog, and not Frob. |
| std::string kReplacedFullMethodName = "fidl.test.frobinator/Frobinator.Frob"; |
| const InterfaceMethod* not_found_method = nullptr; |
| found_interface->GetMethodByFullName(kReplacedFullMethodName, ¬_found_method); |
| |
| ASSERT_EQ(not_found_method, nullptr) << "Found replaced method " << kReplacedFullMethodName; |
| |
| std::string kDesiredFullMethodName = "fidl.test.frobinator/Frobinator.Frog"; |
| const InterfaceMethod* found_method = nullptr; |
| found_interface->GetMethodByFullName(kDesiredFullMethodName, &found_method); |
| |
| ASSERT_NE(found_method, nullptr) << "Could not find method " << kDesiredFullMethodName; |
| EXPECT_EQ("struct Frog {\n string value;\n}", found_method->request()->ToString()); |
| EXPECT_EQ(nullptr, found_method->response()); |
| } |
| |
| TEST(LibraryLoader, InspectTypes) { |
| LibraryReadError err; |
| LibraryLoader loader; |
| fidl_codec_test::FidlcodecExamples examples; |
| for (const auto& element : examples.map()) { |
| loader.AddContent(element.second, &err); |
| ASSERT_EQ(LibraryReadError::kOk, err.value); |
| } |
| |
| Library* library_ptr = loader.GetLibraryFromName("test.fidlcodec.examples"); |
| ASSERT_NE(library_ptr, nullptr); |
| |
| std::string kDesiredInterfaceName = "test.fidlcodec.examples/FidlCodecTestInterface"; |
| Interface* found_interface = nullptr; |
| ASSERT_TRUE(library_ptr->GetInterfaceByName(kDesiredInterfaceName, &found_interface)); |
| |
| const InterfaceMethod* found_method = nullptr; |
| found_interface->GetMethodByFullName( |
| "test.fidlcodec.examples/FidlCodecTestInterface.NullableXUnion", &found_method); |
| |
| ASSERT_NE(nullptr, found_method); |
| ASSERT_NE(nullptr, found_method->request()); |
| EXPECT_EQ( |
| "struct NullableXUnion {\n" |
| " union test.fidlcodec.examples/IntStructXunion {\n" |
| " 1: int32 variant_i;\n" |
| " 2: struct test.fidlcodec.examples/TwoStringStruct {\n" |
| " string value1;\n" |
| " string value2;\n" |
| " } variant_tss;\n" |
| " } isu;\n" |
| " int32 i;\n" |
| "}", |
| found_method->request()->ToString(true)); |
| EXPECT_EQ( |
| "struct NullableXUnion {\n" |
| " union test.fidlcodec.examples/IntStructXunion isu;\n" |
| " int32 i;\n" |
| "}", |
| found_method->request()->ToString(false)); |
| |
| found_method = nullptr; |
| found_interface->GetMethodByFullName( |
| "test.fidlcodec.examples/FidlCodecTestInterface.I64BitsMessage", &found_method); |
| |
| ASSERT_NE(nullptr, found_method); |
| ASSERT_NE(nullptr, found_method->request()); |
| EXPECT_EQ( |
| "struct I64BitsMessage {\n" |
| " bits test.fidlcodec.examples/I64Bits {\n" |
| " A = 4294967296;\n" |
| " B = 8589934592;\n" |
| " C = 17179869184;\n" |
| " D = 34359738368;\n" |
| " } v;\n" |
| "}", |
| found_method->request()->ToString(true)); |
| |
| found_method = nullptr; |
| found_interface->GetMethodByFullName("test.fidlcodec.examples/FidlCodecTestInterface.Table", |
| &found_method); |
| |
| ASSERT_NE(nullptr, found_method); |
| ASSERT_NE(nullptr, found_method->request()); |
| EXPECT_EQ( |
| "struct Table {\n" |
| " table test.fidlcodec.examples/ValueTable {\n" |
| " 1: int16 first_int16;\n" |
| " 2: struct test.fidlcodec.examples/TwoStringStruct {\n" |
| " string value1;\n" |
| " string value2;\n" |
| " } second_struct;\n" |
| " 3: reserved;\n" |
| " 4: union test.fidlcodec.examples/IntStructUnion {\n" |
| " 1: int32 variant_i;\n" |
| " 2: struct test.fidlcodec.examples/TwoStringStruct {\n" |
| " string value1;\n" |
| " string value2;\n" |
| " } variant_tss;\n" |
| " } third_union;\n" |
| " } table;\n" |
| " int32 i;\n" |
| "}", |
| found_method->request()->ToString(true)); |
| |
| found_method = nullptr; |
| found_interface->GetMethodByFullName( |
| "test.fidlcodec.examples/FidlCodecTestInterface.DefaultEnumMessage", &found_method); |
| |
| ASSERT_NE(nullptr, found_method); |
| ASSERT_NE(nullptr, found_method->request()); |
| EXPECT_EQ( |
| "struct DefaultEnumMessage {\n" |
| " enum test.fidlcodec.examples/DefaultEnum {\n" |
| " X = 23;\n" |
| " } ev;\n" |
| "}", |
| found_method->request()->ToString(true)); |
| |
| found_method = nullptr; |
| found_interface->GetMethodByFullName( |
| "test.fidlcodec.examples/FidlCodecTestInterface.ShortUnionReserved", &found_method); |
| |
| ASSERT_NE(nullptr, found_method); |
| ASSERT_NE(nullptr, found_method->request()); |
| EXPECT_EQ( |
| "struct ShortUnionReserved {\n" |
| " union test.fidlcodec.examples/U8U16UnionReserved {\n" |
| " 1: uint8 variant_u8;\n" |
| " 2: reserved;\n" |
| " 3: uint16 variant_u16;\n" |
| " } u;\n" |
| " int32 i;\n" |
| "}", |
| found_method->request()->ToString(true)); |
| |
| found_method = nullptr; |
| found_interface->GetMethodByFullName("test.fidlcodec.examples/FidlCodecTestInterface.Array1", |
| &found_method); |
| |
| ASSERT_NE(nullptr, found_method); |
| ASSERT_NE(nullptr, found_method->request()); |
| EXPECT_EQ("struct Array1 {\n array<int32> b_1;\n}", found_method->request()->ToString(true)); |
| |
| found_method = nullptr; |
| found_interface->GetMethodByFullName("test.fidlcodec.examples/FidlCodecTestInterface.Vector", |
| &found_method); |
| |
| ASSERT_NE(nullptr, found_method); |
| ASSERT_NE(nullptr, found_method->request()); |
| EXPECT_EQ("struct Vector {\n vector<int32> v_1;\n}", found_method->request()->ToString(true)); |
| } |
| |
| TEST(LibraryLoader, LoadFromOrdinal) { |
| LibraryReadError err; |
| LibraryLoader loader; |
| fidl_codec_test::FidlcodecExamples examples; |
| for (const auto& element : examples.map()) { |
| loader.AddContent(element.second, &err); |
| ASSERT_EQ(LibraryReadError::kOk, err.value); |
| } |
| |
| Library* library_ptr = loader.GetLibraryFromName("test.fidlcodec.sys"); |
| ASSERT_NE(library_ptr, nullptr); |
| |
| std::string kDesiredInterfaceName = "test.fidlcodec.sys/ComponentController"; |
| Interface* found_interface = nullptr; |
| ASSERT_TRUE(library_ptr->GetInterfaceByName(kDesiredInterfaceName, &found_interface)); |
| |
| const InterfaceMethod* found_method = nullptr; |
| found_interface->GetMethodByFullName("test.fidlcodec.sys/ComponentController.OnDirectoryReady", |
| &found_method); |
| |
| Ordinal64 correct_ordinal = found_method->ordinal(); |
| const std::vector<const InterfaceMethod*>* ordinal_methods = loader.GetByOrdinal(correct_ordinal); |
| const InterfaceMethod* ordinal_method = (*ordinal_methods)[0]; |
| ASSERT_NE(ordinal_method, nullptr); |
| ASSERT_EQ(kDesiredInterfaceName, ordinal_method->enclosing_interface().name()); |
| ASSERT_EQ("OnDirectoryReady", ordinal_method->name()); |
| } |
| |
| void OrdinalCompositionBody(LibraryLoader& loader) { |
| Library* library_ptr = loader.GetLibraryFromName("test.fidlcodec.examples"); |
| ASSERT_NE(library_ptr, nullptr); |
| |
| std::string kDesiredInterfaceName = "test.fidlcodec.examples/ParamProtocol"; |
| Interface* found_interface = nullptr; |
| ASSERT_TRUE(library_ptr->GetInterfaceByName(kDesiredInterfaceName, &found_interface)); |
| |
| const InterfaceMethod* found_method = nullptr; |
| found_interface->GetMethodByFullName("test.fidlcodec.examples/ParamProtocol.Method", |
| &found_method); |
| |
| Ordinal64 correct_ordinal = found_method->ordinal(); |
| const std::vector<const InterfaceMethod*>* ordinal_methods = loader.GetByOrdinal(correct_ordinal); |
| ASSERT_EQ(2UL, ordinal_methods->size()); |
| |
| const InterfaceMethod* ordinal_method_base = (*ordinal_methods)[0]; |
| ASSERT_NE(ordinal_method_base, nullptr); |
| ASSERT_EQ(kDesiredInterfaceName, ordinal_method_base->enclosing_interface().name()); |
| ASSERT_EQ("Method", ordinal_method_base->name()); |
| |
| const InterfaceMethod* ordinal_method_composed = (*ordinal_methods)[1]; |
| ASSERT_NE(ordinal_method_composed, nullptr); |
| ASSERT_EQ("test.fidlcodec.composedinto/ComposedParamProtocol", |
| ordinal_method_composed->enclosing_interface().name()); |
| ASSERT_EQ("Method", ordinal_method_composed->name()); |
| } |
| |
| // Tests that we get the method composed into a protocol when we request a |
| // particular method. The base protocol is ParamProtocol, the protocol that |
| // composes ParamProtocol is ComposedParamProtocol, and the method name is |
| // Method(). We test that we get the base protocol first in the vector |
| // regardless of the order that the libraries were loaded. |
| TEST(LibraryLoader, OrdinalComposition) { |
| { |
| LibraryReadError err; |
| LibraryLoader loader; |
| |
| // Load the libraries in the order in examples.map(). |
| fidl_codec_test::FidlcodecExamples examples; |
| std::vector<std::unique_ptr<std::istream>> library_files; |
| for (auto element = examples.map().begin(); element != examples.map().end(); ++element) { |
| loader.AddContent(element->second, &err); |
| ASSERT_EQ(LibraryReadError::kOk, err.value); |
| } |
| |
| OrdinalCompositionBody(loader); |
| } |
| { |
| LibraryReadError err; |
| LibraryLoader loader; |
| |
| // Load the libraries in the reverse of the order in examples.map(). |
| fidl_codec_test::FidlcodecExamples examples; |
| std::vector<std::unique_ptr<std::istream>> library_files; |
| for (auto element = examples.map().rbegin(); element != examples.map().rend(); ++element) { |
| loader.AddContent(element->second, &err); |
| ASSERT_EQ(LibraryReadError::kOk, err.value); |
| } |
| |
| OrdinalCompositionBody(loader); |
| } |
| } |
| |
| } // namespace fidl_codec |