Raw
1 #include "unit-test.h"
2 #include "lib-reftable.h"
3 #include "reftable/basics.h"
4 #include "reftable/block.h"
5 #include "reftable/blocksource.h"
6 #include "reftable/constants.h"
7 #include "reftable/iter.h"
8 #include "reftable/reftable-error.h"
9 #include "reftable/table.h"
10 #include "strbuf.h"
11
12 void test_reftable_table__seek_once(void)
13 {
14 struct reftable_ref_record records[] = {
15 {
16 .refname = (char *) "refs/heads/main",
17 .value_type = REFTABLE_REF_VAL1,
18 .value.val1 = { 42 },
19 },
20 };
21 struct reftable_block_source source = { 0 };
22 struct reftable_ref_record ref = { 0 };
23 struct reftable_iterator it = { 0 };
24 struct reftable_table *table;
25 struct reftable_buf buf = REFTABLE_BUF_INIT;
26 int ret;
27
28 cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records), NULL, 0,
29 REFTABLE_HASH_SHA1, NULL);
30 block_source_from_buf(&source, &buf);
31
32 ret = reftable_table_new(&table, &source, "name");
33 cl_assert(!ret);
34
35 ret = reftable_table_init_ref_iterator(table, &it);
36 cl_assert_equal_i(ret, 0);
37 ret = reftable_iterator_seek_ref(&it, "");
38 cl_assert(!ret);
39 ret = reftable_iterator_next_ref(&it, &ref);
40 cl_assert(!ret);
41
42 ret = reftable_ref_record_equal(&ref, &records[0],
43 REFTABLE_HASH_SIZE_SHA1);
44 cl_assert_equal_i(ret, 1);
45
46 ret = reftable_iterator_next_ref(&it, &ref);
47 cl_assert_equal_i(ret, 1);
48
49 reftable_ref_record_release(&ref);
50 reftable_iterator_destroy(&it);
51 reftable_table_decref(table);
52 reftable_buf_release(&buf);
53 }
54
55 void test_reftable_table__reseek(void)
56 {
57 struct reftable_ref_record records[] = {
58 {
59 .refname = (char *) "refs/heads/main",
60 .value_type = REFTABLE_REF_VAL1,
61 .value.val1 = { 42 },
62 },
63 };
64 struct reftable_block_source source = { 0 };
65 struct reftable_ref_record ref = { 0 };
66 struct reftable_iterator it = { 0 };
67 struct reftable_table *table;
68 struct reftable_buf buf = REFTABLE_BUF_INIT;
69 int ret;
70
71 cl_reftable_write_to_buf(&buf, records, ARRAY_SIZE(records),
72 NULL, 0, REFTABLE_HASH_SHA1, NULL);
73 block_source_from_buf(&source, &buf);
74
75 ret = reftable_table_new(&table, &source, "name");
76 cl_assert(!ret);
77
78 ret = reftable_table_init_ref_iterator(table, &it);
79 cl_assert_equal_i(ret, 0);
80
81 for (size_t i = 0; i < 5; i++) {
82 ret = reftable_iterator_seek_ref(&it, "");
83 cl_assert(!ret);
84 ret = reftable_iterator_next_ref(&it, &ref);
85 cl_assert(!ret);
86
87 ret = reftable_ref_record_equal(&ref, &records[0], REFTABLE_HASH_SIZE_SHA1);
88 cl_assert_equal_i(ret, 1);
89
90 ret = reftable_iterator_next_ref(&it, &ref);
91 cl_assert_equal_i(ret, 1);
92 }
93
94 reftable_ref_record_release(&ref);
95 reftable_iterator_destroy(&it);
96 reftable_table_decref(table);
97 reftable_buf_release(&buf);
98 }
99
100 void test_reftable_table__block_iterator(void)
101 {
102 struct reftable_block_source source = { 0 };
103 struct reftable_table_iterator it = { 0 };
104 struct reftable_ref_record *records;
105 const struct reftable_block *block;
106 struct reftable_table *table;
107 struct reftable_buf buf = REFTABLE_BUF_INIT;
108 struct {
109 uint8_t block_type;
110 uint16_t header_off;
111 uint16_t restart_count;
112 uint16_t record_count;
113 } expected_blocks[] = {
114 {
115 .block_type = REFTABLE_BLOCK_TYPE_REF,
116 .header_off = 24,
117 .restart_count = 10,
118 .record_count = 158,
119 },
120 {
121 .block_type = REFTABLE_BLOCK_TYPE_REF,
122 .restart_count = 10,
123 .record_count = 159,
124 },
125 {
126 .block_type = REFTABLE_BLOCK_TYPE_REF,
127 .restart_count = 10,
128 .record_count = 159,
129 },
130 {
131 .block_type = REFTABLE_BLOCK_TYPE_REF,
132 .restart_count = 2,
133 .record_count = 24,
134 },
135 {
136 .block_type = REFTABLE_BLOCK_TYPE_INDEX,
137 .restart_count = 1,
138 .record_count = 4,
139 },
140 {
141 .block_type = REFTABLE_BLOCK_TYPE_OBJ,
142 .restart_count = 1,
143 .record_count = 1,
144 },
145 };
146 const size_t nrecords = 500;
147 int ret;
148
149 REFTABLE_CALLOC_ARRAY(records, nrecords);
150 for (size_t i = 0; i < nrecords; i++) {
151 records[i].value_type = REFTABLE_REF_VAL1;
152 records[i].refname = xstrfmt("refs/heads/branch-%03"PRIuMAX,
153 (uintmax_t) i);
154 }
155
156 cl_reftable_write_to_buf(&buf, records, nrecords, NULL, 0,
157 REFTABLE_HASH_SHA1, NULL);
158 block_source_from_buf(&source, &buf);
159
160 ret = reftable_table_new(&table, &source, "name");
161 cl_assert(!ret);
162
163 ret = reftable_table_iterator_init(&it, table);
164 cl_assert(!ret);
165
166 for (size_t i = 0; i < ARRAY_SIZE(expected_blocks); i++) {
167 struct reftable_iterator record_it = { 0 };
168 struct reftable_record record = {
169 .type = expected_blocks[i].block_type,
170 };
171
172 ret = reftable_table_iterator_next(&it, &block);
173 cl_assert(!ret);
174
175 cl_assert_equal_i(block->block_type,
176 expected_blocks[i].block_type);
177 cl_assert_equal_i(block->header_off,
178 expected_blocks[i].header_off);
179 cl_assert_equal_i(block->restart_count,
180 expected_blocks[i].restart_count);
181
182 ret = reftable_block_init_iterator(block, &record_it);
183 cl_assert(!ret);
184
185 for (size_t j = 0; ; j++) {
186 ret = iterator_next(&record_it, &record);
187 if (ret > 0) {
188 cl_assert_equal_i(j,
189 expected_blocks[i].record_count);
190 break;
191 }
192 cl_assert(!ret);
193 }
194
195 reftable_iterator_destroy(&record_it);
196 reftable_record_release(&record);
197 }
198
199 ret = reftable_table_iterator_next(&it, &block);
200 cl_assert_equal_i(ret, 1);
201
202 for (size_t i = 0; i < nrecords; i++)
203 reftable_free(records[i].refname);
204 reftable_table_iterator_release(&it);
205 reftable_table_decref(table);
206 reftable_buf_release(&buf);
207 reftable_free(records);
208 }
209
210 void test_reftable_table__seek_invalid_log_offset(void)
211 {
212 struct reftable_ref_record refs[] = {
213 {
214 .refname = (char *) "refs/heads/main",
215 .value_type = REFTABLE_REF_VAL1,
216 .value.val1 = { 42 },
217 },
218 };
219 struct reftable_log_record logs[] = {
220 {
221 .refname = (char *) "refs/heads/main",
222 .update_index = 1,
223 .value_type = REFTABLE_LOG_UPDATE,
224 .value.update = {
225 .name = (char *) "user",
226 .email = (char *) "user@example.com",
227 .message = (char *) "message\n",
228 },
229 },
230 };
231 struct reftable_block_source source = { 0 };
232 struct reftable_log_record log = { 0 };
233 struct reftable_iterator it = { 0 };
234 struct reftable_table *table;
235 struct reftable_buf buf = REFTABLE_BUF_INIT;
236 size_t fsize = footer_size(1);
237 uint8_t *footer;
238
239 cl_reftable_write_to_buf(&buf, refs, ARRAY_SIZE(refs),
240 logs, ARRAY_SIZE(logs), REFTABLE_HASH_SHA1, NULL);
241
242 /*
243 * Corrupt the log section offset stored in the footer so that it points
244 * past the end of the table. The footer is checksummed, so we also have
245 * to recompute and rewrite the CRC.
246 */
247 footer = (uint8_t *) buf.buf + buf.len - fsize;
248 reftable_put_be64(footer + header_size(1) + 24, UINT64_MAX);
249 reftable_put_be32(footer + fsize - 4, crc32(0, footer, fsize - 4));
250
251 block_source_from_buf(&source, &buf);
252 cl_must_pass(reftable_table_new(&table, &source, "name"));
253
254 /*
255 * Seeking the log iterator must not crash even though the log section
256 * offset is bogus. As the offset points past the end of the table we
257 * know that the table is corrupt, so the seek must report a format
258 * error instead of pretending that the section is empty.
259 */
260 reftable_table_init_log_iterator(table, &it);
261 cl_assert_equal_i(reftable_iterator_seek_log(&it, ""),
262 REFTABLE_FORMAT_ERROR);
263
264 reftable_log_record_release(&log);
265 reftable_iterator_destroy(&it);
266 reftable_table_decref(table);
267 reftable_buf_release(&buf);
268 }
269
270 void test_reftable_table__new_with_truncated_table(void)
271 {
272 struct reftable_ref_record refs[] = {
273 {
274 .refname = (char *) "refs/heads/main",
275 .value_type = REFTABLE_REF_VAL1,
276 .value.val1 = { 42 },
277 },
278 };
279 struct reftable_block_source source = { 0 };
280 struct reftable_table *table;
281 struct reftable_buf buf = REFTABLE_BUF_INIT;
282
283 cl_reftable_write_to_buf(&buf, refs, ARRAY_SIZE(refs), NULL, 0,
284 REFTABLE_HASH_SHA1, NULL);
285
286 /*
287 * Truncate the table so that it is large enough to read the header, but
288 * too small to also contain the footer.
289 */
290 buf.len = footer_size(1) - 1;
291 block_source_from_buf(&source, &buf);
292
293 cl_assert_equal_i(reftable_table_new(&table, &source, "name"),
294 REFTABLE_FORMAT_ERROR);
295
296 reftable_buf_release(&buf);
297 }