| // 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 <zircon/syscalls.h> |
| #include <zircon/device/device.h> |
| #include <zircon/device/test.h> |
| #include <unittest/unittest.h> |
| |
| #define DRIVER_TEST_DIR "/boot/driver/test" |
| |
| static void do_one_test(int 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, 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; |
| } |
| |
| // TODO some waiting needed before opening.., |
| usleep(1000); |
| |
| int fd; |
| int retry = 0; |
| do { |
| fd = open(devpath, O_RDWR); |
| if (fd >= 0) { |
| 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; |
| goto end_device_created; |
| } |
| |
| char libpath[PATH_MAX]; |
| int n = snprintf(libpath, sizeof(libpath), "%s/%s", DRIVER_TEST_DIR, drv_libname); |
| rc = ioctl_device_bind(fd, libpath, n); |
| if (rc < 0) { |
| printf("driver-tests: error %zd binding to %s\n", rc, libpath); |
| report->n_tests = 1; |
| report->n_failed = 1; |
| goto end_device_opened; |
| } |
| |
| 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; |
| goto end_device_opened; |
| } |
| |
| ioctl_test_set_output_socket(fd, &h); |
| |
| rc = ioctl_test_run_tests(fd, NULL, 0, report); |
| if (rc < 0) { |
| printf("driver-tests: error %zd running tests\n", rc); |
| report->n_tests = 1; |
| report->n_failed = 1; |
| } |
| |
| end_device_created: |
| ioctl_test_destroy_device(fd); |
| end_device_opened: |
| close(fd); |
| } |
| |
| static 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; |
| } |
| |
| int main(int argc, char** argv) { |
| zx_handle_t socket[2]; |
| zx_status_t status = zx_socket_create(0u, socket, socket + 1); |
| if (status != ZX_OK) { |
| printf("driver-tests: error creating socket\n"); |
| return -1; |
| } |
| |
| int fd = open(TEST_CONTROL_DEVICE, O_RDWR); |
| if (fd < 0) { |
| printf("driver-tests: no %s device found\n", TEST_CONTROL_DEVICE); |
| 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); |
| close(fd); |
| 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; |
| } |
| test_ioctl_test_report_t one_report; |
| memset(&one_report, 0, sizeof(one_report)); |
| do_one_test(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(fd); |
| |
| // 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; |
| } |