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