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 (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
);
109 pending_
[i
].second
.Run(found
->second
->root
.get());
114 // LoaderTest ------------------------------------------------------------------
116 class LoaderTest
: public testing::Test
{
119 build_settings_
.SetBuildDir(SourceDir("//out/Debug/"));
121 virtual ~LoaderTest() {
125 Scheduler scheduler_
;
126 BuildSettings build_settings_
;
127 MockInputFileManager mock_ifm_
;
132 // -----------------------------------------------------------------------------
134 TEST_F(LoaderTest
, Foo
) {
135 SourceFile
build_config("//build/config/BUILDCONFIG.gn");
136 build_settings_
.set_build_config_file(build_config
);
138 scoped_refptr
<LoaderImpl
> loader(new LoaderImpl(&build_settings_
));
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\")");
144 loader
->set_async_load_file(mock_ifm_
.GetCallback());
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
));
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
));
157 // Load the root build file.
158 mock_ifm_
.IssueAllPending();
159 scheduler_
.main_loop()->RunUntilIdle();
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")));
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();
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
));
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
));
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
));
190 EXPECT_FALSE(scheduler_
.is_failed());