1// Copyright 2015 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "dyndep_parser.h"
16
17#include <map>
18#include <vector>
19
20#include "dyndep.h"
21#include "graph.h"
22#include "state.h"
23#include "test.h"
24
25using namespace std;
26
27struct DyndepParserTest : public testing::Test {
28 void AssertParse(const char* input) {
29 DyndepParser parser(&state_, &fs_, &dyndep_file_);
30 string err;
31 EXPECT_TRUE(parser.ParseTest(input, &err));
32 ASSERT_EQ("", err);
33 }
34
35 virtual void SetUp() {
36 ::AssertParse(&state_,
37"rule touch\n"
38" command = touch $out\n"
39"build out otherout: touch\n");
40 }
41
42 State state_;
43 VirtualFileSystem fs_;
44 DyndepFile dyndep_file_;
45};
46
47TEST_F(DyndepParserTest, Empty) {
48 const char kInput[] =
49"";
50 DyndepParser parser(&state_, &fs_, &dyndep_file_);
51 string err;
52 EXPECT_FALSE(parser.ParseTest(kInput, &err));
53 EXPECT_EQ("input:1: expected 'ninja_dyndep_version = ...'\n", err);
54}
55
56TEST_F(DyndepParserTest, Version1) {
57 ASSERT_NO_FATAL_FAILURE(AssertParse(
58"ninja_dyndep_version = 1\n"));
59}
60
61TEST_F(DyndepParserTest, Version1Extra) {
62 ASSERT_NO_FATAL_FAILURE(AssertParse(
63"ninja_dyndep_version = 1-extra\n"));
64}
65
66TEST_F(DyndepParserTest, Version1_0) {
67 ASSERT_NO_FATAL_FAILURE(AssertParse(
68"ninja_dyndep_version = 1.0\n"));
69}
70
71TEST_F(DyndepParserTest, Version1_0Extra) {
72 ASSERT_NO_FATAL_FAILURE(AssertParse(
73"ninja_dyndep_version = 1.0-extra\n"));
74}
75
76TEST_F(DyndepParserTest, CommentVersion) {
77 ASSERT_NO_FATAL_FAILURE(AssertParse(
78"# comment\n"
79"ninja_dyndep_version = 1\n"));
80}
81
82TEST_F(DyndepParserTest, BlankLineVersion) {
83 ASSERT_NO_FATAL_FAILURE(AssertParse(
84"\n"
85"ninja_dyndep_version = 1\n"));
86}
87
88TEST_F(DyndepParserTest, VersionCRLF) {
89 ASSERT_NO_FATAL_FAILURE(AssertParse(
90"ninja_dyndep_version = 1\r\n"));
91}
92
93TEST_F(DyndepParserTest, CommentVersionCRLF) {
94 ASSERT_NO_FATAL_FAILURE(AssertParse(
95"# comment\r\n"
96"ninja_dyndep_version = 1\r\n"));
97}
98
99TEST_F(DyndepParserTest, BlankLineVersionCRLF) {
100 ASSERT_NO_FATAL_FAILURE(AssertParse(
101"\r\n"
102"ninja_dyndep_version = 1\r\n"));
103}
104
105TEST_F(DyndepParserTest, VersionUnexpectedEOF) {
106 const char kInput[] =
107"ninja_dyndep_version = 1.0";
108 DyndepParser parser(&state_, &fs_, &dyndep_file_);
109 string err;
110 EXPECT_FALSE(parser.ParseTest(kInput, &err));
111 EXPECT_EQ("input:1: unexpected EOF\n"
112 "ninja_dyndep_version = 1.0\n"
113 " ^ near here", err);
114}
115
116TEST_F(DyndepParserTest, UnsupportedVersion0) {
117 const char kInput[] =
118"ninja_dyndep_version = 0\n";
119 DyndepParser parser(&state_, &fs_, &dyndep_file_);
120 string err;
121 EXPECT_FALSE(parser.ParseTest(kInput, &err));
122 EXPECT_EQ("input:1: unsupported 'ninja_dyndep_version = 0'\n"
123 "ninja_dyndep_version = 0\n"
124 " ^ near here", err);
125}
126
127TEST_F(DyndepParserTest, UnsupportedVersion1_1) {
128 const char kInput[] =
129"ninja_dyndep_version = 1.1\n";
130 DyndepParser parser(&state_, &fs_, &dyndep_file_);
131 string err;
132 EXPECT_FALSE(parser.ParseTest(kInput, &err));
133 EXPECT_EQ("input:1: unsupported 'ninja_dyndep_version = 1.1'\n"
134 "ninja_dyndep_version = 1.1\n"
135 " ^ near here", err);
136}
137
138TEST_F(DyndepParserTest, DuplicateVersion) {
139 const char kInput[] =
140"ninja_dyndep_version = 1\n"
141"ninja_dyndep_version = 1\n";
142 DyndepParser parser(&state_, &fs_, &dyndep_file_);
143 string err;
144 EXPECT_FALSE(parser.ParseTest(kInput, &err));
145 EXPECT_EQ("input:2: unexpected identifier\n", err);
146}
147
148TEST_F(DyndepParserTest, MissingVersionOtherVar) {
149 const char kInput[] =
150"not_ninja_dyndep_version = 1\n";
151 DyndepParser parser(&state_, &fs_, &dyndep_file_);
152 string err;
153 EXPECT_FALSE(parser.ParseTest(kInput, &err));
154 EXPECT_EQ("input:1: expected 'ninja_dyndep_version = ...'\n"
155 "not_ninja_dyndep_version = 1\n"
156 " ^ near here", err);
157}
158
159TEST_F(DyndepParserTest, MissingVersionBuild) {
160 const char kInput[] =
161"build out: dyndep\n";
162 DyndepParser parser(&state_, &fs_, &dyndep_file_);
163 string err;
164 EXPECT_FALSE(parser.ParseTest(kInput, &err));
165 EXPECT_EQ("input:1: expected 'ninja_dyndep_version = ...'\n", err);
166}
167
168TEST_F(DyndepParserTest, UnexpectedEqual) {
169 const char kInput[] =
170"= 1\n";
171 DyndepParser parser(&state_, &fs_, &dyndep_file_);
172 string err;
173 EXPECT_FALSE(parser.ParseTest(kInput, &err));
174 EXPECT_EQ("input:1: unexpected '='\n", err);
175}
176
177TEST_F(DyndepParserTest, UnexpectedIndent) {
178 const char kInput[] =
179" = 1\n";
180 DyndepParser parser(&state_, &fs_, &dyndep_file_);
181 string err;
182 EXPECT_FALSE(parser.ParseTest(kInput, &err));
183 EXPECT_EQ("input:1: unexpected indent\n", err);
184}
185
186TEST_F(DyndepParserTest, OutDuplicate) {
187 const char kInput[] =
188"ninja_dyndep_version = 1\n"
189"build out: dyndep\n"
190"build out: dyndep\n";
191 DyndepParser parser(&state_, &fs_, &dyndep_file_);
192 string err;
193 EXPECT_FALSE(parser.ParseTest(kInput, &err));
194 EXPECT_EQ("input:3: multiple statements for 'out'\n"
195 "build out: dyndep\n"
196 " ^ near here", err);
197}
198
199TEST_F(DyndepParserTest, OutDuplicateThroughOther) {
200 const char kInput[] =
201"ninja_dyndep_version = 1\n"
202"build out: dyndep\n"
203"build otherout: dyndep\n";
204 DyndepParser parser(&state_, &fs_, &dyndep_file_);
205 string err;
206 EXPECT_FALSE(parser.ParseTest(kInput, &err));
207 EXPECT_EQ("input:3: multiple statements for 'otherout'\n"
208 "build otherout: dyndep\n"
209 " ^ near here", err);
210}
211
212TEST_F(DyndepParserTest, NoOutEOF) {
213 const char kInput[] =
214"ninja_dyndep_version = 1\n"
215"build";
216 DyndepParser parser(&state_, &fs_, &dyndep_file_);
217 string err;
218 EXPECT_FALSE(parser.ParseTest(kInput, &err));
219 EXPECT_EQ("input:2: unexpected EOF\n"
220 "build\n"
221 " ^ near here", err);
222}
223
224TEST_F(DyndepParserTest, NoOutColon) {
225 const char kInput[] =
226"ninja_dyndep_version = 1\n"
227"build :\n";
228 DyndepParser parser(&state_, &fs_, &dyndep_file_);
229 string err;
230 EXPECT_FALSE(parser.ParseTest(kInput, &err));
231 EXPECT_EQ("input:2: expected path\n"
232 "build :\n"
233 " ^ near here", err);
234}
235
236TEST_F(DyndepParserTest, OutNoStatement) {
237 const char kInput[] =
238"ninja_dyndep_version = 1\n"
239"build missing: dyndep\n";
240 DyndepParser parser(&state_, &fs_, &dyndep_file_);
241 string err;
242 EXPECT_FALSE(parser.ParseTest(kInput, &err));
243 EXPECT_EQ("input:2: no build statement exists for 'missing'\n"
244 "build missing: dyndep\n"
245 " ^ near here", err);
246}
247
248TEST_F(DyndepParserTest, OutEOF) {
249 const char kInput[] =
250"ninja_dyndep_version = 1\n"
251"build out";
252 DyndepParser parser(&state_, &fs_, &dyndep_file_);
253 string err;
254 EXPECT_FALSE(parser.ParseTest(kInput, &err));
255 EXPECT_EQ("input:2: unexpected EOF\n"
256 "build out\n"
257 " ^ near here", err);
258}
259
260TEST_F(DyndepParserTest, OutNoRule) {
261 const char kInput[] =
262"ninja_dyndep_version = 1\n"
263"build out:";
264 DyndepParser parser(&state_, &fs_, &dyndep_file_);
265 string err;
266 EXPECT_FALSE(parser.ParseTest(kInput, &err));
267 EXPECT_EQ("input:2: expected build command name 'dyndep'\n"
268 "build out:\n"
269 " ^ near here", err);
270}
271
272TEST_F(DyndepParserTest, OutBadRule) {
273 const char kInput[] =
274"ninja_dyndep_version = 1\n"
275"build out: touch";
276 DyndepParser parser(&state_, &fs_, &dyndep_file_);
277 string err;
278 EXPECT_FALSE(parser.ParseTest(kInput, &err));
279 EXPECT_EQ("input:2: expected build command name 'dyndep'\n"
280 "build out: touch\n"
281 " ^ near here", err);
282}
283
284TEST_F(DyndepParserTest, BuildEOF) {
285 const char kInput[] =
286"ninja_dyndep_version = 1\n"
287"build out: dyndep";
288 DyndepParser parser(&state_, &fs_, &dyndep_file_);
289 string err;
290 EXPECT_FALSE(parser.ParseTest(kInput, &err));
291 EXPECT_EQ("input:2: unexpected EOF\n"
292 "build out: dyndep\n"
293 " ^ near here", err);
294}
295
296TEST_F(DyndepParserTest, ExplicitOut) {
297 const char kInput[] =
298"ninja_dyndep_version = 1\n"
299"build out exp: dyndep\n";
300 DyndepParser parser(&state_, &fs_, &dyndep_file_);
301 string err;
302 EXPECT_FALSE(parser.ParseTest(kInput, &err));
303 EXPECT_EQ("input:2: explicit outputs not supported\n"
304 "build out exp: dyndep\n"
305 " ^ near here", err);
306}
307
308TEST_F(DyndepParserTest, ExplicitIn) {
309 const char kInput[] =
310"ninja_dyndep_version = 1\n"
311"build out: dyndep exp\n";
312 DyndepParser parser(&state_, &fs_, &dyndep_file_);
313 string err;
314 EXPECT_FALSE(parser.ParseTest(kInput, &err));
315 EXPECT_EQ("input:2: explicit inputs not supported\n"
316 "build out: dyndep exp\n"
317 " ^ near here", err);
318}
319
320TEST_F(DyndepParserTest, OrderOnlyIn) {
321 const char kInput[] =
322"ninja_dyndep_version = 1\n"
323"build out: dyndep ||\n";
324 DyndepParser parser(&state_, &fs_, &dyndep_file_);
325 string err;
326 EXPECT_FALSE(parser.ParseTest(kInput, &err));
327 EXPECT_EQ("input:2: order-only inputs not supported\n"
328 "build out: dyndep ||\n"
329 " ^ near here", err);
330}
331
332TEST_F(DyndepParserTest, BadBinding) {
333 const char kInput[] =
334"ninja_dyndep_version = 1\n"
335"build out: dyndep\n"
336" not_restat = 1\n";
337 DyndepParser parser(&state_, &fs_, &dyndep_file_);
338 string err;
339 EXPECT_FALSE(parser.ParseTest(kInput, &err));
340 EXPECT_EQ("input:3: binding is not 'restat'\n"
341 " not_restat = 1\n"
342 " ^ near here", err);
343}
344
345TEST_F(DyndepParserTest, RestatTwice) {
346 const char kInput[] =
347"ninja_dyndep_version = 1\n"
348"build out: dyndep\n"
349" restat = 1\n"
350" restat = 1\n";
351 DyndepParser parser(&state_, &fs_, &dyndep_file_);
352 string err;
353 EXPECT_FALSE(parser.ParseTest(kInput, &err));
354 EXPECT_EQ("input:4: unexpected indent\n", err);
355}
356
357TEST_F(DyndepParserTest, NoImplicit) {
358 ASSERT_NO_FATAL_FAILURE(AssertParse(
359"ninja_dyndep_version = 1\n"
360"build out: dyndep\n"));
361
362 EXPECT_EQ(1u, dyndep_file_.size());
363 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
364 ASSERT_NE(i, dyndep_file_.end());
365 EXPECT_EQ(false, i->second.restat_);
366 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
367 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
368}
369
370TEST_F(DyndepParserTest, EmptyImplicit) {
371 ASSERT_NO_FATAL_FAILURE(AssertParse(
372"ninja_dyndep_version = 1\n"
373"build out | : dyndep |\n"));
374
375 EXPECT_EQ(1u, dyndep_file_.size());
376 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
377 ASSERT_NE(i, dyndep_file_.end());
378 EXPECT_EQ(false, i->second.restat_);
379 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
380 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
381}
382
383TEST_F(DyndepParserTest, ImplicitIn) {
384 ASSERT_NO_FATAL_FAILURE(AssertParse(
385"ninja_dyndep_version = 1\n"
386"build out: dyndep | impin\n"));
387
388 EXPECT_EQ(1u, dyndep_file_.size());
389 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
390 ASSERT_NE(i, dyndep_file_.end());
391 EXPECT_EQ(false, i->second.restat_);
392 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
393 ASSERT_EQ(1u, i->second.implicit_inputs_.size());
394 EXPECT_EQ("impin", i->second.implicit_inputs_[0]->path());
395}
396
397TEST_F(DyndepParserTest, ImplicitIns) {
398 ASSERT_NO_FATAL_FAILURE(AssertParse(
399"ninja_dyndep_version = 1\n"
400"build out: dyndep | impin1 impin2\n"));
401
402 EXPECT_EQ(1u, dyndep_file_.size());
403 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
404 ASSERT_NE(i, dyndep_file_.end());
405 EXPECT_EQ(false, i->second.restat_);
406 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
407 ASSERT_EQ(2u, i->second.implicit_inputs_.size());
408 EXPECT_EQ("impin1", i->second.implicit_inputs_[0]->path());
409 EXPECT_EQ("impin2", i->second.implicit_inputs_[1]->path());
410}
411
412TEST_F(DyndepParserTest, ImplicitOut) {
413 ASSERT_NO_FATAL_FAILURE(AssertParse(
414"ninja_dyndep_version = 1\n"
415"build out | impout: dyndep\n"));
416
417 EXPECT_EQ(1u, dyndep_file_.size());
418 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
419 ASSERT_NE(i, dyndep_file_.end());
420 EXPECT_EQ(false, i->second.restat_);
421 ASSERT_EQ(1u, i->second.implicit_outputs_.size());
422 EXPECT_EQ("impout", i->second.implicit_outputs_[0]->path());
423 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
424}
425
426TEST_F(DyndepParserTest, ImplicitOuts) {
427 ASSERT_NO_FATAL_FAILURE(AssertParse(
428"ninja_dyndep_version = 1\n"
429"build out | impout1 impout2 : dyndep\n"));
430
431 EXPECT_EQ(1u, dyndep_file_.size());
432 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
433 ASSERT_NE(i, dyndep_file_.end());
434 EXPECT_EQ(false, i->second.restat_);
435 ASSERT_EQ(2u, i->second.implicit_outputs_.size());
436 EXPECT_EQ("impout1", i->second.implicit_outputs_[0]->path());
437 EXPECT_EQ("impout2", i->second.implicit_outputs_[1]->path());
438 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
439}
440
441TEST_F(DyndepParserTest, ImplicitInsAndOuts) {
442 ASSERT_NO_FATAL_FAILURE(AssertParse(
443"ninja_dyndep_version = 1\n"
444"build out | impout1 impout2: dyndep | impin1 impin2\n"));
445
446 EXPECT_EQ(1u, dyndep_file_.size());
447 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
448 ASSERT_NE(i, dyndep_file_.end());
449 EXPECT_EQ(false, i->second.restat_);
450 ASSERT_EQ(2u, i->second.implicit_outputs_.size());
451 EXPECT_EQ("impout1", i->second.implicit_outputs_[0]->path());
452 EXPECT_EQ("impout2", i->second.implicit_outputs_[1]->path());
453 ASSERT_EQ(2u, i->second.implicit_inputs_.size());
454 EXPECT_EQ("impin1", i->second.implicit_inputs_[0]->path());
455 EXPECT_EQ("impin2", i->second.implicit_inputs_[1]->path());
456}
457
458TEST_F(DyndepParserTest, Restat) {
459 ASSERT_NO_FATAL_FAILURE(AssertParse(
460"ninja_dyndep_version = 1\n"
461"build out: dyndep\n"
462" restat = 1\n"));
463
464 EXPECT_EQ(1u, dyndep_file_.size());
465 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
466 ASSERT_NE(i, dyndep_file_.end());
467 EXPECT_EQ(true, i->second.restat_);
468 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
469 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
470}
471
472TEST_F(DyndepParserTest, OtherOutput) {
473 ASSERT_NO_FATAL_FAILURE(AssertParse(
474"ninja_dyndep_version = 1\n"
475"build otherout: dyndep\n"));
476
477 EXPECT_EQ(1u, dyndep_file_.size());
478 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
479 ASSERT_NE(i, dyndep_file_.end());
480 EXPECT_EQ(false, i->second.restat_);
481 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
482 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
483}
484
485TEST_F(DyndepParserTest, MultipleEdges) {
486 ::AssertParse(&state_,
487"build out2: touch\n");
488 ASSERT_EQ(2u, state_.edges_.size());
489 ASSERT_EQ(1u, state_.edges_[1]->outputs_.size());
490 EXPECT_EQ("out2", state_.edges_[1]->outputs_[0]->path());
491 EXPECT_EQ(0u, state_.edges_[0]->inputs_.size());
492
493 ASSERT_NO_FATAL_FAILURE(AssertParse(
494"ninja_dyndep_version = 1\n"
495"build out: dyndep\n"
496"build out2: dyndep\n"
497" restat = 1\n"));
498
499 EXPECT_EQ(2u, dyndep_file_.size());
500 {
501 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[0]);
502 ASSERT_NE(i, dyndep_file_.end());
503 EXPECT_EQ(false, i->second.restat_);
504 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
505 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
506 }
507 {
508 DyndepFile::iterator i = dyndep_file_.find(state_.edges_[1]);
509 ASSERT_NE(i, dyndep_file_.end());
510 EXPECT_EQ(true, i->second.restat_);
511 EXPECT_EQ(0u, i->second.implicit_outputs_.size());
512 EXPECT_EQ(0u, i->second.implicit_inputs_.size());
513 }
514}
515