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/kyuafile.hpp"
30*11be35a1SLionel Sambuc
31*11be35a1SLionel Sambuc #include <algorithm>
32*11be35a1SLionel Sambuc #include <iterator>
33*11be35a1SLionel Sambuc #include <stdexcept>
34*11be35a1SLionel Sambuc
35*11be35a1SLionel Sambuc #include <lutok/exceptions.hpp>
36*11be35a1SLionel Sambuc #include <lutok/operations.hpp>
37*11be35a1SLionel Sambuc #include <lutok/stack_cleaner.hpp>
38*11be35a1SLionel Sambuc #include <lutok/state.ipp>
39*11be35a1SLionel Sambuc
40*11be35a1SLionel Sambuc #include "engine/exceptions.hpp"
41*11be35a1SLionel Sambuc #include "engine/test_program.hpp"
42*11be35a1SLionel Sambuc #include "engine/testers.hpp"
43*11be35a1SLionel Sambuc #include "utils/datetime.hpp"
44*11be35a1SLionel Sambuc #include "utils/format/macros.hpp"
45*11be35a1SLionel Sambuc #include "utils/fs/lua_module.hpp"
46*11be35a1SLionel Sambuc #include "utils/fs/operations.hpp"
47*11be35a1SLionel Sambuc #include "utils/logging/macros.hpp"
48*11be35a1SLionel Sambuc #include "utils/noncopyable.hpp"
49*11be35a1SLionel Sambuc #include "utils/optional.ipp"
50*11be35a1SLionel Sambuc #include "utils/sanity.hpp"
51*11be35a1SLionel Sambuc
52*11be35a1SLionel Sambuc namespace datetime = utils::datetime;
53*11be35a1SLionel Sambuc namespace fs = utils::fs;
54*11be35a1SLionel Sambuc
55*11be35a1SLionel Sambuc using utils::none;
56*11be35a1SLionel Sambuc using utils::optional;
57*11be35a1SLionel Sambuc
58*11be35a1SLionel Sambuc
59*11be35a1SLionel Sambuc // History of Kyuafile file versions:
60*11be35a1SLionel Sambuc //
61*11be35a1SLionel Sambuc // 2 - Changed the syntax() call to take only a version number, instead of the
62*11be35a1SLionel Sambuc // word 'config' as the first argument and the version as the second one.
63*11be35a1SLionel Sambuc // Files now start with syntax(2) instead of syntax('kyuafile', 1).
64*11be35a1SLionel Sambuc //
65*11be35a1SLionel Sambuc // 1 - Initial version.
66*11be35a1SLionel Sambuc
67*11be35a1SLionel Sambuc
68*11be35a1SLionel Sambuc namespace {
69*11be35a1SLionel Sambuc
70*11be35a1SLionel Sambuc
71*11be35a1SLionel Sambuc static int lua_atf_test_program(lutok::state&);
72*11be35a1SLionel Sambuc static int lua_current_kyuafile(lutok::state&);
73*11be35a1SLionel Sambuc static int lua_include(lutok::state&);
74*11be35a1SLionel Sambuc static int lua_plain_test_program(lutok::state&);
75*11be35a1SLionel Sambuc static int lua_syntax(lutok::state&);
76*11be35a1SLionel Sambuc static int lua_test_suite(lutok::state&);
77*11be35a1SLionel Sambuc
78*11be35a1SLionel Sambuc
79*11be35a1SLionel Sambuc /// Concatenates two paths while avoiding paths to start with './'.
80*11be35a1SLionel Sambuc ///
81*11be35a1SLionel Sambuc /// \param root Path to the directory containing the file.
82*11be35a1SLionel Sambuc /// \param file Path to concatenate to root. Cannot be absolute.
83*11be35a1SLionel Sambuc ///
84*11be35a1SLionel Sambuc /// \return The concatenated path.
85*11be35a1SLionel Sambuc static fs::path
relativize(const fs::path & root,const fs::path & file)86*11be35a1SLionel Sambuc relativize(const fs::path& root, const fs::path& file)
87*11be35a1SLionel Sambuc {
88*11be35a1SLionel Sambuc PRE(!file.is_absolute());
89*11be35a1SLionel Sambuc
90*11be35a1SLionel Sambuc if (root == fs::path("."))
91*11be35a1SLionel Sambuc return file;
92*11be35a1SLionel Sambuc else
93*11be35a1SLionel Sambuc return root / file;
94*11be35a1SLionel Sambuc }
95*11be35a1SLionel Sambuc
96*11be35a1SLionel Sambuc
97*11be35a1SLionel Sambuc /// Implementation of a parser for Kyuafiles.
98*11be35a1SLionel Sambuc ///
99*11be35a1SLionel Sambuc /// The main purpose of having this as a class is to keep track of global state
100*11be35a1SLionel Sambuc /// within the Lua files and allowing the Lua callbacks to easily access such
101*11be35a1SLionel Sambuc /// data.
102*11be35a1SLionel Sambuc class parser : utils::noncopyable {
103*11be35a1SLionel Sambuc /// Lua state to parse a single Kyuafile file.
104*11be35a1SLionel Sambuc lutok::state _state;
105*11be35a1SLionel Sambuc
106*11be35a1SLionel Sambuc /// Root directory of the test suite represented by the Kyuafile.
107*11be35a1SLionel Sambuc const fs::path _source_root;
108*11be35a1SLionel Sambuc
109*11be35a1SLionel Sambuc /// Root directory of the test programs.
110*11be35a1SLionel Sambuc const fs::path _build_root;
111*11be35a1SLionel Sambuc
112*11be35a1SLionel Sambuc /// Name of the Kyuafile to load relative to _source_root.
113*11be35a1SLionel Sambuc const fs::path _relative_filename;
114*11be35a1SLionel Sambuc
115*11be35a1SLionel Sambuc /// Version of the Kyuafile file format requested by the parsed file.
116*11be35a1SLionel Sambuc ///
117*11be35a1SLionel Sambuc /// This is set once the Kyuafile invokes the syntax() call.
118*11be35a1SLionel Sambuc optional< int > _version;
119*11be35a1SLionel Sambuc
120*11be35a1SLionel Sambuc /// Name of the test suite defined by the Kyuafile.
121*11be35a1SLionel Sambuc ///
122*11be35a1SLionel Sambuc /// This is set once the Kyuafile invokes the test_suite() call.
123*11be35a1SLionel Sambuc optional< std::string > _test_suite;
124*11be35a1SLionel Sambuc
125*11be35a1SLionel Sambuc /// Collection of test programs defined by the Kyuafile.
126*11be35a1SLionel Sambuc ///
127*11be35a1SLionel Sambuc /// This acts as an accumulator for all the *_test_program() calls within
128*11be35a1SLionel Sambuc /// the Kyuafile.
129*11be35a1SLionel Sambuc engine::test_programs_vector _test_programs;
130*11be35a1SLionel Sambuc
131*11be35a1SLionel Sambuc public:
132*11be35a1SLionel Sambuc /// Initializes the parser and the Lua state.
133*11be35a1SLionel Sambuc ///
134*11be35a1SLionel Sambuc /// \param source_root_ The root directory of the test suite represented by
135*11be35a1SLionel Sambuc /// the Kyuafile.
136*11be35a1SLionel Sambuc /// \param build_root_ The root directory of the test programs.
137*11be35a1SLionel Sambuc /// \param relative_filename_ Name of the Kyuafile to load relative to
138*11be35a1SLionel Sambuc /// source_root_.
parser(const fs::path & source_root_,const fs::path & build_root_,const fs::path & relative_filename_)139*11be35a1SLionel Sambuc parser(const fs::path& source_root_, const fs::path& build_root_,
140*11be35a1SLionel Sambuc const fs::path& relative_filename_) :
141*11be35a1SLionel Sambuc _source_root(source_root_), _build_root(build_root_),
142*11be35a1SLionel Sambuc _relative_filename(relative_filename_)
143*11be35a1SLionel Sambuc {
144*11be35a1SLionel Sambuc lutok::stack_cleaner cleaner(_state);
145*11be35a1SLionel Sambuc
146*11be35a1SLionel Sambuc _state.push_cxx_function(lua_syntax);
147*11be35a1SLionel Sambuc _state.set_global("syntax");
148*11be35a1SLionel Sambuc *_state.new_userdata< parser* >() = this;
149*11be35a1SLionel Sambuc _state.set_global("_parser");
150*11be35a1SLionel Sambuc
151*11be35a1SLionel Sambuc _state.push_cxx_function(lua_atf_test_program);
152*11be35a1SLionel Sambuc _state.set_global("atf_test_program");
153*11be35a1SLionel Sambuc _state.push_cxx_function(lua_current_kyuafile);
154*11be35a1SLionel Sambuc _state.set_global("current_kyuafile");
155*11be35a1SLionel Sambuc _state.push_cxx_function(lua_include);
156*11be35a1SLionel Sambuc _state.set_global("include");
157*11be35a1SLionel Sambuc _state.push_cxx_function(lua_plain_test_program);
158*11be35a1SLionel Sambuc _state.set_global("plain_test_program");
159*11be35a1SLionel Sambuc _state.push_cxx_function(lua_test_suite);
160*11be35a1SLionel Sambuc _state.set_global("test_suite");
161*11be35a1SLionel Sambuc
162*11be35a1SLionel Sambuc _state.open_base();
163*11be35a1SLionel Sambuc _state.open_string();
164*11be35a1SLionel Sambuc _state.open_table();
165*11be35a1SLionel Sambuc fs::open_fs(_state);
166*11be35a1SLionel Sambuc }
167*11be35a1SLionel Sambuc
168*11be35a1SLionel Sambuc /// Destructor.
~parser(void)169*11be35a1SLionel Sambuc ~parser(void)
170*11be35a1SLionel Sambuc {
171*11be35a1SLionel Sambuc }
172*11be35a1SLionel Sambuc
173*11be35a1SLionel Sambuc /// Gets the parser object associated to a Lua state.
174*11be35a1SLionel Sambuc ///
175*11be35a1SLionel Sambuc /// \param state The Lua state from which to obtain the parser object.
176*11be35a1SLionel Sambuc ///
177*11be35a1SLionel Sambuc /// \return A pointer to the parser.
178*11be35a1SLionel Sambuc static parser*
get_from_state(lutok::state & state)179*11be35a1SLionel Sambuc get_from_state(lutok::state& state)
180*11be35a1SLionel Sambuc {
181*11be35a1SLionel Sambuc lutok::stack_cleaner cleaner(state);
182*11be35a1SLionel Sambuc state.get_global("_parser");
183*11be35a1SLionel Sambuc return *state.to_userdata< parser* >();
184*11be35a1SLionel Sambuc }
185*11be35a1SLionel Sambuc
186*11be35a1SLionel Sambuc /// Callback for the Kyuafile current_kyuafile() function.
187*11be35a1SLionel Sambuc ///
188*11be35a1SLionel Sambuc /// \return Returns the absolute path to the current Kyuafile.
189*11be35a1SLionel Sambuc fs::path
callback_current_kyuafile(void) const190*11be35a1SLionel Sambuc callback_current_kyuafile(void) const
191*11be35a1SLionel Sambuc {
192*11be35a1SLionel Sambuc const fs::path file = relativize(_source_root, _relative_filename);
193*11be35a1SLionel Sambuc if (file.is_absolute())
194*11be35a1SLionel Sambuc return file;
195*11be35a1SLionel Sambuc else
196*11be35a1SLionel Sambuc return file.to_absolute();
197*11be35a1SLionel Sambuc }
198*11be35a1SLionel Sambuc
199*11be35a1SLionel Sambuc /// Callback for the Kyuafile include() function.
200*11be35a1SLionel Sambuc ///
201*11be35a1SLionel Sambuc /// \post _test_programs is extended with the the test programs defined by
202*11be35a1SLionel Sambuc /// the included file.
203*11be35a1SLionel Sambuc ///
204*11be35a1SLionel Sambuc /// \param raw_file Path to the file to include.
205*11be35a1SLionel Sambuc void
callback_include(const fs::path & raw_file)206*11be35a1SLionel Sambuc callback_include(const fs::path& raw_file)
207*11be35a1SLionel Sambuc {
208*11be35a1SLionel Sambuc const fs::path file = relativize(_relative_filename.branch_path(),
209*11be35a1SLionel Sambuc raw_file);
210*11be35a1SLionel Sambuc const engine::test_programs_vector subtps =
211*11be35a1SLionel Sambuc parser(_source_root, _build_root, file).parse();
212*11be35a1SLionel Sambuc
213*11be35a1SLionel Sambuc std::copy(subtps.begin(), subtps.end(),
214*11be35a1SLionel Sambuc std::back_inserter(_test_programs));
215*11be35a1SLionel Sambuc }
216*11be35a1SLionel Sambuc
217*11be35a1SLionel Sambuc /// Callback for the Kyuafile syntax() function.
218*11be35a1SLionel Sambuc ///
219*11be35a1SLionel Sambuc /// \post _version is set to the requested version.
220*11be35a1SLionel Sambuc ///
221*11be35a1SLionel Sambuc /// \param version Version of the Kyuafile syntax requested by the file.
222*11be35a1SLionel Sambuc ///
223*11be35a1SLionel Sambuc /// \throw std::runtime_error If the format or the version are invalid, or
224*11be35a1SLionel Sambuc /// if syntax() has already been called.
225*11be35a1SLionel Sambuc void
callback_syntax(const int version)226*11be35a1SLionel Sambuc callback_syntax(const int version)
227*11be35a1SLionel Sambuc {
228*11be35a1SLionel Sambuc if (_version)
229*11be35a1SLionel Sambuc throw std::runtime_error("Can only call syntax() once");
230*11be35a1SLionel Sambuc
231*11be35a1SLionel Sambuc if (version < 1 || version > 2)
232*11be35a1SLionel Sambuc throw std::runtime_error(F("Unsupported file version %s") %
233*11be35a1SLionel Sambuc version);
234*11be35a1SLionel Sambuc
235*11be35a1SLionel Sambuc _version = utils::make_optional(version);
236*11be35a1SLionel Sambuc }
237*11be35a1SLionel Sambuc
238*11be35a1SLionel Sambuc /// Callback for the various Kyuafile *_test_program() functions.
239*11be35a1SLionel Sambuc ///
240*11be35a1SLionel Sambuc /// \post _test_programs is extended to include the newly defined test
241*11be35a1SLionel Sambuc /// program.
242*11be35a1SLionel Sambuc ///
243*11be35a1SLionel Sambuc /// \param interface Name of the test program interface.
244*11be35a1SLionel Sambuc /// \param raw_path Path to the test program, relative to the Kyuafile.
245*11be35a1SLionel Sambuc /// This has to be adjusted according to the relative location of this
246*11be35a1SLionel Sambuc /// Kyuafile to _source_root.
247*11be35a1SLionel Sambuc /// \param test_suite_override Name of the test suite this test program
248*11be35a1SLionel Sambuc /// belongs to, if explicitly defined at the test program level.
249*11be35a1SLionel Sambuc /// \param metadata Metadata variables passed to the test program.
250*11be35a1SLionel Sambuc ///
251*11be35a1SLionel Sambuc /// \throw std::runtime_error If the test program definition is invalid or
252*11be35a1SLionel Sambuc /// if the test program does not exist.
253*11be35a1SLionel Sambuc void
callback_test_program(const std::string & interface,const fs::path & raw_path,const std::string & test_suite_override,const engine::metadata & metadata)254*11be35a1SLionel Sambuc callback_test_program(const std::string& interface,
255*11be35a1SLionel Sambuc const fs::path& raw_path,
256*11be35a1SLionel Sambuc const std::string& test_suite_override,
257*11be35a1SLionel Sambuc const engine::metadata& metadata)
258*11be35a1SLionel Sambuc {
259*11be35a1SLionel Sambuc if (raw_path.is_absolute())
260*11be35a1SLionel Sambuc throw std::runtime_error(F("Got unexpected absolute path for test "
261*11be35a1SLionel Sambuc "program '%s'") % raw_path);
262*11be35a1SLionel Sambuc else if (raw_path.str() != raw_path.leaf_name())
263*11be35a1SLionel Sambuc throw std::runtime_error(F("Test program '%s' cannot contain path "
264*11be35a1SLionel Sambuc "components") % raw_path);
265*11be35a1SLionel Sambuc
266*11be35a1SLionel Sambuc const fs::path path = relativize(_relative_filename.branch_path(),
267*11be35a1SLionel Sambuc raw_path);
268*11be35a1SLionel Sambuc
269*11be35a1SLionel Sambuc if (!fs::exists(_build_root / path))
270*11be35a1SLionel Sambuc throw std::runtime_error(F("Non-existent test program '%s'") %
271*11be35a1SLionel Sambuc path);
272*11be35a1SLionel Sambuc
273*11be35a1SLionel Sambuc const std::string test_suite = test_suite_override.empty()
274*11be35a1SLionel Sambuc ? _test_suite.get() : test_suite_override;
275*11be35a1SLionel Sambuc _test_programs.push_back(engine::test_program_ptr(
276*11be35a1SLionel Sambuc new engine::test_program(interface, path, _build_root, test_suite,
277*11be35a1SLionel Sambuc metadata)));
278*11be35a1SLionel Sambuc }
279*11be35a1SLionel Sambuc
280*11be35a1SLionel Sambuc /// Callback for the Kyuafile test_suite() function.
281*11be35a1SLionel Sambuc ///
282*11be35a1SLionel Sambuc /// \post _version is set to the requested version.
283*11be35a1SLionel Sambuc ///
284*11be35a1SLionel Sambuc /// \param name Name of the test suite.
285*11be35a1SLionel Sambuc ///
286*11be35a1SLionel Sambuc /// \throw std::runtime_error If test_suite() has already been called.
287*11be35a1SLionel Sambuc void
callback_test_suite(const std::string & name)288*11be35a1SLionel Sambuc callback_test_suite(const std::string& name)
289*11be35a1SLionel Sambuc {
290*11be35a1SLionel Sambuc if (_test_suite)
291*11be35a1SLionel Sambuc throw std::runtime_error("Can only call test_suite() once");
292*11be35a1SLionel Sambuc _test_suite = utils::make_optional(name);
293*11be35a1SLionel Sambuc }
294*11be35a1SLionel Sambuc
295*11be35a1SLionel Sambuc /// Parses the Kyuafile.
296*11be35a1SLionel Sambuc ///
297*11be35a1SLionel Sambuc /// \pre Can only be invoked once.
298*11be35a1SLionel Sambuc ///
299*11be35a1SLionel Sambuc /// \return The collection of test programs defined by the Kyuafile.
300*11be35a1SLionel Sambuc ///
301*11be35a1SLionel Sambuc /// \throw load_error If there is any problem parsing the file.
302*11be35a1SLionel Sambuc const engine::test_programs_vector&
parse(void)303*11be35a1SLionel Sambuc parse(void)
304*11be35a1SLionel Sambuc {
305*11be35a1SLionel Sambuc PRE(_test_programs.empty());
306*11be35a1SLionel Sambuc
307*11be35a1SLionel Sambuc const fs::path load_path = relativize(_source_root, _relative_filename);
308*11be35a1SLionel Sambuc try {
309*11be35a1SLionel Sambuc lutok::do_file(_state, load_path.str());
310*11be35a1SLionel Sambuc } catch (const std::runtime_error& e) {
311*11be35a1SLionel Sambuc // It is tempting to think that all of our various auxiliary
312*11be35a1SLionel Sambuc // functions above could raise load_error by themselves thus making
313*11be35a1SLionel Sambuc // this exception rewriting here unnecessary. Howver, that would
314*11be35a1SLionel Sambuc // not work because the helper functions above are executed within a
315*11be35a1SLionel Sambuc // Lua context, and we lose their type when they are propagated out
316*11be35a1SLionel Sambuc // of it.
317*11be35a1SLionel Sambuc throw engine::load_error(load_path, e.what());
318*11be35a1SLionel Sambuc }
319*11be35a1SLionel Sambuc
320*11be35a1SLionel Sambuc if (!_version)
321*11be35a1SLionel Sambuc throw engine::load_error(load_path, "syntax() never called");
322*11be35a1SLionel Sambuc
323*11be35a1SLionel Sambuc return _test_programs;
324*11be35a1SLionel Sambuc }
325*11be35a1SLionel Sambuc };
326*11be35a1SLionel Sambuc
327*11be35a1SLionel Sambuc
328*11be35a1SLionel Sambuc /// Gets a string field from a Lua table.
329*11be35a1SLionel Sambuc ///
330*11be35a1SLionel Sambuc /// \pre state(-1) contains a table.
331*11be35a1SLionel Sambuc ///
332*11be35a1SLionel Sambuc /// \param state The Lua state.
333*11be35a1SLionel Sambuc /// \param field The name of the field to query.
334*11be35a1SLionel Sambuc /// \param error The error message to raise when an error condition is
335*11be35a1SLionel Sambuc /// encoutered.
336*11be35a1SLionel Sambuc ///
337*11be35a1SLionel Sambuc /// \return The string value from the table.
338*11be35a1SLionel Sambuc ///
339*11be35a1SLionel Sambuc /// \throw std::runtime_error If there is any problem accessing the table.
340*11be35a1SLionel Sambuc static inline std::string
get_table_string(lutok::state & state,const char * field,const std::string & error)341*11be35a1SLionel Sambuc get_table_string(lutok::state& state, const char* field,
342*11be35a1SLionel Sambuc const std::string& error)
343*11be35a1SLionel Sambuc {
344*11be35a1SLionel Sambuc PRE(state.is_table());
345*11be35a1SLionel Sambuc
346*11be35a1SLionel Sambuc lutok::stack_cleaner cleaner(state);
347*11be35a1SLionel Sambuc
348*11be35a1SLionel Sambuc state.push_string(field);
349*11be35a1SLionel Sambuc state.get_table();
350*11be35a1SLionel Sambuc if (!state.is_string())
351*11be35a1SLionel Sambuc throw std::runtime_error(error);
352*11be35a1SLionel Sambuc return state.to_string();
353*11be35a1SLionel Sambuc }
354*11be35a1SLionel Sambuc
355*11be35a1SLionel Sambuc
356*11be35a1SLionel Sambuc /// Checks if the given interface name is valid.
357*11be35a1SLionel Sambuc ///
358*11be35a1SLionel Sambuc /// \param interface The name of the interface to validate.
359*11be35a1SLionel Sambuc ///
360*11be35a1SLionel Sambuc /// \throw std::runtime_error If the given interface is not supported.
361*11be35a1SLionel Sambuc static void
ensure_valid_interface(const std::string & interface)362*11be35a1SLionel Sambuc ensure_valid_interface(const std::string& interface)
363*11be35a1SLionel Sambuc {
364*11be35a1SLionel Sambuc try {
365*11be35a1SLionel Sambuc (void)engine::tester_path(interface);
366*11be35a1SLionel Sambuc } catch (const engine::error& e) {
367*11be35a1SLionel Sambuc throw std::runtime_error(F("Unsupported test interface '%s'") %
368*11be35a1SLionel Sambuc interface);
369*11be35a1SLionel Sambuc }
370*11be35a1SLionel Sambuc }
371*11be35a1SLionel Sambuc
372*11be35a1SLionel Sambuc
373*11be35a1SLionel Sambuc /// Glue to invoke parser::callback_test_program() from Lua.
374*11be35a1SLionel Sambuc ///
375*11be35a1SLionel Sambuc /// This is a helper function for the various *_test_program() calls, as they
376*11be35a1SLionel Sambuc /// only differ in the interface of the defined test program.
377*11be35a1SLionel Sambuc ///
378*11be35a1SLionel Sambuc /// \pre state(-1) A table with the arguments that define the test program. The
379*11be35a1SLionel Sambuc /// special argument 'test_suite' provides an override to the global test suite
380*11be35a1SLionel Sambuc /// name. The rest of the arguments are part of the test program metadata.
381*11be35a1SLionel Sambuc ///
382*11be35a1SLionel Sambuc /// \param state The Lua state that executed the function.
383*11be35a1SLionel Sambuc /// \param interface Name of the test program interface.
384*11be35a1SLionel Sambuc ///
385*11be35a1SLionel Sambuc /// \return Number of return values left on the Lua stack.
386*11be35a1SLionel Sambuc ///
387*11be35a1SLionel Sambuc /// \throw std::runtime_error If the arguments to the function are invalid.
388*11be35a1SLionel Sambuc static int
lua_generic_test_program(lutok::state & state,const std::string & interface)389*11be35a1SLionel Sambuc lua_generic_test_program(lutok::state& state, const std::string& interface)
390*11be35a1SLionel Sambuc {
391*11be35a1SLionel Sambuc if (!state.is_table())
392*11be35a1SLionel Sambuc throw std::runtime_error(
393*11be35a1SLionel Sambuc F("%s_test_program expects a table of properties as its single "
394*11be35a1SLionel Sambuc "argument") % interface);
395*11be35a1SLionel Sambuc
396*11be35a1SLionel Sambuc ensure_valid_interface(interface);
397*11be35a1SLionel Sambuc
398*11be35a1SLionel Sambuc lutok::stack_cleaner cleaner(state);
399*11be35a1SLionel Sambuc
400*11be35a1SLionel Sambuc state.push_string("name");
401*11be35a1SLionel Sambuc state.get_table();
402*11be35a1SLionel Sambuc if (!state.is_string())
403*11be35a1SLionel Sambuc throw std::runtime_error("Test program name not defined or not a "
404*11be35a1SLionel Sambuc "string");
405*11be35a1SLionel Sambuc const fs::path path(state.to_string());
406*11be35a1SLionel Sambuc state.pop(1);
407*11be35a1SLionel Sambuc
408*11be35a1SLionel Sambuc state.push_string("test_suite");
409*11be35a1SLionel Sambuc state.get_table();
410*11be35a1SLionel Sambuc std::string test_suite;
411*11be35a1SLionel Sambuc if (state.is_nil()) {
412*11be35a1SLionel Sambuc // Leave empty to use the global test-suite value.
413*11be35a1SLionel Sambuc } else if (state.is_string()) {
414*11be35a1SLionel Sambuc test_suite = state.to_string();
415*11be35a1SLionel Sambuc } else {
416*11be35a1SLionel Sambuc throw std::runtime_error(F("Found non-string value in the test_suite "
417*11be35a1SLionel Sambuc "property of test program '%s'") % path);
418*11be35a1SLionel Sambuc }
419*11be35a1SLionel Sambuc state.pop(1);
420*11be35a1SLionel Sambuc
421*11be35a1SLionel Sambuc engine::metadata_builder mdbuilder;
422*11be35a1SLionel Sambuc state.push_nil();
423*11be35a1SLionel Sambuc while (state.next()) {
424*11be35a1SLionel Sambuc if (!state.is_string(-2))
425*11be35a1SLionel Sambuc throw std::runtime_error(F("Found non-string metadata property "
426*11be35a1SLionel Sambuc "name in test program '%s'") %
427*11be35a1SLionel Sambuc path);
428*11be35a1SLionel Sambuc const std::string property = state.to_string(-2);
429*11be35a1SLionel Sambuc
430*11be35a1SLionel Sambuc if (property != "name" && property != "test_suite") {
431*11be35a1SLionel Sambuc if (!state.is_number(-1) && !state.is_string(-1))
432*11be35a1SLionel Sambuc throw std::runtime_error(
433*11be35a1SLionel Sambuc F("Metadata property '%s' in test program '%s' cannot be "
434*11be35a1SLionel Sambuc "converted to a string") % property % path);
435*11be35a1SLionel Sambuc const std::string value = state.to_string(-1);
436*11be35a1SLionel Sambuc
437*11be35a1SLionel Sambuc mdbuilder.set_string(property, value);
438*11be35a1SLionel Sambuc }
439*11be35a1SLionel Sambuc
440*11be35a1SLionel Sambuc state.pop(1);
441*11be35a1SLionel Sambuc }
442*11be35a1SLionel Sambuc
443*11be35a1SLionel Sambuc parser::get_from_state(state)->callback_test_program(
444*11be35a1SLionel Sambuc interface, path, test_suite, mdbuilder.build());
445*11be35a1SLionel Sambuc return 0;
446*11be35a1SLionel Sambuc }
447*11be35a1SLionel Sambuc
448*11be35a1SLionel Sambuc
449*11be35a1SLionel Sambuc /// Specialization of lua_generic_test_program for ATF test programs.
450*11be35a1SLionel Sambuc ///
451*11be35a1SLionel Sambuc /// \param state The Lua state that executed the function.
452*11be35a1SLionel Sambuc ///
453*11be35a1SLionel Sambuc /// \return Number of return values left on the Lua stack.
454*11be35a1SLionel Sambuc static int
lua_atf_test_program(lutok::state & state)455*11be35a1SLionel Sambuc lua_atf_test_program(lutok::state& state)
456*11be35a1SLionel Sambuc {
457*11be35a1SLionel Sambuc return lua_generic_test_program(state, "atf");
458*11be35a1SLionel Sambuc }
459*11be35a1SLionel Sambuc
460*11be35a1SLionel Sambuc
461*11be35a1SLionel Sambuc /// Glue to invoke parser::callback_current_kyuafile() from Lua.
462*11be35a1SLionel Sambuc ///
463*11be35a1SLionel Sambuc /// \param state The Lua state that executed the function.
464*11be35a1SLionel Sambuc ///
465*11be35a1SLionel Sambuc /// \return Number of return values left on the Lua stack.
466*11be35a1SLionel Sambuc static int
lua_current_kyuafile(lutok::state & state)467*11be35a1SLionel Sambuc lua_current_kyuafile(lutok::state& state)
468*11be35a1SLionel Sambuc {
469*11be35a1SLionel Sambuc state.push_string(parser::get_from_state(state)->
470*11be35a1SLionel Sambuc callback_current_kyuafile().str());
471*11be35a1SLionel Sambuc return 1;
472*11be35a1SLionel Sambuc }
473*11be35a1SLionel Sambuc
474*11be35a1SLionel Sambuc
475*11be35a1SLionel Sambuc /// Glue to invoke parser::callback_include() from Lua.
476*11be35a1SLionel Sambuc ///
477*11be35a1SLionel Sambuc /// \param state The Lua state that executed the function.
478*11be35a1SLionel Sambuc ///
479*11be35a1SLionel Sambuc /// \return Number of return values left on the Lua stack.
480*11be35a1SLionel Sambuc static int
lua_include(lutok::state & state)481*11be35a1SLionel Sambuc lua_include(lutok::state& state)
482*11be35a1SLionel Sambuc {
483*11be35a1SLionel Sambuc parser::get_from_state(state)->callback_include(
484*11be35a1SLionel Sambuc fs::path(state.to_string()));
485*11be35a1SLionel Sambuc return 0;
486*11be35a1SLionel Sambuc }
487*11be35a1SLionel Sambuc
488*11be35a1SLionel Sambuc
489*11be35a1SLionel Sambuc /// Specialization of lua_generic_test_program for plain test programs.
490*11be35a1SLionel Sambuc ///
491*11be35a1SLionel Sambuc /// \param state The Lua state that executed the function.
492*11be35a1SLionel Sambuc ///
493*11be35a1SLionel Sambuc /// \return Number of return values left on the Lua stack.
494*11be35a1SLionel Sambuc static int
lua_plain_test_program(lutok::state & state)495*11be35a1SLionel Sambuc lua_plain_test_program(lutok::state& state)
496*11be35a1SLionel Sambuc {
497*11be35a1SLionel Sambuc return lua_generic_test_program(state, "plain");
498*11be35a1SLionel Sambuc }
499*11be35a1SLionel Sambuc
500*11be35a1SLionel Sambuc
501*11be35a1SLionel Sambuc /// Glue to invoke parser::callback_syntax() from Lua.
502*11be35a1SLionel Sambuc ///
503*11be35a1SLionel Sambuc /// \pre state(-2) The syntax format name, if a v1 file.
504*11be35a1SLionel Sambuc /// \pre state(-1) The syntax format version.
505*11be35a1SLionel Sambuc ///
506*11be35a1SLionel Sambuc /// \param state The Lua state that executed the function.
507*11be35a1SLionel Sambuc ///
508*11be35a1SLionel Sambuc /// \return Number of return values left on the Lua stack.
509*11be35a1SLionel Sambuc static int
lua_syntax(lutok::state & state)510*11be35a1SLionel Sambuc lua_syntax(lutok::state& state)
511*11be35a1SLionel Sambuc {
512*11be35a1SLionel Sambuc if (!state.is_number(-1))
513*11be35a1SLionel Sambuc throw std::runtime_error("Last argument to syntax must be a number");
514*11be35a1SLionel Sambuc const int syntax_version = state.to_integer(-1);
515*11be35a1SLionel Sambuc
516*11be35a1SLionel Sambuc if (syntax_version == 1) {
517*11be35a1SLionel Sambuc if (state.get_top() != 2)
518*11be35a1SLionel Sambuc throw std::runtime_error("Version 1 files need two arguments to "
519*11be35a1SLionel Sambuc "syntax()");
520*11be35a1SLionel Sambuc if (!state.is_string(-2) || state.to_string(-2) != "kyuafile")
521*11be35a1SLionel Sambuc throw std::runtime_error("First argument to syntax must be "
522*11be35a1SLionel Sambuc "'kyuafile' for version 1 files");
523*11be35a1SLionel Sambuc } else {
524*11be35a1SLionel Sambuc if (state.get_top() != 1)
525*11be35a1SLionel Sambuc throw std::runtime_error("syntax() only takes one argument");
526*11be35a1SLionel Sambuc }
527*11be35a1SLionel Sambuc
528*11be35a1SLionel Sambuc parser::get_from_state(state)->callback_syntax(syntax_version);
529*11be35a1SLionel Sambuc return 0;
530*11be35a1SLionel Sambuc }
531*11be35a1SLionel Sambuc
532*11be35a1SLionel Sambuc
533*11be35a1SLionel Sambuc /// Glue to invoke parser::callback_test_suite() from Lua.
534*11be35a1SLionel Sambuc ///
535*11be35a1SLionel Sambuc /// \param state The Lua state that executed the function.
536*11be35a1SLionel Sambuc ///
537*11be35a1SLionel Sambuc /// \return Number of return values left on the Lua stack.
538*11be35a1SLionel Sambuc static int
lua_test_suite(lutok::state & state)539*11be35a1SLionel Sambuc lua_test_suite(lutok::state& state)
540*11be35a1SLionel Sambuc {
541*11be35a1SLionel Sambuc parser::get_from_state(state)->callback_test_suite(state.to_string());
542*11be35a1SLionel Sambuc return 0;
543*11be35a1SLionel Sambuc }
544*11be35a1SLionel Sambuc
545*11be35a1SLionel Sambuc
546*11be35a1SLionel Sambuc } // anonymous namespace
547*11be35a1SLionel Sambuc
548*11be35a1SLionel Sambuc
549*11be35a1SLionel Sambuc /// Constructs a kyuafile form initialized data.
550*11be35a1SLionel Sambuc ///
551*11be35a1SLionel Sambuc /// Use load() to parse a test suite configuration file and construct a
552*11be35a1SLionel Sambuc /// kyuafile object.
553*11be35a1SLionel Sambuc ///
554*11be35a1SLionel Sambuc /// \param source_root_ The root directory for the test suite represented by the
555*11be35a1SLionel Sambuc /// Kyuafile. In other words, the directory containing the first Kyuafile
556*11be35a1SLionel Sambuc /// processed.
557*11be35a1SLionel Sambuc /// \param build_root_ The root directory for the test programs themselves. In
558*11be35a1SLionel Sambuc /// general, this will be the same as source_root_. If different, the
559*11be35a1SLionel Sambuc /// specified directory must follow the exact same layout of source_root_.
560*11be35a1SLionel Sambuc /// \param tps_ Collection of test programs that belong to this test suite.
kyuafile(const fs::path & source_root_,const fs::path & build_root_,const test_programs_vector & tps_)561*11be35a1SLionel Sambuc engine::kyuafile::kyuafile(const fs::path& source_root_,
562*11be35a1SLionel Sambuc const fs::path& build_root_,
563*11be35a1SLionel Sambuc const test_programs_vector& tps_) :
564*11be35a1SLionel Sambuc _source_root(source_root_),
565*11be35a1SLionel Sambuc _build_root(build_root_),
566*11be35a1SLionel Sambuc _test_programs(tps_)
567*11be35a1SLionel Sambuc {
568*11be35a1SLionel Sambuc }
569*11be35a1SLionel Sambuc
570*11be35a1SLionel Sambuc
571*11be35a1SLionel Sambuc /// Destructor.
~kyuafile(void)572*11be35a1SLionel Sambuc engine::kyuafile::~kyuafile(void)
573*11be35a1SLionel Sambuc {
574*11be35a1SLionel Sambuc }
575*11be35a1SLionel Sambuc
576*11be35a1SLionel Sambuc
577*11be35a1SLionel Sambuc /// Parses a test suite configuration file.
578*11be35a1SLionel Sambuc ///
579*11be35a1SLionel Sambuc /// \param file The file to parse.
580*11be35a1SLionel Sambuc /// \param user_build_root If not none, specifies a path to a directory
581*11be35a1SLionel Sambuc /// containing the test programs themselves. The layout of the build root
582*11be35a1SLionel Sambuc /// must match the layout of the source root (which is just the directory
583*11be35a1SLionel Sambuc /// from which the Kyuafile is being read).
584*11be35a1SLionel Sambuc ///
585*11be35a1SLionel Sambuc /// \return High-level representation of the configuration file.
586*11be35a1SLionel Sambuc ///
587*11be35a1SLionel Sambuc /// \throw load_error If there is any problem loading the file. This includes
588*11be35a1SLionel Sambuc /// file access errors and syntax errors.
589*11be35a1SLionel Sambuc engine::kyuafile
load(const fs::path & file,const optional<fs::path> user_build_root)590*11be35a1SLionel Sambuc engine::kyuafile::load(const fs::path& file,
591*11be35a1SLionel Sambuc const optional< fs::path > user_build_root)
592*11be35a1SLionel Sambuc {
593*11be35a1SLionel Sambuc const fs::path source_root_ = file.branch_path();
594*11be35a1SLionel Sambuc const fs::path build_root_ = user_build_root ?
595*11be35a1SLionel Sambuc user_build_root.get() : source_root_;
596*11be35a1SLionel Sambuc
597*11be35a1SLionel Sambuc return kyuafile(source_root_, build_root_,
598*11be35a1SLionel Sambuc parser(source_root_, build_root_,
599*11be35a1SLionel Sambuc fs::path(file.leaf_name())).parse());
600*11be35a1SLionel Sambuc }
601*11be35a1SLionel Sambuc
602*11be35a1SLionel Sambuc
603*11be35a1SLionel Sambuc /// Gets the root directory of the test suite.
604*11be35a1SLionel Sambuc ///
605*11be35a1SLionel Sambuc /// \return A path.
606*11be35a1SLionel Sambuc const fs::path&
source_root(void) const607*11be35a1SLionel Sambuc engine::kyuafile::source_root(void) const
608*11be35a1SLionel Sambuc {
609*11be35a1SLionel Sambuc return _source_root;
610*11be35a1SLionel Sambuc }
611*11be35a1SLionel Sambuc
612*11be35a1SLionel Sambuc
613*11be35a1SLionel Sambuc /// Gets the root directory of the test programs.
614*11be35a1SLionel Sambuc ///
615*11be35a1SLionel Sambuc /// \return A path.
616*11be35a1SLionel Sambuc const fs::path&
build_root(void) const617*11be35a1SLionel Sambuc engine::kyuafile::build_root(void) const
618*11be35a1SLionel Sambuc {
619*11be35a1SLionel Sambuc return _build_root;
620*11be35a1SLionel Sambuc }
621*11be35a1SLionel Sambuc
622*11be35a1SLionel Sambuc
623*11be35a1SLionel Sambuc /// Gets the collection of test programs that belong to this test suite.
624*11be35a1SLionel Sambuc ///
625*11be35a1SLionel Sambuc /// \return Collection of test program executable names.
626*11be35a1SLionel Sambuc const engine::test_programs_vector&
test_programs(void) const627*11be35a1SLionel Sambuc engine::kyuafile::test_programs(void) const
628*11be35a1SLionel Sambuc {
629*11be35a1SLionel Sambuc return _test_programs;
630*11be35a1SLionel Sambuc }
631