blob: 9aa5559a211aff8b290f3c0d02edef621332d304 [file] [log] [blame]
// Copyright 2018 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 <fuchsia/logger/llcpp/fidl.h>
#include <iostream>
#include <gtest/gtest.h>
#include "config.h"
namespace netemul {
namespace config {
namespace testing {
class ModelTest : public ::testing::Test {
protected:
void ExpectFailedParse(const char* json, const char* msg) {
Config config;
json::JSONParser parser;
auto doc = parser.ParseFromString(
json, ::testing::UnitTest::GetInstance()->current_test_info()->name());
ASSERT_FALSE(parser.HasError()) << json;
EXPECT_FALSE(config.ParseFromJSON(doc, &parser)) << msg;
ASSERT_TRUE(parser.HasError());
std::cout << "Parse failed as expected: " << parser.error_str() << std::endl;
}
void ExpectSuccessfulParse(const char* json, Config* config) {
json::JSONParser parser;
auto doc = parser.ParseFromString(
json, ::testing::UnitTest::GetInstance()->current_test_info()->name());
ASSERT_FALSE(parser.HasError());
ASSERT_TRUE(config->ParseFromJSON(doc, &parser));
ASSERT_FALSE(parser.HasError());
}
};
TEST_F(ModelTest, ParseTest) {
const char* json =
R"(
{
"default_url": "fuchsia-pkg://fuchsia.com/netemul-sandbox-test#meta/default.cmx",
"guest": [
{
"label": "test_guest",
"url": "fuchsia-pkg://fuchsia.com/test_guest#meta/test_guest.cmx",
"files": {
"/pkg/data/test.sh": "/root/test_copy.sh"
},
"macs": {
"01:02:03:04:05:06": "test-net"
}
}
],
"environment": {
"children": [
{
"name": "child-1",
"test": [
{
"arguments": [
"-t",
"1",
"-n",
"child-1-url"
],
"url": "fuchsia-pkg://fuchsia.com/netemul-sandbox-test#meta/env_build_run.cmx"
},
{
"arguments": [
"-t",
"1",
"-n",
"child-1-no-url"
]
}
],
"logger_options": {
"enabled": false,
"filters": null
}
},
{
"inherit_services": false,
"name": "child-2",
"test": [ "fuchsia-pkg://fuchsia.com/some_test#meta/some_test.cmx" ],
"apps" : [ "fuchsia-pkg://fuchsia.com/some_app#meta/some_app.cmx" ],
"logger_options": {
"enabled": true,
"klogs_enabled": true,
"filters": {
"verbosity": 1,
"tags": ["testtag"]
}
}
}
],
"devices": [
"ep0",
"ep1"
],
"name": "root",
"setup": [
{
"url": "fuchsia-pkg://fuchsia.com/some_setup#meta/some_setup.cmx",
"arguments": ["-arg"]
}
],
"services": {
"fuchsia.netstack.Netstack": "fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cmx",
"fuchsia.some.Service" : {
"url" : "fuchsia-pkg://fuchsia.com/some_service#meta/some_service.cmx",
"arguments" : ["-a1", "-a2"]
}
}
},
"networks": [
{
"endpoints": [
{
"mac": "70:00:01:02:03:04",
"mtu": 1000,
"name": "ep0",
"up": false
},
{
"name": "ep1",
"backing": "NETWORK_DEVICE"
}
],
"name": "test-net"
}
]
}
)";
config::Config config;
json::JSONParser parser;
auto doc = parser.ParseFromString(json, "ParseTest");
EXPECT_FALSE(parser.HasError()) << "Parse error: " << parser.error_str();
EXPECT_TRUE(config.ParseFromJSON(doc, &parser)) << "Parse error: " << parser.error_str();
EXPECT_EQ(config.default_url(),
"fuchsia-pkg://fuchsia.com/netemul-sandbox-test#meta/default.cmx");
EXPECT_EQ(config.disabled(), false);
EXPECT_EQ(config.timeout(), zx::duration::infinite());
EXPECT_EQ(config.capture(), CaptureMode::NONE);
// sanity check the objects:
auto& root_env = config.environment();
EXPECT_EQ(root_env.name(), "root");
EXPECT_EQ(root_env.inherit_services(), true);
EXPECT_EQ(root_env.children().size(), 2ul);
EXPECT_EQ(root_env.devices().size(), 2ul);
EXPECT_EQ(root_env.services().size(), 2ul);
EXPECT_TRUE(root_env.test().empty());
EXPECT_TRUE(root_env.apps().empty());
EXPECT_EQ(root_env.setup().size(), 1ul);
// check the guests
EXPECT_EQ(config.guests().size(), 1ul);
EXPECT_EQ(config.guests()[0].guest_label(), "test_guest");
EXPECT_EQ(config.guests()[0].guest_image_url(),
"fuchsia-pkg://fuchsia.com/test_guest#meta/test_guest.cmx");
EXPECT_EQ(config.guests()[0].files().size(), 1ul);
EXPECT_EQ(config.guests()[0].macs().size(), 1ul);
// check the devices
EXPECT_EQ(root_env.devices()[0], "ep0");
EXPECT_EQ(root_env.devices()[1], "ep1");
// check the services
EXPECT_EQ(root_env.services()[0].launch().url(),
"fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cmx");
EXPECT_TRUE(root_env.services()[0].launch().arguments().empty());
EXPECT_EQ(root_env.services()[0].name(), "fuchsia.netstack.Netstack");
EXPECT_EQ(root_env.services()[0].launch().url(),
"fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cmx");
EXPECT_TRUE(root_env.services()[0].launch().arguments().empty());
EXPECT_EQ(root_env.services()[1].name(), "fuchsia.some.Service");
EXPECT_EQ(root_env.services()[1].launch().url(),
"fuchsia-pkg://fuchsia.com/some_service#meta/some_service.cmx");
EXPECT_EQ(root_env.services()[1].launch().arguments().size(), 2ul);
// check the logger_options
EXPECT_TRUE(root_env.logger_options().enabled());
EXPECT_FALSE(root_env.logger_options().klogs_enabled());
EXPECT_EQ(root_env.logger_options().filters().verbosity(), 0);
EXPECT_TRUE(root_env.logger_options().filters().tags().empty());
// check the child environments
auto& c0 = root_env.children()[0];
EXPECT_EQ(c0.name(), "child-1");
EXPECT_EQ(c0.inherit_services(), true);
EXPECT_TRUE(c0.children().empty());
EXPECT_TRUE(c0.devices().empty());
EXPECT_TRUE(c0.services().empty());
EXPECT_EQ(c0.test().size(), 2ul);
EXPECT_TRUE(c0.apps().empty());
EXPECT_TRUE(c0.setup().empty());
auto& c1 = root_env.children()[1];
EXPECT_EQ(c1.name(), "child-2");
EXPECT_EQ(c1.inherit_services(), false);
EXPECT_TRUE(c1.children().empty());
EXPECT_TRUE(c1.devices().empty());
EXPECT_TRUE(c1.services().empty());
EXPECT_TRUE(c1.setup().empty());
EXPECT_EQ(c1.test().size(), 1ul);
EXPECT_EQ(c1.apps().size(), 1ul);
// check test structures:
auto& t0 = c0.test()[0];
auto& t1 = c0.test()[1];
auto& t2 = c1.test()[0];
EXPECT_EQ(t0.url(), "fuchsia-pkg://fuchsia.com/netemul-sandbox-test#meta/env_build_run.cmx");
EXPECT_EQ(t0.arguments().size(), 4ul);
EXPECT_TRUE(t1.url().empty());
EXPECT_EQ(t1.arguments().size(), 4ul);
EXPECT_EQ(t2.url(), "fuchsia-pkg://fuchsia.com/some_test#meta/some_test.cmx");
EXPECT_TRUE(t2.arguments().empty());
// check apps:
auto& app0 = c1.apps()[0];
EXPECT_EQ(app0.url(), "fuchsia-pkg://fuchsia.com/some_app#meta/some_app.cmx");
EXPECT_TRUE(app0.arguments().empty());
// check setup:
auto& setup = root_env.setup()[0];
EXPECT_EQ(setup.url(), "fuchsia-pkg://fuchsia.com/some_setup#meta/some_setup.cmx");
EXPECT_EQ(setup.arguments().size(), 1ul);
EXPECT_EQ(setup.arguments()[0], "-arg");
// check network object:
EXPECT_EQ(config.networks().size(), 1ul);
auto& net = config.networks()[0];
EXPECT_EQ(net.name(), "test-net");
EXPECT_EQ(net.endpoints().size(), 2ul);
// check endpoints:
auto& ep0 = net.endpoints()[0];
auto& ep1 = net.endpoints()[1];
EXPECT_EQ(ep0.name(), "ep0");
EXPECT_EQ(ep0.mtu(), 1000u);
EXPECT_TRUE(ep0.mac());
const uint8_t mac_cmp[] = {0x70, 0x00, 0x01, 0x02, 0x03, 0x04};
EXPECT_EQ(memcmp(mac_cmp, ep0.mac()->octets.data(), sizeof(mac_cmp)), 0);
EXPECT_EQ(ep0.up(), false);
EXPECT_EQ(ep0.backing(), fuchsia::netemul::network::EndpointBacking::ETHERTAP);
EXPECT_EQ(ep1.name(), "ep1");
EXPECT_EQ(ep1.mtu(), 1500u); // default mtu check
EXPECT_FALSE(ep1.mac()); // mac not set
EXPECT_EQ(ep1.up(), true); // default up
EXPECT_EQ(ep1.backing(), fuchsia::netemul::network::EndpointBacking::NETWORK_DEVICE);
// check logger options
EXPECT_FALSE(c0.logger_options().enabled());
EXPECT_FALSE(c0.logger_options().klogs_enabled());
EXPECT_EQ(c0.logger_options().filters().verbosity(), 0);
EXPECT_TRUE(c0.logger_options().filters().tags().empty());
EXPECT_TRUE(c1.logger_options().enabled());
EXPECT_TRUE(c1.logger_options().klogs_enabled());
EXPECT_EQ(c1.logger_options().filters().verbosity(), 1);
EXPECT_EQ(c1.logger_options().filters().tags().size(), 1lu);
EXPECT_EQ(c1.logger_options().filters().tags()[0], "testtag");
}
TEST_F(ModelTest, NetworkNoName) {
const char* json = R"({"networks":[{}]})";
ExpectFailedParse(json, "network without name accepted");
const char* json2 = R"({"networks":[{"name":""}]})";
ExpectFailedParse(json2, "network with empty name accepted");
};
TEST_F(ModelTest, EndpointNoName) {
const char* json = R"({"networks":[{"name":"net","endpoints":[{}]}]})";
ExpectFailedParse(json, "endpoint without name accepted");
const char* json2 = R"({"networks":[{"name":"net","endpoints":[{"name":""}]}]})";
ExpectFailedParse(json2, "endpoint with empty name accepted");
};
TEST_F(ModelTest, EndpointBadMtu) {
const char* json = R"({"networks":[{"name":"net","endpoints":[{"name":"a","mtu":0}]}]})";
ExpectFailedParse(json, "endpoint without 0 mtu accepted");
}
TEST_F(ModelTest, EndpointBadMac) {
const char* json = R"({"networks":[{"name":"net","endpoints":[{"name":"a","mac":"xx:xx:xx"}]}]})";
ExpectFailedParse(json, "endpoint with invalid mac accepted");
}
TEST_F(ModelTest, TestBadUrl) {
const char* json = R"({"environment":{"test":[{"url":"blablabla"}]}})";
ExpectFailedParse(json, "test with bad url accepted");
}
TEST_F(ModelTest, TestBadLoggerOptions) {
const char* json = R"({"environment":{ "logger_options": [] }})";
ExpectFailedParse(json, "test with non object for logger_options accepted");
json = R"({"environment":{ "logger_options": {"enabled": 0 } }})";
ExpectFailedParse(json, "test with non boolean for logger_options.enabled accepted");
json = R"({"environment":{ "logger_options": {"klogs_enabled": 0 } }})";
ExpectFailedParse(json, "test with non boolean for logger_options.klogs_enabled accepted");
}
TEST_F(ModelTest, TestNullLoggerOptions) {
const char* json = R"({"environment": {"logger_options": null}})";
config::Config config;
json::JSONParser parser;
auto doc = parser.ParseFromString(json, "ParseTest");
EXPECT_FALSE(parser.HasError()) << "Parse error: " << parser.error_str();
EXPECT_TRUE(config.ParseFromJSON(doc, &parser)) << "Parse error: " << parser.error_str();
auto& root_env = config.environment();
// check the logger_options defaults with null is supplied to logger_options
EXPECT_EQ(root_env.logger_options().enabled(), true);
EXPECT_EQ(root_env.logger_options().klogs_enabled(), false);
}
TEST_F(ModelTest, TestBadLoggerFilterOptions) {
const char* json = R"({"environment": {"logger_options": {"filters": []}}})";
ExpectFailedParse(json, "test with non object for logger_options.filters accepted");
json = R"({"environment": {"logger_options": {"filters": {"verbosity": true}}}})";
ExpectFailedParse(json, "test with non uint for logger_options.filters.verbosity");
json = R"({"environment": {"logger_options": {"filters": {"tags": {}}}}})";
ExpectFailedParse(json, "test with non array for logger_options.filters.tags");
std::string json_string = R"({"environment": {"logger_options": {"filters": {"tags": [)";
for (int i = 0; i < llcpp::fuchsia::logger::MAX_TAGS + 1; ++i) {
char tag[] = R"(, "a")";
tag[3] += i;
json_string.append(i == 0 ? tag + 2 : tag);
}
json_string.append("]}}}}");
ExpectFailedParse(json_string.c_str(), "test with too many tags for logger_options.filters.tags");
json =
R"({"environment": {"logger_options": {"filters": {"tags": ["sdfkjaskhfgaskjfhASFSADFSAFSADFsdfkjaskhfgaskjfhASFSADFSAFSADFDD"]}}}})";
ExpectFailedParse(json, "test with too long of a tag for logger_options.filters.tags");
}
TEST_F(ModelTest, TestNullLoggerFilterOptions) {
const char* json = R"({"environment": {"logger_options": {"filters": null}}})";
config::Config config;
json::JSONParser parser;
auto doc = parser.ParseFromString(json, "ParseTest");
EXPECT_FALSE(parser.HasError()) << "Parse error: " << parser.error_str();
EXPECT_TRUE(config.ParseFromJSON(doc, &parser)) << "Parse error: " << parser.error_str();
auto& root_env = config.environment();
// check the logger_options defaults with null is supplied to logger_options
EXPECT_EQ(root_env.logger_options().filters().verbosity(), 0);
EXPECT_TRUE(root_env.logger_options().filters().tags().empty());
}
TEST_F(ModelTest, ServiceBadUrl) {
const char* json = R"({"environment":{"services":{"some.service":"blablabla"}}})";
ExpectFailedParse(json, "service with bad url accepted");
}
TEST_F(ModelTest, LaunchAppGetOrDefault) {
const char* json1 = R"({"url":"fuchsia-pkg://fuchsia.com/some_url#meta/some_url.cmx"})";
json::JSONParser parser;
auto doc1 = parser.ParseFromString(json1, "LaunchApGetOrDefault");
config::LaunchApp app1;
EXPECT_FALSE(parser.HasError()) << "Parse error: " << parser.error_str();
EXPECT_TRUE(app1.ParseFromJSON(doc1, &parser)) << "Parse error: " << parser.error_str();
const char* json2 = R"({"url":""})";
auto doc2 = parser.ParseFromString(json2, "LaunchApGetOrDefault");
config::LaunchApp app2;
EXPECT_FALSE(parser.HasError()) << "Parse error: " << parser.error_str();
EXPECT_TRUE(app2.ParseFromJSON(doc2, &parser)) << "Parse error: " << parser.error_str();
const char* fallback = "fuchsia-pkg://fuchsia.com/fallback#meta/fallback.cmx";
EXPECT_EQ(app1.GetUrlOrDefault(fallback), "fuchsia-pkg://fuchsia.com/some_url#meta/some_url.cmx");
EXPECT_EQ(app2.GetUrlOrDefault(fallback), fallback);
}
TEST_F(ModelTest, TimeoutParsing) {
const char* jsonbad = R"({"timeout": -10})";
ExpectFailedParse(jsonbad, "negative timeout value accepted");
const char* jsongood = R"({"timeout": 10})";
json::JSONParser parser;
auto doc = parser.ParseFromString(jsongood, "Good timeout JSON");
config::Config config;
EXPECT_FALSE(parser.HasError()) << "Parse error: " << parser.error_str();
EXPECT_TRUE(config.ParseFromJSON(doc, &parser)) << "Parse error: " << parser.error_str();
EXPECT_EQ(config.timeout(), zx::sec(10));
}
TEST_F(ModelTest, CaptureParsing) {
Config config;
ExpectSuccessfulParse(R"({"capture" : true})", &config);
EXPECT_EQ(config.capture(), CaptureMode::ON_ERROR);
ExpectSuccessfulParse(R"({"capture" : false})", &config);
EXPECT_EQ(config.capture(), CaptureMode::NONE);
ExpectSuccessfulParse(R"({"capture" : "NO"})", &config);
EXPECT_EQ(config.capture(), CaptureMode::NONE);
ExpectSuccessfulParse(R"({"capture" : "ON_ERROR"})", &config);
EXPECT_EQ(config.capture(), CaptureMode::ON_ERROR);
ExpectSuccessfulParse(R"({"capture" : "ALWAYS"})", &config);
EXPECT_EQ(config.capture(), CaptureMode::ALWAYS);
ExpectFailedParse(R"({"capture" : "foo"})", "Can't parse bad capture value");
}
TEST_F(ModelTest, InvalidKeys) {
ExpectFailedParse(R"({ "foo" : "bar" })", "Bad config key accepted");
ExpectFailedParse(R"({ "environment" : {"foo" : "bar"} })", "Bad environment key accepted");
ExpectFailedParse(R"({ "networks" : [{"name" : "net", "foo" : "bar"}] })",
"Bad network key accepted");
ExpectFailedParse(R"({ "environment" : { "setup": [{"foo" : "bar"}] } })",
"Bad launch_app key accepted");
ExpectFailedParse(
R"({ "networks" : [ {"name" : "net", "endpoints" : [{"name" : "ep", "foo" : "bar"}] }] })",
"Bad endpoint key accepted");
}
TEST_F(ModelTest, InvalidGuestConfig) {
ExpectFailedParse(R"({"guest" : {}})", "Guest model accepted non-array guest config");
ExpectFailedParse(R"({"guest" : [{"files" : ["file1", "file2"]}]})",
"Guest model accepted non-object file definitions");
ExpectFailedParse(R"({"guest" : [{"bogus_key" : []}]})",
"Guest model accepted too many ethertap networks");
ExpectFailedParse(R"({"guest" : [{"macs": []}]})",
"Guest model accepted non-object macs definition");
ExpectFailedParse(R"({"guest" : [{"macs": {"00:11:22:33:44:55": {}}}]})",
"Guest model accepted non-string mac mapping");
}
} // namespace testing
} // namespace config
} // namespace netemul