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