1 /* Copyright (c) 2017-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 #define CONSDIFFMGR_PRIVATE
6 #include "core/or/or.h"
7 #include "app/config/config.h"
8 #include "feature/dircache/conscache.h"
9 #include "feature/dircommon/consdiff.h"
10 #include "feature/dircache/consdiffmgr.h"
11 #include "core/mainloop/cpuworker.h"
12 #include "lib/crypt_ops/crypto_rand.h"
13 #include "feature/nodelist/networkstatus.h"
14 #include "feature/dirparse/ns_parse.h"
15 #include "lib/evloop/workqueue.h"
16 #include "lib/compress/compress.h"
17 #include "lib/encoding/confline.h"
19 #include "feature/nodelist/networkstatus_st.h"
21 #include "test/test.h"
22 #include "test/log_test_helpers.h"
24 #define consdiffmgr_add_consensus consdiffmgr_add_consensus_nulterm
27 consensus_diff_apply_(const char *c
, const char *d
)
29 size_t c_len
= strlen(c
);
30 size_t d_len
= strlen(d
);
31 // We use memdup here to ensure that the input is NOT nul-terminated.
32 // This makes it likelier for us to spot bugs.
33 char *c_tmp
= tor_memdup(c
, c_len
);
34 char *d_tmp
= tor_memdup(d
, d_len
);
35 char *result
= consensus_diff_apply(c_tmp
, c_len
, d_tmp
, d_len
);
41 // ============================== Setup/teardown the consdiffmgr
42 // These functions get run before/after each test in this module
45 consdiffmgr_test_setup(const struct testcase_t
*arg
)
48 char *ddir_fname
= tor_strdup(get_fname_rnd("datadir_cdm"));
49 tor_free(get_options_mutable()->CacheDirectory
);
50 get_options_mutable()->CacheDirectory
= ddir_fname
; // now owns the pointer.
51 check_private_dir(ddir_fname
, CPD_CREATE
, NULL
);
53 consdiff_cfg_t consdiff_cfg
= { 300 };
54 consdiffmgr_configure(&consdiff_cfg
);
55 return (void *)1; // must return something non-null.
58 consdiffmgr_test_teardown(const struct testcase_t
*arg
, void *ignore
)
62 consdiffmgr_free_all();
65 static struct testcase_setup_t setup_diffmgr
= {
66 consdiffmgr_test_setup
,
67 consdiffmgr_test_teardown
70 // ============================== NS faking functions
71 // These functions are for making quick fake consensus objects and
72 // strings that are just good enough for consdiff and consdiffmgr.
74 static networkstatus_t
*
75 fake_ns_new(consensus_flavor_t flav
, time_t valid_after
)
77 networkstatus_t
*ns
= tor_malloc_zero(sizeof(networkstatus_t
));
78 ns
->type
= NS_TYPE_CONSENSUS
;
80 ns
->valid_after
= valid_after
;
85 fake_ns_body_new(consensus_flavor_t flav
, time_t valid_after
)
87 const char *flavor_string
= flav
== FLAV_NS
? "" : " microdesc";
88 char valid_after_string
[ISO_TIME_LEN
+1];
90 format_iso_time(valid_after_string
, valid_after
);
91 char *random_stuff
= crypto_random_hostname(3, 25, "junk ", "");
92 char *random_stuff2
= crypto_random_hostname(3, 10, "", "");
95 tor_asprintf(&consensus
,
96 "network-status-version 3%s\n"
97 "vote-status consensus\n"
99 "r name ccccccccccccccccc etc\nsample\n"
100 "r name eeeeeeeeeeeeeeeee etc\nbar\n"
102 "directory-signature hello-there\n"
103 "directory-signature %s\n",
108 tor_free(random_stuff
);
109 tor_free(random_stuff2
);
113 // ============================== Cpuworker mocking code
114 // These mocking functions and types capture the cpuworker calls
115 // so we can inspect them and run them in the main thread.
116 static smartlist_t
*fake_cpuworker_queue
= NULL
;
117 typedef struct fake_work_queue_ent_t
{
118 enum workqueue_reply_t (*fn
)(void *, void *);
119 void (*reply_fn
)(void *);
121 } fake_work_queue_ent_t
;
122 static struct workqueue_entry_t
*
123 mock_cpuworker_queue_work(workqueue_priority_t prio
,
124 enum workqueue_reply_t (*fn
)(void *, void *),
125 void (*reply_fn
)(void *),
130 if (! fake_cpuworker_queue
)
131 fake_cpuworker_queue
= smartlist_new();
133 fake_work_queue_ent_t
*ent
= tor_malloc_zero(sizeof(*ent
));
135 ent
->reply_fn
= reply_fn
;
137 smartlist_add(fake_cpuworker_queue
, ent
);
138 return (struct workqueue_entry_t
*)ent
;
141 mock_cpuworker_run_work(void)
143 if (! fake_cpuworker_queue
)
145 SMARTLIST_FOREACH(fake_cpuworker_queue
, fake_work_queue_ent_t
*, ent
, {
146 enum workqueue_reply_t r
= ent
->fn(NULL
, ent
->arg
);
147 if (r
!= WQ_RPL_REPLY
)
153 mock_cpuworker_handle_replies(void)
155 if (! fake_cpuworker_queue
)
157 SMARTLIST_FOREACH(fake_cpuworker_queue
, fake_work_queue_ent_t
*, ent
, {
158 ent
->reply_fn(ent
->arg
);
161 smartlist_free(fake_cpuworker_queue
);
162 fake_cpuworker_queue
= NULL
;
165 // ============================== Other helpers
167 static consdiff_status_t
168 lookup_diff_from(consensus_cache_entry_t
**out
,
169 consensus_flavor_t flav
,
172 uint8_t digest
[DIGEST256_LEN
];
173 if (router_get_networkstatus_v3_sha3_as_signed(digest
,
174 str1
, strlen(str1
))<0) {
175 TT_FAIL(("Unable to compute sha3-as-signed"));
176 return CONSDIFF_NOT_FOUND
;
178 return consdiffmgr_find_diff_from(out
, flav
,
179 DIGEST_SHA3_256
, digest
, sizeof(digest
),
184 lookup_apply_and_verify_diff(consensus_flavor_t flav
,
188 consensus_cache_entry_t
*ent
= NULL
;
189 consdiff_status_t status
= lookup_diff_from(&ent
, flav
, str1
);
190 if (ent
== NULL
|| status
!= CONSDIFF_AVAILABLE
) {
194 consensus_cache_entry_incref(ent
);
196 const char *diff_string
= NULL
;
197 char *diff_owned
= NULL
;
198 int r
= uncompress_or_set_ptr(&diff_string
, &size
, &diff_owned
, ent
);
199 consensus_cache_entry_decref(ent
);
200 if (diff_string
== NULL
|| r
< 0)
203 char *applied
= consensus_diff_apply(str1
, strlen(str1
), diff_string
, size
);
204 tor_free(diff_owned
);
208 int match
= !strcmp(applied
, str2
);
210 return match
? 0 : -1;
216 consdiffmgr_free_all();
218 consdiffmgr_rescan();
221 // ============================== Beginning of tests
224 static int got_failure
= 0;
226 got_assertion_failure(void)
231 /* XXXX This test won't work, because there is currently no way to actually
232 * XXXX capture a real assertion failure. */
234 test_consdiffmgr_init_failure(void *arg
)
237 // Capture assertions and bugs.
239 /* As in ...test_setup, but do not create the datadir. The missing directory
240 * will cause a failure. */
241 char *ddir_fname
= tor_strdup(get_fname_rnd("datadir_cdm"));
242 tor_free(get_options_mutable()->CacheDirectory
);
243 get_options_mutable()->CacheDirectory
= ddir_fname
; // now owns the pointer.
245 consdiff_cfg_t consdiff_cfg
= { 7200, 300 };
247 tor_set_failed_assertion_callback(got_assertion_failure
);
248 tor_capture_bugs_(1);
249 consdiffmgr_configure(&consdiff_cfg
); // This should fail.
250 tt_int_op(got_failure
, OP_EQ
, 1);
251 const smartlist_t
*bugs
= tor_get_captured_bug_log_();
252 tt_int_op(smartlist_len(bugs
), OP_EQ
, 1);
255 tor_end_capture_bugs_();
260 test_consdiffmgr_sha3_helper(void *arg
)
263 consensus_cache_t
*cache
= cdm_cache_get(); // violate abstraction barrier
264 config_line_t
*lines
= NULL
;
265 char *mem_op_hex_tmp
= NULL
;
266 config_line_prepend(&lines
, "good-sha",
267 "F00DF00DF00DF00DF00DF00DF00DF00D"
268 "F00DF00DF00DF00DF00DF00DF00DF00D");
269 config_line_prepend(&lines
, "short-sha",
270 "F00DF00DF00DF00DF00DF00DF00DF00D"
271 "F00DF00DF00DF00DF00DF00DF00DF0");
272 config_line_prepend(&lines
, "long-sha",
273 "F00DF00DF00DF00DF00DF00DF00DF00D"
274 "F00DF00DF00DF00DF00DF00DF00DF00DF00D");
275 config_line_prepend(&lines
, "not-sha",
276 "F00DF00DF00DF00DF00DF00DF00DF00D"
277 "F00DF00DF00DF00DF00DF00DF00DXXXX");
278 consensus_cache_entry_t
*ent
=
279 consensus_cache_add(cache
, lines
, (const uint8_t *)"Hi there", 8);
281 uint8_t buf
[DIGEST256_LEN
];
282 tt_int_op(-1, OP_EQ
, cdm_entry_get_sha3_value(buf
, NULL
, "good-sha"));
283 tt_int_op(0, OP_EQ
, cdm_entry_get_sha3_value(buf
, ent
, "good-sha"));
284 test_memeq_hex(buf
, "F00DF00DF00DF00DF00DF00DF00DF00D"
285 "F00DF00DF00DF00DF00DF00DF00DF00D");
287 tt_int_op(-1, OP_EQ
, cdm_entry_get_sha3_value(buf
, ent
, "missing-sha"));
288 tt_int_op(-2, OP_EQ
, cdm_entry_get_sha3_value(buf
, ent
, "short-sha"));
289 tt_int_op(-2, OP_EQ
, cdm_entry_get_sha3_value(buf
, ent
, "long-sha"));
290 tt_int_op(-2, OP_EQ
, cdm_entry_get_sha3_value(buf
, ent
, "not-sha"));
293 consensus_cache_entry_decref(ent
);
294 config_free_lines(lines
);
295 tor_free(mem_op_hex_tmp
);
299 test_consdiffmgr_add(void *arg
)
302 time_t now
= approx_time();
304 const char *body
= NULL
;
305 char *body_owned
= NULL
;
307 consensus_cache_entry_t
*ent
= NULL
;
308 networkstatus_t
*ns_tmp
= fake_ns_new(FLAV_NS
, now
);
309 const char *dummy
= "foo";
310 int r
= consdiffmgr_add_consensus(dummy
, ns_tmp
);
311 tt_int_op(r
, OP_EQ
, 0);
313 /* If we add it again, it won't work */
314 setup_capture_of_logs(LOG_INFO
);
316 r
= consdiffmgr_add_consensus(dummy
, ns_tmp
);
317 tt_int_op(r
, OP_EQ
, -1);
318 expect_single_log_msg_containing("We already have a copy of that "
320 mock_clean_saved_logs();
322 /* But it will work fine if the flavor is different */
324 ns_tmp
->flavor
= FLAV_MICRODESC
;
325 r
= consdiffmgr_add_consensus(dummy
, ns_tmp
);
326 tt_int_op(r
, OP_EQ
, 0);
328 /* And it will work fine if the time is different */
330 ns_tmp
->flavor
= FLAV_NS
;
331 ns_tmp
->valid_after
= now
- 60;
332 r
= consdiffmgr_add_consensus(dummy
, ns_tmp
);
333 tt_int_op(r
, OP_EQ
, 0);
335 /* If we add one a long long time ago, it will fail. */
337 ns_tmp
->valid_after
= 86400 * 100; /* A few months into 1970 */
338 r
= consdiffmgr_add_consensus(dummy
, ns_tmp
);
339 tt_int_op(r
, OP_EQ
, -1);
340 expect_log_msg_containing("it's too old.");
342 /* Try looking up a consensuses. */
343 ent
= cdm_cache_lookup_consensus(FLAV_NS
, now
-60);
345 consensus_cache_entry_incref(ent
);
347 r
= uncompress_or_set_ptr(&body
, &s
, &body_owned
, ent
);
348 tt_int_op(r
, OP_EQ
, 0);
349 tt_int_op(s
, OP_EQ
, 4);
350 tt_mem_op(body
, OP_EQ
, "quux", 4);
352 /* Try looking up another entry, but fail */
353 tt_ptr_op(cdm_cache_lookup_consensus(FLAV_MICRODESC
, now
- 60), OP_EQ
, NULL
);
354 tt_ptr_op(cdm_cache_lookup_consensus(FLAV_NS
, now
- 61), OP_EQ
, NULL
);
357 networkstatus_vote_free(ns_tmp
);
358 teardown_capture_of_logs();
359 consensus_cache_entry_decref(ent
);
360 tor_free(body_owned
);
364 test_consdiffmgr_make_diffs(void *arg
)
367 networkstatus_t
*ns
= NULL
;
368 char *ns_body
= NULL
, *md_ns_body
= NULL
, *md_ns_body_2
= NULL
;
369 char *applied
= NULL
, *diff_text
= NULL
;
370 time_t now
= approx_time();
372 consensus_cache_entry_t
*diff
= NULL
;
373 uint8_t md_ns_sha3
[DIGEST256_LEN
];
374 consdiff_status_t diff_status
;
376 MOCK(cpuworker_queue_work
, mock_cpuworker_queue_work
);
378 // Try rescan with no consensuses: shouldn't crash or queue work.
379 consdiffmgr_rescan();
380 tt_ptr_op(NULL
, OP_EQ
, fake_cpuworker_queue
);
382 // Make two consensuses, 1 hour sec ago.
383 ns
= fake_ns_new(FLAV_NS
, now
-3600);
384 ns_body
= fake_ns_body_new(FLAV_NS
, now
-3600);
385 r
= consdiffmgr_add_consensus(ns_body
, ns
);
386 networkstatus_vote_free(ns
);
388 tt_int_op(r
, OP_EQ
, 0);
390 ns
= fake_ns_new(FLAV_MICRODESC
, now
-3600);
391 md_ns_body
= fake_ns_body_new(FLAV_MICRODESC
, now
-3600);
392 r
= consdiffmgr_add_consensus(md_ns_body
, ns
);
393 router_get_networkstatus_v3_sha3_as_signed(md_ns_sha3
, md_ns_body
,
395 networkstatus_vote_free(ns
);
396 tt_int_op(r
, OP_EQ
, 0);
398 // No diffs will be generated.
399 consdiffmgr_rescan();
400 tt_ptr_op(NULL
, OP_EQ
, fake_cpuworker_queue
);
402 // Add a MD consensus from 45 minutes ago. This should cause one diff
403 // worth of work to get queued.
404 ns
= fake_ns_new(FLAV_MICRODESC
, now
-45*60);
405 md_ns_body_2
= fake_ns_body_new(FLAV_MICRODESC
, now
-45*60);
406 r
= consdiffmgr_add_consensus(md_ns_body_2
, ns
);
407 networkstatus_vote_free(ns
);
408 tt_int_op(r
, OP_EQ
, 0);
410 consdiffmgr_rescan();
411 tt_ptr_op(NULL
, OP_NE
, fake_cpuworker_queue
);
412 tt_int_op(1, OP_EQ
, smartlist_len(fake_cpuworker_queue
));
413 diff_status
= consdiffmgr_find_diff_from(&diff
, FLAV_MICRODESC
,
415 md_ns_sha3
, DIGEST256_LEN
,
417 tt_int_op(CONSDIFF_IN_PROGRESS
, OP_EQ
, diff_status
);
419 // Now run that process and get the diff.
420 r
= mock_cpuworker_run_work();
421 tt_int_op(r
, OP_EQ
, 0);
422 mock_cpuworker_handle_replies();
424 // At this point we should be able to get that diff.
425 diff_status
= consdiffmgr_find_diff_from(&diff
, FLAV_MICRODESC
,
427 md_ns_sha3
, DIGEST256_LEN
,
429 tt_int_op(CONSDIFF_AVAILABLE
, OP_EQ
, diff_status
);
432 /* Make sure applying the diff actually works */
433 const uint8_t *diff_body
;
435 r
= consensus_cache_entry_get_body(diff
, &diff_body
, &diff_size
);
436 tt_int_op(r
, OP_EQ
, 0);
437 diff_text
= tor_memdup_nulterm(diff_body
, diff_size
);
438 applied
= consensus_diff_apply_(md_ns_body
, diff_text
);
440 tt_str_op(applied
, OP_EQ
, md_ns_body_2
);
442 /* Rescan again: no more work to do. */
443 consdiffmgr_rescan();
444 tt_ptr_op(NULL
, OP_EQ
, fake_cpuworker_queue
);
447 tor_free(md_ns_body
);
448 tor_free(md_ns_body_2
);
454 test_consdiffmgr_diff_rules(void *arg
)
458 char *md_body
[N
], *ns_body
[N
];
459 networkstatus_t
*md_ns
[N
], *ns_ns
[N
];
462 MOCK(cpuworker_queue_work
, mock_cpuworker_queue_work
);
464 /* Create a bunch of consensus things at 15-second intervals. */
465 time_t start
= approx_time() - 120;
466 for (i
= 0; i
< N
; ++i
) {
467 time_t when
= start
+ i
* 15;
468 md_body
[i
] = fake_ns_body_new(FLAV_MICRODESC
, when
);
469 ns_body
[i
] = fake_ns_body_new(FLAV_NS
, when
);
470 md_ns
[i
] = fake_ns_new(FLAV_MICRODESC
, when
);
471 ns_ns
[i
] = fake_ns_new(FLAV_NS
, when
);
474 /* For the MD consensuses: add 4 of them, and make sure that
475 * diffs are created to one consensus (the most recent) only. */
476 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[1], md_ns
[1]));
477 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[2], md_ns
[2]));
478 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[3], md_ns
[3]));
479 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[4], md_ns
[4]));
480 consdiffmgr_rescan();
481 tt_ptr_op(NULL
, OP_NE
, fake_cpuworker_queue
);
482 tt_int_op(3, OP_EQ
, smartlist_len(fake_cpuworker_queue
));
483 tt_int_op(0, OP_EQ
, mock_cpuworker_run_work());
484 mock_cpuworker_handle_replies();
485 tt_ptr_op(NULL
, OP_EQ
, fake_cpuworker_queue
);
487 /* For the NS consensuses: add 3, generate, and add one older one and
488 * make sure that older one is the only one whose diff is generated */
489 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(ns_body
[0], ns_ns
[0]));
490 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(ns_body
[1], ns_ns
[1]));
491 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(ns_body
[5], ns_ns
[5]));
492 consdiffmgr_rescan();
493 tt_ptr_op(NULL
, OP_NE
, fake_cpuworker_queue
);
494 tt_int_op(2, OP_EQ
, smartlist_len(fake_cpuworker_queue
));
495 tt_int_op(0, OP_EQ
, mock_cpuworker_run_work());
496 mock_cpuworker_handle_replies();
498 /* At this point, we should actually have working diffs! */
500 lookup_apply_and_verify_diff(FLAV_NS
, ns_body
[0], ns_body
[5]));
502 lookup_apply_and_verify_diff(FLAV_NS
, ns_body
[1], ns_body
[5]));
505 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[1], md_body
[4]));
507 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[2], md_body
[4]));
509 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[3], md_body
[4]));
511 /* Self-to-self diff won't be present */
512 consensus_cache_entry_t
*ent
;
513 tt_int_op(CONSDIFF_NOT_FOUND
, OP_EQ
,
514 lookup_diff_from(&ent
, FLAV_NS
, ns_body
[5]));
515 /* No diff from 2 has been added yet */
516 tt_int_op(CONSDIFF_NOT_FOUND
, OP_EQ
,
517 lookup_diff_from(&ent
, FLAV_NS
, ns_body
[2]));
518 /* No diff arriving at old things. */
520 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[1], md_body
[2]));
521 /* No backwards diff */
523 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[4], md_body
[3]));
525 /* Now, an update: add number 2 and make sure it's the only one whose diff
527 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(ns_body
[2], ns_ns
[2]));
528 consdiffmgr_rescan();
529 tt_ptr_op(NULL
, OP_NE
, fake_cpuworker_queue
);
530 tt_int_op(1, OP_EQ
, smartlist_len(fake_cpuworker_queue
));
531 tt_int_op(0, OP_EQ
, mock_cpuworker_run_work());
532 mock_cpuworker_handle_replies();
535 lookup_apply_and_verify_diff(FLAV_NS
, ns_body
[2], ns_body
[5]));
537 /* Finally: reload, and make sure that the information is still indexed */
541 lookup_apply_and_verify_diff(FLAV_NS
, ns_body
[0], ns_body
[5]));
543 lookup_apply_and_verify_diff(FLAV_NS
, ns_body
[2], ns_body
[5]));
545 lookup_apply_and_verify_diff(FLAV_NS
, ns_body
[1], ns_body
[5]));
548 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[1], md_body
[4]));
550 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[2], md_body
[4]));
552 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[3], md_body
[4]));
555 for (i
= 0; i
< N
; ++i
) {
556 tor_free(md_body
[i
]);
557 tor_free(ns_body
[i
]);
558 networkstatus_vote_free(md_ns
[i
]);
559 networkstatus_vote_free(ns_ns
[i
]);
561 UNMOCK(cpuworker_queue_work
);
566 test_consdiffmgr_diff_failure(void *arg
)
569 MOCK(cpuworker_queue_work
, mock_cpuworker_queue_work
);
571 /* We're going to make sure that if we have a bogus request where
572 * we can't actually compute a diff, the world must not end. */
573 networkstatus_t
*ns1
= NULL
;
574 networkstatus_t
*ns2
= NULL
;
577 ns1
= fake_ns_new(FLAV_NS
, approx_time()-100);
578 ns2
= fake_ns_new(FLAV_NS
, approx_time()-50);
579 r
= consdiffmgr_add_consensus("foo bar baz\n", ns1
);
580 tt_int_op(r
, OP_EQ
, 0);
581 // We refuse to compute a diff to or from a line holding only a single dot.
582 // We can add it here, though.
583 r
= consdiffmgr_add_consensus("foo bar baz\n.\n.\n", ns2
);
584 tt_int_op(r
, OP_EQ
, 0);
586 consdiffmgr_rescan();
587 tt_ptr_op(NULL
, OP_NE
, fake_cpuworker_queue
);
588 setup_capture_of_logs(LOG_WARN
);
589 tt_int_op(1, OP_EQ
, smartlist_len(fake_cpuworker_queue
));
590 tt_int_op(0, OP_EQ
, mock_cpuworker_run_work());
591 expect_single_log_msg_containing("one of the lines to be added is \".\".");
592 mock_clean_saved_logs();
593 mock_cpuworker_handle_replies();
594 expect_single_log_msg_containing("Worker was unable to compute consensus "
597 /* Make sure the diff is not present */
598 consensus_cache_entry_t
*ent
;
599 tt_int_op(CONSDIFF_NOT_FOUND
, OP_EQ
,
600 lookup_diff_from(&ent
, FLAV_NS
, "foo bar baz\n"));
603 teardown_capture_of_logs();
604 UNMOCK(cpuworker_queue_work
);
605 networkstatus_vote_free(ns1
);
606 networkstatus_vote_free(ns2
);
610 test_consdiffmgr_diff_pending(void *arg
)
615 networkstatus_t
*md_ns
[N
];
616 time_t start
= approx_time() - 120;
618 for (i
= 0; i
< N
; ++i
) {
619 time_t when
= start
+ i
* 30;
620 md_body
[i
] = fake_ns_body_new(FLAV_MICRODESC
, when
);
621 md_ns
[i
] = fake_ns_new(FLAV_MICRODESC
, when
);
624 MOCK(cpuworker_queue_work
, mock_cpuworker_queue_work
);
626 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[1], md_ns
[1]));
627 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[2], md_ns
[2]));
629 consdiffmgr_rescan();
630 tt_int_op(1, OP_EQ
, smartlist_len(fake_cpuworker_queue
));
632 /* Look it up. Is it pending? */
633 consensus_cache_entry_t
*ent
= NULL
;
634 consdiff_status_t diff_status
;
635 diff_status
= lookup_diff_from(&ent
, FLAV_MICRODESC
, md_body
[1]);
636 tt_int_op(CONSDIFF_IN_PROGRESS
, OP_EQ
, diff_status
);
637 tt_ptr_op(ent
, OP_EQ
, NULL
);
639 /* Add another old consensus. only one new diff should launch! */
640 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[0], md_ns
[0]));
641 consdiffmgr_rescan();
642 tt_int_op(2, OP_EQ
, smartlist_len(fake_cpuworker_queue
));
644 tt_int_op(0, OP_EQ
, mock_cpuworker_run_work());
645 mock_cpuworker_handle_replies();
648 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[0], md_body
[2]));
650 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[1], md_body
[2]));
653 UNMOCK(cpuworker_queue_work
);
654 for (i
= 0; i
< N
; ++i
) {
655 tor_free(md_body
[i
]);
656 networkstatus_vote_free(md_ns
[i
]);
662 test_consdiffmgr_cleanup_old(void *arg
)
665 config_line_t
*labels
= NULL
;
666 consensus_cache_entry_t
*ent
= NULL
;
667 consensus_cache_t
*cache
= cdm_cache_get(); // violate abstraction barrier
669 /* This item will be will be cleanable because it has a valid-after
670 * time far in the past. */
671 config_line_prepend(&labels
, "document-type", "confribble-blarg");
672 config_line_prepend(&labels
, "consensus-valid-after",
673 "1980-10-10T10:10:10");
674 ent
= consensus_cache_add(cache
, labels
, (const uint8_t*)"Foo", 3);
676 consensus_cache_entry_decref(ent
);
678 setup_capture_of_logs(LOG_DEBUG
);
679 tt_int_op(1, OP_EQ
, consdiffmgr_cleanup());
680 expect_log_msg_containing("Deleting entry because its consensus-valid-"
681 "after value (1980-10-10T10:10:10) was too old");
684 teardown_capture_of_logs();
685 config_free_lines(labels
);
689 test_consdiffmgr_cleanup_bad_valid_after(void *arg
)
691 /* This will seem cleanable, but isn't, because its valid-after time is
695 config_line_t
*labels
= NULL
;
696 consensus_cache_entry_t
*ent
= NULL
;
697 consensus_cache_t
*cache
= cdm_cache_get(); // violate abstraction barrier
699 config_line_prepend(&labels
, "document-type", "consensus");
700 config_line_prepend(&labels
, "consensus-valid-after",
701 "whan that aprille with his shoures soote"); // (~1385?)
702 ent
= consensus_cache_add(cache
, labels
, (const uint8_t*)"Foo", 3);
704 consensus_cache_entry_decref(ent
);
706 setup_capture_of_logs(LOG_DEBUG
);
707 tt_int_op(0, OP_EQ
, consdiffmgr_cleanup());
708 expect_log_msg_containing("Ignoring entry because its consensus-valid-"
709 "after value (\"whan that aprille with his "
710 "shoures soote\") was unparseable");
713 teardown_capture_of_logs();
714 config_free_lines(labels
);
718 test_consdiffmgr_cleanup_no_valid_after(void *arg
)
721 config_line_t
*labels
= NULL
;
722 consensus_cache_entry_t
*ent
= NULL
;
723 consensus_cache_t
*cache
= cdm_cache_get(); // violate abstraction barrier
725 /* This item will be will be uncleanable because it has no recognized
727 config_line_prepend(&labels
, "document-type", "consensus");
728 config_line_prepend(&labels
, "confrooble-voolid-oofter",
729 "2010-10-10T09:08:07");
730 ent
= consensus_cache_add(cache
, labels
, (const uint8_t*)"Foo", 3);
732 consensus_cache_entry_decref(ent
);
734 setup_capture_of_logs(LOG_DEBUG
);
735 tt_int_op(0, OP_EQ
, consdiffmgr_cleanup());
736 expect_log_msg_containing("Ignoring entry because it had no consensus-"
737 "valid-after label");
740 teardown_capture_of_logs();
741 config_free_lines(labels
);
745 test_consdiffmgr_cleanup_old_diffs(void *arg
)
750 networkstatus_t
*md_ns
[N
];
752 consensus_cache_entry_t
*hold_ent
= NULL
, *ent
;
754 /* Make sure that the cleanup function removes diffs to the not-most-recent
757 MOCK(cpuworker_queue_work
, mock_cpuworker_queue_work
);
759 /* Create a bunch of consensus things at 15-second intervals. */
760 time_t start
= approx_time() - 120;
761 for (i
= 0; i
< N
; ++i
) {
762 time_t when
= start
+ i
* 15;
763 md_body
[i
] = fake_ns_body_new(FLAV_MICRODESC
, when
);
764 md_ns
[i
] = fake_ns_new(FLAV_MICRODESC
, when
);
767 /* add the first 3. */
768 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[0], md_ns
[0]));
769 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[1], md_ns
[1]));
770 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[2], md_ns
[2]));
772 consdiffmgr_rescan();
773 tt_ptr_op(NULL
, OP_NE
, fake_cpuworker_queue
);
774 tt_int_op(2, OP_EQ
, smartlist_len(fake_cpuworker_queue
));
775 tt_int_op(0, OP_EQ
, mock_cpuworker_run_work());
776 mock_cpuworker_handle_replies();
777 tt_ptr_op(NULL
, OP_EQ
, fake_cpuworker_queue
);
779 /* Nothing is deletable now */
780 tt_int_op(0, OP_EQ
, consdiffmgr_cleanup());
782 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[0], md_body
[2]));
784 lookup_apply_and_verify_diff(FLAV_MICRODESC
, md_body
[1], md_body
[2]));
786 tt_int_op(CONSDIFF_AVAILABLE
, OP_EQ
,
787 lookup_diff_from(&hold_ent
, FLAV_MICRODESC
, md_body
[1]));
788 consensus_cache_entry_incref(hold_ent
); // incref, so it is preserved.
790 /* Now add an even-more-recent consensus; this should make all previous
791 * diffs deletable, and make delete */
792 tt_int_op(0, OP_EQ
, consdiffmgr_add_consensus(md_body
[3], md_ns
[3]));
793 tt_int_op(2 * n_diff_compression_methods() +
794 (n_consensus_compression_methods() - 1) , OP_EQ
,
795 consdiffmgr_cleanup());
797 tt_int_op(CONSDIFF_NOT_FOUND
, OP_EQ
,
798 lookup_diff_from(&ent
, FLAV_MICRODESC
, md_body
[0]));
799 /* This one is marked deletable but still in the hashtable */
800 tt_int_op(CONSDIFF_AVAILABLE
, OP_EQ
,
801 lookup_diff_from(&ent
, FLAV_MICRODESC
, md_body
[1]));
802 tt_int_op(CONSDIFF_NOT_FOUND
, OP_EQ
,
803 lookup_diff_from(&ent
, FLAV_MICRODESC
, md_body
[2]));
805 /* Everything should be valid at this point */
806 tt_int_op(0, OP_EQ
, consdiffmgr_validate());
808 /* And if we recan NOW, we'll purge the hashtable of the entries,
809 * and launch attempts to generate new ones */
810 consdiffmgr_rescan();
811 tt_int_op(CONSDIFF_IN_PROGRESS
, OP_EQ
,
812 lookup_diff_from(&ent
, FLAV_MICRODESC
, md_body
[0]));
813 tt_int_op(CONSDIFF_IN_PROGRESS
, OP_EQ
,
814 lookup_diff_from(&ent
, FLAV_MICRODESC
, md_body
[1]));
815 tt_int_op(CONSDIFF_IN_PROGRESS
, OP_EQ
,
816 lookup_diff_from(&ent
, FLAV_MICRODESC
, md_body
[2]));
818 /* We're still holding on to this, though, so we can still map it! */
819 const uint8_t *t1
= NULL
;
821 int r
= consensus_cache_entry_get_body(hold_ent
, &t1
, &s
);
822 tt_int_op(r
, OP_EQ
, 0);
826 for (i
= 0; i
< N
; ++i
) {
827 tor_free(md_body
[i
]);
828 networkstatus_vote_free(md_ns
[i
]);
830 consensus_cache_entry_decref(hold_ent
);
831 UNMOCK(cpuworker_queue_work
);
836 test_consdiffmgr_validate(void *arg
)
839 config_line_t
*lines
= NULL
;
840 consensus_cache_entry_t
*ent
= NULL
;
841 consensus_cache_t
*cache
= cdm_cache_get(); // violate abstraction barrier
842 smartlist_t
*vals
= smartlist_new();
844 /* Put these: objects in the cache: one with a good sha3, one with bad sha3,
845 * one with a wrong sha3, and one with no sha3. */
846 config_line_prepend(&lines
, "id", "wrong sha3");
847 config_line_prepend(&lines
, "sha3-digest",
848 "F00DF00DF00DF00DF00DF00DF00DF00D"
849 "F00DF00DF00DF00DF00DF00DF00DF00D");
850 ent
= consensus_cache_add(cache
, lines
, (const uint8_t *)"Hi there", 8);
851 consensus_cache_entry_decref(ent
);
852 config_free_lines(lines
);
855 config_line_prepend(&lines
, "id", "bad sha3");
856 config_line_prepend(&lines
, "sha3-digest",
857 "now is the winter of our dicotheque");
858 ent
= consensus_cache_add(cache
, lines
, (const uint8_t *)"Hi there", 8);
859 consensus_cache_entry_decref(ent
);
860 config_free_lines(lines
);
863 config_line_prepend(&lines
, "id", "no sha3");
864 ent
= consensus_cache_add(cache
, lines
, (const uint8_t *)"Hi there", 8);
865 consensus_cache_entry_decref(ent
);
866 config_free_lines(lines
);
869 config_line_prepend(&lines
, "id", "good sha3");
870 config_line_prepend(&lines
, "sha3-digest",
871 "8d8b1998616cd6b4c4055da8d38728dc"
872 "93c758d4131a53c7d81aa6337dee1c05");
873 ent
= consensus_cache_add(cache
, lines
, (const uint8_t *)"Hi there", 8);
874 consensus_cache_entry_decref(ent
);
875 config_free_lines(lines
);
879 cache
= cdm_cache_get();
880 tt_int_op(1, OP_EQ
, consdiffmgr_validate());
882 consensus_cache_find_all(vals
, cache
, "id", "good sha3");
883 tt_int_op(smartlist_len(vals
), OP_EQ
, 1);
884 smartlist_clear(vals
);
886 consensus_cache_find_all(vals
, cache
, "id", "no sha3");
887 tt_int_op(smartlist_len(vals
), OP_EQ
, 1);
888 smartlist_clear(vals
);
890 consensus_cache_find_all(vals
, cache
, "id", "wrong sha3");
891 tt_int_op(smartlist_len(vals
), OP_EQ
, 0);
892 consensus_cache_find_all(vals
, cache
, "id", "bad sha3");
893 tt_int_op(smartlist_len(vals
), OP_EQ
, 0);
896 smartlist_free(vals
);
900 { #name, test_consdiffmgr_ ## name , TT_FORK, &setup_diffmgr, NULL }
902 struct testcase_t consdiffmgr_tests
[] = {
904 { "init_failure", test_consdiffmgr_init_failure
, TT_FORK
, NULL
, NULL
},
913 TEST(cleanup_bad_valid_after
),
914 TEST(cleanup_no_valid_after
),
915 TEST(cleanup_old_diffs
),
918 // XXXX Test: non-cacheing cases of replyfn().