Line data Source code
1 : /* Copyright (c) 2014, Daniel MartÃ
2 : * Copyright (c) 2014-2021, The Tor Project, Inc. */
3 : /* See LICENSE for licensing information */
4 :
5 : #define CONSDIFF_PRIVATE
6 :
7 : #include "core/or/or.h"
8 : #include "test/test.h"
9 :
10 : #include "feature/dircommon/consdiff.h"
11 : #include "lib/memarea/memarea.h"
12 : #include "test/log_test_helpers.h"
13 :
14 : #define tt_str_eq_line(a,b) \
15 : tt_assert(line_str_eq((b),(a)))
16 :
17 : static int
18 26 : consensus_split_lines_(smartlist_t *out, const char *s, memarea_t *area)
19 : {
20 26 : size_t len = strlen(s);
21 26 : return consensus_split_lines(out, s, len, area);
22 : }
23 :
24 : static int
25 2 : consensus_compute_digest_(const char *cons,
26 : consensus_digest_t *digest_out)
27 : {
28 2 : size_t len = strlen(cons);
29 2 : char *tmp = tor_memdup(cons, len);
30 : // We use memdup here to ensure that the input is NOT nul-terminated.
31 : // This makes it likelier for us to spot bugs.
32 2 : int r = consensus_compute_digest(tmp, len, digest_out);
33 2 : tor_free(tmp);
34 2 : return r;
35 : }
36 :
37 : static int
38 2 : consensus_compute_digest_as_signed_(const char *cons,
39 : consensus_digest_t *digest_out)
40 : {
41 2 : size_t len = strlen(cons);
42 2 : char *tmp = tor_memdup(cons, len);
43 : // We use memdup here to ensure that the input is NOT nul-terminated.
44 : // This makes it likelier for us to spot bugs.
45 2 : int r = consensus_compute_digest_as_signed(tmp, len, digest_out);
46 2 : tor_free(tmp);
47 2 : return r;
48 : }
49 :
50 : static void
51 1 : test_consdiff_smartlist_slice(void *arg)
52 : {
53 1 : smartlist_t *sl = smartlist_new();
54 1 : smartlist_slice_t *sls;
55 1 : int items[6] = {0,0,0,0,0,0};
56 :
57 : /* Create a regular smartlist. */
58 1 : (void)arg;
59 1 : smartlist_add(sl, &items[1]);
60 1 : smartlist_add(sl, &items[2]);
61 1 : smartlist_add(sl, &items[3]);
62 1 : smartlist_add(sl, &items[4]);
63 1 : smartlist_add(sl, &items[5]);
64 :
65 : /* See if the slice was done correctly. */
66 1 : sls = smartlist_slice(sl, 2, 5);
67 1 : tt_ptr_op(sl, OP_EQ, sls->list);
68 1 : tt_ptr_op(&items[3], OP_EQ, smartlist_get(sls->list, sls->offset));
69 1 : tt_ptr_op(&items[5], OP_EQ,
70 : smartlist_get(sls->list, sls->offset + (sls->len-1)));
71 1 : tor_free(sls);
72 :
73 : /* See that using -1 as the end does get to the last element. */
74 1 : sls = smartlist_slice(sl, 2, -1);
75 1 : tt_ptr_op(sl, OP_EQ, sls->list);
76 1 : tt_ptr_op(&items[3], OP_EQ, smartlist_get(sls->list, sls->offset));
77 1 : tt_ptr_op(&items[5], OP_EQ,
78 : smartlist_get(sls->list, sls->offset + (sls->len-1)));
79 :
80 1 : done:
81 1 : tor_free(sls);
82 1 : smartlist_free(sl);
83 1 : }
84 :
85 : static void
86 1 : test_consdiff_smartlist_slice_string_pos(void *arg)
87 : {
88 1 : smartlist_t *sl = smartlist_new();
89 1 : smartlist_slice_t *sls;
90 1 : memarea_t *area = memarea_new();
91 :
92 : /* Create a regular smartlist. */
93 1 : (void)arg;
94 1 : consensus_split_lines_(sl, "a\nd\nc\na\nb\n", area);
95 :
96 : /* See that smartlist_slice_string_pos respects the bounds of the slice. */
97 1 : sls = smartlist_slice(sl, 2, 5);
98 1 : cdline_t a_line = { "a", 1 };
99 1 : tt_int_op(3, OP_EQ, smartlist_slice_string_pos(sls, &a_line));
100 1 : cdline_t d_line = { "d", 1 };
101 1 : tt_int_op(-1, OP_EQ, smartlist_slice_string_pos(sls, &d_line));
102 :
103 1 : done:
104 1 : tor_free(sls);
105 1 : smartlist_free(sl);
106 1 : memarea_drop_all(area);
107 1 : }
108 :
109 : static void
110 1 : test_consdiff_lcs_lengths(void *arg)
111 : {
112 1 : smartlist_t *sl1 = smartlist_new();
113 1 : smartlist_t *sl2 = smartlist_new();
114 1 : smartlist_slice_t *sls1, *sls2;
115 1 : int *lengths1, *lengths2;
116 1 : memarea_t *area = memarea_new();
117 :
118 : /* Expected lcs lengths in regular and reverse order. */
119 1 : int e_lengths1[] = { 0, 1, 2, 3, 3, 4 };
120 1 : int e_lengths2[] = { 0, 1, 1, 2, 3, 4 };
121 :
122 1 : (void)arg;
123 1 : consensus_split_lines_(sl1, "a\nb\nc\nd\ne\n", area);
124 1 : consensus_split_lines_(sl2, "a\nc\nd\ni\ne\n", area);
125 :
126 1 : sls1 = smartlist_slice(sl1, 0, -1);
127 1 : sls2 = smartlist_slice(sl2, 0, -1);
128 :
129 1 : lengths1 = lcs_lengths(sls1, sls2, 1);
130 1 : lengths2 = lcs_lengths(sls1, sls2, -1);
131 1 : tt_mem_op(e_lengths1, OP_EQ, lengths1, sizeof(int) * 6);
132 1 : tt_mem_op(e_lengths2, OP_EQ, lengths2, sizeof(int) * 6);
133 :
134 1 : done:
135 1 : tor_free(lengths1);
136 1 : tor_free(lengths2);
137 1 : tor_free(sls1);
138 1 : tor_free(sls2);
139 1 : smartlist_free(sl1);
140 1 : smartlist_free(sl2);
141 1 : memarea_drop_all(area);
142 1 : }
143 :
144 : static void
145 1 : test_consdiff_trim_slices(void *arg)
146 : {
147 1 : smartlist_t *sl1 = smartlist_new();
148 1 : smartlist_t *sl2 = smartlist_new();
149 1 : smartlist_t *sl3 = smartlist_new();
150 1 : smartlist_t *sl4 = smartlist_new();
151 1 : smartlist_slice_t *sls1, *sls2, *sls3, *sls4;
152 1 : memarea_t *area = memarea_new();
153 :
154 1 : (void)arg;
155 1 : consensus_split_lines_(sl1, "a\nb\nb\nb\nd\n", area);
156 1 : consensus_split_lines_(sl2, "a\nc\nc\nc\nd\n", area);
157 1 : consensus_split_lines_(sl3, "a\nb\nb\nb\na\n", area);
158 1 : consensus_split_lines_(sl4, "c\nb\nb\nb\nc\n", area);
159 1 : sls1 = smartlist_slice(sl1, 0, -1);
160 1 : sls2 = smartlist_slice(sl2, 0, -1);
161 1 : sls3 = smartlist_slice(sl3, 0, -1);
162 1 : sls4 = smartlist_slice(sl4, 0, -1);
163 :
164 : /* They should be trimmed by one line at each end. */
165 1 : tt_int_op(5, OP_EQ, sls1->len);
166 1 : tt_int_op(5, OP_EQ, sls2->len);
167 1 : trim_slices(sls1, sls2);
168 1 : tt_int_op(3, OP_EQ, sls1->len);
169 1 : tt_int_op(3, OP_EQ, sls2->len);
170 :
171 : /* They should not be trimmed at all. */
172 1 : tt_int_op(5, OP_EQ, sls3->len);
173 1 : tt_int_op(5, OP_EQ, sls4->len);
174 1 : trim_slices(sls3, sls4);
175 1 : tt_int_op(5, OP_EQ, sls3->len);
176 1 : tt_int_op(5, OP_EQ, sls4->len);
177 :
178 1 : done:
179 1 : tor_free(sls1);
180 1 : tor_free(sls2);
181 1 : tor_free(sls3);
182 1 : tor_free(sls4);
183 1 : smartlist_free(sl1);
184 1 : smartlist_free(sl2);
185 1 : smartlist_free(sl3);
186 1 : smartlist_free(sl4);
187 1 : memarea_drop_all(area);
188 1 : }
189 :
190 : static void
191 1 : test_consdiff_set_changed(void *arg)
192 : {
193 1 : smartlist_t *sl1 = smartlist_new();
194 1 : smartlist_t *sl2 = smartlist_new();
195 1 : bitarray_t *changed1 = bitarray_init_zero(4);
196 1 : bitarray_t *changed2 = bitarray_init_zero(4);
197 1 : smartlist_slice_t *sls1, *sls2;
198 1 : memarea_t *area = memarea_new();
199 :
200 1 : (void)arg;
201 1 : consensus_split_lines_(sl1, "a\nb\na\na\n", area);
202 1 : consensus_split_lines_(sl2, "a\na\na\na\n", area);
203 :
204 : /* Length of sls1 is 0. */
205 1 : sls1 = smartlist_slice(sl1, 0, 0);
206 1 : sls2 = smartlist_slice(sl2, 1, 3);
207 1 : set_changed(changed1, changed2, sls1, sls2);
208 :
209 : /* The former is not changed, the latter changes all of its elements. */
210 1 : tt_assert(!bitarray_is_set(changed1, 0));
211 1 : tt_assert(!bitarray_is_set(changed1, 1));
212 1 : tt_assert(!bitarray_is_set(changed1, 2));
213 1 : tt_assert(!bitarray_is_set(changed1, 3));
214 :
215 1 : tt_assert(!bitarray_is_set(changed2, 0));
216 1 : tt_assert(bitarray_is_set(changed2, 1));
217 1 : tt_assert(bitarray_is_set(changed2, 2));
218 1 : tt_assert(!bitarray_is_set(changed2, 3));
219 1 : bitarray_clear(changed2, 1);
220 1 : bitarray_clear(changed2, 2);
221 :
222 : /* Length of sls1 is 1 and its element is in sls2. */
223 1 : tor_free(sls1);
224 1 : sls1 = smartlist_slice(sl1, 0, 1);
225 1 : set_changed(changed1, changed2, sls1, sls2);
226 :
227 : /* The latter changes all elements but the (first) common one. */
228 1 : tt_assert(!bitarray_is_set(changed1, 0));
229 1 : tt_assert(!bitarray_is_set(changed1, 1));
230 1 : tt_assert(!bitarray_is_set(changed1, 2));
231 1 : tt_assert(!bitarray_is_set(changed1, 3));
232 :
233 1 : tt_assert(!bitarray_is_set(changed2, 0));
234 1 : tt_assert(!bitarray_is_set(changed2, 1));
235 1 : tt_assert(bitarray_is_set(changed2, 2));
236 1 : tt_assert(!bitarray_is_set(changed2, 3));
237 1 : bitarray_clear(changed2, 2);
238 :
239 : /* Length of sls1 is 1 and its element is not in sls2. */
240 1 : tor_free(sls1);
241 1 : sls1 = smartlist_slice(sl1, 1, 2);
242 1 : set_changed(changed1, changed2, sls1, sls2);
243 :
244 : /* The former changes its element, the latter changes all elements. */
245 1 : tt_assert(!bitarray_is_set(changed1, 0));
246 1 : tt_assert(bitarray_is_set(changed1, 1));
247 1 : tt_assert(!bitarray_is_set(changed1, 2));
248 1 : tt_assert(!bitarray_is_set(changed1, 3));
249 :
250 1 : tt_assert(!bitarray_is_set(changed2, 0));
251 1 : tt_assert(bitarray_is_set(changed2, 1));
252 1 : tt_assert(bitarray_is_set(changed2, 2));
253 1 : tt_assert(!bitarray_is_set(changed2, 3));
254 :
255 1 : done:
256 1 : bitarray_free(changed1);
257 1 : bitarray_free(changed2);
258 1 : smartlist_free(sl1);
259 1 : smartlist_free(sl2);
260 1 : tor_free(sls1);
261 1 : tor_free(sls2);
262 1 : memarea_drop_all(area);
263 1 : }
264 :
265 : static void
266 1 : test_consdiff_calc_changes(void *arg)
267 : {
268 1 : smartlist_t *sl1 = smartlist_new();
269 1 : smartlist_t *sl2 = smartlist_new();
270 1 : smartlist_slice_t *sls1, *sls2;
271 1 : bitarray_t *changed1 = bitarray_init_zero(4);
272 1 : bitarray_t *changed2 = bitarray_init_zero(4);
273 1 : memarea_t *area = memarea_new();
274 :
275 1 : (void)arg;
276 1 : consensus_split_lines_(sl1, "a\na\na\na\n", area);
277 1 : consensus_split_lines_(sl2, "a\na\na\na\n", area);
278 :
279 1 : sls1 = smartlist_slice(sl1, 0, -1);
280 1 : sls2 = smartlist_slice(sl2, 0, -1);
281 1 : calc_changes(sls1, sls2, changed1, changed2);
282 :
283 : /* Nothing should be set to changed. */
284 1 : tt_assert(!bitarray_is_set(changed1, 0));
285 1 : tt_assert(!bitarray_is_set(changed1, 1));
286 1 : tt_assert(!bitarray_is_set(changed1, 2));
287 1 : tt_assert(!bitarray_is_set(changed1, 3));
288 :
289 1 : tt_assert(!bitarray_is_set(changed2, 0));
290 1 : tt_assert(!bitarray_is_set(changed2, 1));
291 1 : tt_assert(!bitarray_is_set(changed2, 2));
292 1 : tt_assert(!bitarray_is_set(changed2, 3));
293 :
294 1 : smartlist_clear(sl2);
295 1 : consensus_split_lines_(sl2, "a\nb\na\nb\n", area);
296 1 : tor_free(sls1);
297 1 : tor_free(sls2);
298 1 : sls1 = smartlist_slice(sl1, 0, -1);
299 1 : sls2 = smartlist_slice(sl2, 0, -1);
300 1 : calc_changes(sls1, sls2, changed1, changed2);
301 :
302 : /* Two elements are changed. */
303 1 : tt_assert(!bitarray_is_set(changed1, 0));
304 1 : tt_assert(bitarray_is_set(changed1, 1));
305 1 : tt_assert(bitarray_is_set(changed1, 2));
306 1 : tt_assert(!bitarray_is_set(changed1, 3));
307 1 : bitarray_clear(changed1, 1);
308 1 : bitarray_clear(changed1, 2);
309 :
310 1 : tt_assert(!bitarray_is_set(changed2, 0));
311 1 : tt_assert(bitarray_is_set(changed2, 1));
312 1 : tt_assert(!bitarray_is_set(changed2, 2));
313 1 : tt_assert(bitarray_is_set(changed2, 3));
314 1 : bitarray_clear(changed1, 1);
315 1 : bitarray_clear(changed1, 3);
316 :
317 1 : smartlist_clear(sl2);
318 1 : consensus_split_lines_(sl2, "b\nb\nb\nb\n", area);
319 1 : tor_free(sls1);
320 1 : tor_free(sls2);
321 1 : sls1 = smartlist_slice(sl1, 0, -1);
322 1 : sls2 = smartlist_slice(sl2, 0, -1);
323 1 : calc_changes(sls1, sls2, changed1, changed2);
324 :
325 : /* All elements are changed. */
326 1 : tt_assert(bitarray_is_set(changed1, 0));
327 1 : tt_assert(bitarray_is_set(changed1, 1));
328 1 : tt_assert(bitarray_is_set(changed1, 2));
329 1 : tt_assert(bitarray_is_set(changed1, 3));
330 :
331 1 : tt_assert(bitarray_is_set(changed2, 0));
332 1 : tt_assert(bitarray_is_set(changed2, 1));
333 1 : tt_assert(bitarray_is_set(changed2, 2));
334 1 : tt_assert(bitarray_is_set(changed2, 3));
335 :
336 1 : done:
337 1 : bitarray_free(changed1);
338 1 : bitarray_free(changed2);
339 1 : smartlist_free(sl1);
340 1 : smartlist_free(sl2);
341 1 : tor_free(sls1);
342 1 : tor_free(sls2);
343 1 : memarea_drop_all(area);
344 1 : }
345 :
346 : static void
347 1 : test_consdiff_get_id_hash(void *arg)
348 : {
349 1 : (void)arg;
350 :
351 1 : cdline_t line1 = { "r name", 6 };
352 1 : cdline_t line2 = { "r name _hash_isnt_base64 etc", 28 };
353 1 : cdline_t line3 = { "r name hash+valid+base64 etc", 28 };
354 1 : cdline_t tmp;
355 :
356 : /* No hash. */
357 1 : tt_int_op(-1, OP_EQ, get_id_hash(&line1, &tmp));
358 : /* The hash contains characters that are not base64. */
359 1 : tt_int_op(-1, OP_EQ, get_id_hash(&line2, &tmp));
360 :
361 : /* valid hash. */
362 1 : tt_int_op(0, OP_EQ, get_id_hash(&line3, &tmp));
363 1 : tt_ptr_op(tmp.s, OP_EQ, line3.s + 7);
364 1 : tt_uint_op(tmp.len, OP_EQ, line3.len - 11);
365 :
366 1 : done:
367 1 : ;
368 1 : }
369 :
370 : static void
371 1 : test_consdiff_is_valid_router_entry(void *arg)
372 : {
373 : /* Doesn't start with "r ". */
374 1 : (void)arg;
375 1 : cdline_t line0 = { "foo", 3 };
376 1 : tt_int_op(0, OP_EQ, is_valid_router_entry(&line0));
377 :
378 : /* These are already tested with get_id_hash, but make sure it's run
379 : * properly. */
380 :
381 1 : cdline_t line1 = { "r name", 6 };
382 1 : cdline_t line2 = { "r name _hash_isnt_base64 etc", 28 };
383 1 : cdline_t line3 = { "r name hash+valid+base64 etc", 28 };
384 1 : tt_int_op(0, OP_EQ, is_valid_router_entry(&line1));
385 1 : tt_int_op(0, OP_EQ, is_valid_router_entry(&line2));
386 1 : tt_int_op(1, OP_EQ, is_valid_router_entry(&line3));
387 :
388 1 : done:
389 1 : ;
390 1 : }
391 :
392 : static void
393 1 : test_consdiff_next_router(void *arg)
394 : {
395 1 : smartlist_t *sl = smartlist_new();
396 1 : memarea_t *area = memarea_new();
397 1 : (void)arg;
398 1 : smartlist_add_linecpy(sl, area, "foo");
399 1 : smartlist_add_linecpy(sl, area,
400 : "r name hash+longer+than+27+chars+and+valid+base64 etc");
401 1 : smartlist_add_linecpy(sl, area, "foo");
402 1 : smartlist_add_linecpy(sl, area, "foo");
403 1 : smartlist_add_linecpy(sl, area,
404 : "r name hash+longer+than+27+chars+and+valid+base64 etc");
405 1 : smartlist_add_linecpy(sl, area, "foo");
406 :
407 : /* Not currently on a router entry line, finding the next one. */
408 1 : tt_int_op(1, OP_EQ, next_router(sl, 0));
409 1 : tt_int_op(4, OP_EQ, next_router(sl, 2));
410 :
411 : /* Already at the beginning of a router entry line, ignore it. */
412 1 : tt_int_op(4, OP_EQ, next_router(sl, 1));
413 :
414 : /* There are no more router entries, so return the line after the last. */
415 1 : tt_int_op(6, OP_EQ, next_router(sl, 4));
416 1 : tt_int_op(6, OP_EQ, next_router(sl, 5));
417 :
418 1 : done:
419 1 : smartlist_free(sl);
420 1 : memarea_drop_all(area);
421 1 : }
422 :
423 : static int
424 13 : base64cmp_wrapper(const char *a, const char *b)
425 : {
426 13 : cdline_t aa = { a, a ? (uint32_t) strlen(a) : 0 };
427 13 : cdline_t bb = { b, b ? (uint32_t) strlen(b) : 0 };
428 13 : return base64cmp(&aa, &bb);
429 : }
430 :
431 : static void
432 1 : test_consdiff_base64cmp(void *arg)
433 : {
434 : /* NULL arguments. */
435 1 : (void)arg;
436 1 : tt_int_op(0, OP_EQ, base64cmp_wrapper(NULL, NULL));
437 1 : tt_int_op(-1, OP_EQ, base64cmp_wrapper(NULL, "foo"));
438 1 : tt_int_op(1, OP_EQ, base64cmp_wrapper("bar", NULL));
439 :
440 : /* Nil base64 values. */
441 1 : tt_int_op(0, OP_EQ, base64cmp_wrapper("", ""));
442 1 : tt_int_op(0, OP_EQ, base64cmp_wrapper("_", "&"));
443 :
444 : /* Exact same valid strings. */
445 1 : tt_int_op(0, OP_EQ, base64cmp_wrapper("abcABC/+", "abcABC/+"));
446 : /* Both end with an invalid base64 char other than '\0'. */
447 1 : tt_int_op(0, OP_EQ, base64cmp_wrapper("abcABC/+ ", "abcABC/+ "));
448 : /* Only one ends with an invalid base64 char other than '\0'. */
449 1 : tt_int_op(-1, OP_EQ, base64cmp_wrapper("abcABC/+ ", "abcABC/+a"));
450 :
451 : /* Comparisons that would return differently with strcmp(). */
452 1 : tt_int_op(strcmp("/foo", "Afoo"), OP_LT, 0);
453 1 : tt_int_op(base64cmp_wrapper("/foo", "Afoo"), OP_GT, 0);
454 1 : tt_int_op(strcmp("Afoo", "0foo"), OP_GT, 0);
455 1 : tt_int_op(base64cmp_wrapper("Afoo", "0foo"), OP_LT, 0);
456 :
457 : /* Comparisons that would return the same as with strcmp(). */
458 1 : tt_int_op(strcmp("afoo", "Afoo"), OP_GT, 0);
459 1 : tt_int_op(base64cmp_wrapper("afoo", "Afoo"), OP_GT, 0);
460 :
461 : /* Different lengths */
462 1 : tt_int_op(base64cmp_wrapper("afoo", "afooo"), OP_LT, 0);
463 1 : tt_int_op(base64cmp_wrapper("afooo", "afoo"), OP_GT, 0);
464 :
465 1 : done:
466 1 : ;
467 1 : }
468 :
469 : static void
470 1 : test_consdiff_gen_ed_diff(void *arg)
471 : {
472 1 : smartlist_t *cons1=NULL, *cons2=NULL, *diff=NULL;
473 1 : int i;
474 1 : memarea_t *area = memarea_new();
475 1 : setup_capture_of_logs(LOG_WARN);
476 :
477 1 : (void)arg;
478 1 : cons1 = smartlist_new();
479 1 : cons2 = smartlist_new();
480 :
481 : /* Identity hashes are not sorted properly, return NULL. */
482 1 : smartlist_add_linecpy(cons1, area, "r name bbbbbbbbbbbbbbbbbbbbbbbbbbb etc");
483 1 : smartlist_add_linecpy(cons1, area, "foo");
484 1 : smartlist_add_linecpy(cons1, area, "r name aaaaaaaaaaaaaaaaaaaaaaaaaaa etc");
485 1 : smartlist_add_linecpy(cons1, area, "bar");
486 :
487 1 : smartlist_add_linecpy(cons2, area, "r name aaaaaaaaaaaaaaaaaaaaaaaaaaa etc");
488 1 : smartlist_add_linecpy(cons2, area, "foo");
489 1 : smartlist_add_linecpy(cons2, area, "r name ccccccccccccccccccccccccccc etc");
490 1 : smartlist_add_linecpy(cons2, area, "bar");
491 :
492 1 : diff = gen_ed_diff(cons1, cons2, area);
493 1 : tt_ptr_op(NULL, OP_EQ, diff);
494 1 : expect_single_log_msg_containing("Refusing to generate consensus diff "
495 : "because the base consensus doesn't have its router entries sorted "
496 : "properly.");
497 :
498 : /* Same, but now with the second consensus. */
499 1 : mock_clean_saved_logs();
500 1 : diff = gen_ed_diff(cons2, cons1, area);
501 1 : tt_ptr_op(NULL, OP_EQ, diff);
502 1 : expect_single_log_msg_containing("Refusing to generate consensus diff "
503 : "because the target consensus doesn't have its router entries sorted "
504 : "properly.");
505 :
506 : /* Same as the two above, but with the reversed thing immediately after a
507 : match. (The code handles this differently) */
508 1 : smartlist_del(cons1, 0);
509 1 : smartlist_add_linecpy(cons1, area, "r name aaaaaaaaaaaaaaaaaaaaaaaaaaa etc");
510 :
511 1 : mock_clean_saved_logs();
512 1 : diff = gen_ed_diff(cons1, cons2, area);
513 1 : tt_ptr_op(NULL, OP_EQ, diff);
514 1 : expect_single_log_msg_containing("Refusing to generate consensus diff "
515 : "because the base consensus doesn't have its router entries sorted "
516 : "properly.");
517 :
518 1 : mock_clean_saved_logs();
519 1 : diff = gen_ed_diff(cons2, cons1, area);
520 1 : tt_ptr_op(NULL, OP_EQ, diff);
521 1 : expect_single_log_msg_containing("Refusing to generate consensus diff "
522 : "because the target consensus doesn't have its router entries sorted "
523 : "properly.");
524 :
525 : /* Identity hashes are repeated, return NULL. */
526 1 : smartlist_clear(cons1);
527 :
528 1 : smartlist_add_linecpy(cons1, area, "r name bbbbbbbbbbbbbbbbbbbbbbbbbbb etc");
529 1 : smartlist_add_linecpy(cons1, area, "foo");
530 1 : smartlist_add_linecpy(cons1, area, "r name bbbbbbbbbbbbbbbbbbbbbbbbbbb etc");
531 1 : smartlist_add_linecpy(cons1, area, "bar");
532 :
533 1 : mock_clean_saved_logs();
534 1 : diff = gen_ed_diff(cons1, cons2, area);
535 1 : tt_ptr_op(NULL, OP_EQ, diff);
536 1 : expect_single_log_msg_containing("Refusing to generate consensus diff "
537 : "because the base consensus doesn't have its router entries sorted "
538 : "properly.");
539 :
540 : /* We have to add a line that is just a dot, return NULL. */
541 1 : smartlist_clear(cons1);
542 1 : smartlist_clear(cons2);
543 :
544 1 : smartlist_add_linecpy(cons1, area, "foo1");
545 1 : smartlist_add_linecpy(cons1, area, "foo2");
546 :
547 1 : smartlist_add_linecpy(cons2, area, "foo1");
548 1 : smartlist_add_linecpy(cons2, area, ".");
549 1 : smartlist_add_linecpy(cons2, area, "foo2");
550 :
551 1 : mock_clean_saved_logs();
552 1 : diff = gen_ed_diff(cons1, cons2, area);
553 1 : tt_ptr_op(NULL, OP_EQ, diff);
554 1 : expect_single_log_msg_containing("Cannot generate consensus diff "
555 : "because one of the lines to be added is \".\".");
556 :
557 : #define MAX_LINE_COUNT (10000)
558 : /* Too many lines to be fed to the quadratic-time function. */
559 1 : smartlist_clear(cons1);
560 1 : smartlist_clear(cons2);
561 :
562 10002 : for (i=0; i < MAX_LINE_COUNT; ++i) smartlist_add_linecpy(cons1, area, "a");
563 10001 : for (i=0; i < MAX_LINE_COUNT; ++i) smartlist_add_linecpy(cons1, area, "b");
564 :
565 1 : mock_clean_saved_logs();
566 1 : diff = gen_ed_diff(cons1, cons2, area);
567 :
568 1 : tt_ptr_op(NULL, OP_EQ, diff);
569 1 : expect_single_log_msg_containing("Refusing to generate consensus diff "
570 : "because we found too few common router ids.");
571 :
572 : /* We have dot lines, but they don't interfere with the script format. */
573 1 : smartlist_clear(cons1);
574 1 : smartlist_clear(cons2);
575 :
576 1 : smartlist_add_linecpy(cons1, area, "foo1");
577 1 : smartlist_add_linecpy(cons1, area, ".");
578 1 : smartlist_add_linecpy(cons1, area, ".");
579 1 : smartlist_add_linecpy(cons1, area, "foo2");
580 :
581 1 : smartlist_add_linecpy(cons2, area, "foo1");
582 1 : smartlist_add_linecpy(cons2, area, ".");
583 1 : smartlist_add_linecpy(cons2, area, "foo2");
584 :
585 1 : diff = gen_ed_diff(cons1, cons2, area);
586 1 : tt_ptr_op(NULL, OP_NE, diff);
587 1 : smartlist_free(diff);
588 :
589 : /* Empty diff tests. */
590 1 : smartlist_clear(cons1);
591 1 : smartlist_clear(cons2);
592 :
593 1 : diff = gen_ed_diff(cons1, cons2, area);
594 1 : tt_ptr_op(NULL, OP_NE, diff);
595 1 : tt_int_op(0, OP_EQ, smartlist_len(diff));
596 1 : smartlist_free(diff);
597 :
598 1 : smartlist_add_linecpy(cons1, area, "foo");
599 1 : smartlist_add_linecpy(cons1, area, "bar");
600 :
601 1 : smartlist_add_linecpy(cons2, area, "foo");
602 1 : smartlist_add_linecpy(cons2, area, "bar");
603 :
604 1 : diff = gen_ed_diff(cons1, cons2, area);
605 1 : tt_ptr_op(NULL, OP_NE, diff);
606 1 : tt_int_op(0, OP_EQ, smartlist_len(diff));
607 1 : smartlist_free(diff);
608 :
609 : /* Everything is deleted. */
610 1 : smartlist_clear(cons2);
611 :
612 1 : diff = gen_ed_diff(cons1, cons2, area);
613 1 : tt_ptr_op(NULL, OP_NE, diff);
614 1 : tt_int_op(1, OP_EQ, smartlist_len(diff));
615 1 : tt_str_eq_line("1,2d", smartlist_get(diff, 0));
616 :
617 1 : smartlist_free(diff);
618 :
619 : /* Everything is added. */
620 1 : diff = gen_ed_diff(cons2, cons1, area);
621 1 : tt_ptr_op(NULL, OP_NE, diff);
622 1 : tt_int_op(4, OP_EQ, smartlist_len(diff));
623 1 : tt_str_eq_line("0a", smartlist_get(diff, 0));
624 1 : tt_str_eq_line("foo", smartlist_get(diff, 1));
625 1 : tt_str_eq_line("bar", smartlist_get(diff, 2));
626 1 : tt_str_eq_line(".", smartlist_get(diff, 3));
627 :
628 1 : smartlist_free(diff);
629 :
630 : /* Everything is changed. */
631 1 : smartlist_add_linecpy(cons2, area, "foo2");
632 1 : smartlist_add_linecpy(cons2, area, "bar2");
633 1 : diff = gen_ed_diff(cons1, cons2, area);
634 1 : tt_ptr_op(NULL, OP_NE, diff);
635 1 : tt_int_op(4, OP_EQ, smartlist_len(diff));
636 1 : tt_str_eq_line("1,2c", smartlist_get(diff, 0));
637 1 : tt_str_eq_line("foo2", smartlist_get(diff, 1));
638 1 : tt_str_eq_line("bar2", smartlist_get(diff, 2));
639 1 : tt_str_eq_line(".", smartlist_get(diff, 3));
640 :
641 1 : smartlist_free(diff);
642 :
643 : /* Test 'a', 'c' and 'd' together. See that it is done in reverse order. */
644 1 : smartlist_clear(cons1);
645 1 : smartlist_clear(cons2);
646 1 : consensus_split_lines_(cons1, "A\nB\nC\nD\nE\n", area);
647 1 : consensus_split_lines_(cons2, "A\nC\nO\nE\nU\n", area);
648 1 : diff = gen_ed_diff(cons1, cons2, area);
649 1 : tt_ptr_op(NULL, OP_NE, diff);
650 1 : tt_int_op(7, OP_EQ, smartlist_len(diff));
651 1 : tt_str_eq_line("5a", smartlist_get(diff, 0));
652 1 : tt_str_eq_line("U", smartlist_get(diff, 1));
653 1 : tt_str_eq_line(".", smartlist_get(diff, 2));
654 1 : tt_str_eq_line("4c", smartlist_get(diff, 3));
655 1 : tt_str_eq_line("O", smartlist_get(diff, 4));
656 1 : tt_str_eq_line(".", smartlist_get(diff, 5));
657 1 : tt_str_eq_line("2d", smartlist_get(diff, 6));
658 :
659 1 : smartlist_free(diff);
660 :
661 1 : smartlist_clear(cons1);
662 1 : smartlist_clear(cons2);
663 1 : consensus_split_lines_(cons1, "B\n", area);
664 1 : consensus_split_lines_(cons2, "A\nB\n", area);
665 1 : diff = gen_ed_diff(cons1, cons2, area);
666 1 : tt_ptr_op(NULL, OP_NE, diff);
667 1 : tt_int_op(3, OP_EQ, smartlist_len(diff));
668 1 : tt_str_eq_line("0a", smartlist_get(diff, 0));
669 1 : tt_str_eq_line("A", smartlist_get(diff, 1));
670 1 : tt_str_eq_line(".", smartlist_get(diff, 2));
671 :
672 : /* TODO: small real use-cases, i.e. consensuses. */
673 :
674 1 : done:
675 1 : teardown_capture_of_logs();
676 1 : smartlist_free(cons1);
677 1 : smartlist_free(cons2);
678 1 : smartlist_free(diff);
679 1 : memarea_drop_all(area);
680 1 : }
681 :
682 : static void
683 1 : test_consdiff_apply_ed_diff(void *arg)
684 : {
685 1 : smartlist_t *cons1=NULL, *cons2=NULL, *diff=NULL;
686 1 : memarea_t *area = memarea_new();
687 1 : (void)arg;
688 1 : cons1 = smartlist_new();
689 1 : diff = smartlist_new();
690 1 : setup_capture_of_logs(LOG_WARN);
691 :
692 1 : consensus_split_lines_(cons1, "A\nB\nC\nD\nE\n", area);
693 :
694 : /* Command without range. */
695 1 : smartlist_add_linecpy(diff, area, "a");
696 1 : cons2 = apply_ed_diff(cons1, diff, 0);
697 1 : tt_ptr_op(NULL, OP_EQ, cons2);
698 1 : smartlist_clear(diff);
699 1 : expect_single_log_msg_containing("an ed command was missing a line number");
700 :
701 : /* Range without command. */
702 1 : smartlist_add_linecpy(diff, area, "1");
703 1 : mock_clean_saved_logs();
704 1 : cons2 = apply_ed_diff(cons1, diff, 0);
705 1 : tt_ptr_op(NULL, OP_EQ, cons2);
706 1 : expect_single_log_msg_containing("a line with no ed command was found");
707 :
708 1 : smartlist_clear(diff);
709 :
710 : /* Range without end. */
711 1 : smartlist_add_linecpy(diff, area, "1,");
712 1 : mock_clean_saved_logs();
713 1 : cons2 = apply_ed_diff(cons1, diff, 0);
714 1 : tt_ptr_op(NULL, OP_EQ, cons2);
715 1 : expect_single_log_msg_containing("an ed command was missing a range "
716 : "end line number.");
717 :
718 1 : smartlist_clear(diff);
719 :
720 : /* Incoherent ranges. */
721 1 : smartlist_add_linecpy(diff, area, "1,1");
722 1 : mock_clean_saved_logs();
723 1 : cons2 = apply_ed_diff(cons1, diff, 0);
724 1 : tt_ptr_op(NULL, OP_EQ, cons2);
725 1 : expect_single_log_msg_containing("an invalid range was found");
726 :
727 1 : smartlist_clear(diff);
728 :
729 1 : smartlist_add_linecpy(diff, area, "3,2");
730 1 : mock_clean_saved_logs();
731 1 : cons2 = apply_ed_diff(cons1, diff, 0);
732 1 : tt_ptr_op(NULL, OP_EQ, cons2);
733 1 : expect_single_log_msg_containing("an invalid range was found");
734 :
735 1 : smartlist_clear(diff);
736 :
737 : /* Unexpected range for add command. */
738 1 : smartlist_add_linecpy(diff, area, "1,2a");
739 1 : mock_clean_saved_logs();
740 1 : cons2 = apply_ed_diff(cons1, diff, 0);
741 1 : tt_ptr_op(NULL, OP_EQ, cons2);
742 1 : expect_single_log_msg_containing("add lines after a range");
743 :
744 1 : smartlist_clear(diff);
745 :
746 : /* $ for a non-delete command. */
747 1 : smartlist_add_linecpy(diff, area, "1,$c");
748 1 : mock_clean_saved_logs();
749 1 : cons2 = apply_ed_diff(cons1, diff, 0);
750 1 : tt_ptr_op(NULL, OP_EQ, cons2);
751 1 : expect_single_log_msg_containing("it wanted to use $ with a command "
752 : "other than delete");
753 :
754 1 : smartlist_clear(diff);
755 :
756 : /* Script is not in reverse order. */
757 1 : smartlist_add_linecpy(diff, area, "1d");
758 1 : smartlist_add_linecpy(diff, area, "3d");
759 1 : mock_clean_saved_logs();
760 1 : cons2 = apply_ed_diff(cons1, diff, 0);
761 1 : tt_ptr_op(NULL, OP_EQ, cons2);
762 1 : expect_single_log_msg_containing("its commands are not properly sorted");
763 :
764 1 : smartlist_clear(diff);
765 :
766 : /* Script contains unrecognised commands longer than one char. */
767 1 : smartlist_add_linecpy(diff, area, "1foo");
768 1 : mock_clean_saved_logs();
769 1 : cons2 = apply_ed_diff(cons1, diff, 0);
770 1 : tt_ptr_op(NULL, OP_EQ, cons2);
771 1 : expect_single_log_msg_containing("an ed command longer than one char was "
772 : "found");
773 :
774 1 : smartlist_clear(diff);
775 :
776 : /* Script contains unrecognised commands. */
777 1 : smartlist_add_linecpy(diff, area, "1e");
778 1 : mock_clean_saved_logs();
779 1 : cons2 = apply_ed_diff(cons1, diff, 0);
780 1 : tt_ptr_op(NULL, OP_EQ, cons2);
781 1 : expect_single_log_msg_containing("an unrecognised ed command was found");
782 :
783 1 : smartlist_clear(diff);
784 :
785 : /* Command that should be followed by at least one line and a ".", but
786 : * isn't. */
787 1 : smartlist_add_linecpy(diff, area, "0a");
788 1 : mock_clean_saved_logs();
789 1 : cons2 = apply_ed_diff(cons1, diff, 0);
790 1 : tt_ptr_op(NULL, OP_EQ, cons2);
791 1 : expect_single_log_msg_containing("it has an ed command that tries to "
792 : "insert zero lines.");
793 :
794 : /* Now it is followed by a ".", but it inserts zero lines. */
795 1 : smartlist_add_linecpy(diff, area, ".");
796 1 : mock_clean_saved_logs();
797 1 : cons2 = apply_ed_diff(cons1, diff, 0);
798 1 : tt_ptr_op(NULL, OP_EQ, cons2);
799 1 : expect_single_log_msg_containing("it has an ed command that tries to "
800 : "insert zero lines.");
801 :
802 1 : smartlist_clear(diff);
803 :
804 : /* Now it it inserts something, but has no terminator. */
805 1 : smartlist_add_linecpy(diff, area, "0a");
806 1 : smartlist_add_linecpy(diff, area, "hello");
807 1 : mock_clean_saved_logs();
808 1 : cons2 = apply_ed_diff(cons1, diff, 0);
809 1 : tt_ptr_op(NULL, OP_EQ, cons2);
810 1 : expect_single_log_msg_containing("lines to be inserted that don't end with "
811 : "a \".\".");
812 :
813 1 : smartlist_clear(diff);
814 :
815 : /* Ranges must be numeric only and cannot contain spaces. */
816 1 : smartlist_add_linecpy(diff, area, "0, 4d");
817 1 : mock_clean_saved_logs();
818 1 : cons2 = apply_ed_diff(cons1, diff, 0);
819 1 : tt_ptr_op(NULL, OP_EQ, cons2);
820 1 : expect_single_log_msg_containing("an ed command was missing a range "
821 : "end line number.");
822 :
823 1 : smartlist_clear(diff);
824 :
825 : /* '+' is not a number. */
826 1 : smartlist_add_linecpy(diff, area, "+0,4d");
827 1 : mock_clean_saved_logs();
828 1 : cons2 = apply_ed_diff(cons1, diff, 0);
829 1 : tt_ptr_op(NULL, OP_EQ, cons2);
830 1 : expect_single_log_msg_containing("an ed command was missing a line number");
831 :
832 1 : smartlist_clear(diff);
833 :
834 : /* range duplication */
835 1 : smartlist_add_linecpy(diff, area, "0,4d,5d");
836 1 : mock_clean_saved_logs();
837 1 : cons2 = apply_ed_diff(cons1, diff, 0);
838 1 : tt_ptr_op(NULL, OP_EQ, cons2);
839 1 : expect_single_log_msg_containing("an ed command longer than one char was "
840 : "found");
841 :
842 1 : smartlist_clear(diff);
843 :
844 : /* space before command */
845 1 : smartlist_add_linecpy(diff, area, "0,4 d");
846 1 : mock_clean_saved_logs();
847 1 : cons2 = apply_ed_diff(cons1, diff, 0);
848 1 : tt_ptr_op(NULL, OP_EQ, cons2);
849 1 : expect_single_log_msg_containing("an ed command longer than one char was "
850 : "found");
851 :
852 1 : smartlist_clear(diff);
853 :
854 : /* space inside number */
855 1 : smartlist_add_linecpy(diff, area, "0,4 5d");
856 1 : mock_clean_saved_logs();
857 1 : cons2 = apply_ed_diff(cons1, diff, 0);
858 1 : tt_ptr_op(NULL, OP_EQ, cons2);
859 1 : expect_single_log_msg_containing("an ed command longer than one char was "
860 : "found");
861 :
862 1 : smartlist_clear(diff);
863 :
864 : /* Test appending text, 'a'. */
865 1 : consensus_split_lines_(diff, "3a\nU\nO\n.\n0a\nV\n.\n", area);
866 1 : cons2 = apply_ed_diff(cons1, diff, 0);
867 1 : tt_ptr_op(NULL, OP_NE, cons2);
868 1 : tt_int_op(8, OP_EQ, smartlist_len(cons2));
869 1 : tt_str_eq_line("V", smartlist_get(cons2, 0));
870 1 : tt_str_eq_line("A", smartlist_get(cons2, 1));
871 1 : tt_str_eq_line("B", smartlist_get(cons2, 2));
872 1 : tt_str_eq_line("C", smartlist_get(cons2, 3));
873 1 : tt_str_eq_line("U", smartlist_get(cons2, 4));
874 1 : tt_str_eq_line("O", smartlist_get(cons2, 5));
875 1 : tt_str_eq_line("D", smartlist_get(cons2, 6));
876 1 : tt_str_eq_line("E", smartlist_get(cons2, 7));
877 :
878 1 : smartlist_clear(diff);
879 1 : smartlist_free(cons2);
880 :
881 : /* Test deleting text, 'd'. */
882 1 : consensus_split_lines_(diff, "4d\n1,2d\n", area);
883 1 : cons2 = apply_ed_diff(cons1, diff, 0);
884 1 : tt_ptr_op(NULL, OP_NE, cons2);
885 1 : tt_int_op(2, OP_EQ, smartlist_len(cons2));
886 1 : tt_str_eq_line("C", smartlist_get(cons2, 0));
887 1 : tt_str_eq_line("E", smartlist_get(cons2, 1));
888 :
889 1 : smartlist_clear(diff);
890 1 : smartlist_free(cons2);
891 :
892 : /* Test changing text, 'c'. */
893 1 : consensus_split_lines_(diff, "4c\nT\nX\n.\n1,2c\nM\n.\n", area);
894 1 : cons2 = apply_ed_diff(cons1, diff, 0);
895 1 : tt_ptr_op(NULL, OP_NE, cons2);
896 1 : tt_int_op(5, OP_EQ, smartlist_len(cons2));
897 1 : tt_str_eq_line("M", smartlist_get(cons2, 0));
898 1 : tt_str_eq_line("C", smartlist_get(cons2, 1));
899 1 : tt_str_eq_line("T", smartlist_get(cons2, 2));
900 1 : tt_str_eq_line("X", smartlist_get(cons2, 3));
901 1 : tt_str_eq_line("E", smartlist_get(cons2, 4));
902 :
903 1 : smartlist_clear(diff);
904 1 : smartlist_free(cons2);
905 :
906 : /* Test 'a', 'd' and 'c' together. */
907 1 : consensus_split_lines_(diff, "4c\nT\nX\n.\n2d\n0a\nM\n.\n", area);
908 1 : cons2 = apply_ed_diff(cons1, diff, 0);
909 1 : tt_ptr_op(NULL, OP_NE, cons2);
910 1 : tt_int_op(6, OP_EQ, smartlist_len(cons2));
911 1 : tt_str_eq_line("M", smartlist_get(cons2, 0));
912 1 : tt_str_eq_line("A", smartlist_get(cons2, 1));
913 1 : tt_str_eq_line("C", smartlist_get(cons2, 2));
914 1 : tt_str_eq_line("T", smartlist_get(cons2, 3));
915 1 : tt_str_eq_line("X", smartlist_get(cons2, 4));
916 1 : tt_str_eq_line("E", smartlist_get(cons2, 5));
917 :
918 1 : done:
919 1 : teardown_capture_of_logs();
920 1 : smartlist_free(cons1);
921 1 : smartlist_free(cons2);
922 1 : smartlist_free(diff);
923 1 : memarea_drop_all(area);
924 1 : }
925 :
926 : static void
927 1 : test_consdiff_gen_diff(void *arg)
928 : {
929 1 : char *cons1_str=NULL, *cons2_str=NULL;
930 1 : smartlist_t *cons1=NULL, *cons2=NULL, *diff=NULL;
931 1 : consensus_digest_t digests1, digests2;
932 1 : memarea_t *area = memarea_new();
933 1 : (void)arg;
934 1 : cons1 = smartlist_new();
935 1 : cons2 = smartlist_new();
936 :
937 : /* Identity hashes are not sorted properly, return NULL.
938 : * Already tested in gen_ed_diff, but see that a NULL ed diff also makes
939 : * gen_diff return NULL. */
940 1 : cons1_str = tor_strdup(
941 : "network-status-version foo\n"
942 : "r name bbbbbbbbbbbbbbbbb etc\nfoo\n"
943 : "r name aaaaaaaaaaaaaaaaa etc\nbar\n"
944 : "directory-signature foo bar\nbar\n"
945 : );
946 1 : cons2_str = tor_strdup(
947 : "network-status-version foo\n"
948 : "r name aaaaaaaaaaaaaaaaa etc\nfoo\n"
949 : "r name ccccccccccccccccc etc\nbar\n"
950 : "directory-signature foo bar\nbar\n"
951 : );
952 :
953 1 : tt_int_op(0, OP_EQ,
954 : consensus_compute_digest_as_signed_(cons1_str, &digests1));
955 1 : tt_int_op(0, OP_EQ,
956 : consensus_compute_digest_(cons2_str, &digests2));
957 :
958 1 : consensus_split_lines_(cons1, cons1_str, area);
959 1 : consensus_split_lines_(cons2, cons2_str, area);
960 :
961 1 : diff = consdiff_gen_diff(cons1, cons2, &digests1, &digests2, area);
962 1 : tt_ptr_op(NULL, OP_EQ, diff);
963 :
964 : /* Check that the headers are done properly. */
965 1 : tor_free(cons1_str);
966 1 : cons1_str = tor_strdup(
967 : "network-status-version foo\n"
968 : "r name ccccccccccccccccc etc\nfoo\n"
969 : "r name eeeeeeeeeeeeeeeee etc\nbar\n"
970 : "directory-signature foo bar\nbar\n"
971 : );
972 1 : tt_int_op(0, OP_EQ,
973 : consensus_compute_digest_as_signed_(cons1_str, &digests1));
974 1 : smartlist_clear(cons1);
975 1 : consensus_split_lines_(cons1, cons1_str, area);
976 1 : diff = consdiff_gen_diff(cons1, cons2, &digests1, &digests2, area);
977 1 : tt_ptr_op(NULL, OP_NE, diff);
978 1 : tt_int_op(11, OP_EQ, smartlist_len(diff));
979 1 : tt_assert(line_str_eq(smartlist_get(diff, 0),
980 : "network-status-diff-version 1"));
981 1 : tt_assert(line_str_eq(smartlist_get(diff, 1), "hash "
982 : "95D70F5A3CC65F920AA8B44C4563D7781A082674329661884E19E94B79D539C2 "
983 : "7AFECEFA4599BA33D603653E3D2368F648DF4AC4723929B0F7CF39281596B0C1"));
984 1 : tt_assert(line_str_eq(smartlist_get(diff, 2), "6,$d"));
985 1 : tt_assert(line_str_eq(smartlist_get(diff, 3), "3,4c"));
986 1 : tt_assert(line_str_eq(smartlist_get(diff, 4), "bar"));
987 1 : tt_assert(line_str_eq(smartlist_get(diff, 5),
988 : "directory-signature foo bar"));
989 1 : tt_assert(line_str_eq(smartlist_get(diff, 6),
990 : "."));
991 1 : tt_assert(line_str_eq(smartlist_get(diff, 7), "1a"));
992 1 : tt_assert(line_str_eq(smartlist_get(diff, 8),
993 : "r name aaaaaaaaaaaaaaaaa etc"));
994 1 : tt_assert(line_str_eq(smartlist_get(diff, 9), "foo"));
995 1 : tt_assert(line_str_eq(smartlist_get(diff, 10), "."));
996 :
997 : /* TODO: small real use-cases, i.e. consensuses. */
998 :
999 1 : done:
1000 1 : tor_free(cons1_str);
1001 1 : tor_free(cons2_str);
1002 1 : smartlist_free(cons1);
1003 1 : smartlist_free(cons2);
1004 1 : smartlist_free(diff);
1005 1 : memarea_drop_all(area);
1006 1 : }
1007 :
1008 : static void
1009 1 : test_consdiff_apply_diff(void *arg)
1010 : {
1011 1 : smartlist_t *cons1=NULL, *diff=NULL;
1012 1 : char *cons1_str=NULL, *cons2 = NULL;
1013 1 : consensus_digest_t digests1;
1014 1 : (void)arg;
1015 1 : memarea_t *area = memarea_new();
1016 1 : cons1 = smartlist_new();
1017 1 : diff = smartlist_new();
1018 1 : setup_capture_of_logs(LOG_INFO);
1019 :
1020 1 : cons1_str = tor_strdup(
1021 : "network-status-version foo\n"
1022 : "r name ccccccccccccccccc etc\nfoo\n"
1023 : "r name eeeeeeeeeeeeeeeee etc\nbar\n"
1024 : "directory-signature foo bar\nbar\n"
1025 : );
1026 1 : tt_int_op(0, OP_EQ,
1027 : consensus_compute_digest_(cons1_str, &digests1));
1028 1 : consensus_split_lines_(cons1, cons1_str, area);
1029 :
1030 : /* diff doesn't have enough lines. */
1031 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1032 1 : tt_ptr_op(NULL, OP_EQ, cons2);
1033 1 : expect_single_log_msg_containing("too short");
1034 :
1035 : /* first line doesn't match format-version string. */
1036 1 : smartlist_add_linecpy(diff, area, "foo-bar");
1037 1 : smartlist_add_linecpy(diff, area, "header-line");
1038 1 : mock_clean_saved_logs();
1039 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1040 1 : tt_ptr_op(NULL, OP_EQ, cons2);
1041 1 : expect_single_log_msg_containing("format is not known");
1042 :
1043 : /* The first word of the second header line is not "hash". */
1044 1 : smartlist_clear(diff);
1045 1 : smartlist_add_linecpy(diff, area, "network-status-diff-version 1");
1046 1 : smartlist_add_linecpy(diff, area, "word a b");
1047 1 : smartlist_add_linecpy(diff, area, "x");
1048 1 : mock_clean_saved_logs();
1049 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1050 1 : tt_ptr_op(NULL, OP_EQ, cons2);
1051 1 : expect_single_log_msg_containing("does not include the necessary digests");
1052 :
1053 : /* Wrong number of words after "hash". */
1054 1 : smartlist_clear(diff);
1055 1 : smartlist_add_linecpy(diff, area, "network-status-diff-version 1");
1056 1 : smartlist_add_linecpy(diff, area, "hash a b c");
1057 1 : mock_clean_saved_logs();
1058 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1059 1 : tt_ptr_op(NULL, OP_EQ, cons2);
1060 1 : expect_single_log_msg_containing("does not include the necessary digests");
1061 :
1062 : /* base16 digests do not have the expected length. */
1063 1 : smartlist_clear(diff);
1064 1 : smartlist_add_linecpy(diff, area, "network-status-diff-version 1");
1065 1 : smartlist_add_linecpy(diff, area, "hash aaa bbb");
1066 1 : mock_clean_saved_logs();
1067 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1068 1 : tt_ptr_op(NULL, OP_EQ, cons2);
1069 1 : expect_single_log_msg_containing("includes base16-encoded digests of "
1070 : "incorrect size");
1071 :
1072 : /* base16 digests contain non-base16 characters. */
1073 1 : smartlist_clear(diff);
1074 1 : smartlist_add_linecpy(diff, area, "network-status-diff-version 1");
1075 1 : smartlist_add_linecpy(diff, area, "hash"
1076 : " ????????????????????????????????????????????????????????????????"
1077 : " ----------------------------------------------------------------");
1078 1 : mock_clean_saved_logs();
1079 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1080 1 : tt_ptr_op(NULL, OP_EQ, cons2);
1081 1 : expect_single_log_msg_containing("includes malformed digests");
1082 :
1083 : /* Invalid ed diff.
1084 : * As tested in apply_ed_diff, but check that apply_diff does return NULL if
1085 : * the ed diff can't be applied. */
1086 1 : smartlist_clear(diff);
1087 1 : smartlist_add_linecpy(diff, area, "network-status-diff-version 1");
1088 1 : smartlist_add_linecpy(diff, area, "hash"
1089 : /* sha3 of cons1. */
1090 : " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4"
1091 : /* sha256 of cons2. */
1092 : " 635D34593020C08E5ECD865F9986E29D50028EFA62843766A8197AD228A7F6AA");
1093 1 : smartlist_add_linecpy(diff, area, "foobar");
1094 1 : mock_clean_saved_logs();
1095 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1096 1 : tt_ptr_op(NULL, OP_EQ, cons2);
1097 1 : expect_single_log_msg_containing("because an ed command was missing a line "
1098 : "number");
1099 :
1100 : /* Base consensus doesn't match its digest as found in the diff. */
1101 1 : smartlist_clear(diff);
1102 1 : smartlist_add_linecpy(diff, area, "network-status-diff-version 1");
1103 1 : smartlist_add_linecpy(diff, area, "hash"
1104 : /* bogus sha256. */
1105 : " 3333333333333333333333333333333333333333333333333333333333333333"
1106 : /* sha256 of cons2. */
1107 : " 635D34593020C08E5ECD865F9986E29D50028EFA62843766A8197AD228A7F6AA");
1108 1 : mock_clean_saved_logs();
1109 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1110 1 : tt_ptr_op(NULL, OP_EQ, cons2);
1111 1 : expect_log_msg_containing("base consensus doesn't match the digest "
1112 1 : "as found");
1113 :
1114 : /* Resulting consensus doesn't match its digest as found in the diff. */
1115 1 : smartlist_clear(diff);
1116 1 : smartlist_add_linecpy(diff, area, "network-status-diff-version 1");
1117 1 : smartlist_add_linecpy(diff, area, "hash"
1118 : /* sha3 of cons1. */
1119 : " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4"
1120 : /* bogus sha3. */
1121 : " 3333333333333333333333333333333333333333333333333333333333333333");
1122 1 : mock_clean_saved_logs();
1123 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1124 1 : tt_ptr_op(NULL, OP_EQ, cons2);
1125 1 : expect_log_msg_containing("resulting consensus doesn't match the "
1126 1 : "digest as found");
1127 :
1128 : #if 0
1129 : /* XXXX No longer possible, since we aren't using the other algorithm. */
1130 : /* Resulting consensus digest cannot be computed */
1131 : smartlist_clear(diff);
1132 : smartlist_add_linecpy(diff, area, "network-status-diff-version 1");
1133 : smartlist_add_linecpy(diff, area, "hash"
1134 : /* sha3 of cons1. */
1135 : " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4"
1136 : /* bogus sha3. */
1137 : " 3333333333333333333333333333333333333333333333333333333333333333");
1138 : smartlist_add_linecpy(diff, area, "1,2d"); // remove starting line
1139 : mock_clean_saved_logs();
1140 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1141 : tt_ptr_op(NULL, OP_EQ, cons2);
1142 : expect_log_msg_containing("Could not compute digests of the consensus "
1143 : "resulting from applying a consensus diff.");
1144 : #endif /* 0 */
1145 :
1146 : /* Very simple test, only to see that nothing errors. */
1147 1 : smartlist_clear(diff);
1148 1 : smartlist_add_linecpy(diff, area, "network-status-diff-version 1");
1149 1 : smartlist_add_linecpy(diff, area, "hash"
1150 : /* sha3 of cons1. */
1151 : " 06646D6CF563A41869D3B02E73254372AE3140046C5E7D83C9F71E54976AF9B4"
1152 : /* sha3 of cons2. */
1153 : " 90A418881B2FCAB3D9E60EE02E4D666D56CFA38F8A3B7AA3E0ADBA530DDA9353");
1154 1 : smartlist_add_linecpy(diff, area, "3c");
1155 1 : smartlist_add_linecpy(diff, area, "sample");
1156 1 : smartlist_add_linecpy(diff, area, ".");
1157 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1158 1 : tt_ptr_op(NULL, OP_NE, cons2);
1159 1 : tt_str_op(
1160 : "network-status-version foo\n"
1161 : "r name ccccccccccccccccc etc\nsample\n"
1162 : "r name eeeeeeeeeeeeeeeee etc\nbar\n"
1163 : "directory-signature foo bar\nbar\n", OP_EQ,
1164 : cons2);
1165 1 : tor_free(cons2);
1166 :
1167 : /* Check that lowercase letters in base16-encoded digests work too. */
1168 1 : smartlist_clear(diff);
1169 1 : smartlist_add_linecpy(diff, area, "network-status-diff-version 1");
1170 1 : smartlist_add_linecpy(diff, area, "hash"
1171 : /* sha3 of cons1. */
1172 : " 06646d6cf563a41869d3b02e73254372ae3140046c5e7d83c9f71e54976af9b4"
1173 : /* sha3 of cons2. */
1174 : " 90a418881b2fcab3d9e60ee02e4d666d56cfa38f8a3b7aa3e0adba530dda9353");
1175 1 : smartlist_add_linecpy(diff, area, "3c");
1176 1 : smartlist_add_linecpy(diff, area, "sample");
1177 1 : smartlist_add_linecpy(diff, area, ".");
1178 1 : cons2 = consdiff_apply_diff(cons1, diff, &digests1);
1179 1 : tt_ptr_op(NULL, OP_NE, cons2);
1180 1 : tt_str_op(
1181 : "network-status-version foo\n"
1182 : "r name ccccccccccccccccc etc\nsample\n"
1183 : "r name eeeeeeeeeeeeeeeee etc\nbar\n"
1184 : "directory-signature foo bar\nbar\n", OP_EQ,
1185 : cons2);
1186 1 : tor_free(cons2);
1187 :
1188 1 : smartlist_clear(diff);
1189 :
1190 1 : done:
1191 1 : teardown_capture_of_logs();
1192 1 : tor_free(cons1_str);
1193 1 : smartlist_free(cons1);
1194 1 : smartlist_free(diff);
1195 1 : memarea_drop_all(area);
1196 1 : }
1197 :
1198 : #define CONSDIFF_LEGACY(name) \
1199 : { #name, test_consdiff_ ## name , 0, NULL, NULL }
1200 :
1201 : struct testcase_t consdiff_tests[] = {
1202 : CONSDIFF_LEGACY(smartlist_slice),
1203 : CONSDIFF_LEGACY(smartlist_slice_string_pos),
1204 : CONSDIFF_LEGACY(lcs_lengths),
1205 : CONSDIFF_LEGACY(trim_slices),
1206 : CONSDIFF_LEGACY(set_changed),
1207 : CONSDIFF_LEGACY(calc_changes),
1208 : CONSDIFF_LEGACY(get_id_hash),
1209 : CONSDIFF_LEGACY(is_valid_router_entry),
1210 : CONSDIFF_LEGACY(next_router),
1211 : CONSDIFF_LEGACY(base64cmp),
1212 : CONSDIFF_LEGACY(gen_ed_diff),
1213 : CONSDIFF_LEGACY(apply_ed_diff),
1214 : CONSDIFF_LEGACY(gen_diff),
1215 : CONSDIFF_LEGACY(apply_diff),
1216 : END_OF_TESTCASES
1217 : };
|