blob: a3c8d5becb7b31b6a0270d5d87139697ac8ad33a [file] [log] [blame]
// Copyright 2016 The Chromium 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 "testing/gtest/include/gtest/gtest.h"
#include "tools/gn/analyzer.h"
#include "tools/gn/build_settings.h"
#include "tools/gn/builder.h"
#include "tools/gn/loader.h"
#include "tools/gn/settings.h"
#include "tools/gn/source_file.h"
namespace {
class MockLoader : public Loader {
public:
MockLoader() {}
void Load(const SourceFile& file,
const LocationRange& origin,
const Label& toolchain_name) override {}
void ToolchainLoaded(const Toolchain* toolchain) override {}
Label GetDefaultToolchain() const override {
return Label(SourceDir("//tc/"), "default");
}
const Settings* GetToolchainSettings(const Label& label) const override {
return nullptr;
}
private:
~MockLoader() override {}
};
class AnalyzerTest : public testing::Test {
public:
AnalyzerTest()
: loader_(new MockLoader),
builder_(loader_.get()),
settings_(&build_settings_, std::string()) {
build_settings_.SetBuildDir(SourceDir("//out/"));
settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
settings_.set_default_toolchain_label(settings_.toolchain_label());
tc_dir_ = settings_.toolchain_label().dir();
tc_name_ = settings_.toolchain_label().name();
}
Target* MakeTarget(const std::string dir,
const std::string name,
Target::OutputType type,
const std::vector<std::string>& sources,
const std::vector<Target*>& deps) {
Label lbl(SourceDir(dir), name, tc_dir_, tc_name_);
Target* target = new Target(&settings_, lbl);
target->set_output_type(type);
for (const auto& s : sources)
target->sources().push_back(SourceFile(s));
for (const auto* d : deps)
target->public_deps().push_back(LabelTargetPair(d));
builder_.ItemDefined(std::unique_ptr<Item>(target));
return target;
}
void AddSource(Target* a, std::string path) {}
void AddDep(Target* a, Target* b) {}
void SetUpABasicBuildGraph() {
std::vector<std::string> no_sources;
std::vector<Target*> no_deps;
// All of the targets below are owned by the builder, so none of them
// get leaked.
// Ignore the returned target since nothing depends on it.
MakeTarget("//", "a", Target::EXECUTABLE, {"//a.cc"}, no_deps);
Target* b =
MakeTarget("//d", "b", Target::SOURCE_SET, {"//d/b.cc"}, no_deps);
Target* b_unittests = MakeTarget("//d", "b_unittests", Target::EXECUTABLE,
{"//d/b_unittest.cc"}, {b});
Target* c = MakeTarget("//d", "c", Target::EXECUTABLE, {"//d/c.cc"}, {b});
Target* b_unittests_and_c =
MakeTarget("//d", "b_unittests_and_c", Target::GROUP, no_sources,
{b_unittests, c});
Target* e =
MakeTarget("//d", "e", Target::EXECUTABLE, {"//d/e.cc"}, no_deps);
// Also ignore this returned target since nothing depends on it.
MakeTarget("//d", "d", Target::GROUP, no_sources, {b_unittests_and_c, e});
}
void RunBasicTest(const std::string& input,
const std::string& expected_output) {
SetUpABasicBuildGraph();
Err err;
std::string actual_output = Analyzer(builder_).Analyze(input, &err);
EXPECT_EQ(err.has_error(), false);
EXPECT_EQ(expected_output, actual_output);
}
protected:
scoped_refptr<MockLoader> loader_;
Builder builder_;
BuildSettings build_settings_;
Settings settings_;
SourceDir tc_dir_;
std::string tc_name_;
};
} // namespace
// TODO: clean this up when raw string literals are allowed.
TEST_F(AnalyzerTest, AllWasPruned) {
RunBasicTest(
"{"
" \"files\": [ \"//d/b.cc\" ],"
" \"additional_compile_targets\": [ \"all\" ],"
" \"test_targets\": [ ]"
"}",
"{"
"\"compile_targets\":[\"//d:b_unittests\",\"//d:c\"],"
"\"status\":\"Found dependency\","
"\"test_targets\":[]"
"}");
}
TEST_F(AnalyzerTest, NoDependency) {
RunBasicTest(
"{"
" \"files\":[ \"//missing.cc\" ],"
" \"additional_compile_targets\": [ \"all\" ],"
" \"test_targets\": [ \"//:a\" ]"
"}",
"{"
"\"compile_targets\":[],"
"\"status\":\"No dependency\","
"\"test_targets\":[]"
"}");
}
TEST_F(AnalyzerTest, NoFilesNoTargets) {
RunBasicTest(
"{"
" \"files\": [],"
" \"additional_compile_targets\": [],"
" \"test_targets\": []"
"}",
"{"
"\"compile_targets\":[],"
"\"status\":\"No dependency\","
"\"test_targets\":[]"
"}");
}
TEST_F(AnalyzerTest, OneTestTargetModified) {
RunBasicTest(
"{"
" \"files\": [ \"//a.cc\" ],"
" \"additional_compile_targets\": [],"
" \"test_targets\": [ \"//:a\" ]"
"}",
"{"
"\"compile_targets\":[],"
"\"status\":\"Found dependency\","
"\"test_targets\":[\"//:a\"]"
"}");
}
TEST_F(AnalyzerTest, FilesArentSourceAbsolute) {
RunBasicTest(
"{"
" \"files\": [ \"a.cc\" ],"
" \"additional_compile_targets\": [],"
" \"test_targets\": [ \"//:a\" ]"
"}",
"{"
"\"error\":"
"\"\\\"a.cc\\\" is not a source-absolute or absolute path.\","
"\"invalid_targets\":[]"
"}");
}
TEST_F(AnalyzerTest, WrongInputFields) {
RunBasicTest(
"{"
" \"files\": [ \"//a.cc\" ],"
" \"compile_targets\": [],"
" \"test_targets\": [ \"//:a\" ]"
"}",
"{"
"\"error\":"
"\"Input does not have a key named "
"\\\"additional_compile_targets\\\" with a list value.\","
"\"invalid_targets\":[]"
"}");
}
TEST_F(AnalyzerTest, BuildFilesWereModified) {
// This tests that if a build file is modified, we bail out early with
// "Found dependency (all)" error since we can't handle changes to
// build files yet (crbug.com/555273).
RunBasicTest(
"{"
" \"files\": [ \"//a.cc\", \"//BUILD.gn\" ],"
" \"additional_compile_targets\": [],"
" \"test_targets\": [ \"//:a\" ]"
"}",
"{"
"\"compile_targets\":[\"//:a\"],"
"\"status\":\"Found dependency (all)\","
"\"test_targets\":[\"//:a\"]"
"}");
}
TEST_F(AnalyzerTest, BuildFilesWereModifiedAndCompilingAll) {
// This tests that if a build file is modified, we bail out early with
// "Found dependency (all)" error since we can't handle changes to
// build files yet (crbug.com/555273).
RunBasicTest(
"{"
" \"files\": [ \"//a.cc\", \"//BUILD.gn\" ],"
" \"additional_compile_targets\": [ \"all\" ],"
" \"test_targets\": [ \"//:a\" ]"
"}",
"{"
"\"compile_targets\":[\"all\"],"
"\"status\":\"Found dependency (all)\","
"\"test_targets\":[\"//:a\"]"
"}");
}