Files
carbon-lang/toolchain/testing/coverage_helper.h
T
Dana Jansens 9a6c74f0cd Introduce FindIfOrNull() FindIfOrNone() and Contains() (#5322)
`FindIfOrNull` returns a pointer to the element in the range if it's
found, and nullptr otherwise. `FindIfOrNone` returns a copy of the
element in the range if it's found, and `T::None` (for a range of
elements of type `T`) otherwise. `Contains` returns a bool indicating
whether the element in the range is found.

These functions replace `llvm::find()` and `llvm::find_if()` when you
want a single answer back instead of an iterator. This avoids the need
to check against `end()`, allowing the return condition to be tested as
a standard bool.

We replace uses of `find()` and `find_if()` that did not require an
iterator with these new helpers.

Note that the return type of `FindIfOrNull` is a pointer since we can
not write `optional<T&>`, which must be tested for null. If the null
check is omitted, UB occurs and the resulting code may end up with an
incorrect pointer (https://crbug.com/40153300) into the range (or
elsewhere), rather than a null dereference. And this would be very
confusing to debug. Hopefully debug builds and sanitizers keep this from
being an issue we sink a bunch of time into debugging.
2025-04-18 14:17:48 +00:00

86 lines
2.7 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#ifndef CARBON_TOOLCHAIN_TESTING_COVERAGE_HELPER_H_
#define CARBON_TOOLCHAIN_TESTING_COVERAGE_HELPER_H_
#include <gtest/gtest.h>
#include <fstream>
#include <string>
#include "common/find.h"
#include "common/set.h"
#include "llvm/ADT/StringExtras.h"
#include "re2/re2.h"
namespace Carbon::Testing {
// Looks for kinds that aren't covered by a file_test in the manifest path.
// Kinds are identified by the provided regular expression kind_pattern.
//
// should_be_covered should return false when a kind is either untestable or not
// yet tested.
//
// TODO: Switch `kind_pattern` to `llvm::StringLiteral` if
// `llvm::StringLiteral::c_str` is added.
template <typename KindT>
auto TestKindCoverage(const std::string& manifest_path,
const char* kind_pattern, llvm::ArrayRef<KindT> kinds,
llvm::ArrayRef<KindT> untested_kinds) {
std::ifstream manifest_in(manifest_path.c_str());
ASSERT_TRUE(manifest_in.good());
RE2 kind_re(kind_pattern);
ASSERT_TRUE(kind_re.ok()) << kind_re.error();
Set<std::string> covered_kinds;
std::string test_filename;
while (std::getline(manifest_in, test_filename)) {
std::ifstream test_in(test_filename);
ASSERT_TRUE(test_in.good());
std::string line;
while (std::getline(test_in, line)) {
std::string kind;
if (RE2::PartialMatch(line, kind_re, &kind)) {
covered_kinds.Insert(kind);
}
}
}
llvm::SmallVector<llvm::StringRef> missing_kinds;
for (auto kind : kinds) {
if (Contains(untested_kinds, kind)) {
EXPECT_FALSE(covered_kinds.Erase(kind.name()))
<< "Kind " << kind
<< " has coverage even though none was expected. If this has "
"changed, update the coverage test.";
continue;
}
if (!covered_kinds.Erase(kind.name())) {
missing_kinds.push_back(kind.name());
}
}
constexpr llvm::StringLiteral Bullet = "\n - ";
llvm::sort(missing_kinds);
EXPECT_TRUE(missing_kinds.empty()) << "Some kinds have no tests:" << Bullet
<< llvm::join(missing_kinds, Bullet);
llvm::SmallVector<std::string> unexpected_matches;
covered_kinds.ForEach(
[&](const std::string& match) { unexpected_matches.push_back(match); });
llvm::sort(unexpected_matches);
EXPECT_TRUE(unexpected_matches.empty())
<< "Matched things that aren't in the kind list:" << Bullet
<< llvm::join(unexpected_matches, Bullet);
}
} // namespace Carbon::Testing
#endif // CARBON_TOOLCHAIN_TESTING_COVERAGE_HELPER_H_