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