Start point
This commit is contained in:
40
lib/libusb/tests/Makefile.am
Normal file
40
lib/libusb/tests/Makefile.am
Normal file
@@ -0,0 +1,40 @@
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/libusb
|
||||
LDADD = ../libusb/libusb-1.0.la
|
||||
AM_LDFLAGS = -static
|
||||
|
||||
stress_SOURCES = stress.c testlib.c
|
||||
stress_mt_SOURCES = stress_mt.c
|
||||
set_option_SOURCES = set_option.c testlib.c
|
||||
init_context_SOURCES = init_context.c testlib.c
|
||||
macos_SOURCES = macos.c testlib.c
|
||||
|
||||
stress_mt_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
|
||||
stress_mt_LDADD = $(LDADD) $(THREAD_LIBS)
|
||||
stress_mt_LDFLAGS = $(AM_LDFLAGS)
|
||||
|
||||
if OS_EMSCRIPTEN
|
||||
# On the Web you can't block the main thread as this blocks the event loop itself,
|
||||
# causing deadlocks when trying to use async APIs like WebUSB.
|
||||
# We use the PROXY_TO_PTHREAD Emscripten's feature to move the main app to a separate thread
|
||||
# where it can block safely.
|
||||
stress_mt_LDFLAGS += ${AM_LDFLAGS} -s PROXY_TO_PTHREAD -s EXIT_RUNTIME
|
||||
endif
|
||||
|
||||
noinst_HEADERS = libusb_testlib.h
|
||||
noinst_PROGRAMS = stress stress_mt set_option init_context
|
||||
if OS_DARWIN
|
||||
noinst_PROGRAMS += macos
|
||||
endif
|
||||
|
||||
if BUILD_UMOCKDEV_TEST
|
||||
# NOTE: We add libumockdev-preload.so so that we can run tests in-process
|
||||
# We also use -Wl,-lxxx as the compiler doesn't need it and libtool
|
||||
# would reorder the flags otherwise.
|
||||
umockdev_CPPFLAGS = ${UMOCKDEV_CFLAGS} -I$(top_srcdir)/libusb
|
||||
umockdev_LDFLAGS = -Wl,--push-state,--no-as-needed -Wl,-lumockdev-preload -Wl,--pop-state ${UMOCKDEV_LIBS}
|
||||
umockdev_SOURCES = umockdev.c
|
||||
|
||||
noinst_PROGRAMS += umockdev
|
||||
endif
|
||||
|
||||
TESTS=$(noinst_PROGRAMS)
|
||||
153
lib/libusb/tests/init_context.c
Normal file
153
lib/libusb/tests/init_context.c
Normal file
@@ -0,0 +1,153 @@
|
||||
/* -*- Mode: C; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
* Unit tests for libusb_set_option
|
||||
* Copyright © 2023 Nathan Hjelm <hjelmn@cs.unm.edu>
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "libusbi.h"
|
||||
#include "libusb_testlib.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
#include <winbase.h>
|
||||
|
||||
static int unsetenv(const char *env) {
|
||||
return _putenv_s(env, "");
|
||||
}
|
||||
#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 operator 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_init_context_basic(void) {
|
||||
libusb_context *test_ctx = NULL;
|
||||
|
||||
/* test basic functionality */
|
||||
LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL,
|
||||
/*num_options=*/0));
|
||||
|
||||
LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static libusb_testlib_result test_init_context_log_level(void) {
|
||||
libusb_context *test_ctx = NULL;
|
||||
|
||||
struct libusb_init_option options[] = {
|
||||
{
|
||||
.option = LIBUSB_OPTION_LOG_LEVEL,
|
||||
.value = {
|
||||
.ival = LIBUSB_LOG_LEVEL_ERROR,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/* test basic functionality */
|
||||
LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, options,
|
||||
/*num_options=*/1));
|
||||
|
||||
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
|
||||
LIBUSB_EXPECT(==, test_ctx->debug, LIBUSB_LOG_LEVEL_ERROR);
|
||||
#endif
|
||||
|
||||
LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void LIBUSB_CALL 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_init_context_log_cb(void) {
|
||||
libusb_context *test_ctx = NULL;
|
||||
|
||||
struct libusb_init_option options[] = {
|
||||
{
|
||||
.option = LIBUSB_OPTION_LOG_CB,
|
||||
.value = {
|
||||
.log_cbval = (libusb_log_cb) &test_log_cb,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/* test basic functionality */
|
||||
LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, options,
|
||||
/*num_options=*/1));
|
||||
|
||||
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
|
||||
LIBUSB_EXPECT(==, test_ctx->log_handler, test_log_cb);
|
||||
#endif
|
||||
|
||||
LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static const libusb_testlib_test tests[] = {
|
||||
{ "test_init_context_basic", &test_init_context_basic },
|
||||
{ "test_init_context_log_level", &test_init_context_log_level },
|
||||
{ "test_init_context_log_cb", &test_init_context_log_cb },
|
||||
LIBUSB_NULL_TEST
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return libusb_testlib_run_tests(argc, argv, tests);
|
||||
}
|
||||
76
lib/libusb/tests/libusb_testlib.h
Normal file
76
lib/libusb/tests/libusb_testlib.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* libusb test library helper functions
|
||||
* Copyright © 2012 Toby Gray <toby.gray@realvnc.com>
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef LIBUSB_TESTLIB_H
|
||||
#define LIBUSB_TESTLIB_H
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/** Values returned from a test function to indicate test result */
|
||||
typedef enum {
|
||||
/** Indicates that the test ran successfully. */
|
||||
TEST_STATUS_SUCCESS,
|
||||
/** Indicates that the test failed one or more test. */
|
||||
TEST_STATUS_FAILURE,
|
||||
/** Indicates that an unexpected error occurred. */
|
||||
TEST_STATUS_ERROR,
|
||||
/** Indicates that the test can't be run. For example this may be
|
||||
* due to no suitable device being connected to perform the tests. */
|
||||
TEST_STATUS_SKIP
|
||||
} libusb_testlib_result;
|
||||
|
||||
/**
|
||||
* Logs some test information or state
|
||||
*/
|
||||
void libusb_testlib_logf(const char *fmt, ...) PRINTF_FORMAT(1, 2);
|
||||
|
||||
/**
|
||||
* Structure holding a test description.
|
||||
*/
|
||||
typedef struct {
|
||||
/** Human readable name of the test. */
|
||||
const char *name;
|
||||
/** The test library will call this function to run the test.
|
||||
*
|
||||
* Should return TEST_STATUS_SUCCESS on success or another TEST_STATUS value.
|
||||
*/
|
||||
libusb_testlib_result (*function)(void);
|
||||
} libusb_testlib_test;
|
||||
|
||||
/**
|
||||
* Value to use at the end of a test array to indicate the last
|
||||
* element.
|
||||
*/
|
||||
#define LIBUSB_NULL_TEST { NULL, NULL }
|
||||
|
||||
/**
|
||||
* Runs the tests provided.
|
||||
*
|
||||
* Before running any tests argc and argv will be processed
|
||||
* to determine the mode of operation.
|
||||
*
|
||||
* \param argc The argc from main
|
||||
* \param argv The argv from main
|
||||
* \param tests A NULL_TEST terminated array of tests
|
||||
* \return 0 on success, non-zero on failure
|
||||
*/
|
||||
int libusb_testlib_run_tests(int argc, char *argv[],
|
||||
const libusb_testlib_test *tests);
|
||||
|
||||
#endif //LIBUSB_TESTLIB_H
|
||||
130
lib/libusb/tests/macos.c
Normal file
130
lib/libusb/tests/macos.c
Normal file
@@ -0,0 +1,130 @@
|
||||
/* -*- Mode: C; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
* Unit tests for libusb_set_option
|
||||
* Copyright © 2023 Nathan Hjelm <hjelmn@cs.unm.edu>
|
||||
* 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 <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "libusbi.h"
|
||||
#include "libusb_testlib.h"
|
||||
|
||||
#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 operator 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 = (lhs), _rhs = (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)
|
||||
|
||||
|
||||
extern uint32_t libusb_testonly_fake_running_version;
|
||||
extern uint32_t libusb_testonly_using_running_interface_version;
|
||||
extern uint32_t libusb_testonly_using_running_device_version;
|
||||
extern bool libusb_testonly_clear_running_version_cache;
|
||||
|
||||
static libusb_testlib_result test_macos_version_fallback(void) {
|
||||
libusb_context *test_ctx = NULL;
|
||||
libusb_testonly_fake_running_version = 100001;
|
||||
libusb_testonly_clear_running_version_cache = true;
|
||||
|
||||
LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL,
|
||||
/*num_options=*/0));
|
||||
LIBUSB_EXPECT(==, libusb_testonly_using_running_interface_version, 220);
|
||||
LIBUSB_EXPECT(==, libusb_testonly_using_running_device_version, 197);
|
||||
|
||||
libusb_exit(test_ctx);
|
||||
test_ctx = NULL;
|
||||
|
||||
libusb_testonly_fake_running_version = 100900;
|
||||
|
||||
LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL,
|
||||
/*num_options=*/0));
|
||||
LIBUSB_EXPECT(==, libusb_testonly_using_running_interface_version, 650);
|
||||
LIBUSB_EXPECT(==, libusb_testonly_using_running_device_version, 650);
|
||||
|
||||
libusb_exit(test_ctx);
|
||||
test_ctx = NULL;
|
||||
|
||||
libusb_testonly_fake_running_version = 101200;
|
||||
|
||||
LIBUSB_TEST_RETURN_ON_ERROR(libusb_init_context(&test_ctx, /*options=*/NULL,
|
||||
/*num_options=*/0));
|
||||
LIBUSB_EXPECT(==, libusb_testonly_using_running_interface_version, 800);
|
||||
LIBUSB_EXPECT(==, libusb_testonly_using_running_device_version, 650);
|
||||
|
||||
libusb_exit(test_ctx);
|
||||
test_ctx = NULL;
|
||||
|
||||
// Test a version smaller than 10.0. Initialization should fail.
|
||||
libusb_testonly_fake_running_version = 99999;
|
||||
|
||||
int error = libusb_init_context(&test_ctx, /*options=*/NULL,
|
||||
/*num_options=*/0);
|
||||
LIBUSB_EXPECT(!=, error, LIBUSB_SUCCESS);
|
||||
|
||||
|
||||
LIBUSB_TEST_CLEAN_EXIT(TEST_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static const libusb_testlib_test tests[] = {
|
||||
{ "test_macos_version_fallback", &test_macos_version_fallback },
|
||||
LIBUSB_NULL_TEST
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return libusb_testlib_run_tests(argc, argv, tests);
|
||||
}
|
||||
253
lib/libusb/tests/set_option.c
Normal file
253
lib/libusb/tests/set_option.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/* -*- Mode: C; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
* Unit tests for libusb_set_option
|
||||
* Copyright © 2023 Nathan Hjelm <hjelmn@cs.unm.edu>
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "libusbi.h"
|
||||
#include "libusb_testlib.h"
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
#include <winbase.h>
|
||||
|
||||
#if defined(ENABLE_LOGGING)
|
||||
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
|
||||
#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 operator 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));
|
||||
#ifndef ENABLE_DEBUG_LOGGING
|
||||
LIBUSB_EXPECT(==, test_ctx->debug, 4);
|
||||
#endif
|
||||
|
||||
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 */
|
||||
#ifndef ENABLE_DEBUG_LOGGING
|
||||
LIBUSB_EXPECT(==, test_ctx->debug, 4);
|
||||
#endif
|
||||
|
||||
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);
|
||||
test_ctx = NULL;
|
||||
|
||||
if (num_devices == 0) {
|
||||
libusb_testlib_logf("Warning: no devices found, the test will only verify that setting LIBUSB_OPTION_NO_DEVICE_DISCOVERY succeeds.");
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
|
||||
static void LIBUSB_CALL test_log_cb(libusb_context *ctx, enum libusb_log_level level,
|
||||
const char *str) {
|
||||
UNUSED(ctx);
|
||||
UNUSED(level);
|
||||
UNUSED(str);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
172
lib/libusb/tests/stress.c
Normal file
172
lib/libusb/tests/stress.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* libusb stress test program to perform simple stress tests
|
||||
* Copyright © 2012 Toby Gray <toby.gray@realvnc.com>
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "libusb.h"
|
||||
#include "libusb_testlib.h"
|
||||
|
||||
/** Test that creates and destroys a single concurrent context
|
||||
* 10000 times. */
|
||||
static libusb_testlib_result test_init_and_exit(void)
|
||||
{
|
||||
for (int i = 0; i < 10000; ++i) {
|
||||
libusb_context *ctx = NULL;
|
||||
int r;
|
||||
|
||||
r = libusb_init_context(&ctx, /*options=*/NULL, /*num_options=*/0);
|
||||
if (r != LIBUSB_SUCCESS) {
|
||||
libusb_testlib_logf(
|
||||
"Failed to init libusb on iteration %d: %d",
|
||||
i, r);
|
||||
return TEST_STATUS_FAILURE;
|
||||
}
|
||||
libusb_exit(ctx);
|
||||
}
|
||||
|
||||
return TEST_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/** Tests that devices can be listed 1000 times. */
|
||||
static libusb_testlib_result test_get_device_list(void)
|
||||
{
|
||||
libusb_context *ctx;
|
||||
int r;
|
||||
|
||||
r = libusb_init_context(&ctx, /*options=*/NULL, /*num_options=*/0);
|
||||
if (r != LIBUSB_SUCCESS) {
|
||||
libusb_testlib_logf("Failed to init libusb: %d", r);
|
||||
return TEST_STATUS_FAILURE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
libusb_device **device_list = NULL;
|
||||
ssize_t list_size = libusb_get_device_list(ctx, &device_list);
|
||||
if (list_size < 0 || !device_list) {
|
||||
libusb_testlib_logf(
|
||||
"Failed to get device list on iteration %d: %ld (%p)",
|
||||
i, (long) -list_size, (void *) device_list);
|
||||
libusb_exit(ctx);
|
||||
return TEST_STATUS_FAILURE;
|
||||
}
|
||||
libusb_free_device_list(device_list, 1);
|
||||
}
|
||||
|
||||
libusb_exit(ctx);
|
||||
return TEST_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/** Tests that 100 concurrent device lists can be open at a time. */
|
||||
static libusb_testlib_result test_many_device_lists(void)
|
||||
{
|
||||
#define LIST_COUNT 100
|
||||
libusb_testlib_result result = TEST_STATUS_SUCCESS;
|
||||
libusb_context *ctx = NULL;
|
||||
libusb_device **device_lists[LIST_COUNT];
|
||||
int r;
|
||||
|
||||
r = libusb_init_context(&ctx, /*options=*/NULL, /*num_options=*/0);
|
||||
if (r != LIBUSB_SUCCESS) {
|
||||
libusb_testlib_logf("Failed to init libusb: %d", r);
|
||||
return TEST_STATUS_FAILURE;
|
||||
}
|
||||
|
||||
memset(device_lists, 0, sizeof(device_lists));
|
||||
|
||||
/* Create the 100 device lists. */
|
||||
for (int i = 0; i < LIST_COUNT; ++i) {
|
||||
ssize_t list_size = libusb_get_device_list(ctx, &device_lists[i]);
|
||||
if (list_size < 0 || !device_lists[i]) {
|
||||
libusb_testlib_logf(
|
||||
"Failed to get device list on iteration %d: %ld (%p)",
|
||||
i, (long) -list_size, (void *) device_lists[i]);
|
||||
result = TEST_STATUS_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Destroy the 100 device lists. */
|
||||
for (int i = 0; i < LIST_COUNT; ++i) {
|
||||
if (device_lists[i])
|
||||
libusb_free_device_list(device_lists[i], 1);
|
||||
}
|
||||
|
||||
libusb_exit(ctx);
|
||||
return result;
|
||||
#undef LIST_COUNT
|
||||
}
|
||||
|
||||
/** Tests that the default context (used for various things including
|
||||
* logging) works correctly when the first context created in a
|
||||
* process is destroyed. */
|
||||
static libusb_testlib_result test_default_context_change(void)
|
||||
{
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
libusb_context *ctx = NULL;
|
||||
int r;
|
||||
|
||||
|
||||
/* Enable debug output on new context, to be sure to use the context */
|
||||
struct libusb_init_option options[] = {
|
||||
{
|
||||
.option = LIBUSB_OPTION_LOG_LEVEL,
|
||||
.value = {.ival = LIBUSB_LOG_LEVEL_DEBUG},
|
||||
},
|
||||
};
|
||||
int num_options = 1;
|
||||
|
||||
/* First create a new context */
|
||||
r = libusb_init_context(&ctx, options, num_options);
|
||||
if (r != LIBUSB_SUCCESS) {
|
||||
libusb_testlib_logf("Failed to init libusb: %d", r);
|
||||
return TEST_STATUS_FAILURE;
|
||||
}
|
||||
|
||||
/* Now create a reference to the default context */
|
||||
r = libusb_init_context(/*ctx=*/NULL, options, num_options);
|
||||
if (r != LIBUSB_SUCCESS) {
|
||||
libusb_testlib_logf("Failed to init libusb: %d", r);
|
||||
libusb_exit(ctx);
|
||||
return TEST_STATUS_FAILURE;
|
||||
}
|
||||
|
||||
/* Destroy the first context */
|
||||
libusb_exit(ctx);
|
||||
/* Destroy the default context */
|
||||
libusb_exit(NULL);
|
||||
}
|
||||
|
||||
return TEST_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Fill in the list of tests. */
|
||||
static const libusb_testlib_test tests[] = {
|
||||
{ "init_and_exit", &test_init_and_exit },
|
||||
{ "get_device_list", &test_get_device_list },
|
||||
{ "many_device_lists", &test_many_device_lists },
|
||||
{ "default_context_change", &test_default_context_change },
|
||||
LIBUSB_NULL_TEST
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return libusb_testlib_run_tests(argc, argv, tests);
|
||||
}
|
||||
267
lib/libusb/tests/stress_mt.c
Normal file
267
lib/libusb/tests/stress_mt.c
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* libusb multi-thread test program
|
||||
* Copyright 2022-2023 Tormod Volden
|
||||
*
|
||||
* This program 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 program 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 <libusb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined(PLATFORM_POSIX)
|
||||
|
||||
#include <pthread.h>
|
||||
typedef pthread_t thread_t;
|
||||
typedef void * thread_return_t;
|
||||
#define THREAD_RETURN_VALUE NULL
|
||||
#define THREAD_CALL_TYPE
|
||||
|
||||
static inline int thread_create(thread_t *thread,
|
||||
thread_return_t (*thread_entry)(void *arg), void *arg)
|
||||
{
|
||||
return pthread_create(thread, NULL, thread_entry, arg) == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
static inline void thread_join(thread_t thread)
|
||||
{
|
||||
(void)pthread_join(thread, NULL);
|
||||
}
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
|
||||
typedef HANDLE thread_t;
|
||||
#define THREAD_RETURN_VALUE 0
|
||||
#define THREAD_CALL_TYPE __stdcall
|
||||
|
||||
#if defined(__CYGWIN__)
|
||||
typedef DWORD thread_return_t;
|
||||
#else
|
||||
#include <process.h>
|
||||
typedef unsigned thread_return_t;
|
||||
#endif
|
||||
|
||||
static inline int thread_create(thread_t *thread,
|
||||
thread_return_t (__stdcall *thread_entry)(void *arg), void *arg)
|
||||
{
|
||||
#if defined(__CYGWIN__)
|
||||
*thread = CreateThread(NULL, 0, thread_entry, arg, 0, NULL);
|
||||
#else
|
||||
*thread = (HANDLE)_beginthreadex(NULL, 0, thread_entry, arg, 0, NULL);
|
||||
#endif
|
||||
return *thread != NULL ? 0 : -1;
|
||||
}
|
||||
|
||||
static inline void thread_join(thread_t thread)
|
||||
{
|
||||
(void)WaitForSingleObject(thread, INFINITE);
|
||||
(void)CloseHandle(thread);
|
||||
}
|
||||
|
||||
typedef volatile LONG atomic_bool;
|
||||
|
||||
#define atomic_exchange InterlockedExchange
|
||||
#endif /* PLATFORM_WINDOWS */
|
||||
|
||||
/* Test that creates and destroys contexts repeatedly */
|
||||
|
||||
#define NTHREADS 8
|
||||
#define ITERS 64
|
||||
#define MAX_DEVCOUNT 128
|
||||
|
||||
struct thread_info {
|
||||
int number;
|
||||
int enumerate;
|
||||
ssize_t devcount;
|
||||
int err;
|
||||
int iteration;
|
||||
} tinfo[NTHREADS];
|
||||
|
||||
atomic_bool no_access[MAX_DEVCOUNT];
|
||||
|
||||
/* Function called by backend during device initialization to convert
|
||||
* multi-byte fields in the device descriptor to host-endian format.
|
||||
* Copied from libusbi.h as we want test to be realistic and not depend on internals.
|
||||
*/
|
||||
static inline void usbi_localize_device_descriptor(struct libusb_device_descriptor *desc)
|
||||
{
|
||||
desc->bcdUSB = libusb_le16_to_cpu(desc->bcdUSB);
|
||||
desc->idVendor = libusb_le16_to_cpu(desc->idVendor);
|
||||
desc->idProduct = libusb_le16_to_cpu(desc->idProduct);
|
||||
desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice);
|
||||
}
|
||||
|
||||
static thread_return_t THREAD_CALL_TYPE init_and_exit(void * arg)
|
||||
{
|
||||
struct thread_info *ti = (struct thread_info *) arg;
|
||||
|
||||
for (ti->iteration = 0; ti->iteration < ITERS && !ti->err; ti->iteration++) {
|
||||
libusb_context *ctx = NULL;
|
||||
|
||||
ti->err = libusb_init_context(&ctx, /*options=*/NULL, /*num_options=*/0);
|
||||
if (ti->err != 0) {
|
||||
break;
|
||||
}
|
||||
if (ti->enumerate) {
|
||||
libusb_device **devs;
|
||||
ti->devcount = libusb_get_device_list(ctx, &devs);
|
||||
if (ti->devcount < 0) {
|
||||
ti->err = (int)ti->devcount;
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < ti->devcount && ti->err == 0; i++) {
|
||||
libusb_device *dev = devs[i];
|
||||
struct libusb_device_descriptor desc;
|
||||
ti->err = libusb_get_device_descriptor(dev, &desc);
|
||||
if (ti->err != 0) {
|
||||
break;
|
||||
}
|
||||
if (no_access[i]) {
|
||||
continue;
|
||||
}
|
||||
libusb_device_handle *dev_handle;
|
||||
int open_err = libusb_open(dev, &dev_handle);
|
||||
if (open_err == LIBUSB_ERROR_ACCESS
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
|| open_err == LIBUSB_ERROR_NOT_SUPPORTED
|
||||
|| open_err == LIBUSB_ERROR_NOT_FOUND
|
||||
#endif
|
||||
) {
|
||||
/* Use atomic swap to ensure we print warning only once across all threads.
|
||||
This is a warning and not a hard error because it should be fine to run tests
|
||||
even if we don't have access to some devices. */
|
||||
if (!atomic_exchange(&no_access[i], true)) {
|
||||
fprintf(stderr, "No access to device %04x:%04x, skipping transfer tests.\n", desc.idVendor, desc.idProduct);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (open_err != 0) {
|
||||
ti->err = open_err;
|
||||
break;
|
||||
}
|
||||
/* Request raw descriptor via control transfer.
|
||||
This tests opening, transferring and closing from multiple threads in parallel. */
|
||||
struct libusb_device_descriptor raw_desc;
|
||||
int raw_desc_len = libusb_get_descriptor(dev_handle, LIBUSB_DT_DEVICE, 0, (unsigned char *)&raw_desc, sizeof(raw_desc));
|
||||
if (raw_desc_len < 0) {
|
||||
ti->err = raw_desc_len;
|
||||
goto close;
|
||||
}
|
||||
if (raw_desc_len != sizeof(raw_desc)) {
|
||||
fprintf(stderr, "Thread %d: device %d: unexpected raw descriptor length %d\n",
|
||||
ti->number, i, raw_desc_len);
|
||||
ti->err = LIBUSB_ERROR_OTHER;
|
||||
goto close;
|
||||
}
|
||||
usbi_localize_device_descriptor(&raw_desc);
|
||||
#define ASSERT_EQ(field) if (raw_desc.field != desc.field) { \
|
||||
fprintf(stderr, "Thread %d: device %d: mismatch in field " #field ": %d != %d\n", \
|
||||
ti->number, i, raw_desc.field, desc.field); \
|
||||
ti->err = LIBUSB_ERROR_OTHER; \
|
||||
goto close; \
|
||||
}
|
||||
ASSERT_EQ(bLength);
|
||||
ASSERT_EQ(bDescriptorType);
|
||||
#if !defined(PLATFORM_WINDOWS)
|
||||
/* these are hardcoded by the winusbx HID backend */
|
||||
ASSERT_EQ(bcdUSB);
|
||||
ASSERT_EQ(bDeviceClass);
|
||||
ASSERT_EQ(bDeviceSubClass);
|
||||
ASSERT_EQ(bDeviceProtocol);
|
||||
ASSERT_EQ(bMaxPacketSize0);
|
||||
ASSERT_EQ(bcdDevice);
|
||||
#endif
|
||||
ASSERT_EQ(idVendor);
|
||||
ASSERT_EQ(idProduct);
|
||||
ASSERT_EQ(iManufacturer);
|
||||
ASSERT_EQ(iProduct);
|
||||
ASSERT_EQ(iSerialNumber);
|
||||
ASSERT_EQ(bNumConfigurations);
|
||||
close:
|
||||
libusb_close(dev_handle);
|
||||
}
|
||||
libusb_free_device_list(devs, 1);
|
||||
}
|
||||
|
||||
libusb_exit(ctx);
|
||||
}
|
||||
return (thread_return_t) THREAD_RETURN_VALUE;
|
||||
}
|
||||
|
||||
static int test_multi_init(int enumerate)
|
||||
{
|
||||
thread_t threadId[NTHREADS];
|
||||
int errs = 0;
|
||||
int t, i;
|
||||
ssize_t last_devcount = 0;
|
||||
int devcount_mismatch = 0;
|
||||
int access_failures = 0;
|
||||
|
||||
printf("Starting %d threads\n", NTHREADS);
|
||||
for (t = 0; t < NTHREADS; t++) {
|
||||
tinfo[t].err = 0;
|
||||
tinfo[t].number = t;
|
||||
tinfo[t].enumerate = enumerate;
|
||||
thread_create(&threadId[t], &init_and_exit, (void *) &tinfo[t]);
|
||||
}
|
||||
|
||||
for (t = 0; t < NTHREADS; t++) {
|
||||
thread_join(threadId[t]);
|
||||
if (tinfo[t].err) {
|
||||
errs++;
|
||||
fprintf(stderr,
|
||||
"Thread %d failed (iteration %d): %s\n",
|
||||
tinfo[t].number,
|
||||
tinfo[t].iteration,
|
||||
libusb_error_name(tinfo[t].err));
|
||||
} else if (enumerate) {
|
||||
if (t > 0 && tinfo[t].devcount != last_devcount) {
|
||||
devcount_mismatch++;
|
||||
printf("Device count mismatch: Thread %d discovered %ld devices instead of %ld\n",
|
||||
tinfo[t].number,
|
||||
(long int) tinfo[t].devcount,
|
||||
(long int) last_devcount);
|
||||
}
|
||||
last_devcount = tinfo[t].devcount;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_DEVCOUNT; i++)
|
||||
if (no_access[i])
|
||||
access_failures++;
|
||||
|
||||
if (enumerate && !devcount_mismatch)
|
||||
printf("All threads discovered %ld devices (%i not opened)\n",
|
||||
(long int) last_devcount, access_failures);
|
||||
|
||||
return errs + devcount_mismatch;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int errs = 0;
|
||||
|
||||
printf("Running multithreaded init/exit test...\n");
|
||||
errs += test_multi_init(0);
|
||||
printf("Running multithreaded init/exit test with enumeration...\n");
|
||||
errs += test_multi_init(1);
|
||||
printf("All done, %d errors\n", errs);
|
||||
|
||||
return errs != 0;
|
||||
}
|
||||
184
lib/libusb/tests/testlib.c
Normal file
184
lib/libusb/tests/testlib.c
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* libusb test library helper functions
|
||||
* Copyright © 2012 Toby Gray <toby.gray@realvnc.com>
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libusb_testlib.h"
|
||||
|
||||
#if defined(PLATFORM_POSIX)
|
||||
#define NULL_PATH "/dev/null"
|
||||
#elif defined(PLATFORM_WINDOWS)
|
||||
#define NULL_PATH "nul"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Converts a test result code into a human readable string.
|
||||
*/
|
||||
static const char *test_result_to_str(libusb_testlib_result result)
|
||||
{
|
||||
switch (result) {
|
||||
case TEST_STATUS_SUCCESS:
|
||||
return "Success";
|
||||
case TEST_STATUS_FAILURE:
|
||||
return "Failure";
|
||||
case TEST_STATUS_ERROR:
|
||||
return "Error";
|
||||
case TEST_STATUS_SKIP:
|
||||
return "Skip";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void print_usage(const char *progname)
|
||||
{
|
||||
printf("Usage: %s [-l] [-v] [<test_name> ...]\n", progname);
|
||||
printf(" -l List available tests\n");
|
||||
printf(" -v Don't redirect STDERR before running tests\n");
|
||||
printf(" -h Display this help and exit\n");
|
||||
}
|
||||
|
||||
void libusb_testlib_logf(const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
vfprintf(stdout, fmt, va);
|
||||
va_end(va);
|
||||
fputc('\n', stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
int libusb_testlib_run_tests(int argc, char *argv[],
|
||||
const libusb_testlib_test *tests)
|
||||
{
|
||||
int run_count = 0;
|
||||
int idx = 0;
|
||||
int pass_count = 0;
|
||||
int fail_count = 0;
|
||||
int error_count = 0;
|
||||
int skip_count = 0;
|
||||
|
||||
/* Setup default mode of operation */
|
||||
char **test_names = NULL;
|
||||
int test_count = 0;
|
||||
bool list_tests = false;
|
||||
bool verbose = false;
|
||||
|
||||
/* Parse command line options */
|
||||
if (argc >= 2) {
|
||||
for (int j = 1; j < argc; j++) {
|
||||
const char *argstr = argv[j];
|
||||
size_t arglen = strlen(argstr);
|
||||
|
||||
if (argstr[0] == '-' || argstr[0] == '/') {
|
||||
if (arglen == 2) {
|
||||
switch (argstr[1]) {
|
||||
case 'l':
|
||||
list_tests = true;
|
||||
continue;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
continue;
|
||||
case 'h':
|
||||
print_usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Unknown option: '%s'\n", argstr);
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
} else {
|
||||
/* End of command line options, remaining must be list of tests to run */
|
||||
test_names = argv + j;
|
||||
test_count = argc - j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate command line options */
|
||||
if (test_names && list_tests) {
|
||||
fprintf(stderr, "List of tests requested but test list provided\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Setup test log output */
|
||||
if (!verbose) {
|
||||
if (!freopen(NULL_PATH, "w", stderr)) {
|
||||
printf("Failed to open null handle: %d\n", errno);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Act on any options not related to running tests */
|
||||
if (list_tests) {
|
||||
while (tests[idx].function)
|
||||
libusb_testlib_logf("%s", tests[idx++].name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Run any requested tests */
|
||||
while (tests[idx].function) {
|
||||
const libusb_testlib_test *test = &tests[idx++];
|
||||
libusb_testlib_result test_result;
|
||||
|
||||
if (test_count > 0) {
|
||||
/* Filtering tests to run, check if this is one of them */
|
||||
int i;
|
||||
|
||||
for (i = 0; i < test_count; i++) {
|
||||
if (!strcmp(test_names[i], test->name))
|
||||
/* Matches a requested test name */
|
||||
break;
|
||||
}
|
||||
if (i == test_count) {
|
||||
/* Failed to find a test match, so do the next loop iteration */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
libusb_testlib_logf("Starting test run: %s...", test->name);
|
||||
test_result = test->function();
|
||||
libusb_testlib_logf("%s (%d)", test_result_to_str(test_result), test_result);
|
||||
switch (test_result) {
|
||||
case TEST_STATUS_SUCCESS: pass_count++; break;
|
||||
case TEST_STATUS_FAILURE: fail_count++; break;
|
||||
case TEST_STATUS_ERROR: error_count++; break;
|
||||
case TEST_STATUS_SKIP: skip_count++; break;
|
||||
}
|
||||
run_count++;
|
||||
}
|
||||
|
||||
libusb_testlib_logf("---");
|
||||
libusb_testlib_logf("Ran %d tests", run_count);
|
||||
libusb_testlib_logf("Passed %d tests", pass_count);
|
||||
libusb_testlib_logf("Failed %d tests", fail_count);
|
||||
libusb_testlib_logf("Error in %d tests", error_count);
|
||||
libusb_testlib_logf("Skipped %d tests", skip_count);
|
||||
|
||||
return fail_count + error_count;
|
||||
}
|
||||
1175
lib/libusb/tests/umockdev.c
Normal file
1175
lib/libusb/tests/umockdev.c
Normal file
File diff suppressed because it is too large
Load Diff
1
lib/libusb/tests/webusb-test-shim/.gitignore
vendored
Normal file
1
lib/libusb/tests/webusb-test-shim/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/node_modules
|
||||
12
lib/libusb/tests/webusb-test-shim/index.js
Normal file
12
lib/libusb/tests/webusb-test-shim/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
// It's not yet possible to automate actual Chrome's device selection, so
|
||||
// for now run automated tests via Node.js WebUSB implementation.
|
||||
//
|
||||
// It might differ from browser one, but should be enough to catch most obvious issues.
|
||||
|
||||
const { WebUSB } = require('usb');
|
||||
|
||||
globalThis.navigator = {
|
||||
usb: new WebUSB({
|
||||
allowAllDevices: true
|
||||
})
|
||||
};
|
||||
50
lib/libusb/tests/webusb-test-shim/package-lock.json
generated
Normal file
50
lib/libusb/tests/webusb-test-shim/package-lock.json
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "webusb-test-runner",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "webusb-test-runner",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"usb": "^2.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/w3c-web-usb": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.10.tgz",
|
||||
"integrity": "sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ=="
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz",
|
||||
"integrity": "sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA=="
|
||||
},
|
||||
"node_modules/node-gyp-build": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.0.tgz",
|
||||
"integrity": "sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg==",
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
"node-gyp-build-test": "build-test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/usb": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/usb/-/usb-2.11.0.tgz",
|
||||
"integrity": "sha512-u5+NZ6DtoW8TIBtuSArQGAZZ/K15i3lYvZBAYmcgI+RcDS9G50/KPrUd3CrU8M92ahyCvg5e0gc8BDvr5Hwejg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@types/w3c-web-usb": "^1.0.6",
|
||||
"node-addon-api": "^7.0.0",
|
||||
"node-gyp-build": "^4.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.22.0 <13.0 || >=14.17.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
lib/libusb/tests/webusb-test-shim/package.json
Normal file
10
lib/libusb/tests/webusb-test-shim/package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "webusb-test-runner",
|
||||
"private": true,
|
||||
"license": "LGPL-2.1",
|
||||
"main": "index.js",
|
||||
"author": "Ingvar Stepanyan <me@rreverser.com>",
|
||||
"dependencies": {
|
||||
"usb": "^2.11.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user