xref: /freebsd/contrib/kyua/engine/googletest_list.cpp (revision eb8f69902f7de8012a31a85db2f88a54584f81af)
1*eb8f6990SEnji Cooper // Copyright 2024 The Kyua Authors.
2*eb8f6990SEnji Cooper // All rights reserved.
3*eb8f6990SEnji Cooper //
4*eb8f6990SEnji Cooper // Redistribution and use in source and binary forms, with or without
5*eb8f6990SEnji Cooper // modification, are permitted provided that the following conditions are
6*eb8f6990SEnji Cooper // met:
7*eb8f6990SEnji Cooper //
8*eb8f6990SEnji Cooper // * Redistributions of source code must retain the above copyright
9*eb8f6990SEnji Cooper //   notice, this list of conditions and the following disclaimer.
10*eb8f6990SEnji Cooper // * Redistributions in binary form must reproduce the above copyright
11*eb8f6990SEnji Cooper //   notice, this list of conditions and the following disclaimer in the
12*eb8f6990SEnji Cooper //   documentation and/or other materials provided with the distribution.
13*eb8f6990SEnji Cooper // * Neither the name of Google Inc. nor the names of its contributors
14*eb8f6990SEnji Cooper //   may be used to endorse or promote products derived from this software
15*eb8f6990SEnji Cooper //   without specific prior written permission.
16*eb8f6990SEnji Cooper //
17*eb8f6990SEnji Cooper // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*eb8f6990SEnji Cooper // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*eb8f6990SEnji Cooper // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*eb8f6990SEnji Cooper // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*eb8f6990SEnji Cooper // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*eb8f6990SEnji Cooper // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*eb8f6990SEnji Cooper // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*eb8f6990SEnji Cooper // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*eb8f6990SEnji Cooper // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*eb8f6990SEnji Cooper // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*eb8f6990SEnji Cooper // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*eb8f6990SEnji Cooper 
29*eb8f6990SEnji Cooper #include "engine/googletest_list.hpp"
30*eb8f6990SEnji Cooper 
31*eb8f6990SEnji Cooper #include <fstream>
32*eb8f6990SEnji Cooper #include <regex>
33*eb8f6990SEnji Cooper #include <string>
34*eb8f6990SEnji Cooper #include <utility>
35*eb8f6990SEnji Cooper 
36*eb8f6990SEnji Cooper #include "engine/exceptions.hpp"
37*eb8f6990SEnji Cooper #include "model/metadata.hpp"
38*eb8f6990SEnji Cooper #include "model/test_case.hpp"
39*eb8f6990SEnji Cooper #include "utils/config/exceptions.hpp"
40*eb8f6990SEnji Cooper #include "utils/format/macros.hpp"
41*eb8f6990SEnji Cooper 
42*eb8f6990SEnji Cooper namespace config = utils::config;
43*eb8f6990SEnji Cooper namespace fs = utils::fs;
44*eb8f6990SEnji Cooper 
45*eb8f6990SEnji Cooper 
46*eb8f6990SEnji Cooper namespace {
47*eb8f6990SEnji Cooper 
48*eb8f6990SEnji Cooper /// A regular expression that should match either a test suite or a test case.
49*eb8f6990SEnji Cooper const std::string name_expr = "([[:alpha:][:digit:]_]+[[:alpha:][:digit:]_/]*)";
50*eb8f6990SEnji Cooper 
51*eb8f6990SEnji Cooper /// The separator between a test suite and a test case.
52*eb8f6990SEnji Cooper const std::string testsuite_testcase_separator = ".";
53*eb8f6990SEnji Cooper 
54*eb8f6990SEnji Cooper }  // anonymous namespace
55*eb8f6990SEnji Cooper 
56*eb8f6990SEnji Cooper 
57*eb8f6990SEnji Cooper /// Parses the googletest list of test cases from an open stream.
58*eb8f6990SEnji Cooper ///
59*eb8f6990SEnji Cooper /// \param input The stream to read from.
60*eb8f6990SEnji Cooper ///
61*eb8f6990SEnji Cooper /// \return The collection of parsed test cases.
62*eb8f6990SEnji Cooper ///
63*eb8f6990SEnji Cooper /// \throw format_error If there is any problem in the input data.
64*eb8f6990SEnji Cooper model::test_cases_map
65*eb8f6990SEnji Cooper engine::parse_googletest_list(std::istream& input)
66*eb8f6990SEnji Cooper {
67*eb8f6990SEnji Cooper     std::string line, test_suite;
68*eb8f6990SEnji Cooper 
69*eb8f6990SEnji Cooper     // TODO: These were moved here from the anonymous namespace because of a
70*eb8f6990SEnji Cooper     // Doxygen bug that's reproducible with the version used by Travis CI.
71*eb8f6990SEnji Cooper     //
72*eb8f6990SEnji Cooper     /// A complete regular expression representing a line with a test case
73*eb8f6990SEnji Cooper     /// definition, e,g., "  TestCase",  "  TestCase/0", or
74*eb8f6990SEnji Cooper     /// "  TestCase/0  # GetParam() = 4".
75*eb8f6990SEnji Cooper     const std::regex testcase_re(
76*eb8f6990SEnji Cooper         "  " + name_expr + "([[:space:]]+# GetParam\\(\\) = .+)?");
77*eb8f6990SEnji Cooper 
78*eb8f6990SEnji Cooper     /// A complete regular expression representing a line with a test suite
79*eb8f6990SEnji Cooper     /// definition, e,g., * "TestSuite.", "TestSuite/Prefix.", or
80*eb8f6990SEnji Cooper     /// "TestSuite/Prefix.    # TypeParam = .+".
81*eb8f6990SEnji Cooper     const std::regex testsuite_re(
82*eb8f6990SEnji Cooper         name_expr + "\\.([[:space:]]+# TypeParam = .+)?");
83*eb8f6990SEnji Cooper     // END TODO
84*eb8f6990SEnji Cooper 
85*eb8f6990SEnji Cooper     model::test_cases_map_builder test_cases_builder;
86*eb8f6990SEnji Cooper     while (std::getline(input, line).good()) {
87*eb8f6990SEnji Cooper         std::smatch match;
88*eb8f6990SEnji Cooper         if (std::regex_match(line, match, testcase_re)) {
89*eb8f6990SEnji Cooper             if (test_suite.empty()) {
90*eb8f6990SEnji Cooper                 throw format_error("Invalid testcase definition: not preceded "
91*eb8f6990SEnji Cooper                                    "by a test suite definition");
92*eb8f6990SEnji Cooper             }
93*eb8f6990SEnji Cooper             std::string test_case(match[1]);
94*eb8f6990SEnji Cooper             test_cases_builder.add(test_suite + test_case);
95*eb8f6990SEnji Cooper         } else if (std::regex_match(line, match, testsuite_re)) {
96*eb8f6990SEnji Cooper             test_suite = std::string(match[1]) +
97*eb8f6990SEnji Cooper                          testsuite_testcase_separator;
98*eb8f6990SEnji Cooper         } else {
99*eb8f6990SEnji Cooper             // Ignore the line; something might have used output a diagnostic
100*eb8f6990SEnji Cooper             // message to stdout, e.g., gtest_main.
101*eb8f6990SEnji Cooper         }
102*eb8f6990SEnji Cooper     }
103*eb8f6990SEnji Cooper     const model::test_cases_map test_cases = test_cases_builder.build();
104*eb8f6990SEnji Cooper     if (test_cases.empty()) {
105*eb8f6990SEnji Cooper         // The scheduler interface also checks for the presence of at least one
106*eb8f6990SEnji Cooper         // test case.  However, because the atf format itself requires one test
107*eb8f6990SEnji Cooper         // case to be always present, we check for this condition here as well.
108*eb8f6990SEnji Cooper         throw format_error("No test cases");
109*eb8f6990SEnji Cooper     }
110*eb8f6990SEnji Cooper     return test_cases;
111*eb8f6990SEnji Cooper }
112