Libusbg testing guideline ------------------------- Libusbg tests use cmocka library to simulate fake configfs filesystem, by wrapping input/output functions. ## Building and running tests # Requirements Building libusbg tests requires: -CMocka unit testing framework in version >= 0.3 -libconfig in version >= 1.4 # Building and running Before testing make sure that you have successfully built libusbg (see INSTALL for more details). Tests must be enabled in configuration, do it by adding proper flag when configuring: $ ./configure --enable-tests Then, to build and run all provided tests, run following command: $ make check This should execute testing script and produce report on standart output. It contains list of all test cases and its status - OK, FAILED and SKIPPED. At the end of report number of passed/failed tests is written and then all failed test cases are listed. This report is also avaible in tests/test-suite.log file. It's also possible to pass custom configuration file to testing environment. Currently it's used only for skipping tests. Use --generate-config and --use-config options of test.sh to generate default config and read config from file. Run ./test.sh --help for more help with testing environment. # Tests skipping When you want to skip some test cases, use configuration files for test suite. To generate default config run: $ make check GENERATE_CONFIG=[file_name] It will generate tests/[file_name] file with configuration for testing. You can remove test cases from 'tests' list to disable them. With custom configuration file run: $make check USE_CONFIG=[file_name] Where file_name is name of previously generated configuration file. Test suite will skip tests not listed in config. ## Writting tests Before starting your own tests implementation, become familiar with cmocka library (cmocka.org). test.c file contains tests implementation. Test cases are stored in UnitTest structures (from cmocka) and run by run_tests macro. In cmocka each test case can be composed of three parts: setup, test and teardown. # Setup functions In setup input data must be initialized and assigned to pointer given as argument. Libusbg requires initialized usbg_state structure for most of it's api functions. In most cases we define initial state in test_* strutures and pass it to test function, in order to run usbg_init. Each test_state can be defined quite simply by listing gadgets, its configs and functions (using gcc extenstion), e.g.: static struct test_state simple_state = { .path = "config", .gadgets = simple_gadgets, .udcs = simple_udcs }; test_state structure (or other structure, if neccessary for test case) is casted to void * in setup function. Note, that when using test_state you must sort its content and fill additional fields (i.e. pathes strings). It can be done by calling prepare_state before test. # Test functions Test functions contain libusbg functions calls, preparations for them and checking results. When calling usbg function which operates on filesystem proper preparation is needed. Usbg-test framework provides functions which tell cmocka what operations are expected from corresponding usbg function (push_* and pull_* functions). E.g., in most cases you want correctly initialized usbg_state. It can be done by preparing filesystem by push_init and running usbg_init after that: push_init(in); usbg_ret = usbg_init(in->path, out); init_with_state function does that and checks results. When tested usbg function was run, you can check results by cmocka assert macros. Usbg-test also provides set of assert functions for usbg structures. # Teardown functions When test was run, you can define teardown function, which can do the cleanup. Argument is passed same way as before, by assigning it to cmocka state pointer in test function and receiving it in teardown function. In most cases you will just want to cleanup after initializing state and running some usbg functions. To do that teardown_state can be used as teardown function. You can write custom teardown for other cases. Note that in preparation for test some memory is allocated. All allocated pointers are stored on global stack and should be freed by calling cleanup_stack after test is finished (in teardown function). Remember that teardown is called also when test failed. You should assign something correct (NULL for example) for your teardown function to test argument before running functions which may fail. # Composing test cases All test cases are defined in list of UnitTest structures. You can define test case by macros provided by cmocka or by USBG_TEST or USBG_TEST_TS macro. USBG_TEST is similar to unit_test_setup_teardown from cmocka, but always uses the same teardown (teardown_state) and names test case with custom string. It combines setup function with test function, so one test function can be run with many different states. # Documenting test cases For tests documentation few doxygen macros are created. \usbg_test indicates that current comment block contains test case documentation. \test_desc{name, description, function} describe test with its name, function which is tested and short descripttion of what this test does. # Adding tests Simplest way to add more tests is defining test states and new setup functions, combining them with existing testing functions using USBG_TEST. You can also write own test function. When you have test and setup prepared, add USBG_TEST_TS("test_name", test_function, setup_function), or USBG_TEST("test_name", test_function, setup_function, teardown_function), at the end of tests[] array. Remember to add documentation to the case (see Documenting test cases). # Removing tests In order to remove test case just delete corresponding element on tests[] list (including documentation above it). # Modyfing tests When you want to change data using in test case, change corresponding test_* structures. Note, that single test state can be used in many test cases and modyfing it can effect them as well. You can also change test logic (by modifying test function used in case), as long as you know what you're doing. ## Final notes Remember, that in test environment functions operating on files are replaced and operations on files cannot be performed. However, using standard input/output is possible. All test cases are run in single thread, so some failures on one test case (e.g. SIGSEGV) can cause crash on whole tests set.