xref: /freebsd/contrib/kyua/engine/scanner.cpp (revision b0d29bc4)
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