blob: 9701e28268d2a109bc3663e52dd407d1d1c3cd06 [file] [log] [blame]
// Copyright 2014 The Goma Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "deps_cache.h"
#include <glog/logging.h>
#include <gtest/gtest.h>
#include <fstream>
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "absl/memory/memory.h"
#include "compiler_flags.h"
#include "compiler_info.h"
#include "cxx/cxx_compiler_info.h"
#include "cxx/include_processor/include_cache.h"
#include "file_helper.h"
#include "gcc_flags.h"
#include "java/java_compiler_info.h"
#include "java_flags.h"
#include "path.h"
#include "path_resolver.h"
#include "prototmp/deps_cache_data.pb.h"
#include "subprocess.h"
#include "unittest_util.h"
#include "vc_flags.h"
using std::string;
namespace {
const int kDepsCacheAliveDuration = 3 * 24 * 3600;
const int kDepsCacheThreshold = 10;
const int kDepsCacheMaxProtoSizeInMB = 64;
}
namespace devtools_goma {
class DepsCacheTest : public testing::Test {
protected:
typedef DepsCache::DepsHashId DepsHashId;
void SetUp() override {
tmpdir_ = absl::make_unique<TmpdirUtil>("deps_cache_test");
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
CHECK(dc_ != nullptr) << "dc_ == nullptr";
identifier_count_ = 0;
}
void TearDown() override {
DepsCache::Quit();
IncludeCache::Quit();
tmpdir_.reset();
}
void SetFileStat(FileStatCache* cache,
const string& filename,
const FileStat& file_stat) {
std::pair<FileStatCache::FileStatMap::iterator, bool> p =
cache->file_stats_.insert(std::make_pair(filename, file_stat));
if (!p.second)
p.first->second = file_stat;
}
bool GetDepsHashId(const DepsCache::Identifier& identifier,
const string& filename,
DepsCache::DepsHashId* deps_hash_id) const {
CHECK(identifier.has_value());
FilenameIdTable::Id id = dc_->filename_id_table_.ToId(filename);
if (id == FilenameIdTable::kInvalidId)
return false;
return dc_->GetDepsHashId(identifier, id, deps_hash_id);
}
bool UpdateLastUsedTime(const DepsCache::Identifier& identifier,
time_t last_used_time) {
CHECK(identifier.has_value());
return dc_->UpdateLastUsedTime(identifier, last_used_time);
}
void UpdateGomaBuiltRevision() {
const std::string deps_path =
file::JoinPath(tmpdir_->tmpdir(), ".goma_deps");
const std::string deps_sha256_path =
file::JoinPath(tmpdir_->tmpdir(), ".goma_deps.sha256");
GomaDeps goma_deps;
// Load GomaDeps.
{
std::ifstream dot_goma_deps(deps_path.c_str(), std::ifstream::binary);
ASSERT_TRUE(dot_goma_deps.is_open());
ASSERT_TRUE(goma_deps.ParseFromIstream(&dot_goma_deps));
}
goma_deps.set_built_revision(goma_deps.built_revision() + "-new");
// Save GomaDeps + .sha256
// Without updating .sha256, integrity check will revoke the cache. That's
// not what we wan to test.
{
std::ofstream dot_goma_deps(deps_path.c_str(), std::ofstream::binary);
ASSERT_TRUE(dot_goma_deps.is_open());
ASSERT_TRUE(goma_deps.SerializeToOstream(&dot_goma_deps));
}
{
std::string sha256_str;
ASSERT_TRUE(GomaSha256FromFile(deps_path, &sha256_str));
ASSERT_TRUE(WriteStringToFile(sha256_str, deps_sha256_path));
}
}
void UpdateIdentifierLastUsedTime(const DepsCache::Identifier& identifier,
time_t last_used_time) {
CHECK(identifier.has_value());
const string& deps_path = file::JoinPath(tmpdir_->tmpdir(), ".goma_deps");
GomaDeps goma_deps;
// Load GomaDeps
{
std::ifstream dot_goma_deps(deps_path.c_str(), std::ifstream::binary);
ASSERT_TRUE(dot_goma_deps.is_open());
ASSERT_TRUE(goma_deps.ParseFromIstream(&dot_goma_deps));
}
GomaDependencyTable* table = goma_deps.mutable_dependency_table();
for (int i = 0; i < table->record_size(); ++i) {
GomaDependencyTableRecord* record = table->mutable_record(i);
if (record->identifier() == identifier.value().ToHexString()) {
record->set_last_used_time(last_used_time);
}
}
// Save GomaDeps
{
std::ofstream dot_goma_deps(deps_path.c_str(), std::ofstream::binary);
ASSERT_TRUE(dot_goma_deps.is_open());
ASSERT_TRUE(goma_deps.SerializeToOstream(&dot_goma_deps));
}
}
std::unique_ptr<CompilerInfoData> CreateBarebornCompilerInfo(
const string& name) {
std::unique_ptr<CompilerInfoData> cid(new CompilerInfoData);
cid->set_found(true);
cid->set_name(name);
cid->set_hash(name + "1234567890");
cid->mutable_cxx();
return cid;
}
FilenameIdTable::Id GetFilenameTableId(const string& filename) {
return dc_->filename_id_table_.ToId(filename);
}
DepsCache::Identifier MakeDepsIdentifier(
const CompilerInfo& compiler_info,
const CompilerFlags& compiler_flags) {
return DepsCache::MakeDepsIdentifier(compiler_info, compiler_flags);
}
bool SetDependencies(const DepsCache::Identifier& identifier,
const string& input_file,
const std::set<string>& dependencies,
FileStatCache* file_stat_cache) {
return dc_->SetDependencies(identifier, tmpdir_->realcwd(), input_file,
dependencies, file_stat_cache);
}
bool GetDependencies(const DepsCache::Identifier& identifier,
const string& input_file,
std::set<string>* dependencies,
FileStatCache* file_stat_cache) const {
return dc_->GetDependencies(identifier, tmpdir_->realcwd(), input_file,
dependencies, file_stat_cache);
}
void RemoveDependency(const DepsCache::Identifier& identifier) {
return dc_->RemoveDependency(identifier);
}
int DepsCacheSize() const { return static_cast<int>(dc_->deps_table_size()); }
DepsCache::Identifier MakeFreshIdentifier() {
SHA256HashValue hash_value;
SHA256HashValue::ConvertFromHexString(
"1234567890123456789012345678901234567890123456789012345678901234",
&hash_value);
int* p = reinterpret_cast<int*>(&hash_value);
*p = identifier_count_++;
return DepsCache::Identifier(hash_value);
}
std::unique_ptr<TmpdirUtil> tmpdir_;
DepsCache* dc_;
int identifier_count_;
};
TEST_F(DepsCacheTest, SetGetDependencies) {
const DepsCache::Identifier identifier = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
// First compile.
{
FileStatCache file_stat_cache;
std::set<string> deps;
// Since identifier is not registered, we cannot utilize the dependencies
// cache.
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
EXPECT_TRUE(deps.empty());
// Note that deps does not contain the input file itself.
deps.insert(ah);
EXPECT_TRUE(SetDependencies(identifier, acc, deps, &file_stat_cache));
}
// Second compile. We can utilize the dependency cache.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_TRUE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
std::set<string> deps_expected;
deps_expected.insert(ah);
EXPECT_EQ(deps_expected, deps);
}
// Update acc
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyopiyo");
// Third compile.
// Since directive hash is not changed, this should succeed.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_TRUE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
std::set<string> deps_expected;
deps_expected.insert(ah);
EXPECT_EQ(deps_expected, deps);
}
// Update acc. Update directives.
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"#define A\n"
"piyopiyo");
// Fourth compile. Since acc directive hash is changed,
// GetDependencies should return false.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
EXPECT_TRUE(deps.empty());
}
}
TEST_F(DepsCacheTest, SetGetDependenciesRelative) {
const DepsCache::Identifier identifier = MakeFreshIdentifier();
const string& ah = "a.h";
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile(ah, "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
// First compile.
{
FileStatCache file_stat_cache;
std::set<string> deps;
// Since identifier is not registered, we cannot utilize the dependencies
// cache.
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
EXPECT_TRUE(deps.empty());
// Note that deps does not contain the input file itself.
deps.insert(ah);
EXPECT_TRUE(SetDependencies(identifier, acc, deps, &file_stat_cache));
}
// Second compile. We can utilize the dependency cache.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_TRUE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
std::set<string> deps_expected;
deps_expected.insert(ah);
EXPECT_EQ(deps_expected, deps);
}
// Update acc
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyopiyo");
// Third compile.
// Since directive hash is not changed, this should succeed.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_TRUE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
std::set<string> deps_expected;
deps_expected.insert(ah);
EXPECT_EQ(deps_expected, deps);
}
// Update acc. Update directives.
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"#define A\n"
"piyopiyo");
// Fourth compile. Since acc directive hash is changed,
// GetDependencies should return false.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
EXPECT_TRUE(deps.empty());
}
}
TEST_F(DepsCacheTest, RemoveDependencies) {
const DepsCache::Identifier identifier = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
// First compile.
{
FileStatCache file_stat_cache;
std::set<string> deps;
deps.insert(ah);
EXPECT_TRUE(SetDependencies(identifier, acc, deps, &file_stat_cache));
}
// Second compile. We can utilize the dependency cache.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_TRUE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
}
RemoveDependency(identifier);
// Third compile. Since we've removed identifier, we cannot utilize the cache.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
}
}
TEST_F(DepsCacheTest, RemoveFile) {
const DepsCache::Identifier identifier = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& bh = tmpdir_->FullPath("b.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori A");
tmpdir_->CreateTmpFile("b.h", "kotori B");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"#include \"a.h\"\n"
"#include \"b.h\"\n"
"piyo");
// First compile.
{
FileStatCache file_stat_cache;
std::set<string> deps;
deps.insert(ah);
deps.insert(bh);
EXPECT_TRUE(SetDependencies(identifier, acc, deps, &file_stat_cache));
}
// Second compile. We can utilize the dependency cache.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_TRUE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
}
// Delete b.h
tmpdir_->RemoveTmpFile("b.h");
// Third compile. Since we've removed a file, cache should not be used.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
}
}
TEST_F(DepsCacheTest, Restart) {
const DepsCache::Identifier identifier = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
// First compile.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
EXPECT_TRUE(deps.empty());
deps.insert(ah);
EXPECT_TRUE(SetDependencies(identifier, acc, deps, &file_stat_cache));
}
// Restart DepsCache.
DepsCache::Quit();
IncludeCache::Quit();
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
// Second compile. We can utilize the dependency cache.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_TRUE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
std::set<string> deps_expected;
deps_expected.insert(ah);
EXPECT_EQ(deps_expected, deps);
}
}
TEST_F(DepsCacheTest, RestartWithFileStatUpdate) {
const DepsCache::Identifier identifier1 = MakeFreshIdentifier();
const DepsCache::Identifier identifier2 = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
// First compile for identifier1
{
FileStatCache file_stat_cache;
std::set<string> deps;
deps.insert(ah);
ASSERT_TRUE(SetDependencies(identifier1, acc, deps, &file_stat_cache));
}
// Update a.cc with same directive hash.
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyopiyo");
// First compile for identifier2
{
FileStatCache file_stat_cache;
std::set<string> deps;
deps.insert(ah);
ASSERT_TRUE(SetDependencies(identifier2, acc, deps, &file_stat_cache));
}
// Here, a.cc was updated after identifer1 compile.
// FileStat was different, but directive_hash should be the same.
{
DepsHashId deps_hash_id1;
DepsHashId deps_hash_id2;
ASSERT_TRUE(GetDepsHashId(identifier1, acc, &deps_hash_id1));
ASSERT_TRUE(GetDepsHashId(identifier2, acc, &deps_hash_id2));
ASSERT_EQ(deps_hash_id1.directive_hash, deps_hash_id2.directive_hash);
ASSERT_NE(deps_hash_id1.file_stat, deps_hash_id2.file_stat);
}
// Restart DepsCache.
DepsCache::Quit();
IncludeCache::Quit();
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
// DepsHashId will be updated to the latest one.
// Here, a.cc was updated after identifer1 compile.
// FileStat was different, but directive_hash should be the same.
{
DepsHashId deps_hash_id1;
DepsHashId deps_hash_id2;
ASSERT_TRUE(GetDepsHashId(identifier1, acc, &deps_hash_id1));
ASSERT_TRUE(GetDepsHashId(identifier2, acc, &deps_hash_id2));
EXPECT_EQ(deps_hash_id1.directive_hash, deps_hash_id2.directive_hash);
EXPECT_EQ(deps_hash_id1.file_stat, deps_hash_id2.file_stat);
}
}
TEST_F(DepsCacheTest, RestartWithDirectiveHashUpdate) {
// NOTE: dependency
// identifier1 -> a.h, b.h, a.cc
// identifier2 -> a.h, a.cc
const DepsCache::Identifier identifier1 = MakeFreshIdentifier();
const DepsCache::Identifier identifier2 = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& bh = tmpdir_->FullPath("b.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("b.h",
"#include <stdio.h>\n"
"piyo");
tmpdir_->CreateTmpFile("a.cc",
"#include <math.h>\n"
"piyo");
// First compile for identifier1
{
FileStatCache file_stat_cache;
std::set<string> deps;
deps.insert(ah);
deps.insert(bh);
ASSERT_TRUE(SetDependencies(identifier1, acc, deps, &file_stat_cache));
}
// Update a.cc with different directive hash.
tmpdir_->CreateTmpFile("a.cc",
"#include <string.h>\n"
"piyopiyo");
// First compile for identifier2
{
FileStatCache file_stat_cache;
// mtime might be the same as before (machine too fast).
// So, we'd like to update mtime here to improve test stability.
FileStat file_stat = file_stat_cache.Get(acc);
file_stat.mtime += 1;
SetFileStat(&file_stat_cache, acc, file_stat);
std::set<string> deps;
deps.insert(ah);
ASSERT_TRUE(SetDependencies(identifier2, acc, deps, &file_stat_cache));
}
// Here, a.cc was updated after identifer1 compile.
// Both directive_hash and file_stat were different.
{
DepsHashId deps_hash_id1;
DepsHashId deps_hash_id2;
ASSERT_TRUE(GetDepsHashId(identifier1, acc, &deps_hash_id1));
ASSERT_TRUE(GetDepsHashId(identifier2, acc, &deps_hash_id2));
ASSERT_NE(deps_hash_id1.directive_hash, deps_hash_id2.directive_hash);
ASSERT_NE(deps_hash_id1.file_stat, deps_hash_id2.file_stat);
}
// Restart DepsCache.
DepsCache::Quit();
IncludeCache::Quit();
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
// Since identifier1 was old, its entry will be garbage-collected.
{
DepsHashId deps_hash_id1;
EXPECT_FALSE(GetDepsHashId(identifier1, ah, &deps_hash_id1));
EXPECT_FALSE(GetDepsHashId(identifier1, bh, &deps_hash_id1));
EXPECT_FALSE(GetDepsHashId(identifier1, acc, &deps_hash_id1));
}
// FilenameIdTable::Id for 'bh' should be garbage-collected.
EXPECT_EQ(FilenameIdTable::kInvalidId, GetFilenameTableId(bh));
}
TEST_F(DepsCacheTest, RestartWithOldIdentifier) {
const DepsCache::Identifier identifier1 = MakeFreshIdentifier();
const DepsCache::Identifier identifier2 = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
// First compile for identifier1
{
FileStatCache file_stat_cache;
std::set<string> deps;
deps.insert(ah);
ASSERT_TRUE(SetDependencies(identifier1, acc, deps, &file_stat_cache));
}
// First compile for identifier2
{
FileStatCache file_stat_cache;
std::set<string> deps;
deps.insert(ah);
ASSERT_TRUE(SetDependencies(identifier2, acc, deps, &file_stat_cache));
}
// Change the last_used_time of identifier2
{
time_t time_old_enough = 0;
ASSERT_TRUE(UpdateLastUsedTime(identifier2, time_old_enough));
}
// Restart DepsCache.
DepsCache::Quit();
IncludeCache::Quit();
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
// Since last_used_time of identifier2 was old,
// it should be garbage-collected.
// identifier1 should alive.
{
DepsHashId deps_hash_id;
EXPECT_TRUE(GetDepsHashId(identifier1, ah, &deps_hash_id));
EXPECT_TRUE(GetDepsHashId(identifier1, acc, &deps_hash_id));
EXPECT_FALSE(GetDepsHashId(identifier2, ah, &deps_hash_id));
EXPECT_FALSE(GetDepsHashId(identifier2, acc, &deps_hash_id));
}
// Restart DepsCache with updating identifier1.
DepsCache::Quit();
IncludeCache::Quit();
// Update identifier1 last_used_time to time old enough.
{
time_t time_old_enough = 0;
UpdateIdentifierLastUsedTime(identifier1, time_old_enough);
}
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
// All identifiers are garbage-collected.
{
DepsHashId deps_hash_id;
EXPECT_FALSE(GetDepsHashId(identifier1, ah, &deps_hash_id));
EXPECT_FALSE(GetDepsHashId(identifier1, acc, &deps_hash_id));
EXPECT_FALSE(GetDepsHashId(identifier2, ah, &deps_hash_id));
EXPECT_FALSE(GetDepsHashId(identifier2, acc, &deps_hash_id));
}
}
TEST_F(DepsCacheTest, RestartWithOldIdentifierWithNegativeAliveDuration) {
// Restart DepsCache with negative alive duration
DepsCache::Quit();
IncludeCache::Quit();
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
-1, kDepsCacheThreshold, kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
const DepsCache::Identifier identifier1 = MakeFreshIdentifier();
const DepsCache::Identifier identifier2 = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
// Add old identifiers.
{
FileStatCache file_stat_cache;
std::set<string> deps;
deps.insert(ah);
ASSERT_TRUE(SetDependencies(identifier1, acc, deps, &file_stat_cache));
ASSERT_TRUE(SetDependencies(identifier2, acc, deps, &file_stat_cache));
time_t time_old_enough = 0;
ASSERT_TRUE(UpdateLastUsedTime(identifier1, time_old_enough));
ASSERT_TRUE(UpdateLastUsedTime(identifier2, time_old_enough));
}
// Restart DepsCache with negative alive duration
DepsCache::Quit();
IncludeCache::Quit();
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
-1, kDepsCacheThreshold, kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
// All identifiers should alive.
{
DepsHashId deps_hash_id;
EXPECT_TRUE(GetDepsHashId(identifier1, ah, &deps_hash_id));
EXPECT_TRUE(GetDepsHashId(identifier1, acc, &deps_hash_id));
EXPECT_TRUE(GetDepsHashId(identifier2, ah, &deps_hash_id));
EXPECT_TRUE(GetDepsHashId(identifier2, acc, &deps_hash_id));
}
}
TEST_F(DepsCacheTest, RestartWithBuiltRevisionUpdate) {
const DepsCache::Identifier identifier = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
// First compile.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
EXPECT_TRUE(deps.empty());
deps.insert(ah);
EXPECT_TRUE(SetDependencies(identifier, acc, deps, &file_stat_cache));
}
// Restart DepsCache.
DepsCache::Quit();
IncludeCache::Quit();
// Change the built revision of GomaDeps.
UpdateGomaBuiltRevision();
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
// All cache will be disposed.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
}
}
TEST_F(DepsCacheTest, RestartWithMissingSha256) {
const DepsCache::Identifier identifier = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
// First compile.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
EXPECT_TRUE(deps.empty());
deps.insert(ah);
EXPECT_TRUE(SetDependencies(identifier, acc, deps, &file_stat_cache));
}
// Restart DepsCache.
DepsCache::Quit();
IncludeCache::Quit();
// Remove .goma_deps.sha256
{
const string& sha256_deps_path = file::JoinPath(tmpdir_->tmpdir(),
".goma_deps.sha256");
ASSERT_EQ(0, remove(sha256_deps_path.c_str()));
}
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
// All cache will be disposed.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
}
}
TEST_F(DepsCacheTest, RestartWithInvalidSha256) {
const DepsCache::Identifier identifier = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
// First compile.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
EXPECT_TRUE(deps.empty());
deps.insert(ah);
EXPECT_TRUE(SetDependencies(identifier, acc, deps, &file_stat_cache));
}
// Restart DepsCache.
DepsCache::Quit();
IncludeCache::Quit();
// Convert .goma_deps.sha256 to invalid one
{
const string& sha256_deps_path = file::JoinPath(tmpdir_->tmpdir(),
".goma_deps.sha256");
ASSERT_TRUE(WriteStringToFile("invalid-sha256", sha256_deps_path));
}
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
// All cache will be disposed.
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier, acc, &deps, &file_stat_cache));
}
}
TEST_F(DepsCacheTest, RestartWithUpdatedFilesInSomeIdentifier) {
// identifier1: a.h, b.h, a.cc
// identifier2: a.h, a.cc
// "a.h" of identifier2 is latest and updated, but "a.h" of identifier1 is
// older, so identidifer1 won't be saved.
// In this case, "b.h" won't be included in FilenameIdtable.
const DepsCache::Identifier identifier1 = MakeFreshIdentifier();
const DepsCache::Identifier identifier2 = MakeFreshIdentifier();
const string& ah = tmpdir_->FullPath("a.h");
const string& bh = tmpdir_->FullPath("b.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori-a");
tmpdir_->CreateTmpFile("b.h", "kotori-kotori-b");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
{
FileStatCache file_stat_cache;
std::set<string> deps;
deps.insert(ah);
deps.insert(bh);
EXPECT_TRUE(SetDependencies(identifier1, acc, deps, &file_stat_cache));
}
tmpdir_->CreateTmpFile("a.h", "#include <string.h>\n");
{
FileStatCache file_stat_cache;
FileStat file_stat = file_stat_cache.Get(ah);
file_stat.mtime += 1; // Ensure it's newer than the previous.
SetFileStat(&file_stat_cache, ah, file_stat);
std::set<string> deps;
deps.insert(ah);
EXPECT_TRUE(SetDependencies(identifier2, acc, deps, &file_stat_cache));
}
// Restart DepsCache.
DepsCache::Quit();
IncludeCache::Quit();
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifier1, acc, &deps, &file_stat_cache));
}
{
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_TRUE(GetDependencies(identifier2, acc, &deps, &file_stat_cache));
}
}
TEST_F(DepsCacheTest, RestartWithLargeNumberIdentifiers) {
const int N = 30;
ASSERT_GT(N, kDepsCacheThreshold);
std::vector<DepsCache::Identifier> identifiers(N);
for (int i = 0; i < N; ++i) {
identifiers[i] = MakeFreshIdentifier();
}
const string& ah = tmpdir_->FullPath("a.h");
const string& acc = tmpdir_->FullPath("a.cc");
tmpdir_->CreateTmpFile("a.h", "kotori");
tmpdir_->CreateTmpFile("a.cc",
"#include <stdio.h>\n"
"piyo");
for (int i = 0; i < N; ++i) {
FileStatCache file_stat_cache;
std::set<string> deps;
EXPECT_FALSE(GetDependencies(identifiers[i], acc, &deps, &file_stat_cache));
EXPECT_TRUE(deps.empty());
deps.insert(ah);
EXPECT_TRUE(SetDependencies(identifiers[i], acc, deps, &file_stat_cache));
}
// Restart DepsCache.
DepsCache::Quit();
IncludeCache::Quit();
IncludeCache::Init(32, true);
DepsCache::Init(file::JoinPath(tmpdir_->tmpdir(), ".goma_deps"),
kDepsCacheAliveDuration,
kDepsCacheThreshold,
kDepsCacheMaxProtoSizeInMB);
dc_ = DepsCache::instance();
EXPECT_EQ(kDepsCacheThreshold, DepsCacheSize());
}
TEST_F(DepsCacheTest, MakeDepsIdentifierGcc) {
const string bare_gcc = "/usr/bin/gcc";
const string bare_clang = "/usr/bin/clang";
DepsCache::Identifier identifier;
{
std::vector<string> args;
args.push_back("gcc");
args.push_back("-c");
args.push_back("test.c");
GCCFlags flags(args, "/tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_gcc));
identifier = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier.has_value());
}
DepsCache::Identifier identifier_compiler;
{
std::vector<string> args;
args.push_back("clang"); // this differs.
args.push_back("-c");
args.push_back("test.c");
GCCFlags flags(args, "/tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_clang));
identifier_compiler = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_compiler.has_value());
}
DepsCache::Identifier identifier_filename;
{
std::vector<string> args;
args.push_back("gcc");
args.push_back("-c");
args.push_back("test2.c"); // this differs.
GCCFlags flags(args, "/tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_gcc));
identifier_filename = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_filename.has_value());
}
DepsCache::Identifier identifier_include;
{
std::vector<string> args;
args.push_back("gcc");
args.push_back("-I/include"); // this differs.
args.push_back("-c");
args.push_back("test.c");
GCCFlags flags(args, "/tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_gcc));
identifier_include = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_include.has_value());
}
DepsCache::Identifier identifier_systeminclude;
{
std::vector<string> args;
args.push_back("gcc");
args.push_back("-isysteminclude"); // this differs.
args.push_back("-c");
args.push_back("test.c");
GCCFlags flags(args, "/tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_gcc));
identifier_systeminclude = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_systeminclude.has_value());
}
DepsCache::Identifier identifier_macro;
{
std::vector<string> args;
args.push_back("gcc");
args.push_back("-DKOTORI"); // this differs.
args.push_back("-c");
args.push_back("test.c");
GCCFlags flags(args, "/tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_gcc));
identifier_macro = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_macro.has_value());
}
DepsCache::Identifier identifier_cwd;
{
std::vector<string> args;
args.push_back("gcc");
args.push_back("-c");
args.push_back("test.c");
GCCFlags flags(args, "/tmp2"); // this differs.
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_gcc));
identifier_cwd = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_cwd.has_value());
}
EXPECT_NE(identifier.value(), identifier_include.value());
EXPECT_NE(identifier.value(), identifier_compiler.value());
EXPECT_NE(identifier.value(), identifier_filename.value());
EXPECT_NE(identifier.value(), identifier_systeminclude.value());
EXPECT_NE(identifier.value(), identifier_macro.value());
EXPECT_NE(identifier.value(), identifier_cwd.value());
}
TEST_F(DepsCacheTest, MakeDepsIdentifierVC) {
const string bare_cl = "cl.exe";
DepsCache::Identifier identifier;
{
std::vector<string> args;
args.push_back(bare_cl);
args.push_back("/c");
args.push_back("test.c");
VCFlags flags(args, "C:\\tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_cl));
identifier = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier.has_value());
}
DepsCache::Identifier identifier_filename;
{
std::vector<string> args;
args.push_back(bare_cl);
args.push_back("/c");
args.push_back("test2.c"); // this differs.
VCFlags flags(args, "C:\\tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_cl));
identifier_filename = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_filename.has_value());
}
DepsCache::Identifier identifier_include;
{
std::vector<string> args;
args.push_back(bare_cl);
args.push_back("-IC:\\include"); // this differs.
args.push_back("/c");
args.push_back("test.c");
VCFlags flags(args, "C:\\tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_cl));
identifier_include = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_include.has_value());
}
DepsCache::Identifier identifier_compiler;
{
std::vector<string> args;
args.push_back("C:\\clang-cl.exe"); // this differs.
args.push_back("/c");
args.push_back("test.c");
VCFlags flags(args, "C:\\tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo("C:\\clang-cl.exe"));
identifier_compiler = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_compiler.has_value());
}
DepsCache::Identifier identifier_macro;
{
std::vector<string> args;
args.push_back(bare_cl);
args.push_back("/DKOTORI"); // this differs.
args.push_back("/c");
args.push_back("test.c");
VCFlags flags(args, "C:\\tmp");
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_cl));
identifier_macro = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_macro.has_value());
}
DepsCache::Identifier identifier_cwd;
{
std::vector<string> args;
args.push_back(bare_cl);
args.push_back("/c");
args.push_back("test.c");
VCFlags flags(args, "C:\\tmp2"); // this differs.
CxxCompilerInfo info(CreateBarebornCompilerInfo(bare_cl));
identifier_cwd = MakeDepsIdentifier(info, flags);
EXPECT_TRUE(identifier_cwd.has_value());
}
EXPECT_NE(identifier.value(), identifier_filename.value());
EXPECT_NE(identifier.value(), identifier_include.value());
EXPECT_NE(identifier.value(), identifier_compiler.value());
EXPECT_NE(identifier.value(), identifier_macro.value());
EXPECT_NE(identifier.value(), identifier_cwd.value());
}
TEST_F(DepsCacheTest, MakeDepsIdentifierJavac) {
// TODO: Currently DepsCache for java is disabled.
// Invalid RequiredFilesIdentifier is always returned.
std::vector<string> args;
args.push_back("javac");
args.push_back("Test.java");
JavacFlags flags(args, "/tmp");
std::unique_ptr<CompilerInfoData> cid(new CompilerInfoData);
cid->set_found(true);
cid->mutable_javac();
JavacCompilerInfo info(std::move(cid));
DepsCache::Identifier identifier = MakeDepsIdentifier(info, flags);
EXPECT_FALSE(identifier.has_value());
}
} // namespace devtools_goma