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