1 // Copyright 2014 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/scanner.hpp" 30 31 #include <deque> 32 #include <string> 33 34 #include "engine/filters.hpp" 35 #include "model/test_case.hpp" 36 #include "model/test_program.hpp" 37 #include "utils/noncopyable.hpp" 38 #include "utils/optional.ipp" 39 #include "utils/sanity.hpp" 40 41 using utils::none; 42 using utils::optional; 43 44 45 namespace { 46 47 48 /// Extracts the keys of a map as a deque. 49 /// 50 /// \tparam KeyType The type of the map keys. 51 /// \tparam ValueType The type of the map values. 52 /// \param map The input map. 53 /// 54 /// \return A deque with the keys of the map. 55 template< typename KeyType, typename ValueType > 56 static std::deque< KeyType > 57 map_keys(const std::map< KeyType, ValueType >& map) 58 { 59 std::deque< KeyType > keys; 60 for (typename std::map< KeyType, ValueType >::const_iterator iter = 61 map.begin(); iter != map.end(); ++iter) { 62 keys.push_back((*iter).first); 63 } 64 return keys; 65 } 66 67 68 } // anonymous namespace 69 70 71 /// Internal implementation for the scanner class. 72 struct engine::scanner::impl : utils::noncopyable { 73 /// Collection of test programs not yet scanned. 74 /// 75 /// The first element in this deque is the "active" test program when 76 /// first_test_cases is defined. 77 std::deque< model::test_program_ptr > pending_test_programs; 78 79 /// Current state of the provided filters. 80 engine::filters_state filters; 81 82 /// Collection of test cases not yet scanned. 83 /// 84 /// These are the test cases for the first test program in 85 /// pending_test_programs when such test program is active. 86 optional< std::deque< std::string > > first_test_cases; 87 88 /// Constructor. 89 /// 90 /// \param test_programs_ Collection of test programs to scan through. 91 /// \param filters_ List of scan filters as provided by the user. 92 impl(const model::test_programs_vector& test_programs_, 93 const std::set< engine::test_filter >& filters_) : 94 pending_test_programs(test_programs_.begin(), test_programs_.end()), 95 filters(filters_) 96 { 97 } 98 99 /// Positions the internal state to return the next element if any. 100 /// 101 /// \post If there are more elements to read, returns true and 102 /// pending_test_programs[0] points to the active test program and 103 /// first_test_cases[0] has the test case to be returned. 104 /// 105 /// \return True if there is one more result available. 106 bool 107 advance(void) 108 { 109 for (;;) { 110 if (first_test_cases) { 111 if (first_test_cases.get().empty()) { 112 pending_test_programs.pop_front(); 113 first_test_cases = none; 114 } 115 } 116 if (pending_test_programs.empty()) { 117 break; 118 } 119 120 model::test_program_ptr test_program = pending_test_programs[0]; 121 if (!first_test_cases) { 122 if (!filters.match_test_program( 123 test_program->relative_path())) { 124 pending_test_programs.pop_front(); 125 continue; 126 } 127 128 first_test_cases = utils::make_optional( 129 map_keys(test_program->test_cases())); 130 } 131 132 if (!first_test_cases.get().empty()) { 133 std::deque< std::string >::iterator iter = 134 first_test_cases.get().begin(); 135 const std::string test_case_name = *iter; 136 if (!filters.match_test_case(test_program->relative_path(), 137 test_case_name)) { 138 first_test_cases.get().erase(iter); 139 continue; 140 } 141 return true; 142 } else { 143 pending_test_programs.pop_front(); 144 first_test_cases = none; 145 } 146 } 147 return false; 148 } 149 150 /// Extracts the current element. 151 /// 152 /// \pre Must be called only if advance() returns true, and immediately 153 /// afterwards. 154 /// 155 /// \return The current scan result. 156 engine::scan_result 157 consume(void) 158 { 159 const std::string test_case_name = first_test_cases.get()[0]; 160 first_test_cases.get().pop_front(); 161 return scan_result(pending_test_programs[0], test_case_name); 162 } 163 }; 164 165 166 /// Constructor. 167 /// 168 /// \param test_programs Collection of test programs to scan through. 169 /// \param filters List of scan filters as provided by the user. 170 engine::scanner::scanner(const model::test_programs_vector& test_programs, 171 const std::set< engine::test_filter >& filters) : 172 _pimpl(new impl(test_programs, filters)) 173 { 174 } 175 176 177 /// Destructor. 178 engine::scanner::~scanner(void) 179 { 180 } 181 182 183 /// Returns the next scan result. 184 /// 185 /// \return A scan result if there are still pending test cases to be processed, 186 /// or none otherwise. 187 optional< engine::scan_result > 188 engine::scanner::yield(void) 189 { 190 if (_pimpl->advance()) { 191 return utils::make_optional(_pimpl->consume()); 192 } else { 193 return none; 194 } 195 } 196 197 198 /// Checks whether the scan is finished. 199 /// 200 /// \return True if the scan is finished, in which case yield() will return 201 /// none; false otherwise. 202 bool 203 engine::scanner::done(void) 204 { 205 return !_pimpl->advance(); 206 } 207 208 209 /// Returns the list of test filters that did not match any test case. 210 /// 211 /// \return The collection of unmatched test filters. 212 std::set< engine::test_filter > 213 engine::scanner::unused_filters(void) const 214 { 215 return _pimpl->filters.unused(); 216 } 217