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