atffile_test.cpp revision 1.1.1.4
1//
2// Automated Testing Framework (atf)
3//
4// Copyright (c) 2009 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
30extern "C" {
31#include <sys/types.h>
32#include <sys/stat.h>
33}
34
35#include <algorithm>
36#include <fstream>
37#include <memory>
38
39#include "atf-c++/macros.hpp"
40
41#include "atf-c++/detail/exceptions.hpp"
42#include "atf-c++/detail/test_helpers.hpp"
43
44#include "atffile.hpp"
45
46namespace detail = atf::atf_run::detail;
47
48// ------------------------------------------------------------------------
49// Auxiliary functions.
50// ------------------------------------------------------------------------
51
52namespace {
53
54static
55std::auto_ptr< std::ofstream >
56new_atffile(void)
57{
58    std::auto_ptr< std::ofstream > os(new std::ofstream("Atffile"));
59    ATF_REQUIRE(*os);
60
61    (*os) << "Content-Type: application/X-atf-atffile; version=\"1\"\n\n";
62    return os;
63}
64
65static
66void
67touch_exec(const char* name)
68{
69    std::ofstream os(name);
70    ATF_REQUIRE(os);
71    os.close();
72    ATF_REQUIRE(::chmod(name, S_IRWXU) != -1);
73}
74
75static inline
76bool
77is_in(const std::string& value, const std::vector< std::string >& v)
78{
79    return std::find(v.begin(), v.end(), value) != v.end();
80}
81
82} // anonymous namespace
83
84// ------------------------------------------------------------------------
85// Tests cases for the "atffile" parser.
86// ------------------------------------------------------------------------
87
88class atffile_reader : protected detail::atf_atffile_reader {
89    void
90    got_conf(const std::string& name, const std::string& val)
91    {
92        m_calls.push_back("got_conf(" + name + ", " + val + ")");
93    }
94
95    void
96    got_prop(const std::string& name, const std::string& val)
97    {
98        m_calls.push_back("got_prop(" + name + ", " + val + ")");
99    }
100
101    void
102    got_tp(const std::string& name, bool isglob)
103    {
104        m_calls.push_back("got_tp(" + name + ", " + (isglob ? "true" : "false")
105                  + ")");
106    }
107
108    void
109    got_eof(void)
110    {
111        m_calls.push_back("got_eof()");
112    }
113
114public:
115    atffile_reader(std::istream& is) :
116        detail::atf_atffile_reader(is)
117    {
118    }
119
120    void
121    read(void)
122    {
123        atf_atffile_reader::read();
124    }
125
126    std::vector< std::string > m_calls;
127};
128
129ATF_TEST_CASE_WITHOUT_HEAD(atffile_1);
130ATF_TEST_CASE_BODY(atffile_1)
131{
132    const char* input =
133        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
134        "\n"
135    ;
136
137    const char* exp_calls[] = {
138        "got_eof()",
139        NULL
140    };
141
142    const char* exp_errors[] = {
143        NULL
144    };
145
146    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
147}
148
149ATF_TEST_CASE_WITHOUT_HEAD(atffile_2);
150ATF_TEST_CASE_BODY(atffile_2)
151{
152    const char* input =
153        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
154        "\n"
155        "# This is a comment on a line of its own.\n"
156        "# And this is another one.\n"
157        "\n"
158        "	    # Another after some whitespace.\n"
159        "\n"
160        "# The last one after an empty line.\n"
161    ;
162
163    const char* exp_calls[] = {
164        "got_eof()",
165        NULL
166    };
167
168    const char* exp_errors[] = {
169        NULL
170    };
171
172    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
173}
174
175ATF_TEST_CASE_WITHOUT_HEAD(atffile_3);
176ATF_TEST_CASE_BODY(atffile_3)
177{
178    const char* input =
179        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
180        "\n"
181        "conf: var1=value1\n"
182        "conf: var2 = value2\n"
183        "conf: var3	=	value3\n"
184        "conf: var4	    =	    value4\n"
185        "\n"
186        "conf:var5=value5\n"
187        "    conf:var6=value6\n"
188        "\n"
189        "conf: var7 = \"This is a long value.\"\n"
190        "conf: var8 = \"Single-word\"\n"
191        "conf: var9 = \"    Single-word	\"\n"
192        "conf: var10 = Single-word\n"
193    ;
194
195    const char* exp_calls[] = {
196        "got_conf(var1, value1)",
197        "got_conf(var2, value2)",
198        "got_conf(var3, value3)",
199        "got_conf(var4, value4)",
200        "got_conf(var5, value5)",
201        "got_conf(var6, value6)",
202        "got_conf(var7, This is a long value.)",
203        "got_conf(var8, Single-word)",
204        "got_conf(var9,     Single-word	)",
205        "got_conf(var10, Single-word)",
206        "got_eof()",
207        NULL
208    };
209
210    const char* exp_errors[] = {
211        NULL
212    };
213
214    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
215}
216
217ATF_TEST_CASE_WITHOUT_HEAD(atffile_4);
218ATF_TEST_CASE_BODY(atffile_4)
219{
220    const char* input =
221        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
222        "\n"
223        "prop: var1=value1\n"
224        "prop: var2 = value2\n"
225        "prop: var3	=	value3\n"
226        "prop: var4	    =	    value4\n"
227        "\n"
228        "prop:var5=value5\n"
229        "    prop:var6=value6\n"
230        "\n"
231        "prop: var7 = \"This is a long value.\"\n"
232        "prop: var8 = \"Single-word\"\n"
233        "prop: var9 = \"    Single-word	\"\n"
234        "prop: var10 = Single-word\n"
235    ;
236
237    const char* exp_calls[] = {
238        "got_prop(var1, value1)",
239        "got_prop(var2, value2)",
240        "got_prop(var3, value3)",
241        "got_prop(var4, value4)",
242        "got_prop(var5, value5)",
243        "got_prop(var6, value6)",
244        "got_prop(var7, This is a long value.)",
245        "got_prop(var8, Single-word)",
246        "got_prop(var9,     Single-word	)",
247        "got_prop(var10, Single-word)",
248        "got_eof()",
249        NULL
250    };
251
252    const char* exp_errors[] = {
253        NULL
254    };
255
256    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
257}
258
259ATF_TEST_CASE_WITHOUT_HEAD(atffile_5);
260ATF_TEST_CASE_BODY(atffile_5)
261{
262    const char* input =
263        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
264        "\n"
265        "tp:foo\n"
266        "tp: foo\n"
267        "tp:  foo\n"
268        "tp:	foo\n"
269        "tp:	    foo\n"
270        "tp: \"name with spaces\"\n"
271        "tp: \"single-word\"\n"
272        "tp: single-word\n"
273        "\n"
274        "tp-glob:foo*?bar\n"
275        "tp-glob: foo*?bar\n"
276        "tp-glob:  foo*?bar\n"
277        "tp-glob:	foo*?bar\n"
278        "tp-glob:	    foo*?bar\n"
279        "tp-glob: \"glob * with ? spaces\"\n"
280        "tp-glob: \"single-*-word\"\n"
281        "tp-glob: single-*-word\n"
282    ;
283
284    const char* exp_calls[] = {
285        "got_tp(foo, false)",
286        "got_tp(foo, false)",
287        "got_tp(foo, false)",
288        "got_tp(foo, false)",
289        "got_tp(foo, false)",
290        "got_tp(name with spaces, false)",
291        "got_tp(single-word, false)",
292        "got_tp(single-word, false)",
293        "got_tp(foo*?bar, true)",
294        "got_tp(foo*?bar, true)",
295        "got_tp(foo*?bar, true)",
296        "got_tp(foo*?bar, true)",
297        "got_tp(foo*?bar, true)",
298        "got_tp(glob * with ? spaces, true)",
299        "got_tp(single-*-word, true)",
300        "got_tp(single-*-word, true)",
301        "got_eof()",
302        NULL
303    };
304
305    const char* exp_errors[] = {
306        NULL
307    };
308
309    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
310}
311
312ATF_TEST_CASE_WITHOUT_HEAD(atffile_6);
313ATF_TEST_CASE_BODY(atffile_6)
314{
315    const char* input =
316        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
317        "\n"
318        "prop: foo = bar # A comment.\n"
319        "conf: foo = bar # A comment.\n"
320        "tp: foo # A comment.\n"
321        "tp-glob: foo # A comment.\n"
322    ;
323
324    const char* exp_calls[] = {
325        "got_prop(foo, bar)",
326        "got_conf(foo, bar)",
327        "got_tp(foo, false)",
328        "got_tp(foo, true)",
329        "got_eof()",
330        NULL
331    };
332
333    const char* exp_errors[] = {
334        NULL
335    };
336
337    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
338}
339
340ATF_TEST_CASE_WITHOUT_HEAD(atffile_50);
341ATF_TEST_CASE_BODY(atffile_50)
342{
343    const char* input =
344        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
345        "\n"
346        "foo\n"
347    ;
348
349    const char* exp_calls[] = {
350        NULL
351    };
352
353    // NO_CHECK_STYLE_BEGIN
354    const char* exp_errors[] = {
355        "3: Unexpected token `foo'; expected conf, #, prop, tp, tp-glob, a new line or eof",
356        NULL
357    };
358    // NO_CHECK_STYLE_END
359
360    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
361}
362
363ATF_TEST_CASE_WITHOUT_HEAD(atffile_51);
364ATF_TEST_CASE_BODY(atffile_51)
365{
366    const char* input =
367        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
368        "\n"
369        "foo bar\n"
370        "baz\n"
371    ;
372
373    const char* exp_calls[] = {
374        NULL
375    };
376
377    // NO_CHECK_STYLE_BEGIN
378    const char* exp_errors[] = {
379        "3: Unexpected token `foo'; expected conf, #, prop, tp, tp-glob, a new line or eof",
380        "4: Unexpected token `baz'; expected conf, #, prop, tp, tp-glob, a new line or eof",
381        NULL
382    };
383    // NO_CHECK_STYLE_END
384
385    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
386}
387
388ATF_TEST_CASE_WITHOUT_HEAD(atffile_52);
389ATF_TEST_CASE_BODY(atffile_52)
390{
391    const char* input =
392        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
393        "\n"
394        "conf\n"
395        "conf:\n"
396        "conf: foo =\n"
397        "conf: bar = # A comment.\n"
398        "\n"
399        "prop\n"
400        "prop:\n"
401        "prop: foo =\n"
402        "prop: bar = # A comment.\n"
403        "\n"
404        "tp\n"
405        "tp:\n"
406        "tp: # A comment.\n"
407        "\n"
408        "tp-glob\n"
409        "tp-glob:\n"
410        "tp-glob: # A comment.\n"
411    ;
412
413    const char* exp_calls[] = {
414        NULL
415    };
416
417    const char* exp_errors[] = {
418        "3: Unexpected token `<<NEWLINE>>'; expected `:'",
419        "4: Unexpected token `<<NEWLINE>>'; expected variable name",
420        "5: Unexpected token `<<NEWLINE>>'; expected word or quoted string",
421        "6: Unexpected token `#'; expected word or quoted string",
422        "8: Unexpected token `<<NEWLINE>>'; expected `:'",
423        "9: Unexpected token `<<NEWLINE>>'; expected property name",
424        "10: Unexpected token `<<NEWLINE>>'; expected word or quoted string",
425        "11: Unexpected token `#'; expected word or quoted string",
426        "13: Unexpected token `<<NEWLINE>>'; expected `:'",
427        "14: Unexpected token `<<NEWLINE>>'; expected word or quoted string",
428        "15: Unexpected token `#'; expected word or quoted string",
429        "17: Unexpected token `<<NEWLINE>>'; expected `:'",
430        "18: Unexpected token `<<NEWLINE>>'; expected word or quoted string",
431        "19: Unexpected token `#'; expected word or quoted string",
432        NULL
433    };
434
435    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
436}
437
438ATF_TEST_CASE_WITHOUT_HEAD(atffile_53);
439ATF_TEST_CASE_BODY(atffile_53)
440{
441    const char* input =
442        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
443        "\n"
444        "prop: foo = \"Correct value\" # With comment.\n"
445        "\n"
446        "prop: bar = # A comment.\n"
447        "\n"
448        "prop: baz = \"Last variable\"\n"
449        "\n"
450        "# End of file.\n"
451    ;
452
453    const char* exp_calls[] = {
454        "got_prop(foo, Correct value)",
455        NULL
456    };
457
458    const char* exp_errors[] = {
459        "5: Unexpected token `#'; expected word or quoted string",
460        NULL
461    };
462
463    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
464}
465
466ATF_TEST_CASE_WITHOUT_HEAD(atffile_54);
467ATF_TEST_CASE_BODY(atffile_54)
468{
469    const char* input =
470        "Content-Type: application/X-atf-atffile; version=\"1\"\n"
471        "\n"
472        "prop: foo = \"\n"
473        "prop: bar = \"text\n"
474        "prop: baz = \"te\\\"xt\n"
475        "prop: last = \"\\\"\n"
476    ;
477
478    const char* exp_calls[] = {
479        NULL
480    };
481
482    const char* exp_errors[] = {
483        "3: Missing double quotes before end of line",
484        "4: Missing double quotes before end of line",
485        "5: Missing double quotes before end of line",
486        "6: Missing double quotes before end of line",
487        NULL
488    };
489
490    do_parser_test< atffile_reader >(input, exp_calls, exp_errors);
491}
492
493// ------------------------------------------------------------------------
494// Tests cases for the "atffile" class.
495// ------------------------------------------------------------------------
496
497ATF_TEST_CASE(atffile_getters);
498ATF_TEST_CASE_HEAD(atffile_getters) {}
499ATF_TEST_CASE_BODY(atffile_getters) {
500    atf::tests::vars_map config_vars;
501    config_vars["config-var-1"] = "value 1";
502
503    std::vector< std::string > test_program_names;
504    test_program_names.push_back("test-program-1");
505
506    atf::tests::vars_map properties;
507    properties["test-suite"] = "a test name";
508
509    const atf::atf_run::atffile atffile(config_vars, test_program_names,
510                                        properties);
511    ATF_REQUIRE(config_vars == atffile.conf());
512    ATF_REQUIRE(test_program_names == atffile.tps());
513    ATF_REQUIRE(properties == atffile.props());
514}
515
516// ------------------------------------------------------------------------
517// Tests cases for the free functions.
518// ------------------------------------------------------------------------
519
520ATF_TEST_CASE_WITHOUT_HEAD(read_ok_simple);
521ATF_TEST_CASE_BODY(read_ok_simple) {
522    std::auto_ptr< std::ofstream > os = new_atffile();
523    (*os) << "prop: test-suite = foo\n";
524    (*os) << "tp: tp-1\n";
525    (*os) << "conf: var1 = value1\n";
526    (*os) << "tp: tp-2\n";
527    (*os) << "tp: tp-3\n";
528    (*os) << "prop: prop1 = propvalue1\n";
529    (*os) << "conf: var2 = value2\n";
530    (*os).close();
531
532    touch_exec("tp-1");
533    touch_exec("tp-2");
534    touch_exec("tp-3");
535
536    const atf::atf_run::atffile atffile = atf::atf_run::read_atffile(
537        atf::fs::path("Atffile"));
538    ATF_REQUIRE_EQ(2, atffile.conf().size());
539    ATF_REQUIRE_EQ("value1", atffile.conf().find("var1")->second);
540    ATF_REQUIRE_EQ("value2", atffile.conf().find("var2")->second);
541    ATF_REQUIRE_EQ(3, atffile.tps().size());
542    ATF_REQUIRE(is_in("tp-1", atffile.tps()));
543    ATF_REQUIRE(is_in("tp-2", atffile.tps()));
544    ATF_REQUIRE(is_in("tp-3", atffile.tps()));
545    ATF_REQUIRE_EQ(2, atffile.props().size());
546    ATF_REQUIRE_EQ("foo", atffile.props().find("test-suite")->second);
547    ATF_REQUIRE_EQ("propvalue1", atffile.props().find("prop1")->second);
548}
549
550ATF_TEST_CASE_WITHOUT_HEAD(read_ok_some_globs);
551ATF_TEST_CASE_BODY(read_ok_some_globs) {
552    std::auto_ptr< std::ofstream > os = new_atffile();
553    (*os) << "prop: test-suite = foo\n";
554    (*os) << "tp: foo\n";
555    (*os) << "tp-glob: *K*\n";
556    (*os) << "tp: bar\n";
557    (*os) << "tp-glob: t_*\n";
558    (*os).close();
559
560    touch_exec("foo");
561    touch_exec("bar");
562    touch_exec("aK");
563    touch_exec("KKKKK");
564    touch_exec("t_hello");
565    touch_exec("zzzt_hello");
566
567    const atf::atf_run::atffile atffile = atf::atf_run::read_atffile(
568        atf::fs::path("Atffile"));
569    ATF_REQUIRE_EQ(5, atffile.tps().size());
570    ATF_REQUIRE(is_in("foo", atffile.tps()));
571    ATF_REQUIRE(is_in("bar", atffile.tps()));
572    ATF_REQUIRE(is_in("aK", atffile.tps()));
573    ATF_REQUIRE(is_in("KKKKK", atffile.tps()));
574    ATF_REQUIRE(is_in("t_hello", atffile.tps()));
575}
576
577ATF_TEST_CASE_WITHOUT_HEAD(read_missing_test_suite);
578ATF_TEST_CASE_BODY(read_missing_test_suite) {
579    std::auto_ptr< std::ofstream > os = new_atffile();
580    (*os).close();
581
582    try {
583        (void)atf::atf_run::read_atffile(atf::fs::path("Atffile"));
584        ATF_FAIL("Missing property 'test-suite' did not raise an error");
585    } catch (const atf::not_found_error< std::string >& e) {
586        ATF_REQUIRE_EQ("test-suite", e.get_value());
587    }
588}
589
590ATF_TEST_CASE_WITHOUT_HEAD(read_missing_test_program);
591ATF_TEST_CASE_BODY(read_missing_test_program) {
592    std::auto_ptr< std::ofstream > os = new_atffile();
593    (*os) << "tp: foo\n";
594    (*os) << "tp: bar\n";
595    (*os) << "tp: baz\n";
596    (*os).close();
597
598    touch_exec("foo");
599    touch_exec("baz");
600
601    try {
602        (void)atf::atf_run::read_atffile(atf::fs::path("Atffile"));
603        ATF_FAIL("Missing file 'bar' did not raise an error");
604    } catch (const atf::not_found_error< atf::fs::path >& e) {
605        ATF_REQUIRE_EQ("bar", e.get_value().str());
606    }
607}
608
609// ------------------------------------------------------------------------
610// Main.
611// ------------------------------------------------------------------------
612
613ATF_INIT_TEST_CASES(tcs)
614{
615    // Add the test cases for the parser class.
616    ATF_ADD_TEST_CASE(tcs, atffile_1);
617    ATF_ADD_TEST_CASE(tcs, atffile_2);
618    ATF_ADD_TEST_CASE(tcs, atffile_3);
619    ATF_ADD_TEST_CASE(tcs, atffile_4);
620    ATF_ADD_TEST_CASE(tcs, atffile_5);
621    ATF_ADD_TEST_CASE(tcs, atffile_6);
622    ATF_ADD_TEST_CASE(tcs, atffile_50);
623    ATF_ADD_TEST_CASE(tcs, atffile_51);
624    ATF_ADD_TEST_CASE(tcs, atffile_52);
625    ATF_ADD_TEST_CASE(tcs, atffile_53);
626    ATF_ADD_TEST_CASE(tcs, atffile_54);
627
628    // Add the test cases for the atffile class.
629    ATF_ADD_TEST_CASE(tcs, atffile_getters);
630
631    // Add the test cases for the free functions.
632    ATF_ADD_TEST_CASE(tcs, read_ok_simple);
633    ATF_ADD_TEST_CASE(tcs, read_ok_some_globs);
634    ATF_ADD_TEST_CASE(tcs, read_missing_test_suite);
635    ATF_ADD_TEST_CASE(tcs, read_missing_test_program);
636}
637