blob: 8e057745f9ab40af93131efd1884728aa71a24e9 [file] [log] [blame]
// Copyright 2016 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 <fcntl.h>
#include <lib/device-watcher/cpp/device-watcher.h>
#include <lib/zx/clock.h>
#include <fbl/string.h>
#include <fbl/unique_fd.h>
#include <fbl/vector.h>
#include <runtests-utils/fuchsia-run-test.h>
#include <runtests-utils/log-exporter.h>
#include <runtests-utils/runtests-utils.h>
namespace {
// The name of the file containing the syslog.
constexpr char kSyslogFileName[] = "syslog.txt";
const char* kDefaultTestDirs[] = {
// zircon builds place everything in ramdisks so tests are located in /boot
"/boot/test", "/boot/test/c", "/boot/test/core", "/boot/test/libc",
"/boot/test/ddk", "/boot/test/sys", "/boot/test/fs", "/boot/test/storage",
};
class FuchsiaStopwatch final : public runtests::Stopwatch {
public:
FuchsiaStopwatch() { Start(); }
void Start() override { start_time_ = Now(); }
int64_t DurationInMsecs() override { return (Now() - start_time_).to_msecs(); }
private:
static zx::time Now() { return zx::clock::get_monotonic(); }
zx::time start_time_;
};
// Parse |argv| for an output directory argument.
const char* GetOutputDir(int argc, const char* const* argv) {
int i = 1;
while (i < argc - 1 && (strcmp(argv[i], "--output") != 0 && strcmp(argv[i], "-o") != 0)) {
++i;
}
if (i >= argc - 1) {
return nullptr;
}
return argv[i + 1];
}
} // namespace
int main(int argc, char** argv) {
const char* output_dir = GetOutputDir(argc, argv);
// Start Log Listener.
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
std::unique_ptr<runtests::LogExporter> log_exporter_ptr;
if (output_dir != nullptr) {
int error = runtests::MkDirAll(output_dir);
if (error) {
printf("Error: Could not create output directory: %s, %s\n", output_dir, strerror(error));
return -1;
}
runtests::ExporterLaunchError exporter_error;
log_exporter_ptr = runtests::LaunchLogExporter(
loop.dispatcher(), runtests::JoinPath(output_dir, kSyslogFileName), &exporter_error);
// Don't fail if logger service is not available because it is only
// available in core and above.
if (!log_exporter_ptr &&
exporter_error != runtests::ExporterLaunchError::CONNECT_TO_LOGGER_SERVICE) {
printf("Error: Failed to launch log listener: %d\n", static_cast<int>(exporter_error));
return -1;
}
if (zx_status_t status = loop.StartThread(); status != ZX_OK) {
printf("Error: Failed to start log exporter: %d (%s).\n", static_cast<int>(status),
zx_status_get_string(status));
return -1;
}
}
fbl::Vector<fbl::String> default_test_dirs;
for (auto& kDefaultTestDir : kDefaultTestDirs) {
default_test_dirs.push_back(kDefaultTestDir);
}
// runtests is used to run tests in bringup configurations. Because the
// console shell dynamically mounts the "/dev" directory during system
// startup, we need to wait for "/dev" to appear, before running any tests
// which may depend on nodes inside "/dev".
//
// We are able to open the namespace root because the shell has a single
// namespace entry at "/". See /src/bringup/bin/console-launcher/main.cc.
fbl::unique_fd root(open("/", O_RDONLY | O_DIRECTORY));
ZX_ASSERT_MSG(root.is_valid(),
"runtests must be given \"/\" as the sole entry "
"in its incoming namespace");
zx::result channel = device_watcher::RecursiveWaitForFile(root.get(), "dev");
if (channel.is_error()) {
printf("Error: Failed to wait for /dev to appear: %s", channel.status_string());
return -1;
}
FuchsiaStopwatch stopwatch;
return runtests::DiscoverAndRunTests(argc, argv, default_test_dirs, &stopwatch, kSyslogFileName);
}