1*b0d29bc4SBrooks Davis // Copyright 2014 The Kyua Authors.
2*b0d29bc4SBrooks Davis // All rights reserved.
3*b0d29bc4SBrooks Davis //
4*b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without
5*b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are
6*b0d29bc4SBrooks Davis // met:
7*b0d29bc4SBrooks Davis //
8*b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright
9*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer.
10*b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
11*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer in the
12*b0d29bc4SBrooks Davis // documentation and/or other materials provided with the distribution.
13*b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
14*b0d29bc4SBrooks Davis // may be used to endorse or promote products derived from this software
15*b0d29bc4SBrooks Davis // without specific prior written permission.
16*b0d29bc4SBrooks Davis //
17*b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*b0d29bc4SBrooks Davis
29*b0d29bc4SBrooks Davis #include "engine/scanner.hpp"
30*b0d29bc4SBrooks Davis
31*b0d29bc4SBrooks Davis #include <deque>
32*b0d29bc4SBrooks Davis #include <string>
33*b0d29bc4SBrooks Davis
34*b0d29bc4SBrooks Davis #include "engine/filters.hpp"
35*b0d29bc4SBrooks Davis #include "model/test_case.hpp"
36*b0d29bc4SBrooks Davis #include "model/test_program.hpp"
37*b0d29bc4SBrooks Davis #include "utils/noncopyable.hpp"
38*b0d29bc4SBrooks Davis #include "utils/optional.ipp"
39*b0d29bc4SBrooks Davis #include "utils/sanity.hpp"
40*b0d29bc4SBrooks Davis
41*b0d29bc4SBrooks Davis using utils::none;
42*b0d29bc4SBrooks Davis using utils::optional;
43*b0d29bc4SBrooks Davis
44*b0d29bc4SBrooks Davis
45*b0d29bc4SBrooks Davis namespace {
46*b0d29bc4SBrooks Davis
47*b0d29bc4SBrooks Davis
48*b0d29bc4SBrooks Davis /// Extracts the keys of a map as a deque.
49*b0d29bc4SBrooks Davis ///
50*b0d29bc4SBrooks Davis /// \tparam KeyType The type of the map keys.
51*b0d29bc4SBrooks Davis /// \tparam ValueType The type of the map values.
52*b0d29bc4SBrooks Davis /// \param map The input map.
53*b0d29bc4SBrooks Davis ///
54*b0d29bc4SBrooks Davis /// \return A deque with the keys of the map.
55*b0d29bc4SBrooks Davis template< typename KeyType, typename ValueType >
56*b0d29bc4SBrooks Davis static std::deque< KeyType >
map_keys(const std::map<KeyType,ValueType> & map)57*b0d29bc4SBrooks Davis map_keys(const std::map< KeyType, ValueType >& map)
58*b0d29bc4SBrooks Davis {
59*b0d29bc4SBrooks Davis std::deque< KeyType > keys;
60*b0d29bc4SBrooks Davis for (typename std::map< KeyType, ValueType >::const_iterator iter =
61*b0d29bc4SBrooks Davis map.begin(); iter != map.end(); ++iter) {
62*b0d29bc4SBrooks Davis keys.push_back((*iter).first);
63*b0d29bc4SBrooks Davis }
64*b0d29bc4SBrooks Davis return keys;
65*b0d29bc4SBrooks Davis }
66*b0d29bc4SBrooks Davis
67*b0d29bc4SBrooks Davis
68*b0d29bc4SBrooks Davis } // anonymous namespace
69*b0d29bc4SBrooks Davis
70*b0d29bc4SBrooks Davis
71*b0d29bc4SBrooks Davis /// Internal implementation for the scanner class.
72*b0d29bc4SBrooks Davis struct engine::scanner::impl : utils::noncopyable {
73*b0d29bc4SBrooks Davis /// Collection of test programs not yet scanned.
74*b0d29bc4SBrooks Davis ///
75*b0d29bc4SBrooks Davis /// The first element in this deque is the "active" test program when
76*b0d29bc4SBrooks Davis /// first_test_cases is defined.
77*b0d29bc4SBrooks Davis std::deque< model::test_program_ptr > pending_test_programs;
78*b0d29bc4SBrooks Davis
79*b0d29bc4SBrooks Davis /// Current state of the provided filters.
80*b0d29bc4SBrooks Davis engine::filters_state filters;
81*b0d29bc4SBrooks Davis
82*b0d29bc4SBrooks Davis /// Collection of test cases not yet scanned.
83*b0d29bc4SBrooks Davis ///
84*b0d29bc4SBrooks Davis /// These are the test cases for the first test program in
85*b0d29bc4SBrooks Davis /// pending_test_programs when such test program is active.
86*b0d29bc4SBrooks Davis optional< std::deque< std::string > > first_test_cases;
87*b0d29bc4SBrooks Davis
88*b0d29bc4SBrooks Davis /// Constructor.
89*b0d29bc4SBrooks Davis ///
90*b0d29bc4SBrooks Davis /// \param test_programs_ Collection of test programs to scan through.
91*b0d29bc4SBrooks Davis /// \param filters_ List of scan filters as provided by the user.
implengine::scanner::impl92*b0d29bc4SBrooks Davis impl(const model::test_programs_vector& test_programs_,
93*b0d29bc4SBrooks Davis const std::set< engine::test_filter >& filters_) :
94*b0d29bc4SBrooks Davis pending_test_programs(test_programs_.begin(), test_programs_.end()),
95*b0d29bc4SBrooks Davis filters(filters_)
96*b0d29bc4SBrooks Davis {
97*b0d29bc4SBrooks Davis }
98*b0d29bc4SBrooks Davis
99*b0d29bc4SBrooks Davis /// Positions the internal state to return the next element if any.
100*b0d29bc4SBrooks Davis ///
101*b0d29bc4SBrooks Davis /// \post If there are more elements to read, returns true and
102*b0d29bc4SBrooks Davis /// pending_test_programs[0] points to the active test program and
103*b0d29bc4SBrooks Davis /// first_test_cases[0] has the test case to be returned.
104*b0d29bc4SBrooks Davis ///
105*b0d29bc4SBrooks Davis /// \return True if there is one more result available.
106*b0d29bc4SBrooks Davis bool
advanceengine::scanner::impl107*b0d29bc4SBrooks Davis advance(void)
108*b0d29bc4SBrooks Davis {
109*b0d29bc4SBrooks Davis for (;;) {
110*b0d29bc4SBrooks Davis if (first_test_cases) {
111*b0d29bc4SBrooks Davis if (first_test_cases.get().empty()) {
112*b0d29bc4SBrooks Davis pending_test_programs.pop_front();
113*b0d29bc4SBrooks Davis first_test_cases = none;
114*b0d29bc4SBrooks Davis }
115*b0d29bc4SBrooks Davis }
116*b0d29bc4SBrooks Davis if (pending_test_programs.empty()) {
117*b0d29bc4SBrooks Davis break;
118*b0d29bc4SBrooks Davis }
119*b0d29bc4SBrooks Davis
120*b0d29bc4SBrooks Davis model::test_program_ptr test_program = pending_test_programs[0];
121*b0d29bc4SBrooks Davis if (!first_test_cases) {
122*b0d29bc4SBrooks Davis if (!filters.match_test_program(
123*b0d29bc4SBrooks Davis test_program->relative_path())) {
124*b0d29bc4SBrooks Davis pending_test_programs.pop_front();
125*b0d29bc4SBrooks Davis continue;
126*b0d29bc4SBrooks Davis }
127*b0d29bc4SBrooks Davis
128*b0d29bc4SBrooks Davis first_test_cases = utils::make_optional(
129*b0d29bc4SBrooks Davis map_keys(test_program->test_cases()));
130*b0d29bc4SBrooks Davis }
131*b0d29bc4SBrooks Davis
132*b0d29bc4SBrooks Davis if (!first_test_cases.get().empty()) {
133*b0d29bc4SBrooks Davis std::deque< std::string >::iterator iter =
134*b0d29bc4SBrooks Davis first_test_cases.get().begin();
135*b0d29bc4SBrooks Davis const std::string test_case_name = *iter;
136*b0d29bc4SBrooks Davis if (!filters.match_test_case(test_program->relative_path(),
137*b0d29bc4SBrooks Davis test_case_name)) {
138*b0d29bc4SBrooks Davis first_test_cases.get().erase(iter);
139*b0d29bc4SBrooks Davis continue;
140*b0d29bc4SBrooks Davis }
141*b0d29bc4SBrooks Davis return true;
142*b0d29bc4SBrooks Davis } else {
143*b0d29bc4SBrooks Davis pending_test_programs.pop_front();
144*b0d29bc4SBrooks Davis first_test_cases = none;
145*b0d29bc4SBrooks Davis }
146*b0d29bc4SBrooks Davis }
147*b0d29bc4SBrooks Davis return false;
148*b0d29bc4SBrooks Davis }
149*b0d29bc4SBrooks Davis
150*b0d29bc4SBrooks Davis /// Extracts the current element.
151*b0d29bc4SBrooks Davis ///
152*b0d29bc4SBrooks Davis /// \pre Must be called only if advance() returns true, and immediately
153*b0d29bc4SBrooks Davis /// afterwards.
154*b0d29bc4SBrooks Davis ///
155*b0d29bc4SBrooks Davis /// \return The current scan result.
156*b0d29bc4SBrooks Davis engine::scan_result
consumeengine::scanner::impl157*b0d29bc4SBrooks Davis consume(void)
158*b0d29bc4SBrooks Davis {
159*b0d29bc4SBrooks Davis const std::string test_case_name = first_test_cases.get()[0];
160*b0d29bc4SBrooks Davis first_test_cases.get().pop_front();
161*b0d29bc4SBrooks Davis return scan_result(pending_test_programs[0], test_case_name);
162*b0d29bc4SBrooks Davis }
163*b0d29bc4SBrooks Davis };
164*b0d29bc4SBrooks Davis
165*b0d29bc4SBrooks Davis
166*b0d29bc4SBrooks Davis /// Constructor.
167*b0d29bc4SBrooks Davis ///
168*b0d29bc4SBrooks Davis /// \param test_programs Collection of test programs to scan through.
169*b0d29bc4SBrooks Davis /// \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*b0d29bc4SBrooks Davis engine::scanner::scanner(const model::test_programs_vector& test_programs,
171*b0d29bc4SBrooks Davis const std::set< engine::test_filter >& filters) :
172*b0d29bc4SBrooks Davis _pimpl(new impl(test_programs, filters))
173*b0d29bc4SBrooks Davis {
174*b0d29bc4SBrooks Davis }
175*b0d29bc4SBrooks Davis
176*b0d29bc4SBrooks Davis
177*b0d29bc4SBrooks Davis /// Destructor.
~scanner(void)178*b0d29bc4SBrooks Davis engine::scanner::~scanner(void)
179*b0d29bc4SBrooks Davis {
180*b0d29bc4SBrooks Davis }
181*b0d29bc4SBrooks Davis
182*b0d29bc4SBrooks Davis
183*b0d29bc4SBrooks Davis /// Returns the next scan result.
184*b0d29bc4SBrooks Davis ///
185*b0d29bc4SBrooks Davis /// \return A scan result if there are still pending test cases to be processed,
186*b0d29bc4SBrooks Davis /// or none otherwise.
187*b0d29bc4SBrooks Davis optional< engine::scan_result >
yield(void)188*b0d29bc4SBrooks Davis engine::scanner::yield(void)
189*b0d29bc4SBrooks Davis {
190*b0d29bc4SBrooks Davis if (_pimpl->advance()) {
191*b0d29bc4SBrooks Davis return utils::make_optional(_pimpl->consume());
192*b0d29bc4SBrooks Davis } else {
193*b0d29bc4SBrooks Davis return none;
194*b0d29bc4SBrooks Davis }
195*b0d29bc4SBrooks Davis }
196*b0d29bc4SBrooks Davis
197*b0d29bc4SBrooks Davis
198*b0d29bc4SBrooks Davis /// Checks whether the scan is finished.
199*b0d29bc4SBrooks Davis ///
200*b0d29bc4SBrooks Davis /// \return True if the scan is finished, in which case yield() will return
201*b0d29bc4SBrooks Davis /// none; false otherwise.
202*b0d29bc4SBrooks Davis bool
done(void)203*b0d29bc4SBrooks Davis engine::scanner::done(void)
204*b0d29bc4SBrooks Davis {
205*b0d29bc4SBrooks Davis return !_pimpl->advance();
206*b0d29bc4SBrooks Davis }
207*b0d29bc4SBrooks Davis
208*b0d29bc4SBrooks Davis
209*b0d29bc4SBrooks Davis /// Returns the list of test filters that did not match any test case.
210*b0d29bc4SBrooks Davis ///
211*b0d29bc4SBrooks Davis /// \return The collection of unmatched test filters.
212*b0d29bc4SBrooks Davis std::set< engine::test_filter >
unused_filters(void) const213*b0d29bc4SBrooks Davis engine::scanner::unused_filters(void) const
214*b0d29bc4SBrooks Davis {
215*b0d29bc4SBrooks Davis return _pimpl->filters.unused();
216*b0d29bc4SBrooks Davis }
217