1// Copyright 2015 The Kyua Authors.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9//   notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright
11//   notice, this list of conditions and the following disclaimer in the
12//   documentation and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors
14//   may be used to endorse or promote products derived from this software
15//   without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29extern "C" {
30#include <sys/stat.h>
31
32#include <unistd.h>
33
34extern char** environ;
35}
36
37#include <cstdlib>
38#include <cstring>
39#include <fstream>
40#include <iostream>
41
42#include "utils/env.hpp"
43#include "utils/format/containers.ipp"
44#include "utils/format/macros.hpp"
45#include "utils/fs/path.hpp"
46#include "utils/optional.ipp"
47#include "utils/test_utils.ipp"
48
49namespace fs = utils::fs;
50
51
52namespace {
53
54
55/// Logs an error message and exits the test with an error code.
56///
57/// \param str The error message to log.
58static void
59fail(const std::string& str)
60{
61    std::cerr << str << '\n';
62    std::exit(EXIT_FAILURE);
63}
64
65
66/// A test scenario that validates the TEST_ENV_* variables.
67static void
68test_check_configuration_variables(void)
69{
70    std::set< std::string > vars;
71    char** iter;
72    for (iter = environ; *iter != NULL; ++iter) {
73        if (std::strstr(*iter, "TEST_ENV_") == *iter) {
74            vars.insert(*iter);
75        }
76    }
77
78    std::set< std::string > exp_vars;
79    exp_vars.insert("TEST_ENV_first=some value");
80    exp_vars.insert("TEST_ENV_second=some other value");
81    if (vars == exp_vars) {
82        std::cout << "1..1\n"
83                  << "ok 1\n";
84    } else {
85        std::cout << "1..1\n"
86                  << "not ok 1\n"
87                  << F("    Expected: %s\nFound: %s\n") % exp_vars % vars;
88    }
89}
90
91
92/// A test scenario that crashes.
93static void
94test_crash(void)
95{
96    utils::abort_without_coredump();
97}
98
99
100/// A test scenario that reports some tests as failed.
101static void
102test_fail(void)
103{
104    std::cout << "1..5\n"
105              << "ok 1 - This is good!\n"
106              << "not ok 2\n"
107              << "ok 3 - TODO Consider this as passed\n"
108              << "ok 4\n"
109              << "not ok 5\n";
110}
111
112
113/// A test scenario that passes.
114static void
115test_pass(void)
116{
117    std::cout << "1..4\n"
118              << "ok 1 - This is good!\n"
119              << "non-result data\n"
120              << "ok 2 - SKIP Consider this as passed\n"
121              << "ok 3 - TODO Consider this as passed\n"
122              << "ok 4\n";
123}
124
125
126/// A test scenario that passes but then exits with non-zero.
127static void
128test_pass_but_exit_failure(void)
129{
130    std::cout << "1..2\n"
131              << "ok 1\n"
132              << "ok 2\n";
133    std::exit(70);
134}
135
136
137/// A test scenario that times out.
138///
139/// Note that the timeout is defined in the Kyuafile, as the TAP interface has
140/// no means for test programs to specify this by themselves.
141static void
142test_timeout(void)
143{
144    std::cout << "1..2\n"
145              << "ok 1\n";
146
147    ::sleep(10);
148    const fs::path control_dir = fs::path(utils::getenv("CONTROL_DIR").get());
149    std::ofstream file((control_dir / "cookie").c_str());
150    if (!file)
151        fail("Failed to create the control cookie");
152    file.close();
153}
154
155
156}  // anonymous namespace
157
158
159/// Entry point to the test program.
160///
161/// The caller can select which test scenario to run by modifying the program's
162/// basename on disk (either by a copy or by a hard link).
163///
164/// \todo It may be worth to split this binary into separate, smaller binaries,
165/// one for every "test scenario".  We use this program as a dispatcher for
166/// different "main"s, the only reason being to keep the amount of helper test
167/// programs to a minimum.  However, putting this each function in its own
168/// binary could simplify many other things.
169///
170/// \param argc The number of CLI arguments.
171/// \param argv The CLI arguments themselves.  These are not used because
172///     Kyua will not pass any arguments to the plain test program.
173int
174main(int argc, char** argv)
175{
176    if (argc != 1) {
177        std::cerr << "No arguments allowed; select the test scenario with the "
178            "program's basename\n";
179        return EXIT_FAILURE;
180    }
181
182    const std::string& test_scenario = fs::path(argv[0]).leaf_name();
183
184    if (test_scenario == "check_configuration_variables")
185        test_check_configuration_variables();
186    else if (test_scenario == "crash")
187        test_crash();
188    else if (test_scenario == "fail")
189        test_fail();
190    else if (test_scenario == "pass")
191        test_pass();
192    else if (test_scenario == "pass_but_exit_failure")
193        test_pass_but_exit_failure();
194    else if (test_scenario == "timeout")
195        test_timeout();
196    else {
197        std::cerr << "Unknown test scenario\n";
198        return EXIT_FAILURE;
199    }
200
201    return EXIT_SUCCESS;
202}
203