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