blob: 448c584cbefd9af59acc702fbd04642c4a8f81b4 [file] [log] [blame]
// Copyright 2020 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 <memory>
#include <gtest/gtest.h>
#include "src/lib/fidl_codec/library_loader.h"
#include "src/lib/fidl_codec/semantic.h"
#include "src/lib/fidl_codec/semantic_parser_test.h"
#include "src/lib/fidl_codec/wire_object.h"
#include "src/lib/fidl_codec/wire_types.h"
namespace fidl_codec {
namespace semantic {
constexpr uint64_t kPid = 0x1234;
constexpr uint64_t kTid = 0x4321;
constexpr uint32_t kHandle = 0x1111;
constexpr uint32_t kChannel0 = 0x1000;
constexpr uint32_t kChannel1 = 0x2000;
constexpr uint32_t kChannel2 = 0x3000;
constexpr uint32_t kChannel3 = 0x4000;
constexpr int64_t kEventTimestamp = 0;
class BuiltinSemanticTest : public SemanticParserTest {
public:
BuiltinSemanticTest();
void SetHandleSemantic(std::string_view type, std::string_view path) {
handle_semantic_.AddInferredHandleInfo(kPid, kHandle, type, path, "");
}
void SetHandleSemantic(std::string_view type, int64_t fd) {
handle_semantic_.AddInferredHandleInfo(kPid, kHandle, type, fd, "");
}
void ExecuteWrite(const MethodSemantic* method_semantic, const StructValue* request,
const StructValue* response);
void ExecuteRead(const MethodSemantic* method_semantic, const StructValue* request,
const StructValue* response);
void ShortDisplay(std::ostream& os, const MethodDisplay* display, const StructValue* request,
const StructValue* response);
protected:
HandleSemantic handle_semantic_;
const zx_handle_disposition_t channel0_;
const zx_handle_disposition_t channel2_;
};
BuiltinSemanticTest::BuiltinSemanticTest()
: channel0_({fidl_codec::kNoHandleDisposition, kChannel0, 0, 0, ZX_OK}),
channel2_({fidl_codec::kNoHandleDisposition, kChannel2, 0, 0, ZX_OK}) {
library_loader_.ParseBuiltinSemantic();
handle_semantic_.AddLinkedHandles(kPid, kChannel0, kChannel1);
handle_semantic_.AddLinkedHandles(kPid, kChannel2, kChannel3);
}
void BuiltinSemanticTest::ExecuteWrite(const MethodSemantic* method_semantic,
const StructValue* request, const StructValue* response) {
fidl_codec::semantic::AssignmentSemanticContext context(&handle_semantic_, kPid, kTid, kHandle,
ContextType::kWrite, request, response,
kEventTimestamp);
method_semantic->ExecuteAssignments(&context);
}
void BuiltinSemanticTest::ExecuteRead(const MethodSemantic* method_semantic,
const StructValue* request, const StructValue* response) {
fidl_codec::semantic::AssignmentSemanticContext context(&handle_semantic_, kPid, kTid, kHandle,
ContextType::kRead, request, response,
kEventTimestamp);
method_semantic->ExecuteAssignments(&context);
}
void BuiltinSemanticTest::ShortDisplay(std::ostream& os, const MethodDisplay* display,
const StructValue* request, const StructValue* response) {
PrettyPrinter printer(os, WithoutColors, true, "", 100, false);
fidl_codec::semantic::SemanticContext context(&handle_semantic_, kPid, ZX_HANDLE_INVALID, request,
response, kEventTimestamp);
bool first_argument = true;
for (const auto& expression : display->inputs()) {
if (first_argument) {
printer << '(';
first_argument = false;
} else {
printer << ", ";
}
expression->PrettyPrint(printer, &context);
}
if (!first_argument) {
printer << ')';
}
printer << '\n';
bool first_result = true;
for (const auto& expression : display->results()) {
printer << (first_result ? "-> " : ", ");
first_result = false;
expression->PrettyPrint(printer, &context);
}
if (!first_result) {
printer << '\n';
}
}
// Check Node::Clone: request.object = handle
TEST_F(BuiltinSemanticTest, CloneWrite) {
// Checks that Node::Clone exists in fuchsia.io.
Library* library = library_loader_.GetLibraryFromName("fuchsia.io");
ASSERT_NE(library, nullptr);
library->DecodeTypes();
Interface* interface = nullptr;
library->GetInterfaceByName("fuchsia.io/Node", &interface);
ASSERT_NE(interface, nullptr);
InterfaceMethod* method = interface->GetMethodByName("Clone");
ASSERT_NE(method, nullptr);
// Checks that the builtin semantic is defined for Clone.
ASSERT_NE(method->semantic(), nullptr);
// Check that by writing on this handle:
SetHandleSemantic("dir", "/svc");
// This message (we only define the fields used by the semantic):
StructValue request(*method->request());
request.AddField("object", std::make_unique<HandleValue>(channel0_));
ExecuteWrite(method->semantic(), &request, nullptr);
// We have this handle semantic for kChannel1.
const InferredHandleInfo* inferred_handle_info =
handle_semantic_.GetInferredHandleInfo(kPid, kChannel1);
ASSERT_NE(inferred_handle_info, nullptr);
ASSERT_EQ(inferred_handle_info->type(), "dir");
ASSERT_EQ(inferred_handle_info->path(), "/svc");
ASSERT_EQ(inferred_handle_info->attributes(), "cloned");
}
// Check Node::Clone: request.object = handle
TEST_F(BuiltinSemanticTest, CloneRead) {
// Checks that Node::Clone exists in fuchsia.io.
Library* library = library_loader_.GetLibraryFromName("fuchsia.io");
ASSERT_NE(library, nullptr);
library->DecodeTypes();
Interface* interface = nullptr;
library->GetInterfaceByName("fuchsia.io/Node", &interface);
ASSERT_NE(interface, nullptr);
InterfaceMethod* method = interface->GetMethodByName("Clone");
ASSERT_NE(method, nullptr);
// Checks that the builtin semantic is defined for Clone.
ASSERT_NE(method->semantic(), nullptr);
// Check that by writing on this handle:
SetHandleSemantic("dir", "/svc");
// This message (we only define the fields used by the semantic):
StructValue request(*method->request());
request.AddField("object", std::make_unique<HandleValue>(channel0_));
ExecuteRead(method->semantic(), &request, nullptr);
// We have this handle semantic for kChannel1.
const InferredHandleInfo* inferred_handle_info =
handle_semantic_.GetInferredHandleInfo(kPid, kChannel0);
ASSERT_NE(inferred_handle_info, nullptr);
ASSERT_EQ(inferred_handle_info->type(), "dir");
ASSERT_EQ(inferred_handle_info->path(), "/svc");
ASSERT_EQ(inferred_handle_info->attributes(), "cloned");
}
// Check Node::Clone: request.object = handle
TEST_F(BuiltinSemanticTest, CloneFd) {
// Checks that Node::Clone exists in fuchsia.io.
Library* library = library_loader_.GetLibraryFromName("fuchsia.io");
ASSERT_NE(library, nullptr);
library->DecodeTypes();
Interface* interface = nullptr;
library->GetInterfaceByName("fuchsia.io/Node", &interface);
ASSERT_NE(interface, nullptr);
InterfaceMethod* method = interface->GetMethodByName("Clone");
ASSERT_NE(method, nullptr);
// Checks that the builtin semantic is defined for Clone.
ASSERT_NE(method->semantic(), nullptr);
// Check that by writing on this handle:
SetHandleSemantic("handle", 2);
// This message (we only define the fields used by the semantic):
StructValue request(*method->request());
request.AddField("object", std::make_unique<HandleValue>(channel0_));
ExecuteRead(method->semantic(), &request, nullptr);
// We have this handle semantic for kChannel1.
const InferredHandleInfo* inferred_handle_info =
handle_semantic_.GetInferredHandleInfo(kPid, kChannel0);
ASSERT_NE(inferred_handle_info, nullptr);
ASSERT_EQ(inferred_handle_info->type(), "handle");
ASSERT_EQ(inferred_handle_info->attributes(), "cloned");
ASSERT_EQ(inferred_handle_info->fd(), 2);
}
// Check Directory::Open: request.object = handle / request.path
TEST_F(BuiltinSemanticTest, Open) {
// Checks that Directory::Open exists in fuchsia.io.
Library* library = library_loader_.GetLibraryFromName("fuchsia.io");
ASSERT_NE(library, nullptr);
library->DecodeTypes();
Interface* interface = nullptr;
library->GetInterfaceByName("fuchsia.io/Directory", &interface);
ASSERT_NE(interface, nullptr);
InterfaceMethod* method = interface->GetMethodByName("Open");
ASSERT_NE(method, nullptr);
// Checks that the builtin semantic is defined for Open.
ASSERT_NE(method->semantic(), nullptr);
// Check that by writing on this handle:
SetHandleSemantic("dir", "/svc");
// This message (we only define the fields used by the semantic):
StructValue request(*method->request());
request.AddField("path", std::make_unique<StringValue>("fuchsia.sys.Launcher"));
request.AddField("object", std::make_unique<HandleValue>(channel0_));
ExecuteWrite(method->semantic(), &request, nullptr);
// We have this handle semantic for kChannel1.
const InferredHandleInfo* inferred_handle_info =
handle_semantic_.GetInferredHandleInfo(kPid, kChannel1);
ASSERT_NE(inferred_handle_info, nullptr);
ASSERT_EQ(inferred_handle_info->type(), "dir");
ASSERT_EQ(inferred_handle_info->path(), "/svc/fuchsia.sys.Launcher");
}
// Check Launcher::CreateComponent.
TEST_F(BuiltinSemanticTest, CreateComponent) {
// Checks that Launcher::CreateComponent exists in fuchsia.sys.
Library* library = library_loader_.GetLibraryFromName("fuchsia.sys");
ASSERT_NE(library, nullptr);
library->DecodeTypes();
Interface* interface = nullptr;
library->GetInterfaceByName("fuchsia.sys/Launcher", &interface);
ASSERT_NE(interface, nullptr);
InterfaceMethod* method = interface->GetMethodByName("CreateComponent");
ASSERT_NE(method, nullptr);
// Checks that the builtin semantic is defined for CreateComponent.
ASSERT_NE(method->semantic(), nullptr);
// Check that by writing on this handle:
SetHandleSemantic("dir", "/svc/fuchsia.sys.Launcher");
// This message (we only define the fields used by the semantic):
StructValue request(*method->request());
auto launch_info = std::make_unique<StructValue>(
method->request()->SearchMember("launch_info")->type()->AsStructType()->struct_definition());
launch_info->AddField("url",
std::make_unique<StringValue>(
"fuchsia-pkg://fuchsia.com/echo_server_cpp#meta/echo_server_cpp.cmx"));
launch_info->AddField("directory_request", std::make_unique<HandleValue>(channel0_));
request.AddField("launch_info", std::move(launch_info));
request.AddField("controller", std::make_unique<HandleValue>(channel2_));
ExecuteWrite(method->semantic(), &request, nullptr);
// We have these handle semantics for kChannel1 and kChannel3.
const InferredHandleInfo* inferred_handle_info_1 =
handle_semantic_.GetInferredHandleInfo(kPid, kChannel1);
ASSERT_NE(inferred_handle_info_1, nullptr);
ASSERT_EQ(inferred_handle_info_1->type(), "server");
ASSERT_EQ(inferred_handle_info_1->path(),
"fuchsia-pkg://fuchsia.com/echo_server_cpp#meta/echo_server_cpp.cmx");
const InferredHandleInfo* inferred_handle_info_2 =
handle_semantic_.GetInferredHandleInfo(kPid, kChannel3);
ASSERT_NE(inferred_handle_info_2, nullptr);
ASSERT_EQ(inferred_handle_info_2->type(), "server-control");
ASSERT_EQ(inferred_handle_info_2->path(),
"fuchsia-pkg://fuchsia.com/echo_server_cpp#meta/echo_server_cpp.cmx");
}
// Check short display of Directory::Open.
TEST_F(BuiltinSemanticTest, OpenShortDisplay) {
// Checks that Directory::Open exists in fuchsia.io.
Library* library = library_loader_.GetLibraryFromName("fuchsia.io");
ASSERT_NE(library, nullptr);
library->DecodeTypes();
Interface* interface = nullptr;
library->GetInterfaceByName("fuchsia.io/Directory", &interface);
ASSERT_NE(interface, nullptr);
InterfaceMethod* method = interface->GetMethodByName("Open");
ASSERT_NE(method, nullptr);
// Checks that the short display is defined for Open.
ASSERT_NE(method->short_display(), nullptr);
// This message (we only define the fields used by the display):
StructValue request(*method->request());
request.AddField("path", std::make_unique<StringValue>("fuchsia.sys.Launcher"));
request.AddField("object", std::make_unique<HandleValue>(channel0_));
std::stringstream os;
ShortDisplay(os, method->short_display(), &request, nullptr);
ASSERT_EQ(os.str(),
"(\"fuchsia.sys.Launcher\")\n"
"-> 00002000\n");
}
// Check short display of File::Seek.
TEST_F(BuiltinSemanticTest, FileSeekShortDisplay) {
// Checks that Node::Clone exists in fuchsia.io.
Library* library = library_loader_.GetLibraryFromName("fuchsia.io");
ASSERT_NE(library, nullptr);
library->DecodeTypes();
Interface* interface = nullptr;
library->GetInterfaceByName("fuchsia.io/File", &interface);
ASSERT_NE(interface, nullptr);
InterfaceMethod* method = interface->GetMethodByName("Seek");
ASSERT_NE(method, nullptr);
// Checks that the short display is defined for Seek.
ASSERT_NE(method->short_display(), nullptr);
// This message (we only define the fields used by the display):
StructValue request(*method->request());
request.AddField("start", std::make_unique<IntegerValue>(0, false));
request.AddField("offset", std::make_unique<IntegerValue>(1000, false));
std::stringstream os;
ShortDisplay(os, method->short_display(), &request, nullptr);
ASSERT_EQ(os.str(), "(START, 1000)\n");
}
// Check short display of File::Write.
TEST_F(BuiltinSemanticTest, FileWriteShortDisplay) {
// Checks that Node::Clone exists in fuchsia.io.
Library* library = library_loader_.GetLibraryFromName("fuchsia.io");
ASSERT_NE(library, nullptr);
library->DecodeTypes();
Interface* interface = nullptr;
library->GetInterfaceByName("fuchsia.io/File", &interface);
ASSERT_NE(interface, nullptr);
InterfaceMethod* method = interface->GetMethodByName("Write");
ASSERT_NE(method, nullptr);
// Checks that the short display is defined for Write.
ASSERT_NE(method->short_display(), nullptr);
// This message (we only define the fields used by the display):
StructValue request(*method->request());
auto vector = std::make_unique<VectorValue>();
vector->AddValue(std::make_unique<IntegerValue>(10, false));
vector->AddValue(std::make_unique<IntegerValue>(20, false));
vector->AddValue(std::make_unique<IntegerValue>(30, false));
vector->AddValue(std::make_unique<IntegerValue>(40, false));
vector->AddValue(std::make_unique<IntegerValue>(50, false));
request.AddField("data", std::move(vector));
std::stringstream os;
ShortDisplay(os, method->short_display(), &request, nullptr);
ASSERT_EQ(os.str(), "(5 bytes)\n");
}
} // namespace semantic
} // namespace fidl_codec