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 41using utils::none; 42using utils::optional; 43 44 45namespace { 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. 55template< typename KeyType, typename ValueType > 56static std::deque< KeyType > 57map_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. 72struct 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. 170engine::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. 178engine::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. 187optional< engine::scan_result > 188engine::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. 202bool 203engine::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. 212std::set< engine::test_filter > 213engine::scanner::unused_filters(void) const 214{ 215 return _pimpl->filters.unused(); 216} 217