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