[zxtest]: Replace filter stub for an actual impl.
-f (gtest-filter-test) was handle by a stub. Provided an implementation
that follows gtest syntax.
TEST=zxtest-test
Change-Id: Ibb334215bd40521e4114afdefd3b17446b99d432
diff --git a/system/ulib/zxtest/include/zxtest/base/runner.h b/system/ulib/zxtest/include/zxtest/base/runner.h
index 213ea55..6da9ff0 100644
--- a/system/ulib/zxtest/include/zxtest/base/runner.h
+++ b/system/ulib/zxtest/include/zxtest/base/runner.h
@@ -88,6 +88,14 @@
size_t registered_test_case_count = 0;
};
+// Holds the pattern used for filtering.
+struct FilterOp {
+ // Returns true if the test_case and test matches |pattern|.
+ bool operator()(const fbl::String& test_case, const fbl::String& test) const;
+
+ fbl::String pattern;
+};
+
// This class is the entry point for test and constructs registration.
class Runner {
public:
diff --git a/system/ulib/zxtest/runner.cpp b/system/ulib/zxtest/runner.cpp
index a697932..05ea41a 100644
--- a/system/ulib/zxtest/runner.cpp
+++ b/system/ulib/zxtest/runner.cpp
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <fbl/string_piece.h>
#include <zxtest/base/runner.h>
namespace zxtest {
@@ -141,10 +142,10 @@
void Runner::Filter(const fbl::String& pattern) {
summary_.active_test_count = 0;
summary_.active_test_case_count = 0;
-
+ const FilterOp filter_op = {.pattern = pattern};
for (auto& test_case : test_cases_) {
// TODO(gevalentino): replace with filter function.
- test_case.Filter(nullptr);
+ test_case.Filter(filter_op);
if (test_case.MatchingTestCount() > 0) {
summary_.active_test_case_count++;
summary_.active_test_count += test_case.MatchingTestCount();
@@ -186,4 +187,64 @@
return Runner::GetInstance()->Run(options);
}
+namespace {
+bool MatchPatterns(fbl::StringPiece pattern, fbl::StringPiece str) {
+ static constexpr auto match_pattern = [](const char* pattern, const char* str) -> bool {
+ auto advance = [](const char* pattern, const char* str, auto& self) -> bool {
+ switch (*pattern) {
+ // Single characeter matching for gTest
+ case '?':
+ return *str != '\0' && self(pattern + 1, str + 1, self);
+ // Wild card matches anything.
+ case '*':
+ return (*str != '\0' && self(pattern, str + 1, self)) ||
+ self(pattern + 1, str, self);
+ // Pattern completed or another pattern in the list.
+ case '\0':
+ case ':':
+ return *str == '\0';
+ // 1:1 match
+ default:
+ return *str == *pattern && self(pattern + 1, str + 1, self);
+ };
+ };
+ return advance(pattern, str, advance);
+ };
+
+ bool has_next = true;
+ const char* curr_pattern = pattern.data();
+ while (has_next) {
+ if (match_pattern(curr_pattern, str.data())) {
+ return true;
+ }
+ curr_pattern = strchr(curr_pattern, ':');
+ has_next = (curr_pattern != nullptr);
+ // Skip ':'
+ curr_pattern++;
+ }
+
+ return false;
+}
+
+} // namespace
+
+bool FilterOp::operator()(const fbl::String& test_case, const fbl::String& test) const {
+ fbl::String full_test_name = fbl::StringPrintf("%s.%s", test_case.c_str(), test.c_str());
+
+ const char* p = pattern.c_str();
+ const char* d = strchr(p, '-');
+ fbl::StringPiece positive, negative;
+
+ // No negative string.
+ if (d == nullptr) {
+ positive = fbl::StringPiece(p, pattern.size());
+ } else {
+ size_t delta = d - p;
+ positive = fbl::StringPiece(p, delta);
+ negative = fbl::StringPiece(d + 1, pattern.size() - delta - 1);
+ }
+ return (positive.empty() || MatchPatterns(positive, full_test_name)) &&
+ (negative.empty() || !MatchPatterns(negative, full_test_name));
+}
+
} // namespace zxtest
diff --git a/system/ulib/zxtest/test-case.cpp b/system/ulib/zxtest/test-case.cpp
index 4304925..656a187 100644
--- a/system/ulib/zxtest/test-case.cpp
+++ b/system/ulib/zxtest/test-case.cpp
@@ -81,6 +81,10 @@
}
void TestCase::Run(LifecycleObserver* event_broadcaster, TestDriver* driver) {
+ if (selected_indexes_.size() == 0) {
+ return;
+ }
+
auto tear_down = fbl::MakeAutoCall([this, event_broadcaster] {
tear_down_();
event_broadcaster->OnTestCaseEnd(*this);
diff --git a/system/ulib/zxtest/test/runner_test.cpp b/system/ulib/zxtest/test/runner_test.cpp
index 177c2f1..a260003 100644
--- a/system/ulib/zxtest/test/runner_test.cpp
+++ b/system/ulib/zxtest/test/runner_test.cpp
@@ -146,6 +146,38 @@
ZX_ASSERT_MSG(test_2_counter == 1, "test_2 was not executed.\n");
}
+void RunnerRunOnlyFilteredTests() {
+ Runner runner(Reporter(/*stream*/ nullptr));
+ int test_counter = 0;
+ int test_2_counter = 0;
+ Runner::Options options = Runner::kDefaultOptions;
+ options.filter = "TestCase.*";
+
+ TestRef ref = runner.RegisterTest<Test, FakeTest>(
+ kTestCaseName, kTestName, kFileName, kLineNumber, FakeTest::MakeFactory(&test_counter));
+ TestRef ref2 = runner.RegisterTest<Test, FakeTest>(
+ "TestCase2", kTestName, kFileName, kLineNumber, FakeTest::MakeFactory(&test_2_counter));
+
+ ZX_ASSERT_MSG(ref.test_case_index != ref2.test_case_index,
+ "Different TestCase share same index.\n");
+
+ // Verify that the runner actually claims to hold two tests from one test case.
+ ZX_ASSERT_MSG(runner.summary().registered_test_count == 2,
+ "Test failed to register correctly.\n");
+ ZX_ASSERT_MSG(runner.summary().registered_test_case_count == 2,
+ "TestCase failed to register correctly.\n");
+
+ ZX_ASSERT_MSG(runner.Run(options) == 0, "Test Execution Failed.\n");
+
+ // Check that the active count reflects a filter matching all.
+ ZX_ASSERT_MSG(runner.summary().active_test_count == 1, "Failed to filter tests.\n");
+ ZX_ASSERT_MSG(runner.summary().active_test_case_count == 1, "Failed to filter tests.\n");
+
+ // Check that both tests were executed once.
+ ZX_ASSERT_MSG(test_counter == 1, "test was filtered.\n");
+ ZX_ASSERT_MSG(test_2_counter == 0, "test_2 was not filtered.\n");
+}
+
void RunnerRunAllTestsSameTestCase() {
Runner runner(Reporter(/*stream*/ nullptr));
int test_counter = 0;
@@ -377,5 +409,67 @@
ZX_ASSERT_MSG(!errors.is_empty(), "Runner::Options::FromArgs should return error.\n.");
}
+void FilterOpFilterEmptyMatchesAll() {
+ constexpr char kPattern[] = "";
+ FilterOp filter;
+ filter.pattern = kPattern;
+
+ ZX_ASSERT_MSG(filter(kTestCaseName, kTestName), "FilterOp failed to recognize a full match.");
+ ZX_ASSERT_MSG(filter(kTestCaseName, kTestName2), "FilterOp failed to recognize a mismatch.");
+}
+
+void FilterOpFilterFullMatch() {
+ constexpr char kPattern[] = "TestCase.TestName";
+ FilterOp filter;
+ filter.pattern = kPattern;
+
+ ZX_ASSERT_MSG(filter(kTestCaseName, kTestName), "FilterOp failed to recognize a full match.");
+ ZX_ASSERT_MSG(!filter(kTestCaseName, kTestName2), "FilterOp failed to recognize a mismatch.");
+}
+
+void FilterOpFilterFullNegativeMatch() {
+ constexpr char kPattern[] = "-TestCase.TestName";
+ FilterOp filter;
+ filter.pattern = kPattern;
+
+ ZX_ASSERT_MSG(!filter(kTestCaseName, kTestName),
+ "FilterOp failed to recognize a full negative match.");
+ ZX_ASSERT_MSG(filter(kTestCaseName, kTestName2),
+ "FilterOp failed to recognize a negative mismatch.");
+}
+
+void FilterOpFilterPartialMatch() {
+ constexpr char kPattern[] = "TestCase.TestName*";
+ FilterOp filter;
+ filter.pattern = kPattern;
+
+ ZX_ASSERT_MSG(filter(kTestCaseName, kTestName),
+ "FilterOp failed to recognize a partial match.");
+ ZX_ASSERT_MSG(filter(kTestCaseName, kTestName2),
+ "FilterOp failed to recognize a partial match.");
+}
+
+void FilterOpFilterMultiMatch() {
+ constexpr char kPattern[] = "TestCase.TestName:TestCase.TestName2";
+ FilterOp filter;
+ filter.pattern = kPattern;
+
+ ZX_ASSERT_MSG(filter(kTestCaseName, kTestName),
+ "FilterOp failed to recognize first of multiple patterns.");
+ ZX_ASSERT_MSG(filter(kTestCaseName, kTestName2),
+ "FilterOp failed to recognize second of multiple patterns.");
+}
+
+void FilterOpFilterCombined() {
+ constexpr char kPattern[] = "TestCase.TestName:-TestCase.TestName2";
+ FilterOp filter;
+ filter.pattern = kPattern;
+
+ ZX_ASSERT_MSG(filter(kTestCaseName, kTestName),
+ "FilterOp failed to recognize first of multiple patterns.");
+ ZX_ASSERT_MSG(!filter(kTestCaseName, kTestName2),
+ "FilterOp failed to recognize second of multiple patterns.");
+}
+
} // namespace test
} // namespace zxtest
diff --git a/system/ulib/zxtest/test/test-registry.h b/system/ulib/zxtest/test/test-registry.h
index d75e5fd..c78b7c8 100644
--- a/system/ulib/zxtest/test/test-registry.h
+++ b/system/ulib/zxtest/test/test-registry.h
@@ -98,6 +98,7 @@
void RunnerRegisterTestWithCustomFactory();
void RunnerRunAllTests();
void RunnerRunAllTestsSameTestCase();
+void RunnerRunOnlyFilteredTests();
void RunnerRepeatTests();
void RunnerListTests();
@@ -112,6 +113,14 @@
void RunnerOptionsParseFromCmdLineShort();
void RunnerOptionsParseFromCmdLineLong();
+// Verify that the current Filter implementation matches gTest expectations.
+void FilterOpFilterEmptyMatchesAll();
+void FilterOpFilterFullMatch();
+void FilterOpFilterPartialMatch();
+void FilterOpFilterFullNegativeMatch();
+void FilterOpFilterMultiMatch();
+void FilterOpFilterCombined();
+
struct RegisteredTest {
const char* name = nullptr;
void (*test_fn)() = nullptr;
@@ -156,13 +165,19 @@
RUN_TEST(RunnerRegisterTestWithCustomFactory),
RUN_TEST(RunnerRunAllTests),
RUN_TEST(RunnerRunAllTestsSameTestCase),
+ RUN_TEST(RunnerRunOnlyFilteredTests),
RUN_TEST(RunnerListTests),
RUN_TEST(TestDriverImplFatalFailureEndsTest),
RUN_TEST(TestDriverImplNonFatalFailureDoesNotEndTest),
RUN_TEST(TestDriverImplReset),
RUN_TEST(TestDriverImplResetOnTestCompletion),
RUN_TEST(RunnerOptionsParseFromCmdLineShort),
- RUN_TEST(RunnerOptionsParseFromCmdLineLong),
+ RUN_TEST(FilterOpFilterEmptyMatchesAll),
+ RUN_TEST(FilterOpFilterFullMatch),
+ RUN_TEST(FilterOpFilterFullNegativeMatch),
+ RUN_TEST(FilterOpFilterPartialMatch),
+ RUN_TEST(FilterOpFilterMultiMatch),
+ RUN_TEST(FilterOpFilterCombined),
};
#undef RUN_TEST