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
30 extern "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
46 namespace detail = atf::atf_run::detail;
47
48 // ------------------------------------------------------------------------
49 // Auxiliary functions.
50 // ------------------------------------------------------------------------
51
52 namespace {
53
54 static
55 std::auto_ptr< std::ofstream >
new_atffile(void)56 new_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
65 static
66 void
touch_exec(const char * name)67 touch_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
75 static inline
76 bool
is_in(const std::string & value,const std::vector<std::string> & v)77 is_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
88 class atffile_reader : protected detail::atf_atffile_reader {
89 void
got_conf(const std::string & name,const std::string & val)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
got_prop(const std::string & name,const std::string & val)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
got_tp(const std::string & name,bool isglob)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
got_eof(void)109 got_eof(void)
110 {
111 m_calls.push_back("got_eof()");
112 }
113
114 public:
atffile_reader(std::istream & is)115 atffile_reader(std::istream& is) :
116 detail::atf_atffile_reader(is)
117 {
118 }
119
120 void
read(void)121 read(void)
122 {
123 atf_atffile_reader::read();
124 }
125
126 std::vector< std::string > m_calls;
127 };
128
129 ATF_TEST_CASE_WITHOUT_HEAD(atffile_1);
ATF_TEST_CASE_BODY(atffile_1)130 ATF_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
149 ATF_TEST_CASE_WITHOUT_HEAD(atffile_2);
ATF_TEST_CASE_BODY(atffile_2)150 ATF_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
175 ATF_TEST_CASE_WITHOUT_HEAD(atffile_3);
ATF_TEST_CASE_BODY(atffile_3)176 ATF_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
217 ATF_TEST_CASE_WITHOUT_HEAD(atffile_4);
ATF_TEST_CASE_BODY(atffile_4)218 ATF_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
259 ATF_TEST_CASE_WITHOUT_HEAD(atffile_5);
ATF_TEST_CASE_BODY(atffile_5)260 ATF_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
312 ATF_TEST_CASE_WITHOUT_HEAD(atffile_6);
ATF_TEST_CASE_BODY(atffile_6)313 ATF_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
340 ATF_TEST_CASE_WITHOUT_HEAD(atffile_50);
ATF_TEST_CASE_BODY(atffile_50)341 ATF_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
363 ATF_TEST_CASE_WITHOUT_HEAD(atffile_51);
ATF_TEST_CASE_BODY(atffile_51)364 ATF_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
388 ATF_TEST_CASE_WITHOUT_HEAD(atffile_52);
ATF_TEST_CASE_BODY(atffile_52)389 ATF_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
438 ATF_TEST_CASE_WITHOUT_HEAD(atffile_53);
ATF_TEST_CASE_BODY(atffile_53)439 ATF_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
466 ATF_TEST_CASE_WITHOUT_HEAD(atffile_54);
ATF_TEST_CASE_BODY(atffile_54)467 ATF_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
497 ATF_TEST_CASE(atffile_getters);
ATF_TEST_CASE_HEAD(atffile_getters)498 ATF_TEST_CASE_HEAD(atffile_getters) {}
ATF_TEST_CASE_BODY(atffile_getters)499 ATF_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
520 ATF_TEST_CASE_WITHOUT_HEAD(read_ok_simple);
ATF_TEST_CASE_BODY(read_ok_simple)521 ATF_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
550 ATF_TEST_CASE_WITHOUT_HEAD(read_ok_some_globs);
ATF_TEST_CASE_BODY(read_ok_some_globs)551 ATF_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
577 ATF_TEST_CASE_WITHOUT_HEAD(read_missing_test_suite);
ATF_TEST_CASE_BODY(read_missing_test_suite)578 ATF_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
590 ATF_TEST_CASE_WITHOUT_HEAD(read_missing_test_program);
ATF_TEST_CASE_BODY(read_missing_test_program)591 ATF_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
ATF_INIT_TEST_CASES(tcs)613 ATF_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