blob: 4adc3c9116e022553e979fb7626c9d44e78e2c2f [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef NINJA_TEST_H_
#define NINJA_TEST_H_
#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_;
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<bool>(a), __FILE__, __LINE__, #a)
#define EXPECT_FALSE(a) \
g_current_test->Check(!static_cast<bool>(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; }
{ \
int fail_count = g_current_test->AssertionFailures(); \
a; \
if (fail_count != g_current_test->AssertionFailures()) { \
g_current_test->AddAssertionFailure(); \
return; \
} \
// Support utilities for tests.
struct Node;
/// A base test fixture that includes a State object with a
/// builtin "cat" rule.
struct StateTestWithBuiltinRules : public testing::Test {
/// Add a "cat" rule to \a state. Used by some tests; it's
/// otherwise done by the ctor to state_.
void AddCatRule(State* state);
/// Short way to get a Node by its path from state_.
Node* GetNode(const string& path);
State state_;
void AssertParse(State* state, const char* input,
ManifestParserOptions = ManifestParserOptions());
void AssertHash(const char* expected, uint64_t actual);
void VerifyGraph(const State& state);
/// An implementation of DiskInterface that uses an in-memory representation
/// of disk state. It also logs file accesses and directory creations
/// so it can be used by tests to verify disk access patterns.
struct VirtualFileSystem : public DiskInterface {
VirtualFileSystem() : now_(1) {}
/// "Create" a file with contents.
void Create(const string& path, const string& contents);
/// "Create" a symlink pointing to another path.
void CreateSymlink(const string& path, const string& dest);
/// Tick "time" forwards; subsequent file operations will be newer than
/// previous ones.
int Tick() {
return ++now_;
// DiskInterface
virtual TimeStamp Stat(const string& path, string* err) const;
virtual TimeStamp LStat(const string& path, bool* is_dir, bool* is_symlink, string* err) const;
virtual bool IsStatThreadSafe() const;
virtual bool WriteFile(const string& path, const string& contents);
virtual bool MakeDir(const string& path);
virtual Status ReadFile(const string& path, string* contents, string* err);
virtual Status LoadFile(const std::string& path,
std::unique_ptr<LoadedFile>* result,
std::string* err);
virtual int RemoveFile(const string& path);
/// An entry for a single in-memory file.
struct Entry {
int mtime;
string stat_error; // If mtime is -1.
string contents;
bool is_symlink = false;
struct DirEntry {
int mtime;
string stat_error; // If mtime is -1.
vector<string> directories_made_;
vector<string> files_read_;
typedef map<string, DirEntry> DirMap;
DirMap dirs_;
typedef map<string, Entry> FileMap;
FileMap files_;
set<string> files_removed_;
set<string> files_created_;
/// A simple fake timestamp for file operations.
int now_;
struct ScopedTempDir {
/// Create a temporary directory and chdir into it.
void CreateAndEnter(const string& name);
/// Clean up the temporary directory.
void Cleanup();
/// The temp directory containing our dir.
string start_dir_;
/// The subdirectory name for our dir, or empty if it hasn't been set up.
string temp_dir_name_;
// A C++11 version of operator "" s from C++14's std::string_literals.
inline std::string operator "" _s(const char* str, size_t len) {
return { str, len };
#endif // NINJA_TEST_H_