OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <map> |
| 6 #include <utility> |
| 7 #include <vector> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/memory/linked_ptr.h" |
| 11 #include "base/message_loop/message_loop.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 #include "tools/gn/build_settings.h" |
| 14 #include "tools/gn/err.h" |
| 15 #include "tools/gn/loader.h" |
| 16 #include "tools/gn/parse_tree.h" |
| 17 #include "tools/gn/parser.h" |
| 18 #include "tools/gn/scheduler.h" |
| 19 #include "tools/gn/tokenizer.h" |
| 20 |
| 21 namespace { |
| 22 |
| 23 class MockInputFileManager { |
| 24 public: |
| 25 typedef base::Callback<void(const ParseNode*)> Callback; |
| 26 |
| 27 MockInputFileManager() { |
| 28 } |
| 29 |
| 30 LoaderImpl::AsyncLoadFileCallback GetCallback(); |
| 31 |
| 32 // Sets a given response for a given source file. |
| 33 void AddCannedResponse(const SourceFile& source_file, |
| 34 const std::string& source); |
| 35 |
| 36 // Returns true if there is/are pending load(s) matching the given file(s). |
| 37 bool HasOnePending(const SourceFile& f) const; |
| 38 bool HasTwoPending(const SourceFile& f1, const SourceFile& f2) const; |
| 39 |
| 40 void IssueAllPending(); |
| 41 |
| 42 private: |
| 43 struct CannedResult { |
| 44 scoped_ptr<InputFile> input_file; |
| 45 std::vector<Token> tokens; |
| 46 scoped_ptr<ParseNode> root; |
| 47 }; |
| 48 |
| 49 bool AsyncLoadFile(const LocationRange& origin, |
| 50 const BuildSettings* build_settings, |
| 51 const SourceFile& file_name, |
| 52 const Callback& callback, |
| 53 Err* err) { |
| 54 pending_.push_back(std::make_pair(file_name, callback)); |
| 55 return true; |
| 56 } |
| 57 |
| 58 // Owning pointers. |
| 59 typedef std::map<SourceFile, linked_ptr<CannedResult> > CannedResponseMap; |
| 60 CannedResponseMap canned_responses_; |
| 61 |
| 62 std::vector< std::pair<SourceFile, Callback> > pending_; |
| 63 }; |
| 64 |
| 65 LoaderImpl::AsyncLoadFileCallback MockInputFileManager::GetCallback() { |
| 66 return base::Bind(&MockInputFileManager::AsyncLoadFile, |
| 67 base::Unretained(this)); |
| 68 } |
| 69 |
| 70 // Sets a given response for a given source file. |
| 71 void MockInputFileManager::AddCannedResponse(const SourceFile& source_file, |
| 72 const std::string& source) { |
| 73 CannedResult* canned = new CannedResult; |
| 74 canned->input_file.reset(new InputFile(source_file)); |
| 75 canned->input_file->SetContents(source); |
| 76 |
| 77 // Tokenize. |
| 78 Err err; |
| 79 canned->tokens = Tokenizer::Tokenize(canned->input_file.get(), &err); |
| 80 EXPECT_FALSE(err.has_error()); |
| 81 |
| 82 // Parse. |
| 83 canned->root = Parser::Parse(canned->tokens, &err).Pass(); |
| 84 EXPECT_FALSE(err.has_error()); |
| 85 |
| 86 canned_responses_[source_file] = linked_ptr<CannedResult>(canned); |
| 87 } |
| 88 |
| 89 bool MockInputFileManager::HasOnePending(const SourceFile& f) const { |
| 90 return pending_.size() == 1u && pending_[0].first == f; |
| 91 } |
| 92 |
| 93 bool MockInputFileManager::HasTwoPending(const SourceFile& f1, |
| 94 const SourceFile& f2) const { |
| 95 if (pending_.size() != 2u) |
| 96 return false; |
| 97 return pending_[0].first == f1 && pending_[1].first == f2; |
| 98 } |
| 99 |
| 100 void MockInputFileManager::IssueAllPending() { |
| 101 BlockNode block(false); // Default response. |
| 102 |
| 103 for (size_t i = 0; i < pending_.size(); i++) { |
| 104 CannedResponseMap::const_iterator found = |
| 105 canned_responses_.find(pending_[i].first); |
| 106 if (found == canned_responses_.end()) |
| 107 pending_[i].second.Run(&block); |
| 108 else |
| 109 pending_[i].second.Run(found->second->root.get()); |
| 110 } |
| 111 pending_.clear(); |
| 112 } |
| 113 |
| 114 // LoaderTest ------------------------------------------------------------------ |
| 115 |
| 116 class LoaderTest : public testing::Test { |
| 117 public: |
| 118 LoaderTest() { |
| 119 build_settings_.SetBuildDir(SourceDir("//out/Debug/")); |
| 120 } |
| 121 virtual ~LoaderTest() { |
| 122 } |
| 123 |
| 124 protected: |
| 125 Scheduler scheduler_; |
| 126 BuildSettings build_settings_; |
| 127 MockInputFileManager mock_ifm_; |
| 128 }; |
| 129 |
| 130 } // namespace |
| 131 |
| 132 // ----------------------------------------------------------------------------- |
| 133 |
| 134 TEST_F(LoaderTest, Foo) { |
| 135 SourceFile build_config("//build/config/BUILDCONFIG.gn"); |
| 136 build_settings_.set_build_config_file(build_config); |
| 137 |
| 138 scoped_refptr<LoaderImpl> loader(new LoaderImpl(&build_settings_)); |
| 139 |
| 140 // The default toolchain needs to be set by the build config file. |
| 141 mock_ifm_.AddCannedResponse(build_config, |
| 142 "set_default_toolchain(\"//tc:tc\")"); |
| 143 |
| 144 loader->set_async_load_file(mock_ifm_.GetCallback()); |
| 145 |
| 146 // Request the root build file be loaded. This should kick off the default |
| 147 // build config loading. |
| 148 SourceFile root_build("//BUILD.gn"); |
| 149 loader->Load(root_build, Label()); |
| 150 EXPECT_TRUE(mock_ifm_.HasOnePending(build_config)); |
| 151 |
| 152 // Completing the build config load should kick off the root build file load. |
| 153 mock_ifm_.IssueAllPending(); |
| 154 scheduler_.main_loop()->RunUntilIdle(); |
| 155 EXPECT_TRUE(mock_ifm_.HasOnePending(root_build)); |
| 156 |
| 157 // Load the root build file. |
| 158 mock_ifm_.IssueAllPending(); |
| 159 scheduler_.main_loop()->RunUntilIdle(); |
| 160 |
| 161 // Schedule some other file to load in another toolchain. |
| 162 Label second_tc(SourceDir("//tc2/"), "tc2"); |
| 163 SourceFile second_file("//foo/BUILD.gn"); |
| 164 loader->Load(second_file, second_tc); |
| 165 EXPECT_TRUE(mock_ifm_.HasOnePending(SourceFile("//tc2/BUILD.gn"))); |
| 166 |
| 167 // Running the toolchain file should schedule the build config file to load |
| 168 // for that toolchain. |
| 169 mock_ifm_.IssueAllPending(); |
| 170 scheduler_.main_loop()->RunUntilIdle(); |
| 171 |
| 172 // We have to tell it we have a toolchain definition now (normally the |
| 173 // builder would do this). |
| 174 const Settings* default_settings = loader->GetToolchainSettings(Label()); |
| 175 Toolchain second_tc_object(default_settings, second_tc); |
| 176 loader->ToolchainLoaded(&second_tc_object); |
| 177 EXPECT_TRUE(mock_ifm_.HasOnePending(build_config)); |
| 178 |
| 179 // Scheduling a second file to load in that toolchain should not make it |
| 180 // pending yet (it's waiting for the build config). |
| 181 SourceFile third_file("//bar/BUILD.gn"); |
| 182 loader->Load(third_file, second_tc); |
| 183 EXPECT_TRUE(mock_ifm_.HasOnePending(build_config)); |
| 184 |
| 185 // Running the build config file should make our third file pending. |
| 186 mock_ifm_.IssueAllPending(); |
| 187 scheduler_.main_loop()->RunUntilIdle(); |
| 188 EXPECT_TRUE(mock_ifm_.HasTwoPending(second_file, third_file)); |
| 189 |
| 190 EXPECT_FALSE(scheduler_.is_failed()); |
| 191 } |
OLD | NEW |