Browse Source

Add a helper to search for strings in the log, and change option tests to use this helper instead of looking at specific indices in the log list

Ola Bini 8 years ago
parent
commit
fe92e9bb96
3 changed files with 97 additions and 80 deletions
  1. 17 0
      src/test/log_test_helpers.c
  2. 1 0
      src/test/log_test_helpers.h
  3. 79 80
      src/test/test_options.c

+ 17 - 0
src/test/log_test_helpers.c

@@ -83,6 +83,23 @@ mock_saved_logs(void)
   return saved_logs;
 }
 
+int
+mock_saved_log_has_message(const char *msg)
+{
+  int has_msg = 0;
+  if (saved_logs) {
+    SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
+                      {
+                        if (msg && m->generated_msg &&
+                            !strcmp(msg, m->generated_msg)) {
+                          has_msg = 1;
+                        }
+                      });
+  }
+
+  return has_msg;
+}
+
 void
 mock_saving_logv(int severity, log_domain_mask_t domain,
                  const char *funcname, const char *suffix,

+ 1 - 0
src/test/log_test_helpers.h

@@ -26,6 +26,7 @@ void teardown_capture_of_logs(int prev);
 const char *mock_saved_log_at(int ix);
 int mock_saved_severity_at(int ix);
 int mock_saved_log_number(void);
+int mock_saved_log_has_message(const char *msg);
 
 #endif
 

+ 79 - 80
src/test/test_options.c

@@ -367,6 +367,14 @@ free_options_test_data(options_test_data_t *td)
   tor_free(td);
 }
 
+#define expect_log_msg(str) \
+  tt_assert_msg(mock_saved_log_has_message(str), \
+                "expected log to contain " # str);
+
+#define expect_no_log_msg(str)                      \
+  tt_assert_msg(!mock_saved_log_has_message(str), \
+                "expected log to not contain " # str);
+
 static void
 test_options_validate__uname_for_server(void *ignored)
 {
@@ -379,7 +387,7 @@ test_options_validate__uname_for_server(void *ignored)
   MOCK(get_uname, fixed_get_uname);
   fixed_get_uname_result = "Windows 95";
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(0), OP_EQ, "Tor is running as a server, but you"
+  expect_log_msg("Tor is running as a server, but you"
            " are running Windows 95; this probably won't work. See https://www"
            ".torproject.org/docs/faq.html#BestOSForRelay for details.\n");
   tor_free(msg);
@@ -387,7 +395,7 @@ test_options_validate__uname_for_server(void *ignored)
   fixed_get_uname_result = "Windows 98";
   mock_clean_saved_logs();
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(0), OP_EQ, "Tor is running as a server, but you"
+  expect_log_msg("Tor is running as a server, but you"
            " are running Windows 98; this probably won't work. See https://www"
            ".torproject.org/docs/faq.html#BestOSForRelay for details.\n");
   tor_free(msg);
@@ -395,7 +403,7 @@ test_options_validate__uname_for_server(void *ignored)
   fixed_get_uname_result = "Windows Me";
   mock_clean_saved_logs();
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(0), OP_EQ, "Tor is running as a server, but you"
+  expect_log_msg("Tor is running as a server, but you"
            " are running Windows Me; this probably won't work. See https://www"
            ".torproject.org/docs/faq.html#BestOSForRelay for details.\n");
   tor_free(msg);
@@ -512,7 +520,7 @@ test_options_validate__contactinfo(void *ignored)
 
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_str_op(mock_saved_log_at(0), OP_EQ,
+  expect_log_msg(
             "Your ContactInfo config option is not"
             " set. Please consider setting it, so we can contact you if your"
             " server is misconfigured or something else goes wrong.\n");
@@ -524,7 +532,7 @@ test_options_validate__contactinfo(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_str_op(mock_saved_log_at(0), OP_NE,
+  expect_no_log_msg(
             "Your ContactInfo config option is not"
             " set. Please consider setting it, so we can contact you if your"
             " server is misconfigured or something else goes wrong.\n");
@@ -632,7 +640,7 @@ test_options_validate__authdir(void *ignored)
   tt_int_op(ret, OP_EQ, -1);
   tt_str_op(msg, OP_EQ, "Failed to resolve/guess local address. See logs for"
             " details.");
-  tt_str_op(mock_saved_log_at(0), OP_EQ, "Could not resolve local Address "
+  expect_log_msg("Could not resolve local Address "
             "'this.should.not_exist.example.org'. Failing.\n");
   tor_free(msg);
 
@@ -763,7 +771,7 @@ test_options_validate__authdir(void *ignored)
                                 "SchedulerLowWaterMark__ 10\n");
   mock_clean_saved_logs();
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(0), OP_EQ, "Authoritative directory servers "
+  expect_log_msg("Authoritative directory servers "
             "can't set UseEntryGuards. Disabling.\n");
   tt_int_op(tdata->opt->UseEntryGuards, OP_EQ, 0);
   tor_free(msg);
@@ -777,7 +785,7 @@ test_options_validate__authdir(void *ignored)
                                 "SchedulerLowWaterMark__ 10\n");
   mock_clean_saved_logs();
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(0), OP_EQ, "Authoritative directories always try"
+  expect_log_msg("Authoritative directories always try"
             " to download extra-info documents. Setting DownloadExtraInfo.\n");
   tt_int_op(tdata->opt->DownloadExtraInfo, OP_EQ, 1);
   tor_free(msg);
@@ -792,7 +800,7 @@ test_options_validate__authdir(void *ignored)
                                 "SchedulerLowWaterMark__ 10\n");
   mock_clean_saved_logs();
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(0), OP_NE, "Authoritative directories always try"
+  expect_no_log_msg("Authoritative directories always try"
             " to download extra-info documents. Setting DownloadExtraInfo.\n");
   tt_int_op(tdata->opt->DownloadExtraInfo, OP_EQ, 1);
   tor_free(msg);
@@ -935,7 +943,7 @@ test_options_validate__relay_with_hidden_services(void *ignored)
                                                      );
 
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(1), OP_EQ,
+  expect_log_msg(
             "Tor is currently configured as a relay and a hidden service. "
             "That's not very secure: you should probably run your hidden servi"
             "ce in a separate Tor process, at least -- see "
@@ -1145,7 +1153,7 @@ test_options_validate__exclude_nodes(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_str_op(mock_saved_log_at(0), OP_EQ,
+  expect_log_msg(
             "You have asked to exclude certain relays from all positions "
             "in your circuits. Expect hidden services and other Tor "
             "features to be broken in unpredictable ways.\n");
@@ -1158,7 +1166,7 @@ test_options_validate__exclude_nodes(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_str_op(mock_saved_log_at(0), OP_NE,
+  expect_no_log_msg(
             "You have asked to exclude certain relays from all positions "
             "in your circuits. Expect hidden services and other Tor "
             "features to be broken in unpredictable ways.\n");
@@ -1183,8 +1191,7 @@ test_options_validate__scheduler(void *ignored)
 
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_str_op(mock_saved_log_at(0), OP_EQ,
-            "Bad SchedulerLowWaterMark__ option\n");
+  expect_log_msg("Bad SchedulerLowWaterMark__ option\n");
   tor_free(msg);
 
   // TODO: this test cannot run on platforms where UINT32_MAX == UINT64_MAX.
@@ -1207,8 +1214,7 @@ test_options_validate__scheduler(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_str_op(mock_saved_log_at(0), OP_EQ,
-            "Bad SchedulerHighWaterMark option\n");
+  expect_log_msg("Bad SchedulerHighWaterMark option\n");
   tor_free(msg);
 
  done:
@@ -1283,8 +1289,7 @@ test_options_validate__tlsec(void *ignored)
 
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_str_op(mock_saved_log_at(0), OP_EQ,
-            "Unrecognized TLSECGroup: Falling back to the default.\n");
+  expect_log_msg("Unrecognized TLSECGroup: Falling back to the default.\n");
   tt_assert(!tdata->opt->TLSECGroup);
   tor_free(msg);
 
@@ -1295,7 +1300,7 @@ test_options_validate__tlsec(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_str_op(mock_saved_log_at(0), OP_NE,
+  expect_no_log_msg(
             "Unrecognized TLSECGroup: Falling back to the default.\n");
   tt_assert(tdata->opt->TLSECGroup);
   tor_free(msg);
@@ -1307,7 +1312,7 @@ test_options_validate__tlsec(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_str_op(mock_saved_log_at(0), OP_NE,
+  expect_no_log_msg(
             "Unrecognized TLSECGroup: Falling back to the default.\n");
   tt_assert(tdata->opt->TLSECGroup);
   tor_free(msg);
@@ -1360,8 +1365,7 @@ test_options_validate__recommended_packages(void *ignored)
 
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_int_op(mock_saved_log_number(), OP_EQ, 1);
-  tt_str_op(mock_saved_log_at(0), OP_NE, "Invalid RecommendedPackage line "
+  expect_no_log_msg("Invalid RecommendedPackage line "
             "invalid-package-line will be ignored\n");
 
  done:
@@ -1455,9 +1459,7 @@ test_options_validate__paths_needed(void *ignored)
   tt_int_op(ret, OP_EQ, -1);
   tt_assert(tdata->opt->PathsNeededToBuildCircuits > 0.24 &&
             tdata->opt->PathsNeededToBuildCircuits < 0.26);
-  tt_int_op(mock_saved_log_number(), OP_EQ, 1);
-  tt_str_op(mock_saved_log_at(0), OP_EQ,
-            "PathsNeededToBuildCircuits is too low. Increasing to 0.25\n");
+  expect_log_msg("PathsNeededToBuildCircuits is too low. Increasing to 0.25\n");
   tor_free(msg);
 
   free_options_test_data(tdata);
@@ -1471,8 +1473,7 @@ test_options_validate__paths_needed(void *ignored)
   tt_int_op(ret, OP_EQ, -1);
   tt_assert(tdata->opt->PathsNeededToBuildCircuits > 0.94 &&
             tdata->opt->PathsNeededToBuildCircuits < 0.96);
-  tt_int_op(mock_saved_log_number(), OP_EQ, 1);
-  tt_str_op(mock_saved_log_at(0), OP_EQ, "PathsNeededToBuildCircuits is "
+  expect_log_msg("PathsNeededToBuildCircuits is "
             "too high. Decreasing to 0.95\n");
   tor_free(msg);
 
@@ -1638,11 +1639,10 @@ test_options_validate__reachable_addresses(void *ignored)
 
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_int_op(mock_saved_log_number(), OP_EQ, 6);
-  tt_str_op(mock_saved_log_at(1), OP_EQ, "Converting FascistFirewall config "
+  expect_log_msg("Converting FascistFirewall config "
             "option to new format: \"ReachableDirAddresses *:80\"\n");
   tt_str_op(tdata->opt->ReachableDirAddresses->value, OP_EQ, "*:80");
-  tt_str_op(mock_saved_log_at(2), OP_EQ, "Converting FascistFirewall config "
+  expect_log_msg("Converting FascistFirewall config "
             "option to new format: \"ReachableORAddresses *:443\"\n");
   tt_str_op(tdata->opt->ReachableORAddresses->value, OP_EQ, "*:443");
   tor_free(msg);
@@ -1675,8 +1675,7 @@ test_options_validate__reachable_addresses(void *ignored)
 
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_int_op(mock_saved_log_number(), OP_EQ, 5);
-  tt_str_op(mock_saved_log_at(1), OP_EQ, "Converting FascistFirewall and "
+  expect_log_msg("Converting FascistFirewall and "
             "FirewallPorts config options to new format: "
             "\"ReachableAddresses *:123\"\n");
   tt_str_op(tdata->opt->ReachableAddresses->value, OP_EQ, "*:123");
@@ -2056,7 +2055,7 @@ test_options_validate__publish_server_descriptor(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, -1);
-  tt_str_op(mock_saved_log_at(1), OP_EQ, "Can't set a DirPort on a bridge "
+  expect_log_msg("Can't set a DirPort on a bridge "
             "relay; disabling DirPort\n");
   tt_assert(!tdata->opt->DirPort_lines);
   tt_assert(!tdata->opt->DirPort_set);
@@ -2158,7 +2157,7 @@ test_options_validate__hidserv(void *ignored)
   tdata->opt->MinUptimeHidServDirectoryV2 = -1;
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(1), OP_EQ, "MinUptimeHidServDirectoryV2 "
+  expect_log_msg("MinUptimeHidServDirectoryV2 "
             "option must be at least 0 seconds. Changing to 0.\n");
   tt_int_op(tdata->opt->MinUptimeHidServDirectoryV2, OP_EQ, 0);
   tor_free(msg);
@@ -2169,7 +2168,7 @@ test_options_validate__hidserv(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(1), OP_EQ, "RendPostPeriod option is too short;"
+  expect_log_msg("RendPostPeriod option is too short;"
             " raising to 600 seconds.\n");
   tt_int_op(tdata->opt->RendPostPeriod, OP_EQ, 600);
   tor_free(msg);
@@ -2180,7 +2179,7 @@ test_options_validate__hidserv(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(1), OP_EQ, "RendPostPeriod is too large; "
+  expect_log_msg("RendPostPeriod is too large; "
             "clipping to 302400s.\n");
   tt_int_op(tdata->opt->RendPostPeriod, OP_EQ, 302400);
   tor_free(msg);
@@ -2206,7 +2205,7 @@ test_options_validate__predicted_ports(void *ignored)
                                      TEST_OPTIONS_DEFAULT_VALUES);
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(1), OP_EQ, "PredictedPortsRelevanceTime is too "
+  expect_log_msg("PredictedPortsRelevanceTime is too "
             "large; clipping to 3600s.\n");
   tt_int_op(tdata->opt->PredictedPortsRelevanceTime, OP_EQ, 3600);
 
@@ -2433,7 +2432,7 @@ test_options_validate__circuits(void *ignored)
   tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
                                 "MaxCircuitDirtiness 2592001\n");
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(1), OP_EQ, "MaxCircuitDirtiness option is too "
+  expect_log_msg("MaxCircuitDirtiness option is too "
             "high; setting to 30 days.\n");
   tt_int_op(tdata->opt->MaxCircuitDirtiness, OP_EQ, 2592000);
   tor_free(msg);
@@ -2443,7 +2442,7 @@ test_options_validate__circuits(void *ignored)
   tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
                                 "CircuitStreamTimeout 1\n");
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(2), OP_EQ, "CircuitStreamTimeout option is too"
+  expect_log_msg("CircuitStreamTimeout option is too"
             " short; raising to 10 seconds.\n");
   tt_int_op(tdata->opt->CircuitStreamTimeout, OP_EQ, 10);
   tor_free(msg);
@@ -2453,7 +2452,7 @@ test_options_validate__circuits(void *ignored)
   tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
                                 "CircuitStreamTimeout 111\n");
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(2), OP_NE, "CircuitStreamTimeout option is too"
+  expect_no_log_msg("CircuitStreamTimeout option is too"
             " short; raising to 10 seconds.\n");
   tt_int_op(tdata->opt->CircuitStreamTimeout, OP_EQ, 111);
   tor_free(msg);
@@ -2463,7 +2462,7 @@ test_options_validate__circuits(void *ignored)
   tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
                                 "HeartbeatPeriod 1\n");
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(2), OP_EQ, "HeartbeatPeriod option is too short;"
+  expect_log_msg("HeartbeatPeriod option is too short;"
             " raising to 1800 seconds.\n");
   tt_int_op(tdata->opt->HeartbeatPeriod, OP_EQ, 1800);
   tor_free(msg);
@@ -2473,7 +2472,7 @@ test_options_validate__circuits(void *ignored)
   tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
                                 "HeartbeatPeriod 1982\n");
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(2), OP_NE, "HeartbeatPeriod option is too short;"
+  expect_no_log_msg("HeartbeatPeriod option is too short;"
             " raising to 1800 seconds.\n");
   tt_int_op(tdata->opt->HeartbeatPeriod, OP_EQ, 1982);
   tor_free(msg);
@@ -2484,7 +2483,7 @@ test_options_validate__circuits(void *ignored)
                                 "CircuitBuildTimeout 1\n"
                                 );
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(1), OP_EQ, "CircuitBuildTimeout is shorter (1"
+  expect_log_msg("CircuitBuildTimeout is shorter (1"
             " seconds) than the recommended minimum (10 seconds), and "
             "LearnCircuitBuildTimeout is disabled.  If tor isn't working, "
             "raise this value or enable LearnCircuitBuildTimeout.\n");
@@ -2496,7 +2495,7 @@ test_options_validate__circuits(void *ignored)
                                 "CircuitBuildTimeout 11\n"
                                 );
   options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
-  tt_str_op(mock_saved_log_at(1), OP_NE, "CircuitBuildTimeout is shorter (1 "
+  expect_no_log_msg("CircuitBuildTimeout is shorter (1 "
             "seconds) than the recommended minimum (10 seconds), and "
             "LearnCircuitBuildTimeout is disabled.  If tor isn't working, "
             "raise this value or enable LearnCircuitBuildTimeout.\n");
@@ -2590,7 +2589,7 @@ test_options_validate__rend(void *ignored)
                                 );
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(1), OP_EQ, "UseEntryGuards is disabled, but you"
+  expect_log_msg("UseEntryGuards is disabled, but you"
             " have configured one or more hidden services on this Tor "
             "instance.  Your hidden services will be very easy to locate using"
             " a well-known attack -- see http://freehaven.net/anonbib/#hs-"
@@ -2607,7 +2606,7 @@ test_options_validate__rend(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(1), OP_NE, "UseEntryGuards is disabled, but you"
+  expect_no_log_msg("UseEntryGuards is disabled, but you"
             " have configured one or more hidden services on this Tor "
             "instance.  Your hidden services will be very easy to locate using"
             " a well-known attack -- see http://freehaven.net/anonbib/#hs-"
@@ -2710,7 +2709,7 @@ test_options_validate__accounting(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(4), OP_EQ, "Using accounting with a hidden "
+  expect_log_msg("Using accounting with a hidden "
             "service and an ORPort is risky: your hidden service(s) and "
             "your public address will all turn off at the same time, "
             "which may alert observers that they are being run by the "
@@ -2727,7 +2726,7 @@ test_options_validate__accounting(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(4), OP_NE, "Using accounting with a hidden "
+  expect_no_log_msg("Using accounting with a hidden "
             "service and an ORPort is risky: your hidden service(s) and "
             "your public address will all turn off at the same time, "
             "which may alert observers that they are being run by the "
@@ -2746,7 +2745,7 @@ test_options_validate__accounting(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(3), OP_EQ, "Using accounting with multiple "
+  expect_log_msg("Using accounting with multiple "
             "hidden services is risky: they will all turn off at the same"
             " time, which may alert observers that they are being run by "
             "the same party.\n");
@@ -2957,7 +2956,7 @@ test_options_validate__proxy(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_EQ, "HTTPProxy configured, but no SOCKS "
+  expect_log_msg("HTTPProxy configured, but no SOCKS "
             "proxy or HTTPS proxy configured. Watch out: this configuration "
             "will proxy unencrypted directory connections only.\n");
   tor_free(msg);
@@ -2970,7 +2969,7 @@ test_options_validate__proxy(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE, "HTTPProxy configured, but no SOCKS "
+  expect_no_log_msg("HTTPProxy configured, but no SOCKS "
             "proxy or HTTPS proxy configured. Watch out: this configuration "
             "will proxy unencrypted directory connections only.\n");
   tor_free(msg);
@@ -2983,7 +2982,7 @@ test_options_validate__proxy(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE, "HTTPProxy configured, but no SOCKS "
+  expect_no_log_msg("HTTPProxy configured, but no SOCKS "
             "proxy or HTTPS proxy configured. Watch out: this configuration "
             "will proxy unencrypted directory connections only.\n");
   tor_free(msg);
@@ -2996,7 +2995,7 @@ test_options_validate__proxy(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE,
+  expect_no_log_msg(
             "HTTPProxy configured, but no SOCKS proxy or HTTPS proxy "
             "configured. Watch out: this configuration will proxy "
             "unencrypted directory connections only.\n");
@@ -3166,7 +3165,7 @@ test_options_validate__control(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_EQ,
+  expect_log_msg(
             "ControlPort is open, but no authentication method has been "
             "configured.  This means that any program on your computer can "
             "reconfigure your Tor.  That's bad!  You should upgrade your Tor"
@@ -3182,7 +3181,7 @@ test_options_validate__control(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE,
+  expect_no_log_msg(
             "ControlPort is open, but no authentication method has been "
             "configured.  This means that any program on your computer can "
             "reconfigure your Tor.  That's bad!  You should upgrade your Tor "
@@ -3199,7 +3198,7 @@ test_options_validate__control(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE,
+  expect_no_log_msg(
             "ControlPort is open, but no authentication method has been "
             "configured.  This means that any program on your computer can "
             "reconfigure your Tor.  That's bad!  You should upgrade your Tor "
@@ -3214,7 +3213,7 @@ test_options_validate__control(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE,
+  expect_no_log_msg(
             "ControlPort is open, but no authentication method has been "
             "configured.  This means that any program on your computer can "
             "reconfigure your Tor.  That's bad!  You should upgrade your Tor "
@@ -3229,7 +3228,7 @@ test_options_validate__control(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_EQ,
+  expect_log_msg(
             "ControlSocket is world writable, but no authentication method has"
             " been configured.  This means that any program on your computer "
             "can reconfigure your Tor.  That's bad!  You should upgrade your "
@@ -3245,7 +3244,7 @@ test_options_validate__control(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE,
+  expect_no_log_msg(
             "ControlSocket is world writable, but no authentication method has"
             " been configured.  This means that any program on your computer "
             "can reconfigure your Tor.  That's bad!  You should upgrade your "
@@ -3262,7 +3261,7 @@ test_options_validate__control(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE,
+  expect_no_log_msg(
             "ControlSocket is world writable, but no authentication method has"
             " been configured.  This means that any program on your computer "
             "can reconfigure your Tor.  That's bad!  You should upgrade your "
@@ -3277,7 +3276,7 @@ test_options_validate__control(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE,
+  expect_no_log_msg(
             "ControlSocket is world writable, but no authentication method has"
             " been configured.  This means that any program on your computer "
             "can reconfigure your Tor.  That's bad!  You should upgrade your "
@@ -3292,7 +3291,7 @@ test_options_validate__control(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_EQ,
+  expect_log_msg(
             "CookieAuthFileGroupReadable is set, but will have no effect: you "
             "must specify an explicit CookieAuthFile to have it "
             "group-readable.\n");
@@ -3306,7 +3305,7 @@ test_options_validate__control(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE,
+  expect_no_log_msg(
             "CookieAuthFileGroupReadable is set, but will have no effect: you "
             "must specify an explicit CookieAuthFile to have it "
             "group-readable.\n");
@@ -3343,7 +3342,7 @@ test_options_validate__families(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_EQ,
+  expect_log_msg(
             "Listing a family for a bridge relay is not supported: it can "
             "reveal bridge fingerprints to censors. You should also make sure "
             "you aren't listing this bridge's fingerprint in any other "
@@ -3357,7 +3356,7 @@ test_options_validate__families(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE,
+  expect_no_log_msg(
             "Listing a family for a bridge relay is not supported: it can "
             "reveal bridge fingerprints to censors. You should also make sure "
             "you aren't listing this bridge's fingerprint in any other "
@@ -3438,7 +3437,7 @@ test_options_validate__dir_auth(void *ignored)
   tt_str_op(msg, OP_EQ,
             "Directory authority/fallback line did not parse. See logs for "
             "details.");
-  tt_str_op(mock_saved_log_at(2), OP_EQ,
+  expect_log_msg(
             "You cannot set both DirAuthority and Alternate*Authority.\n");
   tor_free(msg);
 
@@ -3524,7 +3523,7 @@ test_options_validate__transport(void *ignored)
   tt_int_op(ret, OP_EQ, -1);
   tt_str_op(msg, OP_EQ,
             "Invalid client transport line. See logs for details.");
-  tt_str_op(mock_saved_log_at(3), OP_EQ,
+  expect_log_msg(
             "Too few arguments on ClientTransportPlugin line.\n");
   tor_free(msg);
 
@@ -3546,7 +3545,7 @@ test_options_validate__transport(void *ignored)
   tt_int_op(ret, OP_EQ, -1);
   tt_str_op(msg, OP_EQ,
             "Invalid server transport line. See logs for details.");
-  tt_str_op(mock_saved_log_at(3), OP_EQ,
+  expect_log_msg(
             "Too few arguments on ServerTransportPlugin line.\n");
   tor_free(msg);
 
@@ -3557,7 +3556,7 @@ test_options_validate__transport(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(3), OP_EQ,
+  expect_log_msg(
             "Tor is not configured as a relay but you specified a "
             "ServerTransportPlugin line (\"foo exec bar\"). The "
             "ServerTransportPlugin line will be ignored.\n");
@@ -3575,7 +3574,7 @@ test_options_validate__transport(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(3), OP_NE,
+  expect_no_log_msg(
             "Tor is not configured as a relay but you specified a "
             "ServerTransportPlugin line (\"foo exec bar\"). The "
             "ServerTransportPlugin line will be ignored.\n");
@@ -3599,7 +3598,7 @@ test_options_validate__transport(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(3), OP_EQ,
+  expect_log_msg(
             "You need at least a single managed-proxy to specify a transport "
             "listen address. The ServerTransportListenAddr line will be "
             "ignored.\n");
@@ -3618,7 +3617,7 @@ test_options_validate__transport(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(3), OP_NE,
+  expect_no_log_msg(
             "You need at least a single managed-proxy to specify a transport "
             "listen address. The ServerTransportListenAddr line will be "
             "ignored.\n");
@@ -3687,7 +3686,7 @@ test_options_validate__constrained_sockets(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_EQ, "You have requested constrained "
+  expect_log_msg("You have requested constrained "
             "socket buffers while also serving directory entries via DirPort."
             "  It is strongly suggested that you disable serving directory"
             " requests when system TCP buffer resources are scarce.\n");
@@ -3701,7 +3700,7 @@ test_options_validate__constrained_sockets(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE,
+  expect_no_log_msg(
             "You have requested constrained socket buffers while also serving"
             " directory entries via DirPort.  It is strongly suggested that "
             "you disable serving directory requests when system TCP buffer "
@@ -3824,7 +3823,7 @@ test_options_validate__v3_auth(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_EQ, "V3AuthVotingInterval does not divide"
+  expect_log_msg("V3AuthVotingInterval does not divide"
             " evenly into 24 hours.\n");
   tor_free(msg);
 
@@ -3837,7 +3836,7 @@ test_options_validate__v3_auth(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE, "V3AuthVotingInterval does not divide"
+  expect_no_log_msg("V3AuthVotingInterval does not divide"
             " evenly into 24 hours.\n");
   tor_free(msg);
 
@@ -3852,7 +3851,7 @@ test_options_validate__v3_auth(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(3), OP_EQ, "V3AuthVotingInterval is very low. "
+  expect_log_msg("V3AuthVotingInterval is very low. "
             "This may lead to failure to synchronise for a consensus.\n");
   tor_free(msg);
 
@@ -3985,7 +3984,7 @@ test_options_validate__exits(void *ignored)
                                 );
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_EQ, "You have set AllowSingleHopExits; "
+  expect_log_msg("You have set AllowSingleHopExits; "
             "now your relay will allow others to make one-hop exits. However,"
             " since by default most clients avoid relays that set this option,"
             " most clients will ignore you.\n");
@@ -3999,7 +3998,7 @@ test_options_validate__exits(void *ignored)
   mock_clean_saved_logs();
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
   tt_int_op(ret, OP_EQ, 0);
-  tt_str_op(mock_saved_log_at(2), OP_NE, "You have set AllowSingleHopExits; "
+  expect_no_log_msg("You have set AllowSingleHopExits; "
             "now your relay will allow others to make one-hop exits. However,"
             " since by default most clients avoid relays that set this option,"
             " most clients will ignore you.\n");
@@ -4044,7 +4043,7 @@ test_options_validate__testing_options(void *ignored)
   mock_clean_saved_logs();                                              \
   ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);\
   tt_int_op(ret, OP_EQ, 0);                                             \
-  tt_str_op(mock_saved_log_at(3), OP_EQ, #name " is insanely high.\n"); \
+  expect_log_msg( #name " is insanely high.\n"); \
   tor_free(msg); \
   STMT_END