diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index f158d1a1ef..4a17f0e084 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -83,7 +83,7 @@ jobs: - name: Install dependencies run: | apt update - apt install -y python3-pytest ninja-build clang-tidy python3-pip clang + apt install -y python3-pytest ninja-build clang-tidy python3-pip clang libgtest-dev pip3 install cmake==3.17.* - name: Configure (GCC) run: cmake -Bbuild-gcc -DCMAKE_BUILD_TYPE=Debug -G'Ninja Multi-Config' @@ -144,7 +144,6 @@ jobs: run: | python3 configure.py --bootstrap ./ninja all - ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots python3 misc/ninja_syntax_test.py ./misc/output_test.py diff --git a/CMakeLists.txt b/CMakeLists.txt index c0745af04b..c9529d1562 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,6 +227,34 @@ endif() include(CTest) if(BUILD_TESTING) + find_package(GTest) + if(NOT GTest_FOUND) + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/release-1.10.0.tar.gz + URL_HASH SHA1=9c89be7df9c5e8cb0bc20b3c4b39bf7e82686770 + ) + FetchContent_MakeAvailable(googletest) + + # Before googletest-1.11.0, the CMake files provided by the source archive + # did not define the GTest::gtest target, only the gtest one, so define + # an alias when needed to ensure the rest of this file works with all + # GoogleTest releases. + # + # Note that surprisingly, this is not needed when using GTEST_ROOT to + # point to a local installation, because this one contains CMake-generated + # files that contain the right target definition, and which will be + # picked up by the find_package(GTest) file above. + # + # This comment and the four lines below can be removed once Ninja only + # depends on release-1.11.0 or above. + if (NOT TARGET GTest::gtest) + message(STATUS "Defining GTest::gtest alias to work-around bug in older release.") + add_library(GTest::gtest ALIAS gtest) + endif() + endif() + # Tests all build into ninja_test executable. add_executable(ninja_test src/build_log_test.cc @@ -254,7 +282,8 @@ if(BUILD_TESTING) target_sources(ninja_test PRIVATE src/includes_normalize_test.cc src/msvc_helper_test.cc windows/ninja.manifest) endif() - target_link_libraries(ninja_test PRIVATE libninja libninja-re2c) + find_package(Threads REQUIRED) + target_link_libraries(ninja_test PRIVATE libninja libninja-re2c GTest::gtest Threads::Threads) foreach(perftest build_log_perftest diff --git a/appveyor.yml b/appveyor.yml index 7c39abab87..ecc9f98315 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,7 +27,6 @@ for: pacman -S --quiet --noconfirm --needed re2c 2>&1\n ./configure.py --bootstrap --platform mingw 2>&1\n ./ninja all\n - ./ninja_test 2>&1\n ./misc/ninja_syntax_test.py 2>&1\n\"@" - matrix: only: @@ -35,7 +34,6 @@ for: build_script: - ./configure.py --bootstrap - ./ninja all - - ./ninja_test - misc/ninja_syntax_test.py - misc/output_test.py diff --git a/configure.py b/configure.py index 939153df60..6ee64a8fc9 100755 --- a/configure.py +++ b/configure.py @@ -582,44 +582,6 @@ def has_re2c(): # build.ninja file. n = ninja_writer -n.comment('Tests all build into ninja_test executable.') - -objs = [] -if platform.is_msvc(): - cxxvariables = [('pdb', 'ninja_test.pdb')] - -for name in ['build_log_test', - 'build_test', - 'clean_test', - 'clparser_test', - 'depfile_parser_test', - 'deps_log_test', - 'dyndep_parser_test', - 'disk_interface_test', - 'edit_distance_test', - 'graph_test', - 'json_test', - 'lexer_test', - 'manifest_parser_test', - 'missing_deps_test', - 'ninja_test', - 'state_test', - 'status_test', - 'string_piece_util_test', - 'subprocess_test', - 'test', - 'util_test']: - objs += cxx(name, variables=cxxvariables) -if platform.is_windows(): - for name in ['includes_normalize_test', 'msvc_helper_test']: - objs += cxx(name, variables=cxxvariables) - -ninja_test = n.build(binary('ninja_test'), 'link', objs, implicit=ninja_lib, - variables=[('libs', libs)]) -n.newline() -all_targets += ninja_test - - n.comment('Ancillary executables.') if platform.is_aix() and '-maix64' not in ldflags: diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc index 9214f53495..12965f9fb1 100644 --- a/src/includes_normalize_test.cc +++ b/src/includes_normalize_test.cc @@ -117,10 +117,10 @@ TEST(IncludesNormalize, LongInvalidPath) { // Construct max size path having cwd prefix. // kExactlyMaxPath = "$cwd\\a\\aaaa...aaaa\0"; char kExactlyMaxPath[_MAX_PATH + 1]; - ASSERT_NE(_getcwd(kExactlyMaxPath, sizeof kExactlyMaxPath), NULL); + ASSERT_STRNE(_getcwd(kExactlyMaxPath, sizeof kExactlyMaxPath), NULL); int cwd_len = strlen(kExactlyMaxPath); - ASSERT_LE(cwd_len + 3 + 1, _MAX_PATH) + ASSERT_LE(cwd_len + 3 + 1, _MAX_PATH); kExactlyMaxPath[cwd_len] = '\\'; kExactlyMaxPath[cwd_len + 1] = 'a'; kExactlyMaxPath[cwd_len + 2] = '\\'; diff --git a/src/missing_deps_test.cc b/src/missing_deps_test.cc index 95035d0f7e..12ae8ed42e 100644 --- a/src/missing_deps_test.cc +++ b/src/missing_deps_test.cc @@ -33,7 +33,7 @@ struct MissingDependencyScannerTest : public testing::Test { scanner_(&delegate_, &deps_log_, &state_, &filesystem_) { std::string err; deps_log_.OpenForWrite(kTestDepsLogFilename, &err); - ASSERT_EQ("", err); + EXPECT_EQ("", err); } ~MissingDependencyScannerTest() { @@ -165,4 +165,3 @@ TEST_F(MissingDependencyScannerTest, CycleInGraph) { std::vector nodes = state_.RootNodes(&err); ASSERT_NE("", err); } - diff --git a/src/ninja_test.cc b/src/ninja_test.cc index 6720dec4ff..7616c85bcd 100644 --- a/src/ninja_test.cc +++ b/src/ninja_test.cc @@ -12,151 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include -#include - -#ifdef _WIN32 -#include "getopt.h" -#elif defined(_AIX) -#include "getopt.h" -#include -#else -#include -#endif - -#include "test.h" -#include "line_printer.h" - -using namespace std; - -struct RegisteredTest { - testing::Test* (*factory)(); - const char *name; - bool should_run; -}; -// This can't be a vector because tests call RegisterTest from static -// initializers and the order static initializers run it isn't specified. So -// the vector constructor isn't guaranteed to run before all of the -// RegisterTest() calls. -static RegisteredTest tests[10000]; -testing::Test* g_current_test; -static int ntests; -static LinePrinter printer; - -void RegisterTest(testing::Test* (*factory)(), const char* name) { - tests[ntests].factory = factory; - tests[ntests++].name = name; -} - -namespace { -string StringPrintf(const char* format, ...) { - const int N = 1024; - char buf[N]; - - va_list ap; - va_start(ap, format); - vsnprintf(buf, N, format, ap); - va_end(ap); - - return buf; -} - -void Usage() { - fprintf(stderr, -"usage: ninja_tests [options]\n" -"\n" -"options:\n" -" --gtest_filter=POSITIVE_PATTERN[-NEGATIVE_PATTERN]\n" -" Run tests whose names match the positive but not the negative pattern.\n" -" '*' matches any substring. (gtest's ':', '?' are not implemented).\n"); -} - -bool PatternMatchesString(const char* pattern, const char* str) { - switch (*pattern) { - case '\0': - case '-': return *str == '\0'; - case '*': return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || - PatternMatchesString(pattern + 1, str); - default: return *pattern == *str && - PatternMatchesString(pattern + 1, str + 1); - } -} - -bool TestMatchesFilter(const char* test, const char* filter) { - // Split --gtest_filter at '-' into positive and negative filters. - const char* const dash = strchr(filter, '-'); - const char* pos = dash == filter ? "*" : filter; //Treat '-test1' as '*-test1' - const char* neg = dash ? dash + 1 : ""; - return PatternMatchesString(pos, test) && !PatternMatchesString(neg, test); -} - -bool ReadFlags(int* argc, char*** argv, const char** test_filter) { - enum { OPT_GTEST_FILTER = 1 }; - const option kLongOptions[] = { - { "gtest_filter", required_argument, NULL, OPT_GTEST_FILTER }, - { NULL, 0, NULL, 0 } - }; - - int opt; - while ((opt = getopt_long(*argc, *argv, "h", kLongOptions, NULL)) != -1) { - switch (opt) { - case OPT_GTEST_FILTER: - if (strchr(optarg, '?') == NULL && strchr(optarg, ':') == NULL) { - *test_filter = optarg; - break; - } // else fall through. - default: - Usage(); - return false; - } - } - *argv += optind; - *argc -= optind; - return true; -} - -} // namespace - -bool testing::Test::Check(bool condition, const char* file, int line, - const char* error) { - if (!condition) { - printer.PrintOnNewLine( - StringPrintf("*** Failure in %s:%d\n%s\n", file, line, error)); - failed_ = true; - } - return condition; -} +#include int main(int argc, char **argv) { - int tests_started = 0; - - const char* test_filter = "*"; - if (!ReadFlags(&argc, &argv, &test_filter)) - return 1; - - int nactivetests = 0; - for (int i = 0; i < ntests; i++) - if ((tests[i].should_run = TestMatchesFilter(tests[i].name, test_filter))) - ++nactivetests; - - bool passed = true; - for (int i = 0; i < ntests; i++) { - if (!tests[i].should_run) continue; - - ++tests_started; - testing::Test* test = tests[i].factory(); - printer.Print( - StringPrintf("[%d/%d] %s", tests_started, nactivetests, tests[i].name), - LinePrinter::ELIDE); - test->SetUp(); - test->Run(); - test->TearDown(); - if (test->Failed()) - passed = false; - delete test; - } - - printer.PrintOnNewLine(passed ? "passed\n" : "failed\n"); - return passed ? EXIT_SUCCESS : EXIT_FAILURE; + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/src/test.h b/src/test.h index 238cb96b6d..a4b9e19f75 100644 --- a/src/test.h +++ b/src/test.h @@ -15,94 +15,11 @@ #ifndef NINJA_TEST_H_ #define NINJA_TEST_H_ +#include + #include "disk_interface.h" #include "manifest_parser.h" #include "state.h" -#include "util.h" - -// A tiny testing framework inspired by googletest, but much simpler and -// faster to compile. It supports most things commonly used from googltest. The -// most noticeable things missing: EXPECT_* and ASSERT_* don't support -// streaming notes to them with operator<<, and for failing tests the lhs and -// rhs are not printed. That's so that this header does not have to include -// sstream, which slows down building ninja_test almost 20%. -namespace testing { -class Test { - bool failed_; - int assertion_failures_; - public: - Test() : failed_(false), assertion_failures_(0) {} - virtual ~Test() {} - virtual void SetUp() {} - virtual void TearDown() {} - virtual void Run() = 0; - - bool Failed() const { return failed_; } - int AssertionFailures() const { return assertion_failures_; } - void AddAssertionFailure() { assertion_failures_++; } - bool Check(bool condition, const char* file, int line, const char* error); -}; -} - -void RegisterTest(testing::Test* (*)(), const char*); - -extern testing::Test* g_current_test; -#define TEST_F_(x, y, name) \ - struct y : public x { \ - static testing::Test* Create() { return g_current_test = new y; } \ - virtual void Run(); \ - }; \ - struct Register##y { \ - Register##y() { RegisterTest(y::Create, name); } \ - }; \ - Register##y g_register_##y; \ - void y::Run() - -#define TEST_F(x, y) TEST_F_(x, x##y, #x "." #y) -#define TEST(x, y) TEST_F_(testing::Test, x##y, #x "." #y) - -#define EXPECT_EQ(a, b) \ - g_current_test->Check(a == b, __FILE__, __LINE__, #a " == " #b) -#define EXPECT_NE(a, b) \ - g_current_test->Check(a != b, __FILE__, __LINE__, #a " != " #b) -#define EXPECT_GT(a, b) \ - g_current_test->Check(a > b, __FILE__, __LINE__, #a " > " #b) -#define EXPECT_LT(a, b) \ - g_current_test->Check(a < b, __FILE__, __LINE__, #a " < " #b) -#define EXPECT_GE(a, b) \ - g_current_test->Check(a >= b, __FILE__, __LINE__, #a " >= " #b) -#define EXPECT_LE(a, b) \ - g_current_test->Check(a <= b, __FILE__, __LINE__, #a " <= " #b) -#define EXPECT_TRUE(a) \ - g_current_test->Check(static_cast(a), __FILE__, __LINE__, #a) -#define EXPECT_FALSE(a) \ - g_current_test->Check(!static_cast(a), __FILE__, __LINE__, #a) - -#define ASSERT_EQ(a, b) \ - if (!EXPECT_EQ(a, b)) { g_current_test->AddAssertionFailure(); return; } -#define ASSERT_NE(a, b) \ - if (!EXPECT_NE(a, b)) { g_current_test->AddAssertionFailure(); return; } -#define ASSERT_GT(a, b) \ - if (!EXPECT_GT(a, b)) { g_current_test->AddAssertionFailure(); return; } -#define ASSERT_LT(a, b) \ - if (!EXPECT_LT(a, b)) { g_current_test->AddAssertionFailure(); return; } -#define ASSERT_GE(a, b) \ - if (!EXPECT_GE(a, b)) { g_current_test->AddAssertionFailure(); return; } -#define ASSERT_LE(a, b) \ - if (!EXPECT_LE(a, b)) { g_current_test->AddAssertionFailure(); return; } -#define ASSERT_TRUE(a) \ - if (!EXPECT_TRUE(a)) { g_current_test->AddAssertionFailure(); return; } -#define ASSERT_FALSE(a) \ - if (!EXPECT_FALSE(a)) { g_current_test->AddAssertionFailure(); return; } -#define ASSERT_NO_FATAL_FAILURE(a) \ - { \ - int fail_count = g_current_test->AssertionFailures(); \ - a; \ - if (fail_count != g_current_test->AssertionFailures()) { \ - g_current_test->AddAssertionFailure(); \ - return; \ - } \ - } // Support utilities for tests.