summaryrefslogtreecommitdiff
path: root/tests/lib/region_file-test.c
blob: 98c4eb650b0f75645d985f5e4d6f9b1ffe890f94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/* SPDX-License-Identifier: GPL-2.0-only */

#include "../lib/region_file.c"

#include <tests/test.h>
#include <stdlib.h>
#include <string.h>
#include <commonlib/region.h>
#include <tests/lib/region_file_data.h>

static void clear_region_file(struct region_device *rdev)
{
	memset(rdev_mmap_full(rdev), 0xff, REGION_FILE_BUFFER_SIZE);
}

static int setup_region_file_test_group(void **state)
{
	void *mem_buffer = malloc(REGION_FILE_BUFFER_SIZE);
	struct region_device *dev = malloc(sizeof(struct region_device));

	if (mem_buffer == NULL || dev == NULL) {
		free(mem_buffer);
		free(dev);
		return -1;
	}

	rdev_chain_mem_rw(dev, mem_buffer, REGION_FILE_BUFFER_SIZE);
	*state = dev;

	clear_region_file(dev);

	return 0;
}

static int teardown_region_file_test_group(void **state)
{
	struct region_device *dev = *state;
	void *mem_buffer = rdev_mmap_full(dev);

	free(mem_buffer);
	free(dev);

	return 0;
}

/* This function clears buffer associated with used region_device, so tests will be in clear
   state at the beginning and leave no trace after successful execution. The cost of memsetting
   everything twice is known, but acceptable as it grants safety and makes tests independent. */
static int setup_teardown_region_file_test(void **state)
{
	struct region_device *dev = *state;

	clear_region_file(dev);

	return 0;
}

static void test_region_file_init_empty(void **state)
{
	struct region_device *rdev = *state;
	struct region_file regf;

	/* Test general approach using valid mem_region_device with buffer filled with 0xff.
	   Parameters cannot be NULL. */
	assert_int_equal(0, region_file_init(&regf, rdev));
	assert_int_equal(RF_EMPTY, regf.slot);
}

static void test_region_file_init_invalid_metadata(void **state)
{
	struct region_device *rdev = *state;
	uint16_t *mem_buffer16 = (uint16_t *)rdev_mmap_full(rdev);
	struct region_file regf;

	/* Set number of metadata blocks to 0 */
	mem_buffer16[0] = 0;
	assert_int_equal(0, region_file_init(&regf, rdev));
	assert_int_equal(RF_NEED_TO_EMPTY, regf.slot);
}

static void test_region_file_init_valid_no_data(void **state)
{
	struct region_device *rdev = *state;
	uint16_t *mem_buffer16 = (uint16_t *)rdev_mmap_full(rdev);
	struct region_file regf;

	/* Manually allocate 4 metadata blocks and no data. */
	mem_buffer16[0] = 4;
	assert_int_equal(0, region_file_init(&regf, rdev));
	assert_int_equal(0, regf.slot);
}

static void test_region_file_init_invalid_data_offset(void **state)
{
	struct region_device *rdev = *state;
	uint16_t *mem_buffer16 = (uint16_t *)rdev_mmap_full(rdev);
	struct region_file regf;

	/* Manually allocate 4 metadata blocks and no data. */
	mem_buffer16[0] = 4;
	mem_buffer16[1] = 4;
	assert_int_equal(0, region_file_init(&regf, rdev));
	assert_int_equal(RF_NEED_TO_EMPTY, regf.slot);

	/* Set data size to be larger than region */
	mem_buffer16[0] = 4;
	mem_buffer16[1] = 4 + 4096;
	assert_int_equal(0, region_file_init(&regf, rdev));
	assert_int_equal(RF_NEED_TO_EMPTY, regf.slot);
}

static void test_region_file_init_correct_data_offset(void **state)
{
	struct region_device *rdev = *state;
	uint16_t *mem_buffer16 = (uint16_t *)rdev_mmap_full(rdev);
	struct region_file regf;

	/* Set data size to 8 blocks which is correct value. */
	mem_buffer16[0] = 4;
	mem_buffer16[1] = 4 + 8;
	assert_int_equal(0, region_file_init(&regf, rdev));
	assert_int_equal(1, regf.slot);
}

static void test_region_file_init_real_data(void **state)
{
	struct region_device rdev;
	struct region_file regf;

	rdev_chain_mem_rw(&rdev, region_file_data_buffer1, REGION_FILE_BUFFER_SIZE);

	/* Check on real example with one update */
	assert_int_equal(0, region_file_init(&regf, &rdev));
	/* There is one update available */
	assert_int_equal(1, regf.slot);


	/* Check on real example with multiple updates */
	rdev_chain_mem_rw(&rdev, region_file_data_buffer2, REGION_FILE_BUFFER_SIZE);
	assert_int_equal(0, region_file_init(&regf, &rdev));
	/* There are three update available */
	assert_int_equal(3, regf.slot);
}

static void test_region_file_init_invalid_region_device(void **state)
{
	struct region_device bad_dev;
	struct region_file regf;

	rdev_chain_mem_rw(&bad_dev, NULL, 0);

	/* Expect fail when passing invalid region_device. */
	assert_int_equal(-1, region_file_init(&regf, &bad_dev));
}

static void test_region_file_data(void **state)
{
	/* region_device with empty data buffer */
	struct region_device *mrdev = *state;
	/* region_device with prepared data buffer */
	struct region_device rdev;
	rdev_chain_mem_rw(&rdev, region_file_data_buffer1, REGION_FILE_BUFFER_SIZE);

	struct region_file regf;
	struct region_device read_rdev;
	int ret;

	/* Check if region_file_data() fails to return region_device for empty region_file */
	ret = region_file_init(&regf, mrdev);
	assert_int_equal(0, ret);
	ret = region_file_data(&regf, &read_rdev);
	assert_int_equal(-1, ret);

	/* Check if region_file_data() correctly returns region_device for hardcoded
	   region_file data with update of 256 bytes */
	ret = region_file_init(&regf, &rdev);
	assert_int_equal(0, ret);
	ret = region_file_data(&regf, &read_rdev);
	assert_int_equal(0, ret);
	assert_int_equal(region_device_sz(&read_rdev),
			 ALIGN_UP(region_file_data_buffer1_update_sz, 16));
}

static void test_region_file_update_data(void **state)
{
	struct region_device *rdev = *state;
	struct region_file regf;
	struct region_device read_rdev;
	const size_t dummy_data_size = 256;
	uint8_t dummy_data[dummy_data_size];
	uint8_t output_buffer[dummy_data_size];
	int ret;

	for (int i = 0; i < dummy_data_size; ++i)
		dummy_data[i] = 'A' + i % ('Z' - 'A');

	ret = region_file_init(&regf, rdev);
	assert_int_equal(0, ret);

	/* Write half of buffer, read it and check, if it is the same.
	   region_file_update_data() should be able to deal with empty region_file. */
	ret = region_file_update_data(&regf, dummy_data, dummy_data_size / 2);
	assert_int_equal(0, ret);
	region_file_data(&regf, &read_rdev);
	assert_int_equal(ALIGN_UP(dummy_data_size / 2, 16), region_device_sz(&read_rdev));
	rdev_readat(&read_rdev, output_buffer, 0, dummy_data_size / 2);
	assert_memory_equal(dummy_data, output_buffer, dummy_data_size / 2);

	/* Update data to a bigger size */
	ret = region_file_update_data(&regf, dummy_data, dummy_data_size);
	assert_int_equal(0, ret);
	region_file_data(&regf, &read_rdev);
	assert_int_equal(ALIGN_UP(dummy_data_size, 16), region_device_sz(&read_rdev));
	rdev_readat(&read_rdev, output_buffer, 0, dummy_data_size);
	assert_memory_equal(dummy_data, output_buffer, dummy_data_size);

	/* Update data to smaller size and check if it was properly stored */
	ret = region_file_update_data(&regf, dummy_data, dummy_data_size / 2 + 3);
	assert_int_equal(0, ret);
	region_file_data(&regf, &read_rdev);
	assert_int_equal(ALIGN_UP(dummy_data_size / 2 + 3, 16), region_device_sz(&read_rdev));
	rdev_readat(&read_rdev, output_buffer, 0, dummy_data_size / 2 + 3);
	assert_memory_equal(dummy_data, output_buffer, dummy_data_size / 2 + 3);
}

static void test_region_file_update_data_arr(void **state)
{
	struct region_device *rdev = *state;
	struct region_file regf;
	struct region_device read_rdev;
	const size_t dummy_data_size = 256;
	uint8_t dummy_data[dummy_data_size];
	uint8_t output_buffer[dummy_data_size * 4];
	struct update_region_file_entry update_entries[3];
	const size_t data1_size = dummy_data_size;
	const size_t data2_size = dummy_data_size / 2;
	const size_t data3_size = dummy_data_size / 4 + 3;
	const size_t data1_offset = 0;
	const size_t data2_offset = dummy_data_size / 4 + 2;
	const size_t data3_offset = dummy_data_size / 8 + 5;
	int ret;

	for (int i = 0; i < dummy_data_size; ++i)
		dummy_data[i] = 'A' + i % ('Z' - 'A');

	update_entries[0] = (struct update_region_file_entry)
				{ .size = data1_size, .data = &dummy_data[data1_offset] };
	update_entries[1] = (struct update_region_file_entry)
				{ .size = data2_size, .data = &dummy_data[data2_offset] };
	update_entries[2] = (struct update_region_file_entry)
				{ .size = data3_size, .data = &dummy_data[data3_offset] };

	ret = region_file_init(&regf, rdev);
	assert_int_equal(0, ret);

	/* Write two update blocks as first data state. region_file_update_data_arr() should
	   be able to deal with empty region_file. */
	ret = region_file_update_data_arr(&regf, update_entries, 2);
	assert_int_equal(0, ret);
	region_file_data(&regf, &read_rdev);
	assert_int_equal(ALIGN_UP(data1_size + data2_size, 16), region_device_sz(&read_rdev));
	ret = rdev_readat(&read_rdev, output_buffer, 0, data1_size + data2_size);
	assert_int_equal(data1_size + data2_size, ret);
	assert_memory_equal(&dummy_data[data1_offset], output_buffer, data1_size);
	assert_memory_equal(&dummy_data[data1_offset + data2_offset],
			    &output_buffer[data1_size], data2_size);

	/* Check if new block of data is added correctly */
	ret = region_file_update_data_arr(&regf, update_entries, 3);
	assert_int_equal(0, ret);
	region_file_data(&regf, &read_rdev);
	assert_int_equal(ALIGN_UP(data1_size + data2_size + data3_size, 16),
			 region_device_sz(&read_rdev));
	ret = rdev_readat(&read_rdev, output_buffer, 0, data1_size + data2_size + data3_size);
	assert_int_equal(data1_size + data2_size + data3_size, ret);
	assert_memory_equal(&dummy_data[data1_offset], output_buffer, data1_size);
	assert_memory_equal(&dummy_data[data2_offset],
			    &output_buffer[data1_size], data2_size);
	assert_memory_equal(&dummy_data[data3_offset],
			    &output_buffer[data1_size + data2_size], data3_size);

	/* Check if data is correctly shrunk down to smaller size and different content */
	ret = region_file_update_data_arr(&regf, &update_entries[1], 2);
	assert_int_equal(0, ret);
	region_file_data(&regf, &read_rdev);
	assert_int_equal(ALIGN_UP(data2_size + data3_size, 16), region_device_sz(&read_rdev));
	ret = rdev_readat(&read_rdev, output_buffer, 0, data2_size + data3_size);
	assert_int_equal(data2_size + data3_size, ret);
	assert_memory_equal(&dummy_data[data2_offset], &output_buffer[0], data2_size);
	assert_memory_equal(&dummy_data[data3_offset], &output_buffer[data2_size], data3_size);
}

int main(void)
{
	const struct CMUnitTest tests[] = {
		cmocka_unit_test_setup_teardown(test_region_file_init_empty,
				setup_teardown_region_file_test,
				setup_teardown_region_file_test),
		cmocka_unit_test_setup_teardown(test_region_file_init_invalid_metadata,
				setup_teardown_region_file_test,
				setup_teardown_region_file_test),
		cmocka_unit_test_setup_teardown(test_region_file_init_valid_no_data,
				setup_teardown_region_file_test,
				setup_teardown_region_file_test),
		cmocka_unit_test_setup_teardown(test_region_file_init_invalid_data_offset,
				setup_teardown_region_file_test,
				setup_teardown_region_file_test),
		cmocka_unit_test_setup_teardown(test_region_file_init_correct_data_offset,
				setup_teardown_region_file_test,
				setup_teardown_region_file_test),
		cmocka_unit_test_setup_teardown(test_region_file_init_real_data,
				setup_teardown_region_file_test,
				setup_teardown_region_file_test),
		cmocka_unit_test_setup_teardown(test_region_file_init_invalid_region_device,
				setup_teardown_region_file_test,
				setup_teardown_region_file_test),
		cmocka_unit_test_setup_teardown(test_region_file_data,
				setup_teardown_region_file_test,
				setup_teardown_region_file_test),
		cmocka_unit_test_setup_teardown(test_region_file_update_data,
				setup_teardown_region_file_test,
				setup_teardown_region_file_test),
		cmocka_unit_test_setup_teardown(test_region_file_update_data_arr,
				setup_teardown_region_file_test,
				setup_teardown_region_file_test),
	};

	return cb_run_group_tests(tests, setup_region_file_test_group,
				  teardown_region_file_test_group);
}