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