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 <cassert> 31 #include <cstdlib> 32 #include <fstream> 33 34 #include "atffile.hpp" 35 #include "exceptions.hpp" 36 #include "expand.hpp" 37 #include "parser.hpp" 38 39 namespace impl = tools; 40 namespace detail = tools::detail; 41 42 namespace { 43 44 typedef std::map< std::string, std::string > vars_map; 45 46 } // anonymous namespace 47 48 // ------------------------------------------------------------------------ 49 // The "atf_atffile" auxiliary parser. 50 // ------------------------------------------------------------------------ 51 52 namespace atf_atffile { 53 54 static const tools::parser::token_type eof_type = 0; 55 static const tools::parser::token_type nl_type = 1; 56 static const tools::parser::token_type text_type = 2; 57 static const tools::parser::token_type colon_type = 3; 58 static const tools::parser::token_type conf_type = 4; 59 static const tools::parser::token_type dblquote_type = 5; 60 static const tools::parser::token_type equal_type = 6; 61 static const tools::parser::token_type hash_type = 7; 62 static const tools::parser::token_type prop_type = 8; 63 static const tools::parser::token_type tp_type = 9; 64 static const tools::parser::token_type tp_glob_type = 10; 65 66 class tokenizer : public tools::parser::tokenizer< std::istream > { 67 public: 68 tokenizer(std::istream& is, size_t curline) : 69 tools::parser::tokenizer< std::istream > 70 (is, true, eof_type, nl_type, text_type, curline) 71 { 72 add_delim(':', colon_type); 73 add_delim('=', equal_type); 74 add_delim('#', hash_type); 75 add_quote('"', dblquote_type); 76 add_keyword("conf", conf_type); 77 add_keyword("prop", prop_type); 78 add_keyword("tp", tp_type); 79 add_keyword("tp-glob", tp_glob_type); 80 } 81 }; 82 83 } // namespace atf_atffile 84 85 // ------------------------------------------------------------------------ 86 // The "atf_atffile_reader" class. 87 // ------------------------------------------------------------------------ 88 89 detail::atf_atffile_reader::atf_atffile_reader(std::istream& is) : 90 m_is(is) 91 { 92 } 93 94 detail::atf_atffile_reader::~atf_atffile_reader(void) 95 { 96 } 97 98 void 99 detail::atf_atffile_reader::got_conf( 100 const std::string& name __attribute__((__unused__)), 101 const std::string& val __attribute__((__unused__))) 102 { 103 } 104 105 void 106 detail::atf_atffile_reader::got_prop( 107 const std::string& name __attribute__((__unused__)), 108 const std::string& val __attribute__((__unused__))) 109 { 110 } 111 112 void 113 detail::atf_atffile_reader::got_tp( 114 const std::string& name __attribute__((__unused__)), 115 bool isglob __attribute__((__unused__))) 116 { 117 } 118 119 void 120 detail::atf_atffile_reader::got_eof(void) 121 { 122 } 123 124 void 125 detail::atf_atffile_reader::read(void) 126 { 127 using tools::parser::parse_error; 128 using namespace atf_atffile; 129 130 std::pair< size_t, tools::parser::headers_map > hml = 131 tools::parser::read_headers(m_is, 1); 132 tools::parser::validate_content_type(hml.second, 133 "application/X-atf-atffile", 1); 134 135 tokenizer tkz(m_is, hml.first); 136 tools::parser::parser< tokenizer > p(tkz); 137 138 for (;;) { 139 try { 140 tools::parser::token t = 141 p.expect(conf_type, hash_type, prop_type, tp_type, 142 tp_glob_type, nl_type, eof_type, 143 "conf, #, prop, tp, tp-glob, a new line or eof"); 144 if (t.type() == eof_type) 145 break; 146 147 if (t.type() == conf_type) { 148 t = p.expect(colon_type, "`:'"); 149 150 t = p.expect(text_type, "variable name"); 151 std::string var = t.text(); 152 153 t = p.expect(equal_type, "equal sign"); 154 155 t = p.expect(text_type, "word or quoted string"); 156 ATF_PARSER_CALLBACK(p, got_conf(var, t.text())); 157 } else if (t.type() == hash_type) { 158 (void)p.rest_of_line(); 159 } else if (t.type() == prop_type) { 160 t = p.expect(colon_type, "`:'"); 161 162 t = p.expect(text_type, "property name"); 163 std::string name = t.text(); 164 165 t = p.expect(equal_type, "equale sign"); 166 167 t = p.expect(text_type, "word or quoted string"); 168 ATF_PARSER_CALLBACK(p, got_prop(name, t.text())); 169 } else if (t.type() == tp_type) { 170 t = p.expect(colon_type, "`:'"); 171 172 t = p.expect(text_type, "word or quoted string"); 173 ATF_PARSER_CALLBACK(p, got_tp(t.text(), false)); 174 } else if (t.type() == tp_glob_type) { 175 t = p.expect(colon_type, "`:'"); 176 177 t = p.expect(text_type, "word or quoted string"); 178 ATF_PARSER_CALLBACK(p, got_tp(t.text(), true)); 179 } else if (t.type() == nl_type) { 180 continue; 181 } else 182 std::abort(); 183 184 t = p.expect(nl_type, hash_type, eof_type, 185 "new line or comment"); 186 if (t.type() == hash_type) { 187 (void)p.rest_of_line(); 188 t = p.next(); 189 } else if (t.type() == eof_type) 190 break; 191 } catch (const parse_error& pe) { 192 p.add_error(pe); 193 p.reset(nl_type); 194 } 195 } 196 197 ATF_PARSER_CALLBACK(p, got_eof()); 198 } 199 200 // ------------------------------------------------------------------------ 201 // The "reader" helper class. 202 // ------------------------------------------------------------------------ 203 204 class reader : public detail::atf_atffile_reader { 205 const tools::fs::directory& m_dir; 206 vars_map m_conf, m_props; 207 std::vector< std::string > m_tps; 208 209 void 210 got_tp(const std::string& name, bool isglob) 211 { 212 if (isglob) { 213 std::vector< std::string > ms = 214 tools::expand::expand_glob(name, m_dir.names()); 215 // Cannot use m_tps.insert(iterator, begin, end) here because it 216 // does not work under Solaris. 217 for (std::vector< std::string >::const_iterator iter = ms.begin(); 218 iter != ms.end(); iter++) 219 m_tps.push_back(*iter); 220 } else { 221 if (m_dir.find(name) == m_dir.end()) 222 throw tools::not_found_error< tools::fs::path > 223 ("Cannot locate the " + name + " file", 224 tools::fs::path(name)); 225 m_tps.push_back(name); 226 } 227 } 228 229 void 230 got_prop(const std::string& name, const std::string& val) 231 { 232 m_props[name] = val; 233 } 234 235 void 236 got_conf(const std::string& var, const std::string& val) 237 { 238 m_conf[var] = val; 239 } 240 241 public: 242 reader(std::istream& is, const tools::fs::directory& dir) : 243 detail::atf_atffile_reader(is), 244 m_dir(dir) 245 { 246 } 247 248 const vars_map& 249 conf(void) 250 const 251 { 252 return m_conf; 253 } 254 255 const vars_map& 256 props(void) 257 const 258 { 259 return m_props; 260 } 261 262 const std::vector< std::string >& 263 tps(void) 264 const 265 { 266 return m_tps; 267 } 268 }; 269 270 // ------------------------------------------------------------------------ 271 // The "atffile" class. 272 // ------------------------------------------------------------------------ 273 274 impl::atffile::atffile(const vars_map& config_vars, 275 const std::vector< std::string >& test_program_names, 276 const vars_map& properties) : 277 m_conf(config_vars), 278 m_tps(test_program_names), 279 m_props(properties) 280 { 281 assert(properties.find("test-suite") != properties.end()); 282 } 283 284 const std::vector< std::string >& 285 impl::atffile::tps(void) 286 const 287 { 288 return m_tps; 289 } 290 291 const vars_map& 292 impl::atffile::conf(void) 293 const 294 { 295 return m_conf; 296 } 297 298 const vars_map& 299 impl::atffile::props(void) 300 const 301 { 302 return m_props; 303 } 304 305 // ------------------------------------------------------------------------ 306 // Free functions. 307 // ------------------------------------------------------------------------ 308 309 // XXX Glob expansion and file existance checks certainly do not belong in 310 // a *parser*. This needs to be taken out... 311 impl::atffile 312 impl::read_atffile(const tools::fs::path& filename) 313 { 314 // Scan the directory where the atffile lives in to gather a list of 315 // all possible test programs in it. 316 tools::fs::directory dir(filename.branch_path()); 317 dir.erase(filename.leaf_name()); 318 tools::fs::directory::iterator iter = dir.begin(); 319 while (iter != dir.end()) { 320 const std::string& name = (*iter).first; 321 const tools::fs::file_info& fi = (*iter).second; 322 323 // Discard hidden files and non-executable ones so that they are 324 // not candidates for glob matching. 325 if (name[0] == '.' || (!fi.is_owner_executable() && 326 !fi.is_group_executable())) 327 dir.erase(iter++); 328 else 329 iter++; 330 } 331 332 // Parse the atffile. 333 std::ifstream is(filename.c_str()); 334 if (!is) 335 throw tools::not_found_error< tools::fs::path > 336 ("Cannot open Atffile", filename); 337 reader r(is, dir); 338 r.read(); 339 is.close(); 340 341 // Sanity checks. 342 if (r.props().find("test-suite") == r.props().end()) 343 throw tools::not_found_error< std::string > 344 ("Undefined property `test-suite'", "test-suite"); 345 346 return atffile(r.conf(), r.tps(), r.props()); 347 } 348