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: 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 85 detail::atf_atffile_reader::atf_atffile_reader(std::istream& is) : 86 m_is(is) 87 { 88 } 89 90 detail::atf_atffile_reader::~atf_atffile_reader(void) 91 { 92 } 93 94 void 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 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 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 116 detail::atf_atffile_reader::got_eof(void) 117 { 118 } 119 120 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 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 226 got_prop(const std::string& name, const std::string& val) 227 { 228 m_props[name] = val; 229 } 230 231 void 232 got_conf(const std::string& var, const std::string& val) 233 { 234 m_conf[var] = val; 235 } 236 237 public: 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& 245 conf(void) 246 const 247 { 248 return m_conf; 249 } 250 251 const atf::tests::vars_map& 252 props(void) 253 const 254 { 255 return m_props; 256 } 257 258 const std::vector< std::string >& 259 tps(void) 260 const 261 { 262 return m_tps; 263 } 264 }; 265 266 // ------------------------------------------------------------------------ 267 // The "atffile" class. 268 // ------------------------------------------------------------------------ 269 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 >& 281 impl::atffile::tps(void) 282 const 283 { 284 return m_tps; 285 } 286 287 const atf::tests::vars_map& 288 impl::atffile::conf(void) 289 const 290 { 291 return m_conf; 292 } 293 294 const atf::tests::vars_map& 295 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 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