blob: f8170802261c4fdf4d2a54f21da1796941fef898 [file] [log] [blame]
// 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 <zircon/rights.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"
#include "src/lib/fidl_codec/wire_types.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 kDesiredProtocolName = "fidl.test.frobinator/Frobinator";
Protocol* found_protocol = nullptr;
ASSERT_TRUE(library_ptr->GetProtocolByName(kDesiredProtocolName, &found_protocol));
ASSERT_NE(found_protocol, nullptr) << "Could not find protocol " << kDesiredProtocolName;
std::string kDesiredFullMethodName = "fidl.test.frobinator/Frobinator.Frob";
const ProtocolMethod* found_method = nullptr;
found_protocol->GetMethodByFullName(kDesiredFullMethodName, &found_method);
ASSERT_NE(found_method, nullptr) << "Could not find method " << kDesiredFullMethodName;
}
TEST(LibraryLoader, ProtocolTypeHasCorrectChannelRights) {
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 kDesiredProtocolName = "fidl.test.frobinator/Frobinator";
bool nullable = false;
auto type = library_ptr->TypeFromIdentifier(nullable, kDesiredProtocolName);
auto handle_type = type->AsHandleType();
ASSERT_NE(handle_type, nullptr);
ASSERT_EQ(handle_type->Rights(), ZX_DEFAULT_CHANNEL_RIGHTS);
ASSERT_EQ(handle_type->ObjectType(), ZX_OBJ_TYPE_CHANNEL);
ASSERT_EQ(handle_type->Nullable(), nullable);
}
// 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 kDesiredProtocolName = "fidl.test.frobinator/Frobinator";
Protocol* found_protocol = nullptr;
ASSERT_TRUE(library_ptr->GetProtocolByName(kDesiredProtocolName, &found_protocol));
ASSERT_NE(found_protocol, nullptr) << "Could not find protocol " << kDesiredProtocolName;
std::string kDesiredFullMethodName = "fidl.test.frobinator/Frobinator.Frob";
const ProtocolMethod* found_method = nullptr;
found_protocol->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());
}
// Do the same for the underlying FrogRequest type
const std::string old_payload_type_name = "\"fidl.test.frobinator/FrobinatorFrobRequest\"";
const std::string new_payload_type_name = "\"fidl.test.frobinator/FrobinatorFrogRequest\"";
pos = frobinator_value.find(old_payload_type_name);
while (pos != std::string::npos) {
frobinator_value.replace(pos, old_payload_type_name.size(), new_payload_type_name);
pos = frobinator_value.find(old_payload_type_name, pos + new_payload_type_name.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 kDesiredProtocolName = "fidl.test.frobinator/Frobinator";
Protocol* found_protocol = nullptr;
ASSERT_TRUE(library_ptr->GetProtocolByName(kDesiredProtocolName, &found_protocol));
ASSERT_NE(found_protocol, nullptr) << "Could not find protocol " << kDesiredProtocolName;
// We should find Frog, and not Frob.
std::string kReplacedFullMethodName = "fidl.test.frobinator/Frobinator.Frob";
const ProtocolMethod* not_found_method = nullptr;
found_protocol->GetMethodByFullName(kReplacedFullMethodName, &not_found_method);
ASSERT_EQ(not_found_method, nullptr) << "Found replaced method " << kReplacedFullMethodName;
std::string kDesiredFullMethodName = "fidl.test.frobinator/Frobinator.Frog";
const ProtocolMethod* found_method = nullptr;
found_protocol->GetMethodByFullName(kDesiredFullMethodName, &found_method);
ASSERT_NE(found_method, nullptr) << "Could not find method " << kDesiredFullMethodName;
EXPECT_EQ("struct fidl.test.frobinator/FrobinatorFrogRequest {\n string value;\n}",
found_method->request()->ToString(false));
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 kDesiredProtocolName = "test.fidlcodec.examples/FidlCodecTestProtocol";
Protocol* found_protocol = nullptr;
ASSERT_TRUE(library_ptr->GetProtocolByName(kDesiredProtocolName, &found_protocol));
const ProtocolMethod* found_method = nullptr;
found_protocol->GetMethodByFullName(
"test.fidlcodec.examples/FidlCodecTestProtocol.NullableXUnion", &found_method);
ASSERT_NE(nullptr, found_method);
ASSERT_NE(nullptr, found_method->request());
EXPECT_EQ(
"struct test.fidlcodec.examples/FidlCodecTestProtocolNullableXUnionRequest {\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 test.fidlcodec.examples/FidlCodecTestProtocolNullableXUnionRequest {\n"
" union test.fidlcodec.examples/IntStructXunion isu;\n"
" int32 i;\n"
"}",
found_method->request()->ToString(false));
found_method = nullptr;
found_protocol->GetMethodByFullName(
"test.fidlcodec.examples/FidlCodecTestProtocol.I64BitsMessage", &found_method);
ASSERT_NE(nullptr, found_method);
ASSERT_NE(nullptr, found_method->request());
EXPECT_EQ(
"struct test.fidlcodec.examples/FidlCodecTestProtocolI64BitsMessageRequest {\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_protocol->GetMethodByFullName("test.fidlcodec.examples/FidlCodecTestProtocol.Table",
&found_method);
ASSERT_NE(nullptr, found_method);
ASSERT_NE(nullptr, found_method->request());
EXPECT_EQ(
"struct test.fidlcodec.examples/FidlCodecTestProtocolTableRequest {\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"
" 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_protocol->GetMethodByFullName(
"test.fidlcodec.examples/FidlCodecTestProtocol.DefaultEnumMessage", &found_method);
ASSERT_NE(nullptr, found_method);
ASSERT_NE(nullptr, found_method->request());
EXPECT_EQ(
"struct test.fidlcodec.examples/FidlCodecTestProtocolDefaultEnumMessageRequest {\n"
" enum test.fidlcodec.examples/DefaultEnum {\n"
" X = 23;\n"
" } ev;\n"
"}",
found_method->request()->ToString(true));
found_method = nullptr;
found_protocol->GetMethodByFullName(
"test.fidlcodec.examples/FidlCodecTestProtocol.ShortUnionReserved", &found_method);
ASSERT_NE(nullptr, found_method);
ASSERT_NE(nullptr, found_method->request());
EXPECT_EQ(
"struct test.fidlcodec.examples/FidlCodecTestProtocolShortUnionReservedRequest {\n"
" union test.fidlcodec.examples/U8U16UnionReserved {\n"
" 1: uint8 variant_u8;\n"
" 3: uint16 variant_u16;\n"
" } u;\n"
" int32 i;\n"
"}",
found_method->request()->ToString(true));
found_method = nullptr;
found_protocol->GetMethodByFullName("test.fidlcodec.examples/FidlCodecTestProtocol.Array1",
&found_method);
ASSERT_NE(nullptr, found_method);
ASSERT_NE(nullptr, found_method->request());
EXPECT_EQ(
"struct test.fidlcodec.examples/FidlCodecTestProtocolArray1Request {\n array<int32> "
"b_1;\n}",
found_method->request()->ToString(true));
found_method = nullptr;
found_protocol->GetMethodByFullName("test.fidlcodec.examples/FidlCodecTestProtocol.Vector",
&found_method);
ASSERT_NE(nullptr, found_method);
ASSERT_NE(nullptr, found_method->request());
EXPECT_EQ(
"struct test.fidlcodec.examples/FidlCodecTestProtocolVectorRequest {\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 kDesiredProtocolName = "test.fidlcodec.sys/ComponentController";
Protocol* found_protocol = nullptr;
ASSERT_TRUE(library_ptr->GetProtocolByName(kDesiredProtocolName, &found_protocol));
const ProtocolMethod* found_method = nullptr;
found_protocol->GetMethodByFullName("test.fidlcodec.sys/ComponentController.OnDirectoryReady",
&found_method);
Ordinal64 correct_ordinal = found_method->ordinal();
const std::vector<ProtocolMethod*>* ordinal_methods = loader.GetByOrdinal(correct_ordinal);
const ProtocolMethod* ordinal_method = (*ordinal_methods)[0];
ASSERT_NE(ordinal_method, nullptr);
ASSERT_EQ(kDesiredProtocolName, ordinal_method->enclosing_protocol().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 kDesiredProtocolName = "test.fidlcodec.examples/ParamProtocol";
Protocol* found_protocol = nullptr;
ASSERT_TRUE(library_ptr->GetProtocolByName(kDesiredProtocolName, &found_protocol));
const ProtocolMethod* found_method = nullptr;
found_protocol->GetMethodByFullName("test.fidlcodec.examples/ParamProtocol.Method",
&found_method);
Ordinal64 correct_ordinal = found_method->ordinal();
const std::vector<ProtocolMethod*>* ordinal_methods = loader.GetByOrdinal(correct_ordinal);
ASSERT_EQ(2UL, ordinal_methods->size());
const ProtocolMethod* ordinal_method_base = (*ordinal_methods)[0];
ASSERT_NE(ordinal_method_base, nullptr);
ASSERT_EQ(kDesiredProtocolName, ordinal_method_base->enclosing_protocol().name());
ASSERT_EQ("Method", ordinal_method_base->name());
const ProtocolMethod* ordinal_method_composed = (*ordinal_methods)[1];
ASSERT_NE(ordinal_method_composed, nullptr);
ASSERT_EQ("test.fidlcodec.composedinto/ComposedParamProtocol",
ordinal_method_composed->enclosing_protocol().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