blob: fcf58302c7952caad44e614f475e384a4228fc8c [file]
#ifndef GOOGLE_PROTOBUF_CONFORMANCE_TEST_MANAGER_H__
#define GOOGLE_PROTOBUF_CONFORMANCE_TEST_MANAGER_H__
#include <string>
#include <utility>
#include <vector>
#include "absl/container/btree_map.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "conformance/conformance.pb.h"
#include "conformance/failure_list_trie_node.h"
#include "conformance/testee.h"
namespace google {
namespace protobuf {
namespace conformance {
namespace internal {
// A test whose result contradicted the failure list. See
// TestManager::UnexpectedFailures() and TestManager::UnexpectedSuccesses().
// TODO: b/563707827 - Remove with conformance_test_runner.
struct UnexpectedResult {
// The full name of the test, as reported to the TestManager.
std::string test_name;
// For an unexpected failure, the test's failure message as
// TestManager::SaveFailureList() would write it. For an unexpected success,
// the message of the failure list entry that matched the test.
std::string failure_message;
// For an unexpected success, the failure list entry that matched
// `test_name`, possibly a wildcard. Unset for an unexpected failure, which
// matched no entry or matched one with a different message.
absl::optional<std::string> matched_entry;
};
// Tracks the expected failures and the actual results of a test suite. The
// conformance matchers (see matchers.h) report the outcome of every test
// here. The test environment reads the results back and reports them as test
// properties. Under --fix the results also populate the new failure list.
class TestManager {
public:
TestManager() : expected_failure_list_("root") {}
~TestManager();
// The lowest priority whose failures fail the suite (see TestPriority in
// testee.h). ReportFailure() tolerates a failing test of a lower priority
// unless it is in the failure list. Defaults to kLowestPriority, which
// enforces every priority.
void set_enforcement_level(TestPriority level) { enforcement_level_ = level; }
// Loads a failure list from disk and adds its entries to the ones loaded so
// far. Each line that isn't blank or a comment names one expected failure,
// optionally followed by `#` and the expected failure message. Returns an
// error if the file can't be opened. Also returns an error if an entry is
// already present, including one from an earlier file, or contains an
// invalid wildcard.
absl::Status LoadFailureList(absl::string_view filename);
// Saves an updated failure list to disk based on the reported results.
// Returns an error if `filename` can't be opened for writing or the write
// fails.
absl::Status SaveFailureList(absl::string_view filename) const;
// Reports a successful test run. This will return an error if the test was
// expected to fail.
absl::Status ReportSuccess(absl::string_view test_name);
// Reports a failed test run along with the failure message. Returns OK if
// the failure doesn't fail the suite. That is the case when the test is in
// the failure list with a matching message (an expected failure, logged at
// INFO), or when it isn't listed and `priority` is above the enforcement
// level (a tolerated failure, logged as a WARNING and only counted by
// tolerated_failures()). Otherwise the failure is unexpected and the error
// says why. A listed test is checked whatever its priority, so that the
// failure list can't go stale unnoticed. The normalized actual message
// only needs to start with the expected message, so an empty expected
// message matches any failure.
absl::Status ReportFailure(absl::string_view test_name, TestPriority priority,
absl::string_view failure_message);
// Reports a test that the testee skipped for `skip_reason`. A skip is not
// a verdict on the test's failure list entry, if any. The entry counts as
// seen and matched, so that Finalize() doesn't report it and
// SaveFailureList() keeps it, but the skip is not an expected failure. A
// listed test that is skipped is recorded (see ListedSkips()) and returns an
// error that names the matched entry, like an unexpected success. Whether
// that error fails the test is the caller's policy. The matchers fail such
// a test.
absl::Status ReportSkip(absl::string_view test_name,
absl::string_view skip_reason);
// Reports a test the runner didn't run because it wasn't selected.
// The original conformance_test_runner matches a test name against the
// failure list before checking whether the test was selected. Like it,
// this only marks the entry the name matches, if any, as matched for
// UnmatchedExpectedFailures(). The test is not counted by any statistic
// and its entry stays unseen.
// TODO: b/563707827 - Remove with conformance_test_runner.
void ReportNotSelected(absl::string_view test_name);
// Whether any of the Report*() methods has been called for `test_name`.
// Yields() checks this to reject a second check of the same test.
bool WasReported(absl::string_view test_name) const {
return seen_tests_.contains(test_name);
}
// Runs sanity checks over the failure list to make sure everything we
// expected to run was reported. Returns an error naming the sorted expected
// failure entries that have not been reported as a failure, an unexpected
// success or a skip. Must be called before destruction.
absl::Status Finalize();
// Returns the sorted expected failure entries that no reported test name has
// matched so far, regardless of the outcome. Names are reported through
// ReportSuccess(), ReportFailure(), ReportSkip() or ReportNotSelected(). A
// wildcard entry counts as matched once it has matched any test name.
// Unlike the unseen entries Finalize() reports, an entry whose test was not
// selected is not returned. It is a real test, only not run here.
// TODO: b/563707827 - Remove with conformance_test_runner.
std::vector<std::string> UnmatchedExpectedFailures() const;
// Returns the tests reported as unexpected failures so far, sorted by test
// name. These are the tests unexpected_failures() counts. Each result's
// `failure_message` is the message as SaveFailureList() would write it, and
// its `matched_entry` is unset. This includes tests that were expected to
// fail, but with a different message. SaveFailureList() replaces their
// entry, so adding these lines to the failure list would duplicate it.
// TODO: b/563707827 - Remove with conformance_test_runner.
std::vector<UnexpectedResult> UnexpectedFailures() const;
// Returns the tests reported as unexpected successes so far, sorted by test
// name. These are the tests unexpected_successes() counts. Each result's
// `matched_entry` is the failure list entry the test matched, and
// `failure_message` is that entry's message.
// TODO: b/563707827 - Remove with conformance_test_runner.
std::vector<UnexpectedResult> UnexpectedSuccesses() const;
// Returns the tests the testee skipped although they are in the failure
// list, sorted by test name. These are the tests listed_skips() counts.
// Each pair is the test name and the failure list entry it matched. Such a
// test fails the gtest run: the matchers fail it with the error
// ReportSkip() returns. It is not an unexpected failure or success. Its
// entry is kept as is, also by SaveFailureList() under --fix, so removing
// the entry is up to the user.
// TODO: b/563707827 - Remove with conformance_test_runner.
std::vector<std::pair<std::string, std::string>> ListedSkips() const;
// The number of tests that were reported skipped. This includes the listed
// skips.
int skipped() const { return skipped_; }
// The number of skipped tests that are in the failure list. See
// ListedSkips().
int listed_skips() const { return listed_skips_; }
// The number of tests that failed but were tolerated because their priority
// is above the enforcement level.
int tolerated_failures() const { return tolerated_failures_; }
// The number of tests that were in the failure list and failed with the
// expected message.
int expected_failures() const { return expected_failures_; }
// The number of tests that failed but were not in the failure list, or
// failed with a different message than the one listed.
int unexpected_failures() const { return unexpected_failures_; }
// The number of tests that were not in the failure list and succeeded.
int expected_successes() const { return expected_successes_; }
// The number of tests that were in the failure list but succeeded.
int unexpected_successes() const { return unexpected_successes_; }
private:
// Adds one failure list entry, as LoadFailureList() does for each line.
// Returns an error if the test name is already present or contains an
// invalid wildcard.
absl::Status AddExpectedFailure(absl::string_view test_name,
absl::string_view failure_message);
// The sorted expected failure entries Finalize() complains about: those not
// reported as a failure, an unexpected success or a skip so far.
std::vector<std::string> UnseenExpectedFailures() const;
// Marks the failure list entry `test_name` matches, if any, as matched (see
// UnmatchedExpectedFailures()) and returns it.
absl::optional<std::string> MarkMatched(absl::string_view test_name);
FailureListTrieNode expected_failure_list_;
absl::flat_hash_map<std::string, std::string> expected_failure_messages_;
absl::flat_hash_set<std::string> unseen_expected_failures_;
// Entries never matched by name by any Report*() call.
absl::flat_hash_set<std::string> unmatched_expected_failures_;
absl::flat_hash_set<std::string> seen_unexpected_successes_;
absl::flat_hash_map<std::string, int> number_of_matches_;
// Every test name reported so far (see WasReported()), so that a test
// reported more than once is only counted once.
absl::flat_hash_set<std::string> seen_tests_;
// The tests counted by unexpected_failures_, mapped to their formatted
// failure message, and the tests counted by unexpected_successes_, mapped to
// the failure list entry they matched.
absl::btree_map<std::string, std::string> unexpected_failure_messages_;
absl::btree_map<std::string, std::string> unexpected_success_matches_;
// The tests counted by listed_skips_, mapped to the entry they matched.
absl::btree_map<std::string, std::string> listed_skip_matches_;
std::vector<std::string> failure_list_lines_;
absl::btree_map<std::string, std::string> new_failures_;
int skipped_ = 0;
int listed_skips_ = 0;
int tolerated_failures_ = 0;
int expected_failures_ = 0;
int unexpected_failures_ = 0;
int expected_successes_ = 0;
int unexpected_successes_ = 0;
bool finalized_ = false;
TestPriority enforcement_level_ = kLowestPriority;
};
} // namespace internal
} // namespace conformance
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_CONFORMANCE_TEST_MANAGER_H__