/* -*- Mode: C; indent-tabs-mode:nil -*- */ /* * Unit tests for libusb_set_option * Copyright © 2023 Nathan Hjelm * Copyright © 2023 Google, LLC. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include "libusbi.h" #include "libusb_testlib.h" #if defined(_WIN32) && !defined(__CYGWIN__) #include static int unsetenv(const char *env) { return _putenv_s(env, ""); } static int setenv(const char *env, const char *value, int overwrite) { if (getenv(env) && !overwrite) return 0; return _putenv_s(env, value); } #endif #define LIBUSB_TEST_CLEAN_EXIT(code) \ do { \ if (test_ctx != NULL) { \ libusb_exit(test_ctx); \ } \ unsetenv("LIBUSB_DEBUG"); \ return (code); \ } while (0) /** * Fail the test if the expression does not evaluate to LIBUSB_SUCCESS. */ #define LIBUSB_TEST_RETURN_ON_ERROR(expr) \ do { \ int _result = (expr); \ if (LIBUSB_SUCCESS != _result) { \ libusb_testlib_logf("Not success (%s) at %s:%d", #expr, \ __FILE__, __LINE__); \ LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_FAILURE); \ } \ } while (0) /** * Use relational operatator to compare two values and fail the test if the * comparison is false. Intended to compare integer or pointer types. * * Example: LIBUSB_EXPECT(==, 0, 1) -> fail, LIBUSB_EXPECT(==, 0, 0) -> ok. */ #define LIBUSB_EXPECT(operator, lhs, rhs) \ do { \ int64_t _lhs = (int64_t)(intptr_t)(lhs), _rhs = (int64_t)(intptr_t)(rhs); \ if (!(_lhs operator _rhs)) { \ libusb_testlib_logf("Expected %s (%" PRId64 ") " #operator \ " %s (%" PRId64 ") at %s:%d", #lhs, \ (int64_t)(intptr_t)_lhs, #rhs, \ (int64_t)(intptr_t)_rhs, __FILE__, \ __LINE__); \ LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_FAILURE); \ } \ } while (0) static libusb_testlib_result test_set_log_level_basic(void) { #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) libusb_context *test_ctx = NULL; /* unset LIBUSB_DEBUG if it is set */ unsetenv("LIBUSB_DEBUG"); /* test basic functionality */ LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL, /*num_options=*/0)); LIBUSB_TEST_RETURN_ON_ERROR(libusb_set_option(test_ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_ERROR)); LIBUSB_EXPECT(==, test_ctx->debug, LIBUSB_LOG_LEVEL_ERROR); LIBUSB_TEST_RETURN_ON_ERROR(libusb_set_option(test_ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE)); LIBUSB_EXPECT(==, test_ctx->debug, LIBUSB_LOG_LEVEL_NONE); LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_SUCCESS); #else return TEST_STATUS_SKIP; #endif } static libusb_testlib_result test_set_log_level_default(void) { #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) libusb_context *test_ctx = NULL; /* set the default debug level */ LIBUSB_TEST_RETURN_ON_ERROR(libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_ERROR)); LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL, /*num_options=*/0)); /* check that debug level came from the default */ LIBUSB_EXPECT(==, test_ctx->debug, LIBUSB_LOG_LEVEL_ERROR); /* try to override the old log level. since this was set from the default it * should be possible to change it */ LIBUSB_TEST_RETURN_ON_ERROR(libusb_set_option(test_ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_NONE)); LIBUSB_EXPECT(==, test_ctx->debug, LIBUSB_LOG_LEVEL_NONE); LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_SUCCESS); #else return TEST_STATUS_SKIP; #endif } static libusb_testlib_result test_set_log_level_env(void) { #if defined(ENABLE_LOGGING) libusb_context *test_ctx = NULL; /* check that libusb_set_option does not change the log level when it was set * from the environment. */ setenv("LIBUSB_DEBUG", "4", /*overwrite=*/0); LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL, /*num_options=*/0)); LIBUSB_EXPECT(==, test_ctx->debug, 4); LIBUSB_TEST_RETURN_ON_ERROR(libusb_set_option(test_ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_ERROR)); /* environment variable should always override LIBUSB_OPTION_LOG_LEVEL if set */ LIBUSB_EXPECT(==, test_ctx->debug, 4); LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_SUCCESS); #else return TEST_STATUS_SKIP; #endif } static libusb_testlib_result test_no_discovery(void) { #if defined(__linux__) libusb_context *test_ctx; LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL, /*num_options=*/0)); libusb_device **device_list = NULL; ssize_t num_devices = libusb_get_device_list(test_ctx, &device_list); libusb_free_device_list(device_list, /*unref_devices=*/1); libusb_exit(test_ctx); LIBUSB_EXPECT(>, num_devices, 0); LIBUSB_TEST_RETURN_ON_ERROR(libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY)); LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL, /*num_options=*/0)); device_list = NULL; num_devices = libusb_get_device_list(test_ctx, &device_list); libusb_free_device_list(device_list, /*unref_devices=*/1); LIBUSB_EXPECT(==, num_devices, 0); LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_SUCCESS); #else return TEST_STATUS_SKIP; #endif } static void test_log_cb(libusb_context *ctx, enum libusb_log_level level, const char *str) { UNUSED(ctx); UNUSED(level); UNUSED(str); } static libusb_testlib_result test_set_log_cb(void) { #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) libusb_context *test_ctx = NULL; /* set the log callback on the context */ LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL, /*num_options=*/0)); LIBUSB_TEST_RETURN_ON_ERROR(libusb_set_option(test_ctx, LIBUSB_OPTION_LOG_CB, test_log_cb)); /* check that debug level came from the default */ LIBUSB_EXPECT(==, test_ctx->log_handler, test_log_cb); libusb_exit(test_ctx); test_ctx = NULL; /* set the log callback for all future contexts */ LIBUSB_TEST_RETURN_ON_ERROR(libusb_set_option(/*ctx=*/NULL, LIBUSB_OPTION_LOG_CB, test_log_cb)); LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL, /*num_options=*/0)); LIBUSB_EXPECT(==, test_ctx->log_handler, test_log_cb); LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_SUCCESS); #else return TEST_STATUS_SKIP; #endif } static const libusb_testlib_test tests[] = { { "test_set_log_level_basic", &test_set_log_level_basic }, { "test_set_log_level_env", &test_set_log_level_env }, { "test_no_discovery", &test_no_discovery }, /* since default options can't be unset, run this one last */ { "test_set_log_level_default", &test_set_log_level_default }, { "test_set_log_cb", &test_set_log_cb }, LIBUSB_NULL_TEST }; int main(int argc, char *argv[]) { return libusb_testlib_run_tests(argc, argv, tests); }