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.
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"
23 class MockInputFileManager
{
25 typedef base::Callback
<void(const ParseNode
*)> Callback
;
27 MockInputFileManager() {
30 LoaderImpl::AsyncLoadFileCallback
GetCallback();
32 // Sets a given response for a given source file.
33 void AddCannedResponse(const SourceFile
& source_file
,
34 const std::string
& source
);
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;
40 void IssueAllPending();
44 scoped_ptr
<InputFile
> input_file
;
45 std::vector
<Token
> tokens
;
46 scoped_ptr
<ParseNode
> root
;
49 bool AsyncLoadFile(const LocationRange
& origin
,
50 const BuildSettings
* build_settings
,
51 const SourceFile
& file_name
,
52 const Callback
& callback
,
54 pending_
.push_back(std::make_pair(file_name
, callback
));
59 typedef std::map
<SourceFile
, linked_ptr
<CannedResult
> > CannedResponseMap
;
60 CannedResponseMap canned_responses_
;
62 std::vector
< std::pair
<SourceFile
, Callback
> > pending_
;
65 LoaderImpl::AsyncLoadFileCallback
MockInputFileManager::GetCallback() {
66 return base::Bind(&MockInputFileManager::AsyncLoadFile
,
67 base::Unretained(this));
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
);
79 canned
->tokens
= Tokenizer::Tokenize(canned
->input_file
.get(), &err
);
80 EXPECT_FALSE(err
.has_error());
83 canned
->root
= Parser::Parse(canned
->tokens
, &err
).Pass();
84 EXPECT_FALSE(err
.has_error());
86 canned_responses_
[source_file
] = linked_ptr
<CannedResult
>(canned
);
89 bool MockInputFileManager::HasOnePending(const SourceFile
& f
) const {
90 return pending_
.size() == 1u && pending_
[0].first
== f
;
93 bool MockInputFileManager::HasTwoPending(const SourceFile
& f1
,
94 const SourceFile
& f2
) const {
95 if (pending_
.size() != 2u)
97 return pending_
[0].first
== f1
&& pending_
[1].first
== f2
;
100 void MockInputFileManager::IssueAllPending() {
101 BlockNode
block(false); // Default response.
103 for (const auto& cur
: pending_
) {
104 CannedResponseMap::const_iterator found
= canned_responses_
.find(cur
.first
);
105 if (found
== canned_responses_
.end())
106 cur
.second
.Run(&block
);
108 cur
.second
.Run(found
->second
->root
.get());
113 // LoaderTest ------------------------------------------------------------------
115 class LoaderTest
: public testing::Test
{
118 build_settings_
.SetBuildDir(SourceDir("//out/Debug/"));
120 virtual ~LoaderTest() {
124 Scheduler scheduler_
;
125 BuildSettings build_settings_
;
126 MockInputFileManager mock_ifm_
;
131 // -----------------------------------------------------------------------------
133 TEST_F(LoaderTest
, Foo
) {
134 SourceFile
build_config("//build/config/BUILDCONFIG.gn");
135 build_settings_
.set_build_config_file(build_config
);
137 scoped_refptr
<LoaderImpl
> loader(new LoaderImpl(&build_settings_
));
139 // The default toolchain needs to be set by the build config file.
140 mock_ifm_
.AddCannedResponse(build_config
,
141 "set_default_toolchain(\"//tc:tc\")");
143 loader
->set_async_load_file(mock_ifm_
.GetCallback());
145 // Request the root build file be loaded. This should kick off the default
146 // build config loading.
147 SourceFile
root_build("//BUILD.gn");
148 loader
->Load(root_build
, LocationRange(), Label());
149 EXPECT_TRUE(mock_ifm_
.HasOnePending(build_config
));
151 // Completing the build config load should kick off the root build file load.
152 mock_ifm_
.IssueAllPending();
153 scheduler_
.main_loop()->RunUntilIdle();
154 EXPECT_TRUE(mock_ifm_
.HasOnePending(root_build
));
156 // Load the root build file.
157 mock_ifm_
.IssueAllPending();
158 scheduler_
.main_loop()->RunUntilIdle();
160 // Schedule some other file to load in another toolchain.
161 Label
second_tc(SourceDir("//tc2/"), "tc2");
162 SourceFile
second_file("//foo/BUILD.gn");
163 loader
->Load(second_file
, LocationRange(), second_tc
);
164 EXPECT_TRUE(mock_ifm_
.HasOnePending(SourceFile("//tc2/BUILD.gn")));
166 // Running the toolchain file should schedule the build config file to load
167 // for that toolchain.
168 mock_ifm_
.IssueAllPending();
169 scheduler_
.main_loop()->RunUntilIdle();
171 // We have to tell it we have a toolchain definition now (normally the
172 // builder would do this).
173 const Settings
* default_settings
= loader
->GetToolchainSettings(Label());
174 Toolchain
second_tc_object(default_settings
, second_tc
);
175 loader
->ToolchainLoaded(&second_tc_object
);
176 EXPECT_TRUE(mock_ifm_
.HasOnePending(build_config
));
178 // Scheduling a second file to load in that toolchain should not make it
179 // pending yet (it's waiting for the build config).
180 SourceFile
third_file("//bar/BUILD.gn");
181 loader
->Load(third_file
, LocationRange(), second_tc
);
182 EXPECT_TRUE(mock_ifm_
.HasOnePending(build_config
));
184 // Running the build config file should make our third file pending.
185 mock_ifm_
.IssueAllPending();
186 scheduler_
.main_loop()->RunUntilIdle();
187 EXPECT_TRUE(mock_ifm_
.HasTwoPending(second_file
, third_file
));
189 EXPECT_FALSE(scheduler_
.is_failed());