xref: /minix3/external/bsd/kyua-cli/dist/engine/kyuafile.cpp (revision 11be35a165022172ed3cea20f2b5df0307540b0e)
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