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 >
map_keys(const std::map<KeyType,ValueType> & map)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.
implengine::scanner::impl92 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
advanceengine::scanner::impl107 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
consumeengine::scanner::impl157 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.
scanner(const model::test_programs_vector & test_programs,const std::set<engine::test_filter> & filters)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.
~scanner(void)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 >
yield(void)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
done(void)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 >
unused_filters(void) const213 engine::scanner::unused_filters(void) const
214 {
215 return _pimpl->filters.unused();
216 }
217