[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