blob: 4286fea036db27fcca3e3a9d41e511855520f145 [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/bringup/bin/netsvc/args.h"
#include <fidl/fuchsia.boot/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/loop.h>
#include <lib/async/cpp/task.h>
#include <lib/async/dispatcher.h>
#include <lib/component/incoming/cpp/protocol.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/fdio/spawn.h>
#include <lib/fit/defer.h>
#include <lib/fit/function.h>
#include <lib/sync/completion.h>
#include <lib/zx/process.h>
#include <mock-boot-arguments/server.h>
#include <zxtest/zxtest.h>
namespace {
constexpr char kInterface[] = "/dev/whatever/whatever";
TEST(ArgsTest, NetsvcNodenamePrintsAndExits) {
const std::string path = "/pkg/bin/netsvc";
const char* argv[] = {path.c_str(), "--nodename", nullptr};
zx::process process;
char err_msg[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
ASSERT_OK(fdio_spawn_etc(ZX_HANDLE_INVALID, FDIO_SPAWN_CLONE_ALL, argv[0], argv, nullptr, 0,
nullptr, process.reset_and_get_address(), err_msg),
"%s", err_msg);
ASSERT_OK(process.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), nullptr));
zx_info_process_t proc_info;
ASSERT_OK(process.get_info(ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr));
ASSERT_EQ(proc_info.return_code, 0);
}
class FakeSvc {
public:
explicit FakeSvc(async_dispatcher_t* dispatcher) {
zx::result server_end = fidl::CreateEndpoints(&root_);
ASSERT_OK(server_end);
async::PostTask(dispatcher, [dispatcher, &mock_boot = mock_boot_,
server_end = std::move(server_end.value())]() mutable {
component::OutgoingDirectory outgoing{dispatcher};
ASSERT_OK(outgoing.AddUnmanagedProtocol<fuchsia_boot::Arguments>(
[&mock_boot, dispatcher](fidl::ServerEnd<fuchsia_boot::Arguments> server_end) {
fidl::BindServer(dispatcher, std::move(server_end), &mock_boot);
}));
zx::result result = outgoing.Serve(std::move(server_end));
ASSERT_OK(result);
// Stash the outgoing directory on the dispatcher so that the dtor runs on the dispatcher
// thread.
async::PostDelayedTask(
dispatcher, [outgoing = std::move(outgoing)]() {}, zx::duration::infinite());
});
}
mock_boot_arguments::Server& mock_boot() { return mock_boot_; }
zx::result<fidl::ClientEnd<fuchsia_io::Directory>> svc() {
return component::ConnectAt<fuchsia_io::Directory>(
root_, component::OutgoingDirectory::kServiceDirectory);
}
private:
mock_boot_arguments::Server mock_boot_;
fidl::ClientEnd<fuchsia_io::Directory> root_;
};
class ArgsTest : public zxtest::Test {
public:
ArgsTest() : loop_(&kAsyncLoopConfigNeverAttachToThread), fake_svc_(loop_.dispatcher()) {}
FakeSvc& fake_svc() { return fake_svc_; }
zx::result<fidl::ClientEnd<fuchsia_io::Directory>> svc_root() { return fake_svc_.svc(); }
async::Loop& loop() { return loop_; }
private:
async::Loop loop_;
FakeSvc fake_svc_;
};
TEST_F(ArgsTest, NetsvcNoneProvided) {
int argc = 1;
const char* argv[] = {"netsvc"};
const char* error = nullptr;
NetsvcArgs args;
zx::result svc = svc_root();
ASSERT_OK(svc);
{
ASSERT_OK(loop().StartThread());
auto cleanup = fit::defer(fit::bind_member<&async::Loop::Shutdown>(&loop()));
ASSERT_EQ(ParseArgs(argc, const_cast<char**>(argv), svc.value(), &error, &args), 0, "%s",
error);
}
ASSERT_FALSE(args.netboot);
ASSERT_FALSE(args.print_nodename_and_exit);
ASSERT_TRUE(args.advertise);
ASSERT_FALSE(args.all_features);
ASSERT_TRUE(args.interface.empty());
ASSERT_EQ(error, nullptr);
}
TEST_F(ArgsTest, NetsvcAllProvided) {
int argc = 7;
const char* argv[] = {
"netsvc", "--netboot", "--nodename", "--advertise",
"--all-features", "--interface", kInterface,
};
const char* error = nullptr;
NetsvcArgs args;
zx::result svc = svc_root();
ASSERT_OK(svc);
{
ASSERT_OK(loop().StartThread());
auto cleanup = fit::defer(fit::bind_member<&async::Loop::Shutdown>(&loop()));
ASSERT_EQ(ParseArgs(argc, const_cast<char**>(argv), svc.value(), &error, &args), 0, "%s",
error);
}
ASSERT_TRUE(args.netboot);
ASSERT_TRUE(args.print_nodename_and_exit);
ASSERT_TRUE(args.advertise);
ASSERT_TRUE(args.all_features);
ASSERT_EQ(args.interface, std::string(kInterface));
ASSERT_EQ(error, nullptr);
}
TEST_F(ArgsTest, NetsvcValidation) {
int argc = 2;
const char* argv[] = {
"netsvc",
"--interface",
};
const char* error = nullptr;
NetsvcArgs args;
zx::result svc = svc_root();
ASSERT_OK(svc);
{
ASSERT_OK(loop().StartThread());
auto cleanup = fit::defer(fit::bind_member<&async::Loop::Shutdown>(&loop()));
ASSERT_LT(ParseArgs(argc, const_cast<char**>(argv), svc.value(), &error, &args), 0);
}
ASSERT_TRUE(args.interface.empty());
ASSERT_TRUE(strstr(error, "interface"));
}
TEST_F(ArgsTest, LogPackets) {
int argc = 2;
const char* argv[] = {
"netsvc",
"--log-packets",
};
NetsvcArgs args;
EXPECT_FALSE(args.log_packets);
const char* error = nullptr;
zx::result svc = svc_root();
ASSERT_OK(svc);
{
ASSERT_OK(loop().StartThread());
auto cleanup = fit::defer(fit::bind_member<&async::Loop::Shutdown>(&loop()));
ASSERT_EQ(ParseArgs(argc, const_cast<char**>(argv), svc.value(), &error, &args), 0, "%s",
error);
}
EXPECT_TRUE(args.log_packets);
}
} // namespace