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/"));
122 Scheduler scheduler_
;
123 BuildSettings build_settings_
;
124 MockInputFileManager mock_ifm_
;
129 // -----------------------------------------------------------------------------
131 TEST_F(LoaderTest
, Foo
) {
132 SourceFile
build_config("//build/config/BUILDCONFIG.gn");
133 build_settings_
.set_build_config_file(build_config
);
135 scoped_refptr
<LoaderImpl
> loader(new LoaderImpl(&build_settings_
));
137 // The default toolchain needs to be set by the build config file.
138 mock_ifm_
.AddCannedResponse(build_config
,
139 "set_default_toolchain(\"//tc:tc\")");
141 loader
->set_async_load_file(mock_ifm_
.GetCallback());
143 // Request the root build file be loaded. This should kick off the default
144 // build config loading.
145 SourceFile
root_build("//BUILD.gn");
146 loader
->Load(root_build
, LocationRange(), Label());
147 EXPECT_TRUE(mock_ifm_
.HasOnePending(build_config
));
149 // Completing the build config load should kick off the root build file load.
150 mock_ifm_
.IssueAllPending();
151 scheduler_
.main_loop()->RunUntilIdle();
152 EXPECT_TRUE(mock_ifm_
.HasOnePending(root_build
));
154 // Load the root build file.
155 mock_ifm_
.IssueAllPending();
156 scheduler_
.main_loop()->RunUntilIdle();
158 // Schedule some other file to load in another toolchain.
159 Label
second_tc(SourceDir("//tc2/"), "tc2");
160 SourceFile
second_file("//foo/BUILD.gn");
161 loader
->Load(second_file
, LocationRange(), second_tc
);
162 EXPECT_TRUE(mock_ifm_
.HasOnePending(SourceFile("//tc2/BUILD.gn")));
164 // Running the toolchain file should schedule the build config file to load
165 // for that toolchain.
166 mock_ifm_
.IssueAllPending();
167 scheduler_
.main_loop()->RunUntilIdle();
169 // We have to tell it we have a toolchain definition now (normally the
170 // builder would do this).
171 const Settings
* default_settings
= loader
->GetToolchainSettings(Label());
172 Toolchain
second_tc_object(default_settings
, second_tc
);
173 loader
->ToolchainLoaded(&second_tc_object
);
174 EXPECT_TRUE(mock_ifm_
.HasOnePending(build_config
));
176 // Scheduling a second file to load in that toolchain should not make it
177 // pending yet (it's waiting for the build config).
178 SourceFile
third_file("//bar/BUILD.gn");
179 loader
->Load(third_file
, LocationRange(), second_tc
);
180 EXPECT_TRUE(mock_ifm_
.HasOnePending(build_config
));
182 // Running the build config file should make our third file pending.
183 mock_ifm_
.IssueAllPending();
184 scheduler_
.main_loop()->RunUntilIdle();
185 EXPECT_TRUE(mock_ifm_
.HasTwoPending(second_file
, third_file
));
187 EXPECT_FALSE(scheduler_
.is_failed());