tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

stripping_test.cc (20408B)


      1 //
      2 // Copyright 2022 The Abseil Authors.
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      https://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 // Tests for stripping of literal strings.
     17 // ---------------------------------------
     18 //
     19 // When a `LOG` statement can be trivially proved at compile time to never fire,
     20 // e.g. due to `ABSL_MIN_LOG_LEVEL`, `NDEBUG`, or some explicit condition, data
     21 // streamed in can be dropped from the compiled program completely if they are
     22 // not used elsewhere.  This most commonly affects string literals, which users
     23 // often want to strip to reduce binary size and/or redact information about
     24 // their program's internals (e.g. in a release build).
     25 //
     26 // These tests log strings and then validate whether they appear in the compiled
     27 // binary.  This is done by opening the file corresponding to the running test
     28 // and running a simple string search on its contents.  The strings to be logged
     29 // and searched for must be unique, and we must take care not to emit them into
     30 // the binary in any other place, e.g. when searching for them.  The latter is
     31 // accomplished by computing them using base64; the source string appears in the
     32 // binary but the target string is computed at runtime.
     33 
     34 #include <stdio.h>
     35 
     36 #if defined(__MACH__)
     37 #include <mach-o/dyld.h>
     38 #elif defined(_WIN32)
     39 #include <Windows.h>
     40 #include <tchar.h>
     41 #endif
     42 
     43 #include <algorithm>
     44 #include <functional>
     45 #include <memory>
     46 #include <ostream>
     47 #include <string>
     48 
     49 #include "gmock/gmock.h"
     50 #include "gtest/gtest.h"
     51 #include "absl/base/internal/strerror.h"
     52 #include "absl/base/log_severity.h"
     53 #include "absl/flags/internal/program_name.h"
     54 #include "absl/log/check.h"
     55 #include "absl/log/internal/test_helpers.h"
     56 #include "absl/log/log.h"
     57 #include "absl/status/status.h"
     58 #include "absl/strings/escaping.h"
     59 #include "absl/strings/str_format.h"
     60 #include "absl/strings/string_view.h"
     61 
     62 // Set a flag that controls whether we actually execute fatal statements, but
     63 // prevent the compiler from optimizing it out.
     64 static volatile bool kReallyDie = false;
     65 
     66 namespace {
     67 using ::testing::_;
     68 using ::testing::Eq;
     69 using ::testing::NotNull;
     70 
     71 using absl::log_internal::kAbslMinLogLevel;
     72 
     73 std::string Base64UnescapeOrDie(absl::string_view data) {
     74  std::string decoded;
     75  CHECK(absl::Base64Unescape(data, &decoded));
     76  return decoded;
     77 }
     78 
     79 // -----------------------------------------------------------------------------
     80 // A Googletest matcher which searches the running binary for a given string
     81 // -----------------------------------------------------------------------------
     82 
     83 // This matcher is used to validate that literal strings streamed into
     84 // `LOG` statements that ought to be compiled out (e.g. `LOG_IF(INFO, false)`)
     85 // do not appear in the binary.
     86 //
     87 // Note that passing the string to be sought directly to `FileHasSubstr()` all
     88 // but forces its inclusion in the binary regardless of the logging library's
     89 // behavior. For example:
     90 //
     91 //   LOG_IF(INFO, false) << "you're the man now dog";
     92 //   // This will always pass:
     93 //   // EXPECT_THAT(fp, FileHasSubstr("you're the man now dog"));
     94 //   // So use this instead:
     95 //   EXPECT_THAT(fp, FileHasSubstr(
     96 //       Base64UnescapeOrDie("eW91J3JlIHRoZSBtYW4gbm93IGRvZw==")));
     97 
     98 class FileHasSubstrMatcher final : public ::testing::MatcherInterface<FILE*> {
     99 public:
    100  explicit FileHasSubstrMatcher(absl::string_view needle) : needle_(needle) {}
    101 
    102  bool MatchAndExplain(
    103      FILE* fp, ::testing::MatchResultListener* listener) const override {
    104    std::string buf(
    105        std::max<std::string::size_type>(needle_.size() * 2, 163840000), '\0');
    106    size_t buf_start_offset = 0;  // The file offset of the byte at `buf[0]`.
    107    size_t buf_data_size = 0;     // The number of bytes of `buf` which contain
    108                                  // data.
    109 
    110    ::fseek(fp, 0, SEEK_SET);
    111    while (true) {
    112      // Fill the buffer to capacity or EOF:
    113      while (buf_data_size < buf.size()) {
    114        const size_t ret = fread(&buf[buf_data_size], sizeof(char),
    115                                 buf.size() - buf_data_size, fp);
    116        if (ret == 0) break;
    117        buf_data_size += ret;
    118      }
    119      if (ferror(fp)) {
    120        *listener << "error reading file";
    121        return false;
    122      }
    123      const absl::string_view haystack(&buf[0], buf_data_size);
    124      const auto off = haystack.find(needle_);
    125      if (off != haystack.npos) {
    126        *listener << "string found at offset " << buf_start_offset + off;
    127        return true;
    128      }
    129      if (feof(fp)) {
    130        *listener << "string not found";
    131        return false;
    132      }
    133      // Copy the end of `buf` to the beginning so we catch matches that span
    134      // buffer boundaries.  `buf` and `buf_data_size` are always large enough
    135      // that these ranges don't overlap.
    136      memcpy(&buf[0], &buf[buf_data_size - needle_.size()], needle_.size());
    137      buf_start_offset += buf_data_size - needle_.size();
    138      buf_data_size = needle_.size();
    139    }
    140  }
    141  void DescribeTo(std::ostream* os) const override {
    142    *os << "contains the string \"" << needle_ << "\" (base64(\""
    143        << Base64UnescapeOrDie(needle_) << "\"))";
    144  }
    145 
    146  void DescribeNegationTo(std::ostream* os) const override {
    147    *os << "does not ";
    148    DescribeTo(os);
    149  }
    150 
    151 private:
    152  std::string needle_;
    153 };
    154 
    155 class StrippingTest : public ::testing::Test {
    156 protected:
    157  void SetUp() override {
    158 #ifndef NDEBUG
    159    // Non-optimized builds don't necessarily eliminate dead code at all, so we
    160    // don't attempt to validate stripping against such builds.
    161    GTEST_SKIP() << "StrippingTests skipped since this build is not optimized";
    162 #elif defined(__EMSCRIPTEN__)
    163    // These tests require a way to examine the running binary and look for
    164    // strings; there's no portable way to do that.
    165    GTEST_SKIP()
    166        << "StrippingTests skipped since this platform is not optimized";
    167 #endif
    168  }
    169 
    170  // Opens this program's executable file.  Returns `nullptr` and writes to
    171  // `stderr` on failure.
    172  std::unique_ptr<FILE, std::function<void(FILE*)>> OpenTestExecutable() {
    173 #if defined(__linux__)
    174    std::unique_ptr<FILE, std::function<void(FILE*)>> fp(
    175        fopen("/proc/self/exe", "rb"), [](FILE* fp) { fclose(fp); });
    176    if (!fp) {
    177      const std::string err = absl::base_internal::StrError(errno);
    178      absl::FPrintF(stderr, "Failed to open /proc/self/exe: %s\n", err);
    179    }
    180    return fp;
    181 #elif defined(__Fuchsia__)
    182    // TODO(b/242579714): We need to restore the test coverage on this platform.
    183    std::unique_ptr<FILE, std::function<void(FILE*)>> fp(
    184        fopen(absl::StrCat("/pkg/bin/",
    185                           absl::flags_internal::ShortProgramInvocationName())
    186                  .c_str(),
    187              "rb"),
    188        [](FILE* fp) { fclose(fp); });
    189    if (!fp) {
    190      const std::string err = absl::base_internal::StrError(errno);
    191      absl::FPrintF(stderr, "Failed to open /pkg/bin/<binary name>: %s\n", err);
    192    }
    193    return fp;
    194 #elif defined(__MACH__)
    195    uint32_t size = 0;
    196    int ret = _NSGetExecutablePath(nullptr, &size);
    197    if (ret != -1) {
    198      absl::FPrintF(stderr,
    199                    "Failed to get executable path: "
    200                    "_NSGetExecutablePath(nullptr) returned %d\n",
    201                    ret);
    202      return nullptr;
    203    }
    204    std::string path(size, '\0');
    205    ret = _NSGetExecutablePath(&path[0], &size);
    206    if (ret != 0) {
    207      absl::FPrintF(
    208          stderr,
    209          "Failed to get executable path: _NSGetExecutablePath(buffer) "
    210          "returned %d\n",
    211          ret);
    212      return nullptr;
    213    }
    214    std::unique_ptr<FILE, std::function<void(FILE*)>> fp(
    215        fopen(path.c_str(), "rb"), [](FILE* fp) { fclose(fp); });
    216    if (!fp) {
    217      const std::string err = absl::base_internal::StrError(errno);
    218      absl::FPrintF(stderr, "Failed to open executable at %s: %s\n", path, err);
    219    }
    220    return fp;
    221 #elif defined(_WIN32)
    222    std::basic_string<TCHAR> path(4096, _T('\0'));
    223    while (true) {
    224      const uint32_t ret = ::GetModuleFileName(nullptr, &path[0],
    225                                               static_cast<DWORD>(path.size()));
    226      if (ret == 0) {
    227        absl::FPrintF(
    228            stderr,
    229            "Failed to get executable path: GetModuleFileName(buffer) "
    230            "returned 0\n");
    231        return nullptr;
    232      }
    233      if (ret < path.size()) break;
    234      path.resize(path.size() * 2, _T('\0'));
    235    }
    236    std::unique_ptr<FILE, std::function<void(FILE*)>> fp(
    237        _tfopen(path.c_str(), _T("rb")), [](FILE* fp) { fclose(fp); });
    238    if (!fp) absl::FPrintF(stderr, "Failed to open executable\n");
    239    return fp;
    240 #else
    241    absl::FPrintF(stderr,
    242                  "OpenTestExecutable() unimplemented on this platform\n");
    243    return nullptr;
    244 #endif
    245  }
    246 
    247  ::testing::Matcher<FILE*> FileHasSubstr(absl::string_view needle) {
    248    return MakeMatcher(new FileHasSubstrMatcher(needle));
    249  }
    250 };
    251 
    252 // This tests whether out methodology for testing stripping works on this
    253 // platform by looking for one string that definitely ought to be there and one
    254 // that definitely ought not to.  If this fails, none of the `StrippingTest`s
    255 // are going to produce meaningful results.
    256 TEST_F(StrippingTest, Control) {
    257  constexpr char kEncodedPositiveControl[] =
    258      "U3RyaXBwaW5nVGVzdC5Qb3NpdGl2ZUNvbnRyb2w=";
    259  const std::string encoded_negative_control =
    260      absl::Base64Escape("StrippingTest.NegativeControl");
    261 
    262  // Verify this mainly so we can encode other strings and know definitely they
    263  // won't encode to `kEncodedPositiveControl`.
    264  EXPECT_THAT(Base64UnescapeOrDie("U3RyaXBwaW5nVGVzdC5Qb3NpdGl2ZUNvbnRyb2w="),
    265              Eq("StrippingTest.PositiveControl"));
    266 
    267  auto exe = OpenTestExecutable();
    268  ASSERT_THAT(exe, NotNull());
    269  EXPECT_THAT(exe.get(), FileHasSubstr(kEncodedPositiveControl));
    270  EXPECT_THAT(exe.get(), Not(FileHasSubstr(encoded_negative_control)));
    271 }
    272 
    273 TEST_F(StrippingTest, Literal) {
    274  // We need to load a copy of the needle string into memory (so we can search
    275  // for it) without leaving it lying around in plaintext in the executable file
    276  // as would happen if we used a literal.  We might (or might not) leave it
    277  // lying around later; that's what the tests are for!
    278  const std::string needle = absl::Base64Escape("StrippingTest.Literal");
    279  LOG(INFO) << "U3RyaXBwaW5nVGVzdC5MaXRlcmFs";
    280  auto exe = OpenTestExecutable();
    281  ASSERT_THAT(exe, NotNull());
    282  if (absl::LogSeverity::kInfo >= kAbslMinLogLevel) {
    283    EXPECT_THAT(exe.get(), FileHasSubstr(needle));
    284  } else {
    285    EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle)));
    286  }
    287 }
    288 
    289 TEST_F(StrippingTest, LiteralInExpression) {
    290  // We need to load a copy of the needle string into memory (so we can search
    291  // for it) without leaving it lying around in plaintext in the executable file
    292  // as would happen if we used a literal.  We might (or might not) leave it
    293  // lying around later; that's what the tests are for!
    294  const std::string needle =
    295      absl::Base64Escape("StrippingTest.LiteralInExpression");
    296  LOG(INFO) << absl::StrCat("secret: ",
    297                            "U3RyaXBwaW5nVGVzdC5MaXRlcmFsSW5FeHByZXNzaW9u");
    298  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
    299  ASSERT_THAT(exe, NotNull());
    300  if (absl::LogSeverity::kInfo >= kAbslMinLogLevel) {
    301    EXPECT_THAT(exe.get(), FileHasSubstr(needle));
    302  } else {
    303    EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle)));
    304  }
    305 }
    306 
    307 TEST_F(StrippingTest, Fatal) {
    308  // We need to load a copy of the needle string into memory (so we can search
    309  // for it) without leaving it lying around in plaintext in the executable file
    310  // as would happen if we used a literal.  We might (or might not) leave it
    311  // lying around later; that's what the tests are for!
    312  const std::string needle = absl::Base64Escape("StrippingTest.Fatal");
    313  // We don't care if the LOG statement is actually executed, we're just
    314  // checking that it's stripped.
    315  if (kReallyDie) LOG(FATAL) << "U3RyaXBwaW5nVGVzdC5GYXRhbA==";
    316 
    317  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
    318  ASSERT_THAT(exe, NotNull());
    319  if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
    320    EXPECT_THAT(exe.get(), FileHasSubstr(needle));
    321  } else {
    322    EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle)));
    323  }
    324 }
    325 
    326 TEST_F(StrippingTest, DFatal) {
    327  // We need to load a copy of the needle string into memory (so we can search
    328  // for it) without leaving it lying around in plaintext in the executable file
    329  // as would happen if we used a literal.  We might (or might not) leave it
    330  // lying around later; that's what the tests are for!
    331  const std::string needle = absl::Base64Escape("StrippingTest.DFatal");
    332  // We don't care if the LOG statement is actually executed, we're just
    333  // checking that it's stripped.
    334  if (kReallyDie) LOG(DFATAL) << "U3RyaXBwaW5nVGVzdC5ERmF0YWw=";
    335 
    336  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
    337  ASSERT_THAT(exe, NotNull());
    338  // `DFATAL` can be `ERROR` or `FATAL`, and a compile-time optimizer doesn't
    339  // know which, because `absl::kLogDebugFatal` is declared `extern` and defined
    340  // in another TU.  Link-time optimization might do better.  We have six cases:
    341  // |         `AMLL` is-> | `<=ERROR` | `FATAL` | `>FATAL` |
    342  // | ------------------- | --------- | ------- | -------- |
    343  // | `DFATAL` is `ERROR` |   present |       ? | stripped |
    344  // | `DFATAL` is `FATAL` |   present | present | stripped |
    345 
    346  // These constexpr variables are used to suppress unreachable code warnings
    347  // in the if-else statements below.
    348 
    349  // "present" in the table above: `DFATAL` exceeds `ABSL_MIN_LOG_LEVEL`, so
    350  // `DFATAL` statements should not be stripped (and they should be logged
    351  // when executed, but that's a different testsuite).
    352  constexpr bool kExpectPresent = absl::kLogDebugFatal >= kAbslMinLogLevel;
    353 
    354  // "stripped" in the table above: even though the compiler may not know
    355  // which value `DFATAL` has, it should be able to strip it since both
    356  // possible values ought to be stripped.
    357  constexpr bool kExpectStripped = kAbslMinLogLevel > absl::LogSeverity::kFatal;
    358 
    359  if (kExpectPresent) {
    360    EXPECT_THAT(exe.get(), FileHasSubstr(needle));
    361  } else if (kExpectStripped) {
    362    EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle)));
    363  } else {
    364    // "?" in the table above; may or may not be stripped depending on whether
    365    // any link-time optimization is done.  Either outcome is ok.
    366  }
    367 }
    368 
    369 TEST_F(StrippingTest, Level) {
    370  const std::string needle = absl::Base64Escape("StrippingTest.Level");
    371  volatile auto severity = absl::LogSeverity::kWarning;
    372  // Ensure that `severity` is not a compile-time constant to prove that
    373  // stripping works regardless:
    374  LOG(LEVEL(severity)) << "U3RyaXBwaW5nVGVzdC5MZXZlbA==";
    375  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
    376  ASSERT_THAT(exe, NotNull());
    377  if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
    378    // This can't be stripped at compile-time because it might evaluate to a
    379    // level that shouldn't be stripped.
    380    EXPECT_THAT(exe.get(), FileHasSubstr(needle));
    381  } else {
    382 #if (defined(_MSC_VER) && !defined(__clang__)) || defined(__APPLE__)
    383    // Dead code elimination misses this case.
    384 #else
    385    // All levels should be stripped, so it doesn't matter what the severity
    386    // winds up being.
    387    EXPECT_THAT(exe.get(), Not(FileHasSubstr(needle)));
    388 #endif
    389  }
    390 }
    391 
    392 TEST_F(StrippingTest, Check) {
    393  // Here we also need a variable name with enough entropy that it's unlikely to
    394  // appear in the binary by chance.  `volatile` keeps the tautological
    395  // comparison (and the rest of the `CHECK`) from being optimized away.
    396  const std::string var_needle = absl::Base64Escape("StrippingTestCheckVar");
    397  const std::string msg_needle = absl::Base64Escape("StrippingTest.Check");
    398  volatile int U3RyaXBwaW5nVGVzdENoZWNrVmFy = 0xCAFE;
    399  // We don't care if the CHECK is actually executed, just that stripping works.
    400  // Hiding it behind `kReallyDie` works around some overly aggressive
    401  // optimizations in older versions of MSVC.
    402  if (kReallyDie) {
    403    CHECK(U3RyaXBwaW5nVGVzdENoZWNrVmFy != U3RyaXBwaW5nVGVzdENoZWNrVmFy)
    404        << "U3RyaXBwaW5nVGVzdC5DaGVjaw==";
    405  }
    406 
    407  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
    408  ASSERT_THAT(exe, NotNull());
    409  if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
    410    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle));
    411    EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle));
    412  } else {
    413    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle)));
    414    EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle)));
    415  }
    416 }
    417 
    418 TEST_F(StrippingTest, CheckOp) {
    419  // See `StrippingTest.Check` for some hairy implementation notes.
    420  const std::string var_needle1 =
    421      absl::Base64Escape("StrippingTestCheckOpVar1");
    422  const std::string var_needle2 =
    423      absl::Base64Escape("StrippingTestCheckOpVar2");
    424  const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckOp");
    425  volatile int U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIx = 0xFEED;
    426  volatile int U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIy = 0xCAFE;
    427  if (kReallyDie) {
    428    CHECK_EQ(U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIx, U3RyaXBwaW5nVGVzdENoZWNrT3BWYXIy)
    429        << "U3RyaXBwaW5nVGVzdC5DaGVja09w";
    430  }
    431 
    432  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
    433  ASSERT_THAT(exe, NotNull());
    434 
    435  if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
    436    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle1));
    437    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle2));
    438    EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle));
    439  } else {
    440    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle1)));
    441    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle2)));
    442    EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle)));
    443  }
    444 }
    445 
    446 TEST_F(StrippingTest, CheckStrOp) {
    447  // See `StrippingTest.Check` for some hairy implementation notes.
    448  const std::string var_needle1 =
    449      absl::Base64Escape("StrippingTestCheckStrOpVar1");
    450  const std::string var_needle2 =
    451      absl::Base64Escape("StrippingTestCheckStrOpVar2");
    452  const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckStrOp");
    453  const char *volatile U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIx = "FEED";
    454  const char *volatile U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIy = "CAFE";
    455  if (kReallyDie) {
    456    CHECK_STREQ(U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIx,
    457                U3RyaXBwaW5nVGVzdENoZWNrU3RyT3BWYXIy)
    458        << "U3RyaXBwaW5nVGVzdC5DaGVja1N0ck9w";
    459  }
    460 
    461  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
    462  ASSERT_THAT(exe, NotNull());
    463 
    464  if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
    465    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle1));
    466    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle2));
    467    EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle));
    468  } else {
    469    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle1)));
    470    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle2)));
    471    EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle)));
    472  }
    473 }
    474 
    475 TEST_F(StrippingTest, CheckOk) {
    476  // See `StrippingTest.Check` for some hairy implementation notes.
    477  const std::string var_needle = absl::Base64Escape("StrippingTestCheckOkVar1");
    478  const std::string msg_needle = absl::Base64Escape("StrippingTest.CheckOk");
    479  volatile bool x = false;
    480  auto U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx = absl::OkStatus();
    481  if (x) {
    482    U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx =
    483        absl::InvalidArgumentError("Stripping this is not my job!");
    484  }
    485  if (kReallyDie) {
    486    CHECK_OK(U3RyaXBwaW5nVGVzdENoZWNrT2tWYXIx)
    487        << "U3RyaXBwaW5nVGVzdC5DaGVja09r";
    488  }
    489 
    490  std::unique_ptr<FILE, std::function<void(FILE*)>> exe = OpenTestExecutable();
    491  ASSERT_THAT(exe, NotNull());
    492 
    493  if (absl::LogSeverity::kFatal >= kAbslMinLogLevel) {
    494    EXPECT_THAT(exe.get(), FileHasSubstr(var_needle));
    495    EXPECT_THAT(exe.get(), FileHasSubstr(msg_needle));
    496  } else {
    497    EXPECT_THAT(exe.get(), Not(FileHasSubstr(var_needle)));
    498    EXPECT_THAT(exe.get(), Not(FileHasSubstr(msg_needle)));
    499  }
    500 }
    501 
    502 }  // namespace