plain_helpers.cpp revision 2af35c15f0b6443224e3bf1c396805a387f42410
1// Copyright 2011 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#include <sstream>
42
43#include "utils/env.hpp"
44#include "utils/format/containers.ipp"
45#include "utils/format/macros.hpp"
46#include "utils/fs/operations.hpp"
47#include "utils/fs/path.hpp"
48#include "utils/optional.ipp"
49#include "utils/test_utils.ipp"
50
51namespace fs = utils::fs;
52
53using utils::optional;
54
55
56namespace {
57
58
59/// Gets the name of the test case to run.
60///
61/// We use the value of the TEST_CASE environment variable if present, or
62/// else the basename of the test program.
63///
64/// \param arg0 Value of argv[0] as passed to main().
65///
66/// \return A test case name.  The name may not be valid.
67static std::string
68guess_test_case_name(const char* arg0)
69{
70    const optional< std::string > test_case_env = utils::getenv("TEST_CASE");
71    if (test_case_env) {
72        return test_case_env.get();
73    } else {
74        return fs::path(arg0).leaf_name();
75    }
76}
77
78
79/// Logs an error message and exits the test with an error code.
80///
81/// \param str The error message to log.
82static void
83fail(const std::string& str)
84{
85    std::cerr << str << '\n';
86    std::exit(EXIT_FAILURE);
87}
88
89
90/// A test case that validates the TEST_ENV_* variables.
91static void
92test_check_configuration_variables(void)
93{
94    std::set< std::string > vars;
95    char** iter;
96    for (iter = environ; *iter != NULL; ++iter) {
97        if (std::strstr(*iter, "TEST_ENV_") == *iter) {
98            vars.insert(*iter);
99        }
100    }
101
102    std::set< std::string > exp_vars;
103    exp_vars.insert("TEST_ENV_first=some value");
104    exp_vars.insert("TEST_ENV_second=some other value");
105    if (vars != exp_vars) {
106        fail(F("Expected: %s\nFound: %s\n") % exp_vars % vars);
107    }
108}
109
110
111/// A test case that crashes.
112static void
113test_crash(void)
114{
115    utils::abort_without_coredump();
116}
117
118
119/// A test case that exits with a non-zero exit code, and not 1.
120static void
121test_fail(void)
122{
123    std::exit(8);
124}
125
126
127/// A test case that passes.
128static void
129test_pass(void)
130{
131}
132
133
134/// A test case that spawns a subchild that gets stuck.
135///
136/// This test case is used by the caller to validate that the whole process tree
137/// is terminated when the test case is killed.
138static void
139test_spawn_blocking_child(void)
140{
141    pid_t pid = ::fork();
142    if (pid == -1)
143        fail("Cannot fork subprocess");
144    else if (pid == 0) {
145        for (;;)
146            ::pause();
147    } else {
148        const fs::path name = fs::path(utils::getenv("CONTROL_DIR").get()) /
149            "pid";
150        std::ofstream pidfile(name.c_str());
151        if (!pidfile)
152            fail("Failed to create the pidfile");
153        pidfile << pid;
154        pidfile.close();
155    }
156}
157
158
159/// A test case that times out.
160///
161/// Note that the timeout is defined in the Kyuafile, as the plain interface has
162/// no means for test programs to specify this by themselves.
163static void
164test_timeout(void)
165{
166    ::sleep(10);
167    const fs::path control_dir = fs::path(utils::getenv("CONTROL_DIR").get());
168    std::ofstream file((control_dir / "cookie").c_str());
169    if (!file)
170        fail("Failed to create the control cookie");
171    file.close();
172}
173
174
175/// A test case that performs basic checks on the runtime environment.
176///
177/// If the runtime environment does not look clean (according to the rules in
178/// the Kyua runtime properties), the test fails.
179static void
180test_validate_isolation(void)
181{
182    if (utils::getenv("HOME").get() == "fake-value")
183        fail("HOME not reset");
184    if (utils::getenv("LANG"))
185        fail("LANG not unset");
186}
187
188
189}  // anonymous namespace
190
191
192/// Entry point to the test program.
193///
194/// The caller can select which test case to run by defining the TEST_CASE
195/// environment variable.  This is not "standard", in the sense this is not a
196/// generic property of the plain test case interface.
197///
198/// \todo It may be worth to split this binary into separate, smaller binaries,
199/// one for every "test case".  We use this program as a dispatcher for
200/// different "main"s, the only reason being to keep the amount of helper test
201/// programs to a minimum.  However, putting this each function in its own
202/// binary could simplify many other things.
203///
204/// \param argc The number of CLI arguments.
205/// \param argv The CLI arguments themselves.  These are not used because
206///     Kyua will not pass any arguments to the plain test program.
207int
208main(int argc, char** argv)
209{
210    if (argc != 1) {
211        std::cerr << "No arguments allowed; select the test case with the "
212            "TEST_CASE variable";
213        return EXIT_FAILURE;
214    }
215
216    const std::string& test_case = guess_test_case_name(argv[0]);
217
218    if (test_case == "check_configuration_variables")
219        test_check_configuration_variables();
220    else if (test_case == "crash")
221        test_crash();
222    else if (test_case == "fail")
223        test_fail();
224    else if (test_case == "pass")
225        test_pass();
226    else if (test_case == "spawn_blocking_child")
227        test_spawn_blocking_child();
228    else if (test_case == "timeout")
229        test_timeout();
230    else if (test_case == "validate_isolation")
231        test_validate_isolation();
232    else {
233        std::cerr << "Unknown test case";
234        return EXIT_FAILURE;
235    }
236
237    return EXIT_SUCCESS;
238}
239