| Index: tools/gn/loader_unittest.cc
|
| diff --git a/tools/gn/loader_unittest.cc b/tools/gn/loader_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7aa267dc55226375649b20766018ec4caa09b5fb
|
| --- /dev/null
|
| +++ b/tools/gn/loader_unittest.cc
|
| @@ -0,0 +1,191 @@
|
| +// Copyright (c) 2013 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 <map>
|
| +#include <utility>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/memory/linked_ptr.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "tools/gn/build_settings.h"
|
| +#include "tools/gn/err.h"
|
| +#include "tools/gn/loader.h"
|
| +#include "tools/gn/parse_tree.h"
|
| +#include "tools/gn/parser.h"
|
| +#include "tools/gn/scheduler.h"
|
| +#include "tools/gn/tokenizer.h"
|
| +
|
| +namespace {
|
| +
|
| +class MockInputFileManager {
|
| + public:
|
| + typedef base::Callback<void(const ParseNode*)> Callback;
|
| +
|
| + MockInputFileManager() {
|
| + }
|
| +
|
| + LoaderImpl::AsyncLoadFileCallback GetCallback();
|
| +
|
| + // Sets a given response for a given source file.
|
| + void AddCannedResponse(const SourceFile& source_file,
|
| + const std::string& source);
|
| +
|
| + // Returns true if there is/are pending load(s) matching the given file(s).
|
| + bool HasOnePending(const SourceFile& f) const;
|
| + bool HasTwoPending(const SourceFile& f1, const SourceFile& f2) const;
|
| +
|
| + void IssueAllPending();
|
| +
|
| + private:
|
| + struct CannedResult {
|
| + scoped_ptr<InputFile> input_file;
|
| + std::vector<Token> tokens;
|
| + scoped_ptr<ParseNode> root;
|
| + };
|
| +
|
| + bool AsyncLoadFile(const LocationRange& origin,
|
| + const BuildSettings* build_settings,
|
| + const SourceFile& file_name,
|
| + const Callback& callback,
|
| + Err* err) {
|
| + pending_.push_back(std::make_pair(file_name, callback));
|
| + return true;
|
| + }
|
| +
|
| + // Owning pointers.
|
| + typedef std::map<SourceFile, linked_ptr<CannedResult> > CannedResponseMap;
|
| + CannedResponseMap canned_responses_;
|
| +
|
| + std::vector< std::pair<SourceFile, Callback> > pending_;
|
| +};
|
| +
|
| +LoaderImpl::AsyncLoadFileCallback MockInputFileManager::GetCallback() {
|
| + return base::Bind(&MockInputFileManager::AsyncLoadFile,
|
| + base::Unretained(this));
|
| +}
|
| +
|
| +// Sets a given response for a given source file.
|
| +void MockInputFileManager::AddCannedResponse(const SourceFile& source_file,
|
| + const std::string& source) {
|
| + CannedResult* canned = new CannedResult;
|
| + canned->input_file.reset(new InputFile(source_file));
|
| + canned->input_file->SetContents(source);
|
| +
|
| + // Tokenize.
|
| + Err err;
|
| + canned->tokens = Tokenizer::Tokenize(canned->input_file.get(), &err);
|
| + EXPECT_FALSE(err.has_error());
|
| +
|
| + // Parse.
|
| + canned->root = Parser::Parse(canned->tokens, &err).Pass();
|
| + EXPECT_FALSE(err.has_error());
|
| +
|
| + canned_responses_[source_file] = linked_ptr<CannedResult>(canned);
|
| +}
|
| +
|
| +bool MockInputFileManager::HasOnePending(const SourceFile& f) const {
|
| + return pending_.size() == 1u && pending_[0].first == f;
|
| +}
|
| +
|
| +bool MockInputFileManager::HasTwoPending(const SourceFile& f1,
|
| + const SourceFile& f2) const {
|
| + if (pending_.size() != 2u)
|
| + return false;
|
| + return pending_[0].first == f1 && pending_[1].first == f2;
|
| +}
|
| +
|
| +void MockInputFileManager::IssueAllPending() {
|
| + BlockNode block(false); // Default response.
|
| +
|
| + for (size_t i = 0; i < pending_.size(); i++) {
|
| + CannedResponseMap::const_iterator found =
|
| + canned_responses_.find(pending_[i].first);
|
| + if (found == canned_responses_.end())
|
| + pending_[i].second.Run(&block);
|
| + else
|
| + pending_[i].second.Run(found->second->root.get());
|
| + }
|
| + pending_.clear();
|
| +}
|
| +
|
| +// LoaderTest ------------------------------------------------------------------
|
| +
|
| +class LoaderTest : public testing::Test {
|
| + public:
|
| + LoaderTest() {
|
| + build_settings_.SetBuildDir(SourceDir("//out/Debug/"));
|
| + }
|
| + virtual ~LoaderTest() {
|
| + }
|
| +
|
| + protected:
|
| + Scheduler scheduler_;
|
| + BuildSettings build_settings_;
|
| + MockInputFileManager mock_ifm_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +// -----------------------------------------------------------------------------
|
| +
|
| +TEST_F(LoaderTest, Foo) {
|
| + SourceFile build_config("//build/config/BUILDCONFIG.gn");
|
| + build_settings_.set_build_config_file(build_config);
|
| +
|
| + scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_));
|
| +
|
| + // The default toolchain needs to be set by the build config file.
|
| + mock_ifm_.AddCannedResponse(build_config,
|
| + "set_default_toolchain(\"//tc:tc\")");
|
| +
|
| + loader->set_async_load_file(mock_ifm_.GetCallback());
|
| +
|
| + // Request the root build file be loaded. This should kick off the default
|
| + // build config loading.
|
| + SourceFile root_build("//BUILD.gn");
|
| + loader->Load(root_build, Label());
|
| + EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
|
| +
|
| + // Completing the build config load should kick off the root build file load.
|
| + mock_ifm_.IssueAllPending();
|
| + scheduler_.main_loop()->RunUntilIdle();
|
| + EXPECT_TRUE(mock_ifm_.HasOnePending(root_build));
|
| +
|
| + // Load the root build file.
|
| + mock_ifm_.IssueAllPending();
|
| + scheduler_.main_loop()->RunUntilIdle();
|
| +
|
| + // Schedule some other file to load in another toolchain.
|
| + Label second_tc(SourceDir("//tc2/"), "tc2");
|
| + SourceFile second_file("//foo/BUILD.gn");
|
| + loader->Load(second_file, second_tc);
|
| + EXPECT_TRUE(mock_ifm_.HasOnePending(SourceFile("//tc2/BUILD.gn")));
|
| +
|
| + // Running the toolchain file should schedule the build config file to load
|
| + // for that toolchain.
|
| + mock_ifm_.IssueAllPending();
|
| + scheduler_.main_loop()->RunUntilIdle();
|
| +
|
| + // We have to tell it we have a toolchain definition now (normally the
|
| + // builder would do this).
|
| + const Settings* default_settings = loader->GetToolchainSettings(Label());
|
| + Toolchain second_tc_object(default_settings, second_tc);
|
| + loader->ToolchainLoaded(&second_tc_object);
|
| + EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
|
| +
|
| + // Scheduling a second file to load in that toolchain should not make it
|
| + // pending yet (it's waiting for the build config).
|
| + SourceFile third_file("//bar/BUILD.gn");
|
| + loader->Load(third_file, second_tc);
|
| + EXPECT_TRUE(mock_ifm_.HasOnePending(build_config));
|
| +
|
| + // Running the build config file should make our third file pending.
|
| + mock_ifm_.IssueAllPending();
|
| + scheduler_.main_loop()->RunUntilIdle();
|
| + EXPECT_TRUE(mock_ifm_.HasTwoPending(second_file, third_file));
|
| +
|
| + EXPECT_FALSE(scheduler_.is_failed());
|
| +}
|
|
|