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()); |
+} |