1ee44dd6cSjmmv //
2ee44dd6cSjmmv // Automated Testing Framework (atf)
3ee44dd6cSjmmv //
4ee44dd6cSjmmv // Copyright (c) 2007 The NetBSD Foundation, Inc.
5ee44dd6cSjmmv // All rights reserved.
6ee44dd6cSjmmv //
7ee44dd6cSjmmv // Redistribution and use in source and binary forms, with or without
8ee44dd6cSjmmv // modification, are permitted provided that the following conditions
9ee44dd6cSjmmv // are met:
10ee44dd6cSjmmv // 1. Redistributions of source code must retain the above copyright
11ee44dd6cSjmmv // notice, this list of conditions and the following disclaimer.
12ee44dd6cSjmmv // 2. Redistributions in binary form must reproduce the above copyright
13ee44dd6cSjmmv // notice, this list of conditions and the following disclaimer in the
14ee44dd6cSjmmv // documentation and/or other materials provided with the distribution.
15ee44dd6cSjmmv //
16ee44dd6cSjmmv // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17ee44dd6cSjmmv // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18ee44dd6cSjmmv // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19ee44dd6cSjmmv // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20ee44dd6cSjmmv // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21ee44dd6cSjmmv // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22ee44dd6cSjmmv // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23ee44dd6cSjmmv // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24ee44dd6cSjmmv // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25ee44dd6cSjmmv // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26ee44dd6cSjmmv // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27ee44dd6cSjmmv // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28ee44dd6cSjmmv //
29ee44dd6cSjmmv
30ee44dd6cSjmmv #include <cassert>
31ee44dd6cSjmmv #include <cstdlib>
32ee44dd6cSjmmv #include <fstream>
33ee44dd6cSjmmv
34ee44dd6cSjmmv #include "atffile.hpp"
35ee44dd6cSjmmv #include "exceptions.hpp"
36ee44dd6cSjmmv #include "expand.hpp"
37ee44dd6cSjmmv #include "parser.hpp"
38ee44dd6cSjmmv
39ee44dd6cSjmmv namespace impl = tools;
40ee44dd6cSjmmv namespace detail = tools::detail;
41ee44dd6cSjmmv
42ee44dd6cSjmmv namespace {
43ee44dd6cSjmmv
44ee44dd6cSjmmv typedef std::map< std::string, std::string > vars_map;
45ee44dd6cSjmmv
46ee44dd6cSjmmv } // anonymous namespace
47ee44dd6cSjmmv
48ee44dd6cSjmmv // ------------------------------------------------------------------------
49ee44dd6cSjmmv // The "atf_atffile" auxiliary parser.
50ee44dd6cSjmmv // ------------------------------------------------------------------------
51ee44dd6cSjmmv
52ee44dd6cSjmmv namespace atf_atffile {
53ee44dd6cSjmmv
54ee44dd6cSjmmv static const tools::parser::token_type eof_type = 0;
55ee44dd6cSjmmv static const tools::parser::token_type nl_type = 1;
56ee44dd6cSjmmv static const tools::parser::token_type text_type = 2;
57ee44dd6cSjmmv static const tools::parser::token_type colon_type = 3;
58ee44dd6cSjmmv static const tools::parser::token_type conf_type = 4;
59ee44dd6cSjmmv static const tools::parser::token_type dblquote_type = 5;
60ee44dd6cSjmmv static const tools::parser::token_type equal_type = 6;
61ee44dd6cSjmmv static const tools::parser::token_type hash_type = 7;
62ee44dd6cSjmmv static const tools::parser::token_type prop_type = 8;
63ee44dd6cSjmmv static const tools::parser::token_type tp_type = 9;
64ee44dd6cSjmmv static const tools::parser::token_type tp_glob_type = 10;
65ee44dd6cSjmmv
66ee44dd6cSjmmv class tokenizer : public tools::parser::tokenizer< std::istream > {
67ee44dd6cSjmmv public:
tokenizer(std::istream & is,size_t curline)68ee44dd6cSjmmv tokenizer(std::istream& is, size_t curline) :
69ee44dd6cSjmmv tools::parser::tokenizer< std::istream >
70ee44dd6cSjmmv (is, true, eof_type, nl_type, text_type, curline)
71ee44dd6cSjmmv {
72ee44dd6cSjmmv add_delim(':', colon_type);
73ee44dd6cSjmmv add_delim('=', equal_type);
74ee44dd6cSjmmv add_delim('#', hash_type);
75ee44dd6cSjmmv add_quote('"', dblquote_type);
76ee44dd6cSjmmv add_keyword("conf", conf_type);
77ee44dd6cSjmmv add_keyword("prop", prop_type);
78ee44dd6cSjmmv add_keyword("tp", tp_type);
79ee44dd6cSjmmv add_keyword("tp-glob", tp_glob_type);
80ee44dd6cSjmmv }
81ee44dd6cSjmmv };
82ee44dd6cSjmmv
83ee44dd6cSjmmv } // namespace atf_atffile
84ee44dd6cSjmmv
85ee44dd6cSjmmv // ------------------------------------------------------------------------
86ee44dd6cSjmmv // The "atf_atffile_reader" class.
87ee44dd6cSjmmv // ------------------------------------------------------------------------
88ee44dd6cSjmmv
atf_atffile_reader(std::istream & is)89ee44dd6cSjmmv detail::atf_atffile_reader::atf_atffile_reader(std::istream& is) :
90ee44dd6cSjmmv m_is(is)
91ee44dd6cSjmmv {
92ee44dd6cSjmmv }
93ee44dd6cSjmmv
~atf_atffile_reader(void)94ee44dd6cSjmmv detail::atf_atffile_reader::~atf_atffile_reader(void)
95ee44dd6cSjmmv {
96ee44dd6cSjmmv }
97ee44dd6cSjmmv
98ee44dd6cSjmmv void
got_conf(const std::string & name,const std::string & val)99ee44dd6cSjmmv detail::atf_atffile_reader::got_conf(
100*de996de9Sjmmv const std::string& name __attribute__((__unused__)),
101*de996de9Sjmmv const std::string& val __attribute__((__unused__)))
102ee44dd6cSjmmv {
103ee44dd6cSjmmv }
104ee44dd6cSjmmv
105ee44dd6cSjmmv void
got_prop(const std::string & name,const std::string & val)106ee44dd6cSjmmv detail::atf_atffile_reader::got_prop(
107*de996de9Sjmmv const std::string& name __attribute__((__unused__)),
108*de996de9Sjmmv const std::string& val __attribute__((__unused__)))
109ee44dd6cSjmmv {
110ee44dd6cSjmmv }
111ee44dd6cSjmmv
112ee44dd6cSjmmv void
got_tp(const std::string & name,bool isglob)113ee44dd6cSjmmv detail::atf_atffile_reader::got_tp(
114*de996de9Sjmmv const std::string& name __attribute__((__unused__)),
115*de996de9Sjmmv bool isglob __attribute__((__unused__)))
116ee44dd6cSjmmv {
117ee44dd6cSjmmv }
118ee44dd6cSjmmv
119ee44dd6cSjmmv void
got_eof(void)120ee44dd6cSjmmv detail::atf_atffile_reader::got_eof(void)
121ee44dd6cSjmmv {
122ee44dd6cSjmmv }
123ee44dd6cSjmmv
124ee44dd6cSjmmv void
read(void)125ee44dd6cSjmmv detail::atf_atffile_reader::read(void)
126ee44dd6cSjmmv {
127ee44dd6cSjmmv using tools::parser::parse_error;
128ee44dd6cSjmmv using namespace atf_atffile;
129ee44dd6cSjmmv
130ee44dd6cSjmmv std::pair< size_t, tools::parser::headers_map > hml =
131ee44dd6cSjmmv tools::parser::read_headers(m_is, 1);
132ee44dd6cSjmmv tools::parser::validate_content_type(hml.second,
133ee44dd6cSjmmv "application/X-atf-atffile", 1);
134ee44dd6cSjmmv
135ee44dd6cSjmmv tokenizer tkz(m_is, hml.first);
136ee44dd6cSjmmv tools::parser::parser< tokenizer > p(tkz);
137ee44dd6cSjmmv
138ee44dd6cSjmmv for (;;) {
139ee44dd6cSjmmv try {
140ee44dd6cSjmmv tools::parser::token t =
141ee44dd6cSjmmv p.expect(conf_type, hash_type, prop_type, tp_type,
142ee44dd6cSjmmv tp_glob_type, nl_type, eof_type,
143ee44dd6cSjmmv "conf, #, prop, tp, tp-glob, a new line or eof");
144ee44dd6cSjmmv if (t.type() == eof_type)
145ee44dd6cSjmmv break;
146ee44dd6cSjmmv
147ee44dd6cSjmmv if (t.type() == conf_type) {
148ee44dd6cSjmmv t = p.expect(colon_type, "`:'");
149ee44dd6cSjmmv
150ee44dd6cSjmmv t = p.expect(text_type, "variable name");
151ee44dd6cSjmmv std::string var = t.text();
152ee44dd6cSjmmv
153ee44dd6cSjmmv t = p.expect(equal_type, "equal sign");
154ee44dd6cSjmmv
155ee44dd6cSjmmv t = p.expect(text_type, "word or quoted string");
156ee44dd6cSjmmv ATF_PARSER_CALLBACK(p, got_conf(var, t.text()));
157ee44dd6cSjmmv } else if (t.type() == hash_type) {
158ee44dd6cSjmmv (void)p.rest_of_line();
159ee44dd6cSjmmv } else if (t.type() == prop_type) {
160ee44dd6cSjmmv t = p.expect(colon_type, "`:'");
161ee44dd6cSjmmv
162ee44dd6cSjmmv t = p.expect(text_type, "property name");
163ee44dd6cSjmmv std::string name = t.text();
164ee44dd6cSjmmv
165ee44dd6cSjmmv t = p.expect(equal_type, "equale sign");
166ee44dd6cSjmmv
167ee44dd6cSjmmv t = p.expect(text_type, "word or quoted string");
168ee44dd6cSjmmv ATF_PARSER_CALLBACK(p, got_prop(name, t.text()));
169ee44dd6cSjmmv } else if (t.type() == tp_type) {
170ee44dd6cSjmmv t = p.expect(colon_type, "`:'");
171ee44dd6cSjmmv
172ee44dd6cSjmmv t = p.expect(text_type, "word or quoted string");
173ee44dd6cSjmmv ATF_PARSER_CALLBACK(p, got_tp(t.text(), false));
174ee44dd6cSjmmv } else if (t.type() == tp_glob_type) {
175ee44dd6cSjmmv t = p.expect(colon_type, "`:'");
176ee44dd6cSjmmv
177ee44dd6cSjmmv t = p.expect(text_type, "word or quoted string");
178ee44dd6cSjmmv ATF_PARSER_CALLBACK(p, got_tp(t.text(), true));
179ee44dd6cSjmmv } else if (t.type() == nl_type) {
180ee44dd6cSjmmv continue;
181ee44dd6cSjmmv } else
182ee44dd6cSjmmv std::abort();
183ee44dd6cSjmmv
184ee44dd6cSjmmv t = p.expect(nl_type, hash_type, eof_type,
185ee44dd6cSjmmv "new line or comment");
186ee44dd6cSjmmv if (t.type() == hash_type) {
187ee44dd6cSjmmv (void)p.rest_of_line();
188ee44dd6cSjmmv t = p.next();
189ee44dd6cSjmmv } else if (t.type() == eof_type)
190ee44dd6cSjmmv break;
191ee44dd6cSjmmv } catch (const parse_error& pe) {
192ee44dd6cSjmmv p.add_error(pe);
193ee44dd6cSjmmv p.reset(nl_type);
194ee44dd6cSjmmv }
195ee44dd6cSjmmv }
196ee44dd6cSjmmv
197ee44dd6cSjmmv ATF_PARSER_CALLBACK(p, got_eof());
198ee44dd6cSjmmv }
199ee44dd6cSjmmv
200ee44dd6cSjmmv // ------------------------------------------------------------------------
201ee44dd6cSjmmv // The "reader" helper class.
202ee44dd6cSjmmv // ------------------------------------------------------------------------
203ee44dd6cSjmmv
204ee44dd6cSjmmv class reader : public detail::atf_atffile_reader {
205ee44dd6cSjmmv const tools::fs::directory& m_dir;
206ee44dd6cSjmmv vars_map m_conf, m_props;
207ee44dd6cSjmmv std::vector< std::string > m_tps;
208ee44dd6cSjmmv
209ee44dd6cSjmmv void
got_tp(const std::string & name,bool isglob)210ee44dd6cSjmmv got_tp(const std::string& name, bool isglob)
211ee44dd6cSjmmv {
212ee44dd6cSjmmv if (isglob) {
213ee44dd6cSjmmv std::vector< std::string > ms =
214ee44dd6cSjmmv tools::expand::expand_glob(name, m_dir.names());
215ee44dd6cSjmmv // Cannot use m_tps.insert(iterator, begin, end) here because it
216ee44dd6cSjmmv // does not work under Solaris.
217ee44dd6cSjmmv for (std::vector< std::string >::const_iterator iter = ms.begin();
218ee44dd6cSjmmv iter != ms.end(); iter++)
219ee44dd6cSjmmv m_tps.push_back(*iter);
220ee44dd6cSjmmv } else {
221ee44dd6cSjmmv if (m_dir.find(name) == m_dir.end())
222ee44dd6cSjmmv throw tools::not_found_error< tools::fs::path >
223ee44dd6cSjmmv ("Cannot locate the " + name + " file",
224ee44dd6cSjmmv tools::fs::path(name));
225ee44dd6cSjmmv m_tps.push_back(name);
226ee44dd6cSjmmv }
227ee44dd6cSjmmv }
228ee44dd6cSjmmv
229ee44dd6cSjmmv void
got_prop(const std::string & name,const std::string & val)230ee44dd6cSjmmv got_prop(const std::string& name, const std::string& val)
231ee44dd6cSjmmv {
232ee44dd6cSjmmv m_props[name] = val;
233ee44dd6cSjmmv }
234ee44dd6cSjmmv
235ee44dd6cSjmmv void
got_conf(const std::string & var,const std::string & val)236ee44dd6cSjmmv got_conf(const std::string& var, const std::string& val)
237ee44dd6cSjmmv {
238ee44dd6cSjmmv m_conf[var] = val;
239ee44dd6cSjmmv }
240ee44dd6cSjmmv
241ee44dd6cSjmmv public:
reader(std::istream & is,const tools::fs::directory & dir)242ee44dd6cSjmmv reader(std::istream& is, const tools::fs::directory& dir) :
243ee44dd6cSjmmv detail::atf_atffile_reader(is),
244ee44dd6cSjmmv m_dir(dir)
245ee44dd6cSjmmv {
246ee44dd6cSjmmv }
247ee44dd6cSjmmv
248ee44dd6cSjmmv const vars_map&
conf(void) const249ee44dd6cSjmmv conf(void)
250ee44dd6cSjmmv const
251ee44dd6cSjmmv {
252ee44dd6cSjmmv return m_conf;
253ee44dd6cSjmmv }
254ee44dd6cSjmmv
255ee44dd6cSjmmv const vars_map&
props(void) const256ee44dd6cSjmmv props(void)
257ee44dd6cSjmmv const
258ee44dd6cSjmmv {
259ee44dd6cSjmmv return m_props;
260ee44dd6cSjmmv }
261ee44dd6cSjmmv
262ee44dd6cSjmmv const std::vector< std::string >&
tps(void) const263ee44dd6cSjmmv tps(void)
264ee44dd6cSjmmv const
265ee44dd6cSjmmv {
266ee44dd6cSjmmv return m_tps;
267ee44dd6cSjmmv }
268ee44dd6cSjmmv };
269ee44dd6cSjmmv
270ee44dd6cSjmmv // ------------------------------------------------------------------------
271ee44dd6cSjmmv // The "atffile" class.
272ee44dd6cSjmmv // ------------------------------------------------------------------------
273ee44dd6cSjmmv
atffile(const vars_map & config_vars,const std::vector<std::string> & test_program_names,const vars_map & properties)274ee44dd6cSjmmv impl::atffile::atffile(const vars_map& config_vars,
275ee44dd6cSjmmv const std::vector< std::string >& test_program_names,
276ee44dd6cSjmmv const vars_map& properties) :
277ee44dd6cSjmmv m_conf(config_vars),
278ee44dd6cSjmmv m_tps(test_program_names),
279ee44dd6cSjmmv m_props(properties)
280ee44dd6cSjmmv {
281ee44dd6cSjmmv assert(properties.find("test-suite") != properties.end());
282ee44dd6cSjmmv }
283ee44dd6cSjmmv
284ee44dd6cSjmmv const std::vector< std::string >&
tps(void) const285ee44dd6cSjmmv impl::atffile::tps(void)
286ee44dd6cSjmmv const
287ee44dd6cSjmmv {
288ee44dd6cSjmmv return m_tps;
289ee44dd6cSjmmv }
290ee44dd6cSjmmv
291ee44dd6cSjmmv const vars_map&
conf(void) const292ee44dd6cSjmmv impl::atffile::conf(void)
293ee44dd6cSjmmv const
294ee44dd6cSjmmv {
295ee44dd6cSjmmv return m_conf;
296ee44dd6cSjmmv }
297ee44dd6cSjmmv
298ee44dd6cSjmmv const vars_map&
props(void) const299ee44dd6cSjmmv impl::atffile::props(void)
300ee44dd6cSjmmv const
301ee44dd6cSjmmv {
302ee44dd6cSjmmv return m_props;
303ee44dd6cSjmmv }
304ee44dd6cSjmmv
305ee44dd6cSjmmv // ------------------------------------------------------------------------
306ee44dd6cSjmmv // Free functions.
307ee44dd6cSjmmv // ------------------------------------------------------------------------
308ee44dd6cSjmmv
309ee44dd6cSjmmv // XXX Glob expansion and file existance checks certainly do not belong in
310ee44dd6cSjmmv // a *parser*. This needs to be taken out...
311ee44dd6cSjmmv impl::atffile
read_atffile(const tools::fs::path & filename)312ee44dd6cSjmmv impl::read_atffile(const tools::fs::path& filename)
313ee44dd6cSjmmv {
314ee44dd6cSjmmv // Scan the directory where the atffile lives in to gather a list of
315ee44dd6cSjmmv // all possible test programs in it.
316ee44dd6cSjmmv tools::fs::directory dir(filename.branch_path());
317ee44dd6cSjmmv dir.erase(filename.leaf_name());
318ee44dd6cSjmmv tools::fs::directory::iterator iter = dir.begin();
319ee44dd6cSjmmv while (iter != dir.end()) {
320ee44dd6cSjmmv const std::string& name = (*iter).first;
321ee44dd6cSjmmv const tools::fs::file_info& fi = (*iter).second;
322ee44dd6cSjmmv
323ee44dd6cSjmmv // Discard hidden files and non-executable ones so that they are
324ee44dd6cSjmmv // not candidates for glob matching.
325ee44dd6cSjmmv if (name[0] == '.' || (!fi.is_owner_executable() &&
326ee44dd6cSjmmv !fi.is_group_executable()))
327ee44dd6cSjmmv dir.erase(iter++);
328ee44dd6cSjmmv else
329ee44dd6cSjmmv iter++;
330ee44dd6cSjmmv }
331ee44dd6cSjmmv
332ee44dd6cSjmmv // Parse the atffile.
333ee44dd6cSjmmv std::ifstream is(filename.c_str());
334ee44dd6cSjmmv if (!is)
335ee44dd6cSjmmv throw tools::not_found_error< tools::fs::path >
336ee44dd6cSjmmv ("Cannot open Atffile", filename);
337ee44dd6cSjmmv reader r(is, dir);
338ee44dd6cSjmmv r.read();
339ee44dd6cSjmmv is.close();
340ee44dd6cSjmmv
341ee44dd6cSjmmv // Sanity checks.
342ee44dd6cSjmmv if (r.props().find("test-suite") == r.props().end())
343ee44dd6cSjmmv throw tools::not_found_error< std::string >
344ee44dd6cSjmmv ("Undefined property `test-suite'", "test-suite");
345ee44dd6cSjmmv
346ee44dd6cSjmmv return atffile(r.conf(), r.tps(), r.props());
347ee44dd6cSjmmv }
348