LCOV - code coverage report
Current view: top level - test - test_consdiff.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 761 761 100.0 %
Date: 2021-11-24 03:28:48 Functions: 18 18 100.0 %

          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             : };

Generated by: LCOV version 1.14