1// Copyright 2011 Google Inc. 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 29#include "engine/drivers/list_tests.hpp" 30 31extern "C" { 32#include <sys/stat.h> 33 34#include <unistd.h> 35} 36 37#include <map> 38#include <set> 39 40#include <atf-c++.hpp> 41 42#include "cli/cmd_list.hpp" 43#include "cli/common.ipp" 44#include "engine/exceptions.hpp" 45#include "engine/filters.hpp" 46#include "utils/env.hpp" 47#include "utils/format/macros.hpp" 48#include "utils/optional.ipp" 49 50namespace list_tests = engine::drivers::list_tests; 51namespace fs = utils::fs; 52 53using utils::none; 54using utils::optional; 55 56 57namespace { 58 59 60/// Gets the path to the helpers for this test program. 61/// 62/// \param test_case A pointer to the currently running test case. 63/// 64/// \return The path to the helpers binary. 65static fs::path 66helpers(const atf::tests::tc* test_case) 67{ 68 return fs::path(test_case->get_config_var("srcdir")) / 69 "list_tests_helpers"; 70} 71 72 73/// Hooks to capture the incremental listing of test cases. 74class capture_hooks : public list_tests::base_hooks { 75public: 76 /// Set of the listed test cases in a program:test_case form. 77 std::set< std::string > test_cases; 78 79 /// Called when a test case is identified in a test suite. 80 /// 81 /// \param test_case The data describing the test case. 82 virtual void 83 got_test_case(const engine::test_case& test_case) 84 { 85 test_cases.insert(F("%s:%s") % 86 test_case.container_test_program().relative_path() % 87 test_case.name()); 88 } 89}; 90 91 92/// Creates a mock test suite. 93/// 94/// \param tc Pointer to the caller test case; needed to obtain the srcdir 95/// variable of the caller. 96/// \param source_root Basename of the directory that will contain the 97/// Kyuafiles. 98/// \param build_root Basename of the directory that will contain the test 99/// programs. May or may not be the same as source_root. 100static void 101create_helpers(const atf::tests::tc* tc, const fs::path& source_root, 102 const fs::path& build_root) 103{ 104 ATF_REQUIRE(::mkdir(source_root.c_str(), 0755) != -1); 105 ATF_REQUIRE(::mkdir((source_root / "dir").c_str(), 0755) != -1); 106 if (source_root != build_root) { 107 ATF_REQUIRE(::mkdir(build_root.c_str(), 0755) != -1); 108 ATF_REQUIRE(::mkdir((build_root / "dir").c_str(), 0755) != -1); 109 } 110 ATF_REQUIRE(::symlink(helpers(tc).c_str(), 111 (build_root / "dir/program").c_str()) != -1); 112 113 atf::utils::create_file( 114 (source_root / "Kyuafile").str(), 115 "syntax(2)\n" 116 "include('dir/Kyuafile')\n"); 117 118 atf::utils::create_file( 119 (source_root / "dir/Kyuafile").str(), 120 "syntax(2)\n" 121 "atf_test_program{name='program', test_suite='suite-name'}\n"); 122} 123 124 125/// Runs the mock test suite. 126/// 127/// \param source_root Path to the directory that contains the Kyuafiles. 128/// \param build_root If not none, path to the directory that contains the test 129/// programs. 130/// \param hooks The hooks to use during the listing. 131/// \param filter_program If not null, the filter on the test program name. 132/// \param filter_test_case If not null, the filter on the test case name. 133/// 134/// \return The result data of the driver. 135static list_tests::result 136run_helpers(const fs::path& source_root, 137 const optional< fs::path > build_root, 138 list_tests::base_hooks& hooks, 139 const char* filter_program = NULL, 140 const char* filter_test_case = NULL) 141{ 142 std::set< engine::test_filter > filters; 143 if (filter_program != NULL && filter_test_case != NULL) 144 filters.insert(engine::test_filter(fs::path(filter_program), 145 filter_test_case)); 146 147 return list_tests::drive(source_root / "Kyuafile", build_root, filters, 148 hooks); 149} 150 151 152} // anonymous namespace 153 154 155ATF_TEST_CASE_WITHOUT_HEAD(one_test_case); 156ATF_TEST_CASE_BODY(one_test_case) 157{ 158 utils::setenv("TESTS", "some_properties"); 159 capture_hooks hooks; 160 create_helpers(this, fs::path("root"), fs::path("root")); 161 run_helpers(fs::path("root"), none, hooks); 162 163 std::set< std::string > exp_test_cases; 164 exp_test_cases.insert("dir/program:some_properties"); 165 ATF_REQUIRE(exp_test_cases == hooks.test_cases); 166} 167 168 169ATF_TEST_CASE_WITHOUT_HEAD(many_test_cases); 170ATF_TEST_CASE_BODY(many_test_cases) 171{ 172 utils::setenv("TESTS", "no_properties some_properties"); 173 capture_hooks hooks; 174 create_helpers(this, fs::path("root"), fs::path("root")); 175 run_helpers(fs::path("root"), none, hooks); 176 177 std::set< std::string > exp_test_cases; 178 exp_test_cases.insert("dir/program:no_properties"); 179 exp_test_cases.insert("dir/program:some_properties"); 180 ATF_REQUIRE(exp_test_cases == hooks.test_cases); 181} 182 183 184ATF_TEST_CASE_WITHOUT_HEAD(filter_match); 185ATF_TEST_CASE_BODY(filter_match) 186{ 187 utils::setenv("TESTS", "no_properties some_properties"); 188 capture_hooks hooks; 189 create_helpers(this, fs::path("root"), fs::path("root")); 190 run_helpers(fs::path("root"), none, hooks, "dir/program", 191 "some_properties"); 192 193 std::set< std::string > exp_test_cases; 194 exp_test_cases.insert("dir/program:some_properties"); 195 ATF_REQUIRE(exp_test_cases == hooks.test_cases); 196} 197 198 199ATF_TEST_CASE_WITHOUT_HEAD(build_root); 200ATF_TEST_CASE_BODY(build_root) 201{ 202 utils::setenv("TESTS", "no_properties some_properties"); 203 capture_hooks hooks; 204 create_helpers(this, fs::path("source"), fs::path("build")); 205 run_helpers(fs::path("source"), utils::make_optional(fs::path("build")), 206 hooks); 207 208 std::set< std::string > exp_test_cases; 209 exp_test_cases.insert("dir/program:no_properties"); 210 exp_test_cases.insert("dir/program:some_properties"); 211 ATF_REQUIRE(exp_test_cases == hooks.test_cases); 212} 213 214 215ATF_TEST_CASE_WITHOUT_HEAD(crash); 216ATF_TEST_CASE_BODY(crash) 217{ 218 utils::setenv("TESTS", "crash_list some_properties"); 219 capture_hooks hooks; 220 create_helpers(this, fs::path("root"), fs::path("root")); 221 run_helpers(fs::path("root"), none, hooks, "dir/program"); 222 223 std::set< std::string > exp_test_cases; 224 exp_test_cases.insert("dir/program:__test_cases_list__"); 225 ATF_REQUIRE(exp_test_cases == hooks.test_cases); 226} 227 228 229ATF_INIT_TEST_CASES(tcs) 230{ 231 ATF_ADD_TEST_CASE(tcs, one_test_case); 232 ATF_ADD_TEST_CASE(tcs, many_test_cases); 233 ATF_ADD_TEST_CASE(tcs, filter_match); 234 ATF_ADD_TEST_CASE(tcs, build_root); 235 ATF_ADD_TEST_CASE(tcs, crash); 236} 237