• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MockInputFileManager()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 
AsyncLoadFile(const LocationRange & origin,const BuildSettings * build_settings,const SourceFile & file_name,const Callback & callback,Err * err)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 
GetCallback()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.
AddCannedResponse(const SourceFile & source_file,const std::string & source)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 
HasOnePending(const SourceFile & f) const89 bool MockInputFileManager::HasOnePending(const SourceFile& f) const {
90   return pending_.size() == 1u && pending_[0].first == f;
91 }
92 
HasTwoPending(const SourceFile & f1,const SourceFile & f2) const93 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 
IssueAllPending()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:
LoaderTest()118   LoaderTest() {
119     build_settings_.SetBuildDir(SourceDir("//out/Debug/"));
120   }
~LoaderTest()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 
TEST_F(LoaderTest,Foo)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 }
192