1*11be35a1SLionel Sambuc // Copyright 2010 Google Inc.
2*11be35a1SLionel Sambuc // All rights reserved.
3*11be35a1SLionel Sambuc //
4*11be35a1SLionel Sambuc // Redistribution and use in source and binary forms, with or without
5*11be35a1SLionel Sambuc // modification, are permitted provided that the following conditions are
6*11be35a1SLionel Sambuc // met:
7*11be35a1SLionel Sambuc //
8*11be35a1SLionel Sambuc // * Redistributions of source code must retain the above copyright
9*11be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer.
10*11be35a1SLionel Sambuc // * Redistributions in binary form must reproduce the above copyright
11*11be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer in the
12*11be35a1SLionel Sambuc // documentation and/or other materials provided with the distribution.
13*11be35a1SLionel Sambuc // * Neither the name of Google Inc. nor the names of its contributors
14*11be35a1SLionel Sambuc // may be used to endorse or promote products derived from this software
15*11be35a1SLionel Sambuc // without specific prior written permission.
16*11be35a1SLionel Sambuc //
17*11be35a1SLionel Sambuc // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*11be35a1SLionel Sambuc // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*11be35a1SLionel Sambuc // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*11be35a1SLionel Sambuc // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*11be35a1SLionel Sambuc // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*11be35a1SLionel Sambuc // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*11be35a1SLionel Sambuc // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*11be35a1SLionel Sambuc // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*11be35a1SLionel Sambuc // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*11be35a1SLionel Sambuc // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*11be35a1SLionel Sambuc // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*11be35a1SLionel Sambuc
29*11be35a1SLionel Sambuc #include "engine/test_program.hpp"
30*11be35a1SLionel Sambuc
31*11be35a1SLionel Sambuc #include <algorithm>
32*11be35a1SLionel Sambuc #include <map>
33*11be35a1SLionel Sambuc #include <sstream>
34*11be35a1SLionel Sambuc #include <stdexcept>
35*11be35a1SLionel Sambuc
36*11be35a1SLionel Sambuc #include <lutok/operations.hpp>
37*11be35a1SLionel Sambuc #include <lutok/state.ipp>
38*11be35a1SLionel Sambuc
39*11be35a1SLionel Sambuc #include "engine/exceptions.hpp"
40*11be35a1SLionel Sambuc #include "engine/test_result.hpp"
41*11be35a1SLionel Sambuc #include "engine/testers.hpp"
42*11be35a1SLionel Sambuc #include "utils/format/macros.hpp"
43*11be35a1SLionel Sambuc #include "utils/logging/macros.hpp"
44*11be35a1SLionel Sambuc #include "utils/logging/operations.hpp"
45*11be35a1SLionel Sambuc #include "utils/optional.ipp"
46*11be35a1SLionel Sambuc #include "utils/sanity.hpp"
47*11be35a1SLionel Sambuc #include "utils/text/operations.ipp"
48*11be35a1SLionel Sambuc
49*11be35a1SLionel Sambuc namespace fs = utils::fs;
50*11be35a1SLionel Sambuc namespace logging = utils::logging;
51*11be35a1SLionel Sambuc namespace text = utils::text;
52*11be35a1SLionel Sambuc
53*11be35a1SLionel Sambuc using utils::none;
54*11be35a1SLionel Sambuc using utils::optional;
55*11be35a1SLionel Sambuc
56*11be35a1SLionel Sambuc
57*11be35a1SLionel Sambuc namespace {
58*11be35a1SLionel Sambuc
59*11be35a1SLionel Sambuc
60*11be35a1SLionel Sambuc /// Lua hook for the test_case function.
61*11be35a1SLionel Sambuc ///
62*11be35a1SLionel Sambuc /// \pre state(-1) contains the arguments to the function.
63*11be35a1SLionel Sambuc ///
64*11be35a1SLionel Sambuc /// \param state The Lua state in which we are running.
65*11be35a1SLionel Sambuc ///
66*11be35a1SLionel Sambuc /// \return The number of return values, which is always 0.
67*11be35a1SLionel Sambuc static int
lua_test_case(lutok::state & state)68*11be35a1SLionel Sambuc lua_test_case(lutok::state& state)
69*11be35a1SLionel Sambuc {
70*11be35a1SLionel Sambuc if (!state.is_table())
71*11be35a1SLionel Sambuc throw std::runtime_error("Oh noes"); // XXX
72*11be35a1SLionel Sambuc
73*11be35a1SLionel Sambuc state.get_global("_test_cases");
74*11be35a1SLionel Sambuc engine::test_cases_vector* test_cases =
75*11be35a1SLionel Sambuc *state.to_userdata< engine::test_cases_vector* >();
76*11be35a1SLionel Sambuc state.pop(1);
77*11be35a1SLionel Sambuc
78*11be35a1SLionel Sambuc state.get_global("_test_program");
79*11be35a1SLionel Sambuc const engine::test_program* test_program =
80*11be35a1SLionel Sambuc *state.to_userdata< engine::test_program* >();
81*11be35a1SLionel Sambuc state.pop(1);
82*11be35a1SLionel Sambuc
83*11be35a1SLionel Sambuc state.push_string("name");
84*11be35a1SLionel Sambuc state.get_table(-2);
85*11be35a1SLionel Sambuc const std::string name = state.to_string();
86*11be35a1SLionel Sambuc state.pop(1);
87*11be35a1SLionel Sambuc
88*11be35a1SLionel Sambuc engine::metadata_builder mdbuilder(test_program->get_metadata());
89*11be35a1SLionel Sambuc
90*11be35a1SLionel Sambuc state.push_nil();
91*11be35a1SLionel Sambuc while (state.next(-2)) {
92*11be35a1SLionel Sambuc if (!state.is_string(-2))
93*11be35a1SLionel Sambuc throw std::runtime_error("Oh oh"); // XXX
94*11be35a1SLionel Sambuc const std::string property = state.to_string(-2);
95*11be35a1SLionel Sambuc
96*11be35a1SLionel Sambuc if (!state.is_string(-1))
97*11be35a1SLionel Sambuc throw std::runtime_error("Oh oh"); // XXX
98*11be35a1SLionel Sambuc const std::string value = state.to_string(-1);
99*11be35a1SLionel Sambuc
100*11be35a1SLionel Sambuc if (property != "name")
101*11be35a1SLionel Sambuc mdbuilder.set_string(property, value);
102*11be35a1SLionel Sambuc
103*11be35a1SLionel Sambuc state.pop(1);
104*11be35a1SLionel Sambuc }
105*11be35a1SLionel Sambuc state.pop(1);
106*11be35a1SLionel Sambuc
107*11be35a1SLionel Sambuc engine::test_case_ptr test_case(
108*11be35a1SLionel Sambuc new engine::test_case(test_program->interface_name(), *test_program,
109*11be35a1SLionel Sambuc name, mdbuilder.build()));
110*11be35a1SLionel Sambuc test_cases->push_back(test_case);
111*11be35a1SLionel Sambuc
112*11be35a1SLionel Sambuc return 0;
113*11be35a1SLionel Sambuc }
114*11be35a1SLionel Sambuc
115*11be35a1SLionel Sambuc
116*11be35a1SLionel Sambuc /// Sets up the Lua state to process the output of a test case list.
117*11be35a1SLionel Sambuc ///
118*11be35a1SLionel Sambuc /// \param [in,out] state The Lua state to configure.
119*11be35a1SLionel Sambuc /// \param test_program Pointer to the test program being loaded.
120*11be35a1SLionel Sambuc /// \param [out] test_cases Vector that will contain the list of test cases.
121*11be35a1SLionel Sambuc static void
setup_lua_state(lutok::state & state,const engine::test_program * test_program,engine::test_cases_vector * test_cases)122*11be35a1SLionel Sambuc setup_lua_state(lutok::state& state, const engine::test_program* test_program,
123*11be35a1SLionel Sambuc engine::test_cases_vector* test_cases)
124*11be35a1SLionel Sambuc {
125*11be35a1SLionel Sambuc *state.new_userdata< engine::test_cases_vector* >() = test_cases;
126*11be35a1SLionel Sambuc state.set_global("_test_cases");
127*11be35a1SLionel Sambuc
128*11be35a1SLionel Sambuc *state.new_userdata< const engine::test_program* >() = test_program;
129*11be35a1SLionel Sambuc state.set_global("_test_program");
130*11be35a1SLionel Sambuc
131*11be35a1SLionel Sambuc state.push_cxx_function(lua_test_case);
132*11be35a1SLionel Sambuc state.set_global("test_case");
133*11be35a1SLionel Sambuc }
134*11be35a1SLionel Sambuc
135*11be35a1SLionel Sambuc
136*11be35a1SLionel Sambuc /// Loads the list of test cases from a test program.
137*11be35a1SLionel Sambuc ///
138*11be35a1SLionel Sambuc /// \param test_program Representation of the test program to load.
139*11be35a1SLionel Sambuc ///
140*11be35a1SLionel Sambuc /// \return A list of test cases.
141*11be35a1SLionel Sambuc static engine::test_cases_vector
load_test_cases(const engine::test_program & test_program)142*11be35a1SLionel Sambuc load_test_cases(const engine::test_program& test_program)
143*11be35a1SLionel Sambuc {
144*11be35a1SLionel Sambuc const engine::tester tester(test_program.interface_name(), none, none);
145*11be35a1SLionel Sambuc const std::string output = tester.list(test_program.absolute_path());
146*11be35a1SLionel Sambuc
147*11be35a1SLionel Sambuc engine::test_cases_vector test_cases;
148*11be35a1SLionel Sambuc lutok::state state;
149*11be35a1SLionel Sambuc setup_lua_state(state, &test_program, &test_cases);
150*11be35a1SLionel Sambuc lutok::do_string(state, output, 0);
151*11be35a1SLionel Sambuc return test_cases;
152*11be35a1SLionel Sambuc }
153*11be35a1SLionel Sambuc
154*11be35a1SLionel Sambuc
155*11be35a1SLionel Sambuc /// Predicate to compare two test cases via pointers to them.
156*11be35a1SLionel Sambuc ///
157*11be35a1SLionel Sambuc /// \param tc1 Entry in a map of test case names to test case pointers.
158*11be35a1SLionel Sambuc /// \param tc2 Entry in a map of test case names to test case pointers.
159*11be35a1SLionel Sambuc ///
160*11be35a1SLionel Sambuc /// \return True if the test case in tc1 is the same as in tc2. Note that the
161*11be35a1SLionel Sambuc /// container test programs are NOT compared.
162*11be35a1SLionel Sambuc static bool
compare_test_case(const std::pair<std::string,engine::test_case_ptr> & tc1,const std::pair<std::string,engine::test_case_ptr> & tc2)163*11be35a1SLionel Sambuc compare_test_case(const std::pair< std::string, engine::test_case_ptr >& tc1,
164*11be35a1SLionel Sambuc const std::pair< std::string, engine::test_case_ptr >& tc2)
165*11be35a1SLionel Sambuc {
166*11be35a1SLionel Sambuc return tc1.first == tc2.first && *tc1.second == *tc2.second;
167*11be35a1SLionel Sambuc }
168*11be35a1SLionel Sambuc
169*11be35a1SLionel Sambuc
170*11be35a1SLionel Sambuc /// Compares if two sets of test cases hold the same values.
171*11be35a1SLionel Sambuc ///
172*11be35a1SLionel Sambuc /// \param tests1 First collection of test cases.
173*11be35a1SLionel Sambuc /// \param tests2 Second collection of test cases.
174*11be35a1SLionel Sambuc ///
175*11be35a1SLionel Sambuc /// \return True if both collections hold the same test cases (value-wise, not
176*11be35a1SLionel Sambuc /// pointer-wise); false otherwise.
177*11be35a1SLionel Sambuc static bool
compare_test_cases(const optional<engine::test_cases_vector> & tests1,const optional<engine::test_cases_vector> & tests2)178*11be35a1SLionel Sambuc compare_test_cases(const optional< engine::test_cases_vector >& tests1,
179*11be35a1SLionel Sambuc const optional< engine::test_cases_vector >& tests2)
180*11be35a1SLionel Sambuc {
181*11be35a1SLionel Sambuc if (!tests1 && !tests2)
182*11be35a1SLionel Sambuc return true;
183*11be35a1SLionel Sambuc else if ((tests1 && !tests2) || (!tests1 && tests2))
184*11be35a1SLionel Sambuc return false;
185*11be35a1SLionel Sambuc INV(tests1 && tests2);
186*11be35a1SLionel Sambuc
187*11be35a1SLionel Sambuc // This is very inefficient, but because it should only be used in our own
188*11be35a1SLionel Sambuc // tests, it doesn't matter.
189*11be35a1SLionel Sambuc std::map< std::string, engine::test_case_ptr > map1, map2;
190*11be35a1SLionel Sambuc for (engine::test_cases_vector::const_iterator iter = tests1.get().begin();
191*11be35a1SLionel Sambuc iter != tests1.get().end(); ++iter)
192*11be35a1SLionel Sambuc map1.insert(make_pair((*iter)->name(), *iter));
193*11be35a1SLionel Sambuc for (engine::test_cases_vector::const_iterator iter = tests2.get().begin();
194*11be35a1SLionel Sambuc iter != tests2.get().end(); ++iter)
195*11be35a1SLionel Sambuc map2.insert(make_pair((*iter)->name(), *iter));
196*11be35a1SLionel Sambuc return std::equal(map1.begin(), map1.end(), map2.begin(),
197*11be35a1SLionel Sambuc compare_test_case);
198*11be35a1SLionel Sambuc }
199*11be35a1SLionel Sambuc
200*11be35a1SLionel Sambuc
201*11be35a1SLionel Sambuc } // anonymous namespace
202*11be35a1SLionel Sambuc
203*11be35a1SLionel Sambuc
204*11be35a1SLionel Sambuc /// Internal implementation of a test_program.
205*11be35a1SLionel Sambuc struct engine::test_program::impl {
206*11be35a1SLionel Sambuc /// Name of the test program interface.
207*11be35a1SLionel Sambuc std::string interface_name;
208*11be35a1SLionel Sambuc
209*11be35a1SLionel Sambuc /// Name of the test program binary relative to root.
210*11be35a1SLionel Sambuc fs::path binary;
211*11be35a1SLionel Sambuc
212*11be35a1SLionel Sambuc /// Root of the test suite containing the test program.
213*11be35a1SLionel Sambuc fs::path root;
214*11be35a1SLionel Sambuc
215*11be35a1SLionel Sambuc /// Name of the test suite this program belongs to.
216*11be35a1SLionel Sambuc std::string test_suite_name;
217*11be35a1SLionel Sambuc
218*11be35a1SLionel Sambuc /// Metadata of the test program.
219*11be35a1SLionel Sambuc metadata md;
220*11be35a1SLionel Sambuc
221*11be35a1SLionel Sambuc /// List of test cases in the test program; lazily initialized.
222*11be35a1SLionel Sambuc optional< test_cases_vector > test_cases;
223*11be35a1SLionel Sambuc
224*11be35a1SLionel Sambuc /// Constructor.
225*11be35a1SLionel Sambuc ///
226*11be35a1SLionel Sambuc /// \param interface_name_ Name of the test program interface.
227*11be35a1SLionel Sambuc /// \param binary_ The name of the test program binary relative to root_.
228*11be35a1SLionel Sambuc /// \param root_ The root of the test suite containing the test program.
229*11be35a1SLionel Sambuc /// \param test_suite_name_ The name of the test suite this program
230*11be35a1SLionel Sambuc /// belongs to.
231*11be35a1SLionel Sambuc /// \param md_ Metadata of the test program.
implengine::test_program::impl232*11be35a1SLionel Sambuc impl(const std::string& interface_name_, const fs::path& binary_,
233*11be35a1SLionel Sambuc const fs::path& root_, const std::string& test_suite_name_,
234*11be35a1SLionel Sambuc const metadata& md_) :
235*11be35a1SLionel Sambuc interface_name(interface_name_),
236*11be35a1SLionel Sambuc binary(binary_),
237*11be35a1SLionel Sambuc root(root_),
238*11be35a1SLionel Sambuc test_suite_name(test_suite_name_),
239*11be35a1SLionel Sambuc md(md_)
240*11be35a1SLionel Sambuc {
241*11be35a1SLionel Sambuc PRE_MSG(!binary.is_absolute(),
242*11be35a1SLionel Sambuc F("The program '%s' must be relative to the root of the test "
243*11be35a1SLionel Sambuc "suite '%s'") % binary % root);
244*11be35a1SLionel Sambuc }
245*11be35a1SLionel Sambuc
246*11be35a1SLionel Sambuc /// Equality comparator.
247*11be35a1SLionel Sambuc ///
248*11be35a1SLionel Sambuc /// \param other The other object to compare this one to.
249*11be35a1SLionel Sambuc ///
250*11be35a1SLionel Sambuc /// \return True if this object and other are equal; false otherwise.
251*11be35a1SLionel Sambuc bool
operator ==engine::test_program::impl252*11be35a1SLionel Sambuc operator==(const impl& other) const
253*11be35a1SLionel Sambuc {
254*11be35a1SLionel Sambuc return (interface_name == other.interface_name &&
255*11be35a1SLionel Sambuc binary == other.binary &&
256*11be35a1SLionel Sambuc root == other.root &&
257*11be35a1SLionel Sambuc test_suite_name == other.test_suite_name &&
258*11be35a1SLionel Sambuc md == other.md &&
259*11be35a1SLionel Sambuc compare_test_cases(test_cases, other.test_cases));
260*11be35a1SLionel Sambuc }
261*11be35a1SLionel Sambuc };
262*11be35a1SLionel Sambuc
263*11be35a1SLionel Sambuc
264*11be35a1SLionel Sambuc /// Constructs a new test program.
265*11be35a1SLionel Sambuc ///
266*11be35a1SLionel Sambuc /// \param interface_name_ Name of the test program interface.
267*11be35a1SLionel Sambuc /// \param binary_ The name of the test program binary relative to root_.
268*11be35a1SLionel Sambuc /// \param root_ The root of the test suite containing the test program.
269*11be35a1SLionel Sambuc /// \param test_suite_name_ The name of the test suite this program belongs to.
270*11be35a1SLionel Sambuc /// \param md_ Metadata of the test program.
test_program(const std::string & interface_name_,const fs::path & binary_,const fs::path & root_,const std::string & test_suite_name_,const metadata & md_)271*11be35a1SLionel Sambuc engine::test_program::test_program(const std::string& interface_name_,
272*11be35a1SLionel Sambuc const fs::path& binary_,
273*11be35a1SLionel Sambuc const fs::path& root_,
274*11be35a1SLionel Sambuc const std::string& test_suite_name_,
275*11be35a1SLionel Sambuc const metadata& md_) :
276*11be35a1SLionel Sambuc _pimpl(new impl(interface_name_, binary_, root_, test_suite_name_, md_))
277*11be35a1SLionel Sambuc {
278*11be35a1SLionel Sambuc }
279*11be35a1SLionel Sambuc
280*11be35a1SLionel Sambuc
281*11be35a1SLionel Sambuc /// Destroys a test program.
~test_program(void)282*11be35a1SLionel Sambuc engine::test_program::~test_program(void)
283*11be35a1SLionel Sambuc {
284*11be35a1SLionel Sambuc }
285*11be35a1SLionel Sambuc
286*11be35a1SLionel Sambuc
287*11be35a1SLionel Sambuc /// Gets the name of the test program interface.
288*11be35a1SLionel Sambuc ///
289*11be35a1SLionel Sambuc /// \return An interface name.
290*11be35a1SLionel Sambuc const std::string&
interface_name(void) const291*11be35a1SLionel Sambuc engine::test_program::interface_name(void) const
292*11be35a1SLionel Sambuc {
293*11be35a1SLionel Sambuc return _pimpl->interface_name;
294*11be35a1SLionel Sambuc }
295*11be35a1SLionel Sambuc
296*11be35a1SLionel Sambuc
297*11be35a1SLionel Sambuc /// Gets the path to the test program relative to the root of the test suite.
298*11be35a1SLionel Sambuc ///
299*11be35a1SLionel Sambuc /// \return The relative path to the test program binary.
300*11be35a1SLionel Sambuc const fs::path&
relative_path(void) const301*11be35a1SLionel Sambuc engine::test_program::relative_path(void) const
302*11be35a1SLionel Sambuc {
303*11be35a1SLionel Sambuc return _pimpl->binary;
304*11be35a1SLionel Sambuc }
305*11be35a1SLionel Sambuc
306*11be35a1SLionel Sambuc
307*11be35a1SLionel Sambuc /// Gets the absolute path to the test program.
308*11be35a1SLionel Sambuc ///
309*11be35a1SLionel Sambuc /// \return The absolute path to the test program binary.
310*11be35a1SLionel Sambuc const fs::path
absolute_path(void) const311*11be35a1SLionel Sambuc engine::test_program::absolute_path(void) const
312*11be35a1SLionel Sambuc {
313*11be35a1SLionel Sambuc const fs::path full_path = _pimpl->root / _pimpl->binary;
314*11be35a1SLionel Sambuc return full_path.is_absolute() ? full_path : full_path.to_absolute();
315*11be35a1SLionel Sambuc }
316*11be35a1SLionel Sambuc
317*11be35a1SLionel Sambuc
318*11be35a1SLionel Sambuc /// Gets the root of the test suite containing this test program.
319*11be35a1SLionel Sambuc ///
320*11be35a1SLionel Sambuc /// \return The path to the root of the test suite.
321*11be35a1SLionel Sambuc const fs::path&
root(void) const322*11be35a1SLionel Sambuc engine::test_program::root(void) const
323*11be35a1SLionel Sambuc {
324*11be35a1SLionel Sambuc return _pimpl->root;
325*11be35a1SLionel Sambuc }
326*11be35a1SLionel Sambuc
327*11be35a1SLionel Sambuc
328*11be35a1SLionel Sambuc /// Gets the name of the test suite containing this test program.
329*11be35a1SLionel Sambuc ///
330*11be35a1SLionel Sambuc /// \return The name of the test suite.
331*11be35a1SLionel Sambuc const std::string&
test_suite_name(void) const332*11be35a1SLionel Sambuc engine::test_program::test_suite_name(void) const
333*11be35a1SLionel Sambuc {
334*11be35a1SLionel Sambuc return _pimpl->test_suite_name;
335*11be35a1SLionel Sambuc }
336*11be35a1SLionel Sambuc
337*11be35a1SLionel Sambuc
338*11be35a1SLionel Sambuc /// Gets the metadata of the test program.
339*11be35a1SLionel Sambuc ///
340*11be35a1SLionel Sambuc /// \return The metadata.
341*11be35a1SLionel Sambuc const engine::metadata&
get_metadata(void) const342*11be35a1SLionel Sambuc engine::test_program::get_metadata(void) const
343*11be35a1SLionel Sambuc {
344*11be35a1SLionel Sambuc return _pimpl->md;
345*11be35a1SLionel Sambuc }
346*11be35a1SLionel Sambuc
347*11be35a1SLionel Sambuc
348*11be35a1SLionel Sambuc /// Gets a test case by its name.
349*11be35a1SLionel Sambuc ///
350*11be35a1SLionel Sambuc /// \param name The name of the test case to locate.
351*11be35a1SLionel Sambuc ///
352*11be35a1SLionel Sambuc /// \return The requested test case.
353*11be35a1SLionel Sambuc ///
354*11be35a1SLionel Sambuc /// \throw not_found_error If the specified test case is not in the test
355*11be35a1SLionel Sambuc /// program.
356*11be35a1SLionel Sambuc const engine::test_case_ptr&
find(const std::string & name) const357*11be35a1SLionel Sambuc engine::test_program::find(const std::string& name) const
358*11be35a1SLionel Sambuc {
359*11be35a1SLionel Sambuc // TODO(jmmv): Should use a test_cases_map instead of a vector to optimize
360*11be35a1SLionel Sambuc // lookups.
361*11be35a1SLionel Sambuc const test_cases_vector& tcs = test_cases();
362*11be35a1SLionel Sambuc for (test_cases_vector::const_iterator iter = tcs.begin();
363*11be35a1SLionel Sambuc iter != tcs.end(); iter++) {
364*11be35a1SLionel Sambuc if ((*iter)->name() == name)
365*11be35a1SLionel Sambuc return *iter;
366*11be35a1SLionel Sambuc }
367*11be35a1SLionel Sambuc throw not_found_error(F("Unknown test case %s in test program %s") % name %
368*11be35a1SLionel Sambuc relative_path());
369*11be35a1SLionel Sambuc }
370*11be35a1SLionel Sambuc
371*11be35a1SLionel Sambuc
372*11be35a1SLionel Sambuc /// Gets the list of test cases from the test program.
373*11be35a1SLionel Sambuc ///
374*11be35a1SLionel Sambuc /// Note that this operation may be expensive because it may lazily load the
375*11be35a1SLionel Sambuc /// test cases list from the test program. Errors during the processing of the
376*11be35a1SLionel Sambuc /// test case list are represented as a single test case describing the failure.
377*11be35a1SLionel Sambuc ///
378*11be35a1SLionel Sambuc /// \return The list of test cases provided by the test program.
379*11be35a1SLionel Sambuc const engine::test_cases_vector&
test_cases(void) const380*11be35a1SLionel Sambuc engine::test_program::test_cases(void) const
381*11be35a1SLionel Sambuc {
382*11be35a1SLionel Sambuc if (!_pimpl->test_cases) {
383*11be35a1SLionel Sambuc try {
384*11be35a1SLionel Sambuc _pimpl->test_cases = load_test_cases(*this);
385*11be35a1SLionel Sambuc } catch (const std::runtime_error& e) {
386*11be35a1SLionel Sambuc // TODO(jmmv): This is a very ugly workaround for the fact that we
387*11be35a1SLionel Sambuc // cannot report failures at the test-program level. We should
388*11be35a1SLionel Sambuc // either address this, or move this reporting to the testers
389*11be35a1SLionel Sambuc // themselves.
390*11be35a1SLionel Sambuc LW(F("Failed to load test cases list: %s") % e.what());
391*11be35a1SLionel Sambuc engine::test_cases_vector fake_test_cases;
392*11be35a1SLionel Sambuc fake_test_cases.push_back(test_case_ptr(new test_case(
393*11be35a1SLionel Sambuc _pimpl->interface_name, *this, "__test_cases_list__",
394*11be35a1SLionel Sambuc "Represents the correct processing of the test cases list",
395*11be35a1SLionel Sambuc test_result(engine::test_result::broken, e.what()))));
396*11be35a1SLionel Sambuc _pimpl->test_cases = fake_test_cases;
397*11be35a1SLionel Sambuc }
398*11be35a1SLionel Sambuc }
399*11be35a1SLionel Sambuc return _pimpl->test_cases.get();
400*11be35a1SLionel Sambuc }
401*11be35a1SLionel Sambuc
402*11be35a1SLionel Sambuc
403*11be35a1SLionel Sambuc /// Sets the collection of test cases included in this test program.
404*11be35a1SLionel Sambuc ///
405*11be35a1SLionel Sambuc /// This function is provided so that when we load test programs from the
406*11be35a1SLionel Sambuc /// database we can populate them with the test cases they include. We don't
407*11be35a1SLionel Sambuc /// want such test programs to be executed to gather this information.
408*11be35a1SLionel Sambuc ///
409*11be35a1SLionel Sambuc /// We cannot provide this collection of tests in the constructor of the test
410*11be35a1SLionel Sambuc /// program because the test cases have to point to their test programs.
411*11be35a1SLionel Sambuc ///
412*11be35a1SLionel Sambuc /// \pre The test program must not have attempted to load its test cases yet.
413*11be35a1SLionel Sambuc /// I.e. test_cases() has not been called.
414*11be35a1SLionel Sambuc ///
415*11be35a1SLionel Sambuc /// \param test_cases_ The test cases to add to this test program.
416*11be35a1SLionel Sambuc void
set_test_cases(const test_cases_vector & test_cases_)417*11be35a1SLionel Sambuc engine::test_program::set_test_cases(const test_cases_vector& test_cases_)
418*11be35a1SLionel Sambuc {
419*11be35a1SLionel Sambuc PRE(!_pimpl->test_cases);
420*11be35a1SLionel Sambuc _pimpl->test_cases = test_cases_;
421*11be35a1SLionel Sambuc }
422*11be35a1SLionel Sambuc
423*11be35a1SLionel Sambuc
424*11be35a1SLionel Sambuc /// Equality comparator.
425*11be35a1SLionel Sambuc ///
426*11be35a1SLionel Sambuc /// \param other The other object to compare this one to.
427*11be35a1SLionel Sambuc ///
428*11be35a1SLionel Sambuc /// \return True if this object and other are equal; false otherwise.
429*11be35a1SLionel Sambuc bool
operator ==(const test_program & other) const430*11be35a1SLionel Sambuc engine::test_program::operator==(const test_program& other) const
431*11be35a1SLionel Sambuc {
432*11be35a1SLionel Sambuc return _pimpl == other._pimpl || *_pimpl == *other._pimpl;
433*11be35a1SLionel Sambuc }
434*11be35a1SLionel Sambuc
435*11be35a1SLionel Sambuc
436*11be35a1SLionel Sambuc /// Inequality comparator.
437*11be35a1SLionel Sambuc ///
438*11be35a1SLionel Sambuc /// \param other The other object to compare this one to.
439*11be35a1SLionel Sambuc ///
440*11be35a1SLionel Sambuc /// \return True if this object and other are different; false otherwise.
441*11be35a1SLionel Sambuc bool
operator !=(const test_program & other) const442*11be35a1SLionel Sambuc engine::test_program::operator!=(const test_program& other) const
443*11be35a1SLionel Sambuc {
444*11be35a1SLionel Sambuc return !(*this == other);
445*11be35a1SLionel Sambuc }
446*11be35a1SLionel Sambuc
447*11be35a1SLionel Sambuc
448*11be35a1SLionel Sambuc /// Injects the object into a stream.
449*11be35a1SLionel Sambuc ///
450*11be35a1SLionel Sambuc /// \param output The stream into which to inject the object.
451*11be35a1SLionel Sambuc /// \param object The object to format.
452*11be35a1SLionel Sambuc ///
453*11be35a1SLionel Sambuc /// \return The output stream.
454*11be35a1SLionel Sambuc std::ostream&
operator <<(std::ostream & output,const test_cases_vector & object)455*11be35a1SLionel Sambuc engine::operator<<(std::ostream& output, const test_cases_vector& object)
456*11be35a1SLionel Sambuc {
457*11be35a1SLionel Sambuc output << "[";
458*11be35a1SLionel Sambuc for (test_cases_vector::size_type i = 0; i < object.size(); ++i) {
459*11be35a1SLionel Sambuc if (i != 0)
460*11be35a1SLionel Sambuc output << ", ";
461*11be35a1SLionel Sambuc output << *object[i];
462*11be35a1SLionel Sambuc }
463*11be35a1SLionel Sambuc output << "]";
464*11be35a1SLionel Sambuc return output;
465*11be35a1SLionel Sambuc }
466*11be35a1SLionel Sambuc
467*11be35a1SLionel Sambuc
468*11be35a1SLionel Sambuc /// Injects the object into a stream.
469*11be35a1SLionel Sambuc ///
470*11be35a1SLionel Sambuc /// \param output The stream into which to inject the object.
471*11be35a1SLionel Sambuc /// \param object The object to format.
472*11be35a1SLionel Sambuc ///
473*11be35a1SLionel Sambuc /// \return The output stream.
474*11be35a1SLionel Sambuc std::ostream&
operator <<(std::ostream & output,const test_program & object)475*11be35a1SLionel Sambuc engine::operator<<(std::ostream& output, const test_program& object)
476*11be35a1SLionel Sambuc {
477*11be35a1SLionel Sambuc output << F("test_program{interface=%s, binary=%s, root=%s, test_suite=%s, "
478*11be35a1SLionel Sambuc "metadata=%s, test_cases=%s}")
479*11be35a1SLionel Sambuc % text::quote(object.interface_name(), '\'')
480*11be35a1SLionel Sambuc % text::quote(object.relative_path().str(), '\'')
481*11be35a1SLionel Sambuc % text::quote(object.root().str(), '\'')
482*11be35a1SLionel Sambuc % text::quote(object.test_suite_name(), '\'')
483*11be35a1SLionel Sambuc % object.get_metadata()
484*11be35a1SLionel Sambuc % object.test_cases();
485*11be35a1SLionel Sambuc return output;
486*11be35a1SLionel Sambuc }
487