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