blob: 6bd1382ad9506ef26f6013965c465ee6f1e35834 [file] [log] [blame]
Jakub Czapigaeb6f80d2021-01-07 09:24:33 +01001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include "../lib/region_file.c"
4
5#include <tests/test.h>
6#include <stdlib.h>
7#include <string.h>
8#include <commonlib/region.h>
9#include <tests/lib/region_file_data.h>
10
11static void clear_region_file(struct mem_region_device *mrdev)
12{
13 uint8_t *mem_buffer = (uint8_t *)mrdev->base;
14
15 memset(mem_buffer, 0xff, REGION_FILE_BUFFER_SIZE);
16}
17
18static int setup_region_file_test_group(void **state)
19{
20 void *mem_buffer = malloc(REGION_FILE_BUFFER_SIZE);
21 struct mem_region_device *dev = malloc(sizeof(struct mem_region_device));
22
23 if (mem_buffer == NULL || dev == NULL) {
24 free(mem_buffer);
25 free(dev);
26 return -1;
27 }
28
29 *dev = (struct mem_region_device)
30 MEM_REGION_DEV_RW_INIT(mem_buffer, REGION_FILE_BUFFER_SIZE);
31 *state = dev;
32
33 clear_region_file(dev);
34
35 return 0;
36}
37
38static int teardown_region_file_test_group(void **state)
39{
40 struct mem_region_device *dev = *state;
41 void *mem_buffer = dev->base;
42
43 free(mem_buffer);
44 free(dev);
45
46 return 0;
47}
48
49/* This function clears buffer associated with used region_device, so tests will be in clear
50 state at the beginning and leave no trace after successful execution. The cost of memsetting
51 everything twice is known, but acceptable as it grants safety and makes tests independent. */
52static int setup_teardown_region_file_test(void **state)
53{
54 struct mem_region_device *dev = *state;
55
56 clear_region_file(dev);
57
58 return 0;
59}
60
61static void test_region_file_init_empty(void **state)
62{
63 struct mem_region_device *mdev = *state;
64 struct region_device *mrdev = &mdev->rdev;
65 struct region_file regf;
66
67 /* Test general approach using valid mem_region_device with buffer filled with 0xff.
68 Parameters cannot be NULL. */
69 assert_int_equal(0, region_file_init(&regf, mrdev));
70 assert_int_equal(RF_EMPTY, regf.slot);
71}
72
73static void test_region_file_init_invalid_metadata(void **state)
74{
75 struct mem_region_device *mdev = *state;
76 struct region_device *mrdev = &mdev->rdev;
77 uint16_t *mem_buffer16 = (uint16_t *)mdev->base;
78 struct region_file regf;
79
80 /* Set number of metadata blocks to 0 */
81 mem_buffer16[0] = 0;
82 assert_int_equal(0, region_file_init(&regf, mrdev));
83 assert_int_equal(RF_NEED_TO_EMPTY, regf.slot);
84}
85
86static void test_region_file_init_valid_no_data(void **state)
87{
88 struct mem_region_device *mdev = *state;
89 struct region_device *mrdev = &mdev->rdev;
90 uint16_t *mem_buffer16 = (uint16_t *)mdev->base;
91 struct region_file regf;
92
93 /* Manually allocate 4 metadata blocks and no data. */
94 mem_buffer16[0] = 4;
95 assert_int_equal(0, region_file_init(&regf, mrdev));
96 assert_int_equal(0, regf.slot);
97}
98
99static void test_region_file_init_invalid_data_offset(void **state)
100{
101 struct mem_region_device *mdev = *state;
102 struct region_device *mrdev = &mdev->rdev;
103 uint16_t *mem_buffer16 = (uint16_t *)mdev->base;
104 struct region_file regf;
105
106 /* Manually allocate 4 metadata blocks and no data. */
107 mem_buffer16[0] = 4;
108 mem_buffer16[1] = 4;
109 assert_int_equal(0, region_file_init(&regf, mrdev));
110 assert_int_equal(RF_NEED_TO_EMPTY, regf.slot);
111
112 /* Set data size to be larger than region */
113 mem_buffer16[0] = 4;
114 mem_buffer16[1] = 4 + 4096;
115 assert_int_equal(0, region_file_init(&regf, mrdev));
116 assert_int_equal(RF_NEED_TO_EMPTY, regf.slot);
117}
118
119static void test_region_file_init_correct_data_offset(void **state)
120{
121 struct mem_region_device *mdev = *state;
122 struct region_device *mrdev = &mdev->rdev;
123 uint16_t *mem_buffer16 = (uint16_t *)mdev->base;
124 struct region_file regf;
125
126 /* Set data size to 8 blocks which is correct value. */
127 mem_buffer16[0] = 4;
128 mem_buffer16[1] = 4 + 8;
129 assert_int_equal(0, region_file_init(&regf, mrdev));
130 assert_int_equal(1, regf.slot);
131}
132
133static void test_region_file_init_real_data(void **state)
134{
135 struct mem_region_device dev = MEM_REGION_DEV_RW_INIT(region_file_data_buffer1,
136 REGION_FILE_BUFFER_SIZE);
137 struct region_device *rdev = &dev.rdev;
138 struct region_file regf;
139
140 /* Check on real example with one update */
141 assert_int_equal(0, region_file_init(&regf, rdev));
142 /* There is one update available */
143 assert_int_equal(1, regf.slot);
144
145
146 /* Check on real example with multiple updates */
147 dev = (struct mem_region_device) MEM_REGION_DEV_RW_INIT(region_file_data_buffer2,
148 REGION_FILE_BUFFER_SIZE);
149 rdev = &dev.rdev;
150 assert_int_equal(0, region_file_init(&regf, rdev));
151 /* There are three update available */
152 assert_int_equal(3, regf.slot);
153}
154
155static void test_region_file_init_invalid_region_device(void **state)
156{
157 struct mem_region_device bad_dev = MEM_REGION_DEV_RW_INIT(NULL, 0);
158 struct region_file regf;
159
160 /* Expect fail when passing invalid region_device. */
161 assert_int_equal(-1, region_file_init(&regf, &bad_dev.rdev));
162}
163
164static void test_region_file_data(void **state)
165{
166 /* region_device with empty data buffer */
167 struct mem_region_device *mdev = *state;
168 struct region_device *mrdev = &mdev->rdev;
169 /* region_device with prepared data buffer */
170 struct mem_region_device dev = MEM_REGION_DEV_RW_INIT(region_file_data_buffer1,
171 REGION_FILE_BUFFER_SIZE);
172 struct region_device *rdev = &dev.rdev;
173 struct region_file regf;
174 struct region_device read_rdev;
175 int ret;
176
177 /* Check if region_file_data() fails to return region_device for empty region_file */
178 ret = region_file_init(&regf, mrdev);
179 assert_int_equal(0, ret);
180 ret = region_file_data(&regf, &read_rdev);
181 assert_int_equal(-1, ret);
182
183 /* Check if region_file_data() correctly returns region_device for hardcoded
184 region_file data with update of 256 bytes */
185 ret = region_file_init(&regf, rdev);
186 assert_int_equal(0, ret);
187 ret = region_file_data(&regf, &read_rdev);
188 assert_int_equal(0, ret);
189 assert_int_equal(region_device_sz(&read_rdev),
190 ALIGN_UP(region_file_data_buffer1_update_sz, 16));
191}
192
193static void test_region_file_update_data(void **state)
194{
195 struct mem_region_device *dev = *state;
196 struct region_device *rdev = &dev->rdev;
197 struct region_file regf;
198 struct region_device read_rdev;
199 const size_t dummy_data_size = 256;
200 uint8_t dummy_data[dummy_data_size];
201 uint8_t output_buffer[dummy_data_size];
202 int ret;
203
204 for (int i = 0; i < dummy_data_size; ++i)
205 dummy_data[i] = 'A' + i % ('Z' - 'A');
206
207 ret = region_file_init(&regf, rdev);
208 assert_int_equal(0, ret);
209
210 /* Write half of buffer, read it and check, if it is the same.
211 region_file_update_data() should be able to deal with empty region_file. */
212 ret = region_file_update_data(&regf, dummy_data, dummy_data_size / 2);
213 assert_int_equal(0, ret);
214 region_file_data(&regf, &read_rdev);
215 assert_int_equal(ALIGN_UP(dummy_data_size / 2, 16), region_device_sz(&read_rdev));
216 rdev_readat(&read_rdev, output_buffer, 0, dummy_data_size / 2);
217 assert_memory_equal(dummy_data, output_buffer, dummy_data_size / 2);
218
219 /* Update data to a bigger size */
220 ret = region_file_update_data(&regf, dummy_data, dummy_data_size);
221 assert_int_equal(0, ret);
222 region_file_data(&regf, &read_rdev);
223 assert_int_equal(ALIGN_UP(dummy_data_size, 16), region_device_sz(&read_rdev));
224 rdev_readat(&read_rdev, output_buffer, 0, dummy_data_size);
225 assert_memory_equal(dummy_data, output_buffer, dummy_data_size);
226
227 /* Update data to smaller size and check if it was properly stored */
228 ret = region_file_update_data(&regf, dummy_data, dummy_data_size / 2 + 3);
229 assert_int_equal(0, ret);
230 region_file_data(&regf, &read_rdev);
231 assert_int_equal(ALIGN_UP(dummy_data_size / 2 + 3, 16), region_device_sz(&read_rdev));
232 rdev_readat(&read_rdev, output_buffer, 0, dummy_data_size / 2 + 3);
233 assert_memory_equal(dummy_data, output_buffer, dummy_data_size / 2 + 3);
234}
235
236static void test_region_file_update_data_arr(void **state)
237{
238 struct mem_region_device *dev = *state;
239 struct region_device *rdev = &dev->rdev;
240 struct region_file regf;
241 struct region_device read_rdev;
242 const size_t dummy_data_size = 256;
243 uint8_t dummy_data[dummy_data_size];
244 uint8_t output_buffer[dummy_data_size * 4];
245 struct update_region_file_entry update_entries[3];
246 const size_t data1_size = dummy_data_size;
247 const size_t data2_size = dummy_data_size / 2;
248 const size_t data3_size = dummy_data_size / 4 + 3;
249 const size_t data1_offset = 0;
250 const size_t data2_offset = dummy_data_size / 4 + 2;
251 const size_t data3_offset = dummy_data_size / 8 + 5;
252 int ret;
253
254 for (int i = 0; i < dummy_data_size; ++i)
255 dummy_data[i] = 'A' + i % ('Z' - 'A');
256
257 update_entries[0] = (struct update_region_file_entry)
258 { .size = data1_size, .data = &dummy_data[data1_offset] };
259 update_entries[1] = (struct update_region_file_entry)
260 { .size = data2_size, .data = &dummy_data[data2_offset] };
261 update_entries[2] = (struct update_region_file_entry)
262 { .size = data3_size, .data = &dummy_data[data3_offset] };
263
264 ret = region_file_init(&regf, rdev);
265 assert_int_equal(0, ret);
266
267 /* Write two update blocks as first data state. region_file_update_data_arr() should
268 be able to deal with empty region_file. */
269 ret = region_file_update_data_arr(&regf, update_entries, 2);
270 assert_int_equal(0, ret);
271 region_file_data(&regf, &read_rdev);
272 assert_int_equal(ALIGN_UP(data1_size + data2_size, 16), region_device_sz(&read_rdev));
273 ret = rdev_readat(&read_rdev, output_buffer, 0, data1_size + data2_size);
274 assert_int_equal(data1_size + data2_size, ret);
275 assert_memory_equal(&dummy_data[data1_offset], output_buffer, data1_size);
276 assert_memory_equal(&dummy_data[data1_offset + data2_offset],
277 &output_buffer[data1_size], data2_size);
278
279 /* Check if new block of data is added correctly */
280 ret = region_file_update_data_arr(&regf, update_entries, 3);
281 assert_int_equal(0, ret);
282 region_file_data(&regf, &read_rdev);
283 assert_int_equal(ALIGN_UP(data1_size + data2_size + data3_size, 16),
284 region_device_sz(&read_rdev));
285 ret = rdev_readat(&read_rdev, output_buffer, 0, data1_size + data2_size + data3_size);
286 assert_int_equal(data1_size + data2_size + data3_size, ret);
287 assert_memory_equal(&dummy_data[data1_offset], output_buffer, data1_size);
288 assert_memory_equal(&dummy_data[data2_offset],
289 &output_buffer[data1_size], data2_size);
290 assert_memory_equal(&dummy_data[data3_offset],
291 &output_buffer[data1_size + data2_size], data3_size);
292
293 /* Check if data is correctly shrunk down to smaller size and different content */
294 ret = region_file_update_data_arr(&regf, &update_entries[1], 2);
295 assert_int_equal(0, ret);
296 region_file_data(&regf, &read_rdev);
297 assert_int_equal(ALIGN_UP(data2_size + data3_size, 16), region_device_sz(&read_rdev));
298 ret = rdev_readat(&read_rdev, output_buffer, 0, data2_size + data3_size);
299 assert_int_equal(data2_size + data3_size, ret);
300 assert_memory_equal(&dummy_data[data2_offset], &output_buffer[0], data2_size);
301 assert_memory_equal(&dummy_data[data3_offset], &output_buffer[data2_size], data3_size);
302}
303
304int main(void)
305{
306 const struct CMUnitTest tests[] = {
307 cmocka_unit_test_setup_teardown(test_region_file_init_empty,
308 setup_teardown_region_file_test,
309 setup_teardown_region_file_test),
310 cmocka_unit_test_setup_teardown(test_region_file_init_invalid_metadata,
311 setup_teardown_region_file_test,
312 setup_teardown_region_file_test),
313 cmocka_unit_test_setup_teardown(test_region_file_init_valid_no_data,
314 setup_teardown_region_file_test,
315 setup_teardown_region_file_test),
316 cmocka_unit_test_setup_teardown(test_region_file_init_invalid_data_offset,
317 setup_teardown_region_file_test,
318 setup_teardown_region_file_test),
319 cmocka_unit_test_setup_teardown(test_region_file_init_correct_data_offset,
320 setup_teardown_region_file_test,
321 setup_teardown_region_file_test),
322 cmocka_unit_test_setup_teardown(test_region_file_init_real_data,
323 setup_teardown_region_file_test,
324 setup_teardown_region_file_test),
325 cmocka_unit_test_setup_teardown(test_region_file_init_invalid_region_device,
326 setup_teardown_region_file_test,
327 setup_teardown_region_file_test),
328 cmocka_unit_test_setup_teardown(test_region_file_data,
329 setup_teardown_region_file_test,
330 setup_teardown_region_file_test),
331 cmocka_unit_test_setup_teardown(test_region_file_update_data,
332 setup_teardown_region_file_test,
333 setup_teardown_region_file_test),
334 cmocka_unit_test_setup_teardown(test_region_file_update_data_arr,
335 setup_teardown_region_file_test,
336 setup_teardown_region_file_test),
337 };
338
339 return cmocka_run_group_tests(tests,
340 setup_region_file_test_group,
341 teardown_region_file_test_group);
342}