blob: b9a146a3a2f939d9159b10c34576ace2c4caa407 [file] [log] [blame]
// Copyright 2017 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <threads.h>
#include <fbl/unique_fd.h>
#include <fbl/unique_ptr.h>
#include <lib/devmgr-integration-test/fixture.h>
#include <lib/zx/time.h>
#include <zircon/syscalls.h>
#include <zircon/device/device.h>
#include <zircon/device/test.h>
#include <unittest/unittest.h>
#define DRIVER_TEST_DIR "/boot/driver/test"
using devmgr_integration_test::IsolatedDevmgr;
namespace {
void do_one_test(const fbl::unique_ptr<IsolatedDevmgr>& devmgr, const fbl::unique_fd& tfd,
const char* drv_libname, zx_handle_t output, test_ioctl_test_report_t* report) {
char devpath[1024];
ssize_t rc = ioctl_test_create_device(tfd.get(), drv_libname, strlen(drv_libname) + 1, devpath,
sizeof(devpath));
if (rc < 0) {
printf("driver-tests: error %zd creating device for %s\n", rc, drv_libname);
report->n_tests = 1;
report->n_failed = 1;
return;
}
const char* kDevPrefix = "/dev/";
if (strncmp(devpath, kDevPrefix, strlen(kDevPrefix))) {
printf("driver-tests: bad path when creating device for %s: %s\n", drv_libname, devpath);
report->n_tests = 1;
report->n_failed = 1;
return;
}
const char* relative_devpath = devpath + strlen(kDevPrefix);
// TODO some waiting needed before opening..,
usleep(1000);
fbl::unique_fd fd;
int retry = 0;
do {
fd.reset(openat(devmgr->devfs_root().get(), relative_devpath, O_RDWR));
if (fd.is_valid()) {
break;
}
usleep(1000);
} while (++retry < 100);
if (retry == 100) {
printf("driver-tests: failed to open %s\n", devpath);
report->n_tests = 1;
report->n_failed = 1;
ioctl_test_destroy_device(fd.get());
return;
}
char libpath[PATH_MAX];
int n = snprintf(libpath, sizeof(libpath), "%s/%s", DRIVER_TEST_DIR, drv_libname);
rc = ioctl_device_bind(fd.get(), libpath, n);
if (rc < 0) {
printf("driver-tests: error %zd binding to %s\n", rc, libpath);
report->n_tests = 1;
report->n_failed = 1;
// TODO(teisenbe): I think ioctl_test_destroy_device() should be called
// here?
return;
}
zx_handle_t h;
zx_status_t status = zx_handle_duplicate(output, ZX_RIGHT_SAME_RIGHTS, &h);
if (status != ZX_OK) {
printf("driver-tests: error %d duplicating output socket\n", status);
report->n_tests = 1;
report->n_failed = 1;
// TODO(teisenbe): I think ioctl_test_destroy_device() should be called
// here?
return;
}
ioctl_test_set_output_socket(fd.get(), &h);
rc = ioctl_test_run_tests(fd.get(), report);
if (rc < 0) {
printf("driver-tests: error %zd running tests\n", rc);
report->n_tests = 1;
report->n_failed = 1;
}
ioctl_test_destroy_device(fd.get());
}
int output_thread(void* arg) {
zx_handle_t h = *(zx_handle_t*)arg;
char buf[1024];
for (;;) {
zx_status_t status = zx_object_wait_one(h, ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED, ZX_TIME_INFINITE, NULL);
if (status != ZX_OK) {
break;
}
size_t bytes = 0;
status = zx_socket_read(h, 0u, buf, sizeof(buf), &bytes);
if (status != ZX_OK) {
break;
}
size_t written = 0;
while (written < bytes) {
ssize_t rc = write(2, buf + written, bytes - written);
if (rc < 0) {
break;
}
written += rc;
}
}
return 0;
}
} // namespace
int main(int argc, char** argv) {
auto args = IsolatedDevmgr::DefaultArgs();
fbl::unique_ptr<IsolatedDevmgr> devmgr;
zx_status_t status = IsolatedDevmgr::Create(args, &devmgr);
if (status != ZX_OK) {
printf("driver-tests: failed to create isolated devmgr\n");
return -1;
}
zx_handle_t socket[2];
status = zx_socket_create(0u, socket, socket + 1);
if (status != ZX_OK) {
printf("driver-tests: error creating socket\n");
return -1;
}
// Wait for /dev/test/test to appear
fbl::unique_fd fd;
status = devmgr_integration_test::RecursiveWaitForFile(devmgr->devfs_root(), "test/test",
zx::deadline_after(zx::sec(5)), &fd);
if (status != ZX_OK) {
printf("driver-tests: failed to find /dev/test/test\n");
return -1;
}
thrd_t t;
int rc = thrd_create_with_name(&t, output_thread, socket, "driver-test-output");
if (rc != thrd_success) {
printf("driver-tests: error %d creating output thread\n", rc);
zx_handle_close(socket[0]);
zx_handle_close(socket[1]);
return -1;
}
test_ioctl_test_report_t final_report;
memset(&final_report, 0, sizeof(final_report));
DIR* dir = opendir(DRIVER_TEST_DIR);
if (dir == NULL) {
printf("driver-tests: failed to open %s\n", DRIVER_TEST_DIR);
return -1;
}
int dfd = dirfd(dir);
if (dfd < 0) {
printf("driver-tests: failed to get fd for %s\n", DRIVER_TEST_DIR);
return -1;
}
struct dirent* de;
// bind test drivers
while ((de = readdir(dir)) != NULL) {
if ((strcmp(de->d_name, ".") == 0) || (strcmp(de->d_name, "..") == 0)) {
continue;
}
// Don't try to bind the fake sysdev
if (strcmp(de->d_name, "sysdev.so") == 0) {
continue;
}
test_ioctl_test_report_t one_report;
memset(&one_report, 0, sizeof(one_report));
do_one_test(devmgr, fd, de->d_name, socket[1], &one_report);
final_report.n_tests += one_report.n_tests;
final_report.n_success += one_report.n_success;
final_report.n_failed += one_report.n_failed;
}
// close this handle before thrd_join to get PEER_CLOSED in output thread
zx_handle_close(socket[1]);
thrd_join(t, NULL);
zx_handle_close(socket[0]);
unittest_printf_critical(
"\n====================================================\n");
unittest_printf_critical(
" CASES: %d SUCCESS: %d FAILED: %d ",
final_report.n_tests, final_report.n_success, final_report.n_failed);
unittest_printf_critical(
"\n====================================================\n");
return final_report.n_failed == 0 ? 0 : -1;
}