1 /* 2 * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 /* 7 * Parameterized Test Framework 8 * 9 * Peripherals like SPI has several parameters like: freq, mode, DMA, etc. 10 * This framework helps to test different parameter sets with the same program. 11 * 12 * Each "parameter set" is a set with different parameters (freq, mode, etc.). 13 * A parameter group is a group of several parameter sets. Each unit test performs 14 * tests among the sets in a group. 15 * 16 * The test will be execute in the following sequence: 17 * 1. ``pre_test``: initialize the context 18 * 2. take a set out of the parameter group 19 * 3. ``def_param``: fill in default value for the parameter set if not set 20 * 4. ``loop``: execute test program for the set in the initialized context 21 * 5. loop executing 2-4 until the last set 22 * 6. ``post_test``: free the resources used. 23 * 24 * Usage example: 25 * 26 * 1. Define your own parameter set type: 27 * typedef struct { 28 * const char pset_name[PSET_NAME_LEN]; 29 * //The test work till the frequency below, 30 * //set the frequency to higher and remove checks in the driver to know how fast the system can run. 31 * const int *freq_list; // list of tested frequency, terminated by 0 32 * int freq_limit; //freq larger (not equal) than this will be ignored 33 * spi_dup_t dup; 34 * int mode; 35 * bool length_aligned; 36 * int test_size; 37 * 38 * int master_limit; // the master disable dummy bits and discard readings over this freq 39 * bool master_iomux; 40 * int master_dma_chan; 41 * 42 * bool slave_iomux; 43 * int slave_dma_chan; 44 * int slave_tv_ns; 45 * bool slave_unaligned_addr; 46 * } spitest_param_set_t; 47 * 48 * 2. Define a parameter set: 49 * spitest_param_set_t mode_pgroup[] = { 50 * //non-DMA tests 51 * { .pset_name = "mode 0, no DMA", 52 * .freq_list = test_freq_mode, 53 * .master_limit = FREQ_LIMIT_MODE, 54 * .dup = FULL_DUPLEX, 55 * .master_iomux= true, 56 * .slave_iomux = true, 57 * .slave_tv_ns = TV_WITH_ESP_SLAVE, 58 * .mode = 0, 59 * }, 60 * { .pset_name = "mode 1, no DMA", 61 * .freq_list = test_freq_mode, 62 * .master_limit = FREQ_LIMIT_MODE, 63 * .dup = FULL_DUPLEX, 64 * .master_iomux= true, 65 * .slave_iomux = true, 66 * .slave_tv_ns = TV_WITH_ESP_SLAVE, 67 * .mode = 1, 68 * }, 69 * // other configurations... 70 * }; 71 * 72 * 3. Define your test functions, and wrap them in the ``param_test_func_t``: 73 * static const param_test_func_t master_test_func = { 74 * .pre_test = test_master_init, 75 * .post_test = test_master_deinit, 76 * .loop = test_master_loop, 77 * .def_param = spitest_def_param, 78 * }; 79 * 80 * 4. Declare the group by PARAM_GROUP_DECLARE right after the param group: 81 * PARAM_GROUP_DECLARE(MODE, mode_pgroup) 82 * 83 * 5. Declare the test function by TEST_SINGLE_BOARD (for single board test), or TEST_MASTER_SLAVE(for multiboard test) 84 * TEST_MASTER_SLAVE(MODE, mode_pgroup, "[spi][timeout=120]", &master_test_func, &slave_test_func) 85 * 86 * or 87 * TEST_SINGLE_BOARD(TIMING, timing_pgroup, "[spi][timeout=120]", &local_test_func) 88 * 89 * NOTE: suggest to define your own macro to wrap 4 and 5 if your tag and test functions are the same. E.g.: 90 * #define TEST_SPI_MASTER_SLAVE(name, pgroup) (backslash) 91 * PARAM_GROUP_DECLARE(name, pgroup) (backslash) 92 * TEST_MASTER_SLAVE(name, pgroup, "[spi][timeout=120]", &master_test_func, &slave_test_func) 93 * 94 * Then declare tests conveniently by: 95 * TEST_SPI_MASTER_SLAVE(TIMING, timing_pgroup) 96 * TEST_SPI_MASTER_SLAVE(MODE, mode_pgroup) 97 * 98 */ 99 100 #define PGROUP_NAME_LEN 20 ///< name length of parameter group 101 #define PGROUP_NAME(name) PGROUP_##name ///< param group name 102 #define PTEST_MASTER_NAME(name) PTEST_MASTER_##name ///< test function name of master 103 #define PTEST_SLAVE_NAME(name) PTEST_SLAVE_##name ///< test function name of slave 104 105 /// Test set structure holding name, param set array pointer, item size and param set num. 106 typedef struct { 107 char name[PGROUP_NAME_LEN]; ///< Name of param group to print 108 void *param_group; ///< Start of the param group array 109 int pset_size; ///< Size of each param set 110 int pset_num; ///< Total number of param sets 111 } param_group_t; 112 113 /// Test functions for the frameowrk 114 typedef struct { 115 void (*pre_test)(void** contxt); ///< Initialization function called before tests begin. Initial your context here 116 void (*post_test)(void* context); ///< Deinit function called after all tests are done. 117 void (*def_param)(void* inout_pset); ///< Function to fill each pset structure before executed, left NULL if not used. 118 void (*loop)(const void* pset, void* context); ///< Function execute each param set 119 } ptest_func_t; 120 121 /** 122 * Test framework to execute init, loop and deinit. 123 * 124 * @param param_group Parameter group holder to test in turns. 125 * @param test_func Function set to execute. 126 */ 127 void test_serializer(const param_group_t *param_group, const ptest_func_t* test_func); 128 129 #define PARAM_GROUP_DECLARE_TYPE(group_name, pset_type, pgroup) \ 130 static const param_group_t PGROUP_NAME(pgroup) = { \ 131 .name = #group_name, \ 132 .param_group = (void*)&pgroup, \ 133 .pset_size = sizeof(pset_type), \ 134 .pset_num = sizeof(pgroup)/sizeof(pset_type), \ 135 }; 136 137 /** 138 * Declare parameter group 139 * 140 * @param group_name Parameter group name to print in the beginning of the test 141 * @param param_group Parameter group structure, should be already defined above, and the size and type is defined. 142 */ 143 #define PARAM_GROUP_DECLARE(group_name, param_group) \ 144 PARAM_GROUP_DECLARE_TYPE(group_name, typeof(param_group[0]), param_group) 145 146 /** 147 * Test parameter group on one board. 148 * 149 * @param name Test name to be printed in the menu. 150 * @param param_group Parameter group to be tested. 151 * @param tag Tag for environment, etc. e.g. [spi][timeout=120] 152 * @param test_func ``ptest_func_t`` to be executed. 153 */ 154 #define TEST_SINGLE_BOARD(name, param_group, tag, test_func) \ 155 TEST_CASE("single board test: "#name, tag) { test_serializer(&PGROUP_NAME(param_group), test_func); } 156 157 /** 158 * Test parameter group for master-slave framework 159 * 160 * @param name Test name to be printed in the menu. 161 * @param param_group Parameter group to be tested. 162 * @param tag Tag for environment, etc. e.g. [spi][timeout=120] 163 * @param master_func ``ptest_func_t`` to be executed by master. 164 * @param slave_func ``ptest_func_t`` to be executed by slave. 165 */ 166 #define TEST_MASTER_SLAVE(name, param_group, tag, master_func, slave_func) \ 167 static void PTEST_MASTER_NAME(name) (void) { test_serializer(&PGROUP_NAME(param_group), master_func); } \ 168 static void PTEST_SLAVE_NAME(name) (void) { test_serializer(&PGROUP_NAME(param_group), slave_func); } \ 169 TEST_CASE_MULTIPLE_DEVICES("master slave test: "#name, tag, PTEST_MASTER_NAME(name), PTEST_SLAVE_NAME(name)) 170