blob: 4eb5fc7c962667a1d4e1f7718d21443e86e134a6 [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 "garnet/bin/guest/vmm/guest_config.h"
#include <zircon/compiler.h>
#include <zircon/syscalls.h>
#include "gtest/gtest.h"
namespace guest {
namespace {
#define TEST_GUID_STRING "14db42cf-beb7-46a2-9ef8-89b13bb80528"
static constexpr uint8_t TEST_GUID_VALUE[] = {
// clang-format off
0xcf, 0x42, 0xdb, 0x14,
0xb7, 0xbe,
0xa2, 0x46,
0x9e, 0xf8, 0x89, 0xb1, 0x3b, 0xb8, 0x05, 0x28
// clang-format on
};
TEST(GuestConfigParserTest, DefaultValues) {
GuestConfig config;
GuestConfigParser parser(&config);
parser.ParseConfig("{}");
ASSERT_EQ(Kernel::ZIRCON, config.kernel());
ASSERT_TRUE(config.kernel_path().empty());
ASSERT_TRUE(config.ramdisk_path().empty());
ASSERT_EQ(zx_system_get_num_cpus(), config.num_cpus());
ASSERT_TRUE(config.block_devices().empty());
ASSERT_TRUE(config.cmdline().empty());
ASSERT_FALSE(config.balloon_demand_page());
ASSERT_FALSE(config.block_wait());
}
TEST(GuestConfigParserTest, ParseConfig) {
GuestConfig config;
GuestConfigParser parser(&config);
ASSERT_EQ(ZX_OK, parser.ParseConfig(
R"JSON({
"zircon": "zircon_path",
"ramdisk": "ramdisk_path",
"cpus": "4",
"block": "/pkg/data/block_path",
"cmdline": "kernel cmdline",
"balloon-demand-page": "true",
"block-wait": "true"
})JSON"));
ASSERT_EQ(Kernel::ZIRCON, config.kernel());
ASSERT_EQ("zircon_path", config.kernel_path());
ASSERT_EQ("ramdisk_path", config.ramdisk_path());
ASSERT_EQ(4, config.num_cpus());
ASSERT_EQ(1, config.block_devices().size());
ASSERT_EQ("/pkg/data/block_path", config.block_devices()[0].path);
ASSERT_EQ("kernel cmdline", config.cmdline());
ASSERT_TRUE(config.balloon_demand_page());
ASSERT_TRUE(config.block_wait());
}
TEST(GuestConfigParserTest, ParseArgs) {
GuestConfig config;
GuestConfigParser parser(&config);
const char* argv[] = {"exe_name",
"--linux=linux_path",
"--ramdisk=ramdisk_path",
"--cpus=4",
"--block=/pkg/data/block_path",
"--cmdline=kernel_cmdline",
"--balloon-demand-page",
"--block-wait"};
ASSERT_EQ(ZX_OK,
parser.ParseArgcArgv(countof(argv), const_cast<char**>(argv)));
ASSERT_EQ(Kernel::LINUX, config.kernel());
ASSERT_EQ("linux_path", config.kernel_path());
ASSERT_EQ("ramdisk_path", config.ramdisk_path());
ASSERT_EQ(4, config.num_cpus());
ASSERT_EQ(1, config.block_devices().size());
ASSERT_EQ("/pkg/data/block_path", config.block_devices()[0].path);
ASSERT_EQ("kernel_cmdline", config.cmdline());
ASSERT_TRUE(config.balloon_demand_page());
ASSERT_TRUE(config.block_wait());
}
TEST(GuestConfigParserTest, UnknownArgument) {
GuestConfig config;
GuestConfigParser parser(&config);
const char* argv[] = {"exe_name", "--invalid-arg"};
ASSERT_EQ(ZX_ERR_INVALID_ARGS,
parser.ParseArgcArgv(countof(argv), const_cast<char**>(argv)));
}
TEST(GuestConfigParserTest, BooleanFlag) {
GuestConfig config;
GuestConfigParser parser(&config);
const char* argv_false[] = {"exe_name", "--balloon-demand-page=false"};
ASSERT_EQ(ZX_OK, parser.ParseArgcArgv(countof(argv_false),
const_cast<char**>(argv_false)));
ASSERT_FALSE(config.balloon_demand_page());
const char* argv_true[] = {"exe_name", "--balloon-demand-page=true"};
ASSERT_EQ(ZX_OK, parser.ParseArgcArgv(countof(argv_true),
const_cast<char**>(argv_true)));
ASSERT_TRUE(config.balloon_demand_page());
}
TEST(GuestConfigParserTest, CommandLineAppend) {
GuestConfig config;
GuestConfigParser parser(&config);
const char* argv[] = {"exe_name", "--cmdline=foo bar",
"--cmdline-append=baz"};
ASSERT_EQ(ZX_OK,
parser.ParseArgcArgv(countof(argv), const_cast<char**>(argv)));
ASSERT_EQ("foo bar baz", config.cmdline());
}
TEST(GuestConfigParserTest, BlockSpecArg) {
GuestConfig config;
GuestConfigParser parser(&config);
const char* argv[] = {"exe_name", "--block=/pkg/data/foo,ro,fdio",
"--block=/dev/class/block/001,rw,fdio",
"--block=guid:" TEST_GUID_STRING ",rw,fdio",
"--block=type-guid:" TEST_GUID_STRING ",ro,fdio"};
ASSERT_EQ(ZX_OK,
parser.ParseArgcArgv(countof(argv), const_cast<char**>(argv)));
ASSERT_EQ(4, config.block_devices().size());
const BlockSpec& spec0 = config.block_devices()[0];
ASSERT_EQ(fuchsia::guest::device::BlockMode::READ_ONLY, spec0.mode);
ASSERT_EQ(fuchsia::guest::device::BlockFormat::RAW, spec0.format);
ASSERT_EQ("/pkg/data/foo", spec0.path);
ASSERT_TRUE(spec0.guid.empty());
const BlockSpec& spec1 = config.block_devices()[1];
ASSERT_EQ(fuchsia::guest::device::BlockMode::READ_WRITE, spec1.mode);
ASSERT_EQ(fuchsia::guest::device::BlockFormat::RAW, spec1.format);
ASSERT_EQ("/dev/class/block/001", spec1.path);
ASSERT_TRUE(spec1.guid.empty());
const BlockSpec& spec2 = config.block_devices()[2];
ASSERT_EQ(fuchsia::guest::device::BlockMode::READ_WRITE, spec2.mode);
ASSERT_EQ(fuchsia::guest::device::BlockFormat::RAW, spec2.format);
ASSERT_TRUE(spec2.path.empty());
ASSERT_EQ(Guid::Type::GPT_PARTITION, spec2.guid.type);
ASSERT_EQ(0, memcmp(spec2.guid.bytes, TEST_GUID_VALUE, GUID_LEN));
const BlockSpec& spec3 = config.block_devices()[3];
ASSERT_EQ(fuchsia::guest::device::BlockMode::READ_ONLY, spec3.mode);
ASSERT_EQ(fuchsia::guest::device::BlockFormat::RAW, spec3.format);
ASSERT_TRUE(spec3.path.empty());
ASSERT_EQ(Guid::Type::GPT_PARTITION_TYPE, spec3.guid.type);
ASSERT_EQ(0, memcmp(spec3.guid.bytes, TEST_GUID_VALUE, GUID_LEN));
}
TEST(GuestConfigParserTest, BlockSpecJson) {
GuestConfig config;
GuestConfigParser parser(&config);
ASSERT_EQ(ZX_OK, parser.ParseConfig(
R"JSON({
"block": [
"/pkg/data/foo,ro,fdio",
"/dev/class/block/001,rw,fdio",
"guid:)JSON" TEST_GUID_STRING R"JSON(,rw,fdio",
"type-guid:)JSON" TEST_GUID_STRING R"JSON(,ro,fdio"
]
})JSON"));
ASSERT_EQ(4, config.block_devices().size());
const BlockSpec& spec0 = config.block_devices()[0];
ASSERT_EQ(fuchsia::guest::device::BlockMode::READ_ONLY, spec0.mode);
ASSERT_EQ(fuchsia::guest::device::BlockFormat::RAW, spec0.format);
ASSERT_EQ("/pkg/data/foo", spec0.path);
const BlockSpec& spec1 = config.block_devices()[1];
ASSERT_EQ(fuchsia::guest::device::BlockMode::READ_WRITE, spec1.mode);
ASSERT_EQ(fuchsia::guest::device::BlockFormat::RAW, spec1.format);
ASSERT_EQ("/dev/class/block/001", spec1.path);
const BlockSpec& spec2 = config.block_devices()[2];
ASSERT_EQ(fuchsia::guest::device::BlockMode::READ_WRITE, spec2.mode);
ASSERT_EQ(fuchsia::guest::device::BlockFormat::RAW, spec2.format);
ASSERT_TRUE(spec2.path.empty());
ASSERT_EQ(Guid::Type::GPT_PARTITION, spec2.guid.type);
ASSERT_EQ(0, memcmp(spec2.guid.bytes, TEST_GUID_VALUE, GUID_LEN));
const BlockSpec& spec3 = config.block_devices()[3];
ASSERT_EQ(fuchsia::guest::device::BlockMode::READ_ONLY, spec3.mode);
ASSERT_EQ(fuchsia::guest::device::BlockFormat::RAW, spec3.format);
ASSERT_TRUE(spec3.path.empty());
ASSERT_EQ(Guid::Type::GPT_PARTITION_TYPE, spec3.guid.type);
ASSERT_EQ(0, memcmp(spec3.guid.bytes, TEST_GUID_VALUE, GUID_LEN));
}
#define TEST_PARSE_GUID(name, guid, result) \
TEST(GuestConfigParserTest, GuidTest##name) { \
GuestConfig config; \
GuestConfigParser parser(&config); \
\
const char* argv[] = {"exe_name", "--block=guid:" guid}; \
ASSERT_EQ((result), \
parser.ParseArgcArgv(countof(argv), const_cast<char**>(argv))); \
}
TEST_PARSE_GUID(LowerCase, "14db42cf-beb7-46a2-9ef8-89b13bb80528", ZX_OK);
TEST_PARSE_GUID(UpperCase, "14DB42CF-BEB7-46A2-9EF8-89B13BB80528", ZX_OK);
TEST_PARSE_GUID(MixedCase, "14DB42CF-BEB7-46A2-9ef8-89b13bb80528", ZX_OK);
TEST_PARSE_GUID(MissingDelimeters, "14db42cfbeb746a29ef889b13bb80528",
ZX_ERR_INVALID_ARGS);
TEST_PARSE_GUID(ExtraDelimeters, "14-db-42cf-beb7-46-a2-9ef8-89b13bb80528",
ZX_ERR_INVALID_ARGS);
TEST_PARSE_GUID(
TooLong,
"14db42cf-beb7-46a2-9ef8-89b13bb80528-14db42cf-beb7-46a2-9ef8-"
"89b13bb80528-14db42cf-beb7-46a2-9ef8-89b13bb80528-14db42cf-beb7-"
"46a2-9ef8-89b13bb80528-14db42cf-beb7-46a2-9ef8-89b13bb80528",
ZX_ERR_INVALID_ARGS);
TEST_PARSE_GUID(TooShort, "14db42cf", ZX_ERR_INVALID_ARGS);
TEST_PARSE_GUID(IllegalCharacters, "abcdefgh-ijkl-mnop-qrst-uvwxyz!@#$%^",
ZX_ERR_INVALID_ARGS);
#define TEST_PARSE_MEM_SIZE(string, result) \
TEST(GuestConfigParserTest, MemSizeTest_##string) { \
GuestConfig config; \
GuestConfigParser parser(&config); \
\
const char* argv[] = {"exe_name", "--memory=" #string}; \
ASSERT_EQ(ZX_OK, \
parser.ParseArgcArgv(countof(argv), const_cast<char**>(argv))); \
ASSERT_EQ((result), config.memory()); \
}
TEST_PARSE_MEM_SIZE(1024k, 1u << 20);
TEST_PARSE_MEM_SIZE(2M, 2ul << 20);
TEST_PARSE_MEM_SIZE(4G, 4ul << 30);
#define TEST_PARSE_MEM_SIZE_ERROR(name, string) \
TEST(GuestConfigParserTest, MemSizeTest_##name) { \
GuestConfig config; \
GuestConfigParser parser(&config); \
\
const char* argv[] = {"exe_name", "--memory=" #string}; \
ASSERT_EQ(ZX_ERR_INVALID_ARGS, \
parser.ParseArgcArgv(countof(argv), const_cast<char**>(argv))); \
}
TEST_PARSE_MEM_SIZE_ERROR(TooSmall, 1024);
TEST_PARSE_MEM_SIZE_ERROR(IllegalModifier, 5l);
TEST_PARSE_MEM_SIZE_ERROR(NonNumber, abc);
TEST(GuestConfigParserTest, DisplayType) {
GuestConfig config;
GuestConfigParser parser(&config);
const char* display_scenic_argv[] = {"exe_name", "--display=scenic"};
ASSERT_EQ(ZX_OK,
parser.ParseArgcArgv(countof(display_scenic_argv),
const_cast<char**>(display_scenic_argv)));
ASSERT_EQ(GuestDisplay::SCENIC, config.display());
const char* display_none_argv[] = {"exe_name", "--display=none"};
ASSERT_EQ(ZX_OK, parser.ParseArgcArgv(countof(display_none_argv),
const_cast<char**>(display_none_argv)));
ASSERT_EQ(GuestDisplay::NONE, config.display());
}
} // namespace
} // namespace guest