1// Copyright 2018 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
15package makedeps
16
17import (
18	"bytes"
19	"io"
20	"io/ioutil"
21	"os"
22	"testing"
23)
24
25func TestParse(t *testing.T) {
26	testCases := []struct {
27		name   string
28		input  string
29		output Deps
30		err    error
31	}{
32		// These come from the ninja test suite
33		{
34			name:  "Basic",
35			input: "build/ninja.o: ninja.cc ninja.h eval_env.h manifest_parser.h",
36			output: Deps{
37				Output: "build/ninja.o",
38				Inputs: []string{
39					"ninja.cc",
40					"ninja.h",
41					"eval_env.h",
42					"manifest_parser.h",
43				},
44			},
45		},
46		{
47			name: "EarlyNewlineAndWhitespace",
48			input: ` \
49  out: in`,
50			output: Deps{
51				Output: "out",
52				Inputs: []string{"in"},
53			},
54		},
55		{
56			name: "Continuation",
57			input: `foo.o: \
58  bar.h baz.h
59`,
60			output: Deps{
61				Output: "foo.o",
62				Inputs: []string{"bar.h", "baz.h"},
63			},
64		},
65		{
66			name:  "CarriageReturnContinuation",
67			input: "foo.o: \\\r\n  bar.h baz.h\r\n",
68			output: Deps{
69				Output: "foo.o",
70				Inputs: []string{"bar.h", "baz.h"},
71			},
72		},
73		{
74			name: "BackSlashes",
75			input: `Project\Dir\Build\Release8\Foo\Foo.res : \
76  Dir\Library\Foo.rc \
77  Dir\Library\Version\Bar.h \
78  Dir\Library\Foo.ico \
79  Project\Thing\Bar.tlb \
80`,
81			output: Deps{
82				Output: `Project\Dir\Build\Release8\Foo\Foo.res`,
83				Inputs: []string{
84					`Dir\Library\Foo.rc`,
85					`Dir\Library\Version\Bar.h`,
86					`Dir\Library\Foo.ico`,
87					`Project\Thing\Bar.tlb`,
88				},
89			},
90		},
91		{
92			name:  "Spaces",
93			input: `a\ bc\ def:   a\ b c d`,
94			output: Deps{
95				Output: `a bc def`,
96				Inputs: []string{"a b", "c", "d"},
97			},
98		},
99		{
100			name:  "Escapes",
101			input: `\!\@\#$$\%\^\&\\:`,
102			output: Deps{
103				Output: `\!\@#$\%\^\&\`,
104			},
105		},
106		{
107			name: "SpecialChars",
108			// Ninja includes a number of '=', but our parser can't handle that,
109			// since it sees the equals and switches over to assuming it's an
110			// assignment.
111			//
112			// We don't have any files in our tree that contain an '=' character,
113			// and Kati can't handle parsing this either, so for now I'm just
114			// going to remove all the '=' characters below.
115			//
116			// It looks like make will only do this for the first
117			// dependency, but not later dependencies.
118			input: `C\:/Program\ Files\ (x86)/Microsoft\ crtdefs.h: \
119 en@quot.header~ t+t-x!1 \
120 openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif \
121 Fu` + "\303\244ball",
122			output: Deps{
123				Output: "C:/Program Files (x86)/Microsoft crtdefs.h",
124				Inputs: []string{
125					"en@quot.header~",
126					"t+t-x!1",
127					"openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif",
128					"Fu\303\244ball",
129				},
130			},
131		},
132		// Ninja's UnifyMultipleOutputs and RejectMultipleDifferentOutputs tests have been omitted,
133		// since we don't want the same behavior.
134
135		// Our own tests
136		{
137			name: "Multiple outputs",
138			input: `a b: c
139a: d
140b: e`,
141			output: Deps{
142				Output: "b",
143				Inputs: []string{
144					"c",
145					"d",
146					"e",
147				},
148			},
149		},
150		{
151			// TODO(b/141372861): remove this
152			// AIDL produces a dep file with no output file for a parcelable (b/
153			name: "AIDL parcelable",
154			input: ` : \
155  frameworks/base/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
156`,
157			output: Deps{
158				Output: "",
159				Inputs: []string{
160					"frameworks/base/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl",
161				},
162			},
163		},
164	}
165
166	for _, tc := range testCases {
167		t.Run(tc.name, func(t *testing.T) {
168			out, err := Parse("test.d", bytes.NewBufferString(tc.input))
169			if err != tc.err {
170				t.Fatalf("Unexpected error: %v (expected %v)", err, tc.err)
171			}
172
173			if out.Output != tc.output.Output {
174				t.Errorf("output file doesn't match:\n"+
175					" str: %#v\n"+
176					"want: %#v\n"+
177					" got: %#v", tc.input, tc.output.Output, out.Output)
178			}
179
180			matches := true
181			if len(out.Inputs) != len(tc.output.Inputs) {
182				matches = false
183			} else {
184				for i := range out.Inputs {
185					if out.Inputs[i] != tc.output.Inputs[i] {
186						matches = false
187					}
188				}
189			}
190			if !matches {
191				t.Errorf("input files don't match:\n"+
192					" str: %#v\n"+
193					"want: %#v\n"+
194					" got: %#v", tc.input, tc.output.Inputs, out.Inputs)
195			}
196		})
197	}
198}
199
200func BenchmarkParsing(b *testing.B) {
201	// Write it out to a file to most closely match ninja's perftest
202	tmpfile, err := ioutil.TempFile("", "depfile")
203	if err != nil {
204		b.Fatal("Failed to create temp file:", err)
205	}
206	defer os.Remove(tmpfile.Name())
207	_, err = io.WriteString(tmpfile, `out/soong/.intermediates/external/ninja/ninja/linux_glibc_x86_64/obj/external/ninja/src/ninja.o: \
208  external/ninja/src/ninja.cc external/libcxx/include/errno.h \
209  external/libcxx/include/__config \
210  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/features.h \
211  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/predefs.h \
212  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/cdefs.h \
213  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/wordsize.h \
214  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/gnu/stubs.h \
215  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
216  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/errno.h \
217  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/errno.h \
218  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/linux/errno.h \
219  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/asm/errno.h \
220  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/asm-generic/errno.h \
221  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/asm-generic/errno-base.h \
222  external/libcxx/include/limits.h \
223  prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/limits.h \
224  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/limits.h \
225  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix1_lim.h \
226  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/local_lim.h \
227  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/linux/limits.h \
228  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix2_lim.h \
229  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/xopen_lim.h \
230  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
231  external/libcxx/include/stdio.h \
232  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdio.h \
233  external/libcxx/include/stddef.h \
234  prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stddef.h \
235  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/types.h \
236  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/typesizes.h \
237  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/libio.h \
238  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/_G_config.h \
239  external/libcxx/include/wchar.h \
240  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/wchar.h \
241  prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stdarg.h \
242  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/sys_errlist.h \
243  external/libcxx/include/stdlib.h \
244  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdlib.h \
245  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/waitflags.h \
246  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/waitstatus.h \
247  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/endian.h \
248  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/endian.h \
249  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/byteswap.h \
250  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/xlocale.h \
251  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/types.h \
252  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/time.h \
253  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/select.h \
254  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/select.h \
255  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/sigset.h \
256  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/time.h \
257  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/select2.h \
258  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/sys/sysmacros.h \
259  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \
260  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/alloca.h \
261  external/libcxx/include/string.h \
262  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/string.h \
263  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/getopt.h \
264  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/unistd.h \
265  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/posix_opt.h \
266  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/environments.h \
267  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/confname.h \
268  external/ninja/src/browse.h external/ninja/src/build.h \
269  external/libcxx/include/cstdio external/libcxx/include/map \
270  external/libcxx/include/__tree external/libcxx/include/iterator \
271  external/libcxx/include/iosfwd \
272  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/wchar.h \
273  external/libcxx/include/__functional_base \
274  external/libcxx/include/type_traits external/libcxx/include/cstddef \
275  prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/__stddef_max_align_t.h \
276  external/libcxx/include/__nullptr external/libcxx/include/typeinfo \
277  external/libcxx/include/exception external/libcxx/include/cstdlib \
278  external/libcxx/include/cstdint external/libcxx/include/stdint.h \
279  prebuilts/clang/host/linux-x86/clang-4639204/lib64/clang/6.0.1/include/stdint.h \
280  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/stdint.h \
281  external/libcxx/include/new external/libcxx/include/utility \
282  external/libcxx/include/__tuple \
283  external/libcxx/include/initializer_list \
284  external/libcxx/include/cstring external/libcxx/include/__debug \
285  external/libcxx/include/memory external/libcxx/include/limits \
286  external/libcxx/include/__undef_macros external/libcxx/include/tuple \
287  external/libcxx/include/stdexcept external/libcxx/include/cassert \
288  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/assert.h \
289  external/libcxx/include/atomic external/libcxx/include/algorithm \
290  external/libcxx/include/functional external/libcxx/include/queue \
291  external/libcxx/include/deque external/libcxx/include/__split_buffer \
292  external/libcxx/include/vector external/libcxx/include/__bit_reference \
293  external/libcxx/include/climits external/libcxx/include/set \
294  external/libcxx/include/string external/libcxx/include/string_view \
295  external/libcxx/include/__string external/libcxx/include/cwchar \
296  external/libcxx/include/cwctype external/libcxx/include/cctype \
297  external/libcxx/include/ctype.h \
298  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/ctype.h \
299  external/libcxx/include/wctype.h \
300  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/wctype.h \
301  external/ninja/src/graph.h external/ninja/src/eval_env.h \
302  external/ninja/src/string_piece.h external/ninja/src/timestamp.h \
303  external/ninja/src/util.h external/ninja/src/exit_status.h \
304  external/ninja/src/line_printer.h external/ninja/src/metrics.h \
305  external/ninja/src/build_log.h external/ninja/src/hash_map.h \
306  external/libcxx/include/unordered_map \
307  external/libcxx/include/__hash_table external/libcxx/include/cmath \
308  external/libcxx/include/math.h \
309  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/math.h \
310  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_val.h \
311  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_valf.h \
312  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/huge_vall.h \
313  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/inf.h \
314  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/nan.h \
315  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/mathdef.h \
316  prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/sysroot/usr/include/x86_64-linux-gnu/bits/mathcalls.h \
317  external/ninja/src/deps_log.h external/ninja/src/clean.h \
318  external/ninja/src/debug_flags.h external/ninja/src/disk_interface.h \
319  external/ninja/src/graphviz.h external/ninja/src/manifest_parser.h \
320  external/ninja/src/lexer.h external/ninja/src/state.h \
321  external/ninja/src/version.h`)
322	tmpfile.Close()
323	if err != nil {
324		b.Fatal("Failed to write dep file:", err)
325	}
326	b.ResetTimer()
327
328	for n := 0; n < b.N; n++ {
329		depfile, err := ioutil.ReadFile(tmpfile.Name())
330		if err != nil {
331			b.Fatal("Failed to read dep file:", err)
332		}
333
334		_, err = Parse(tmpfile.Name(), bytes.NewBuffer(depfile))
335		if err != nil {
336			b.Fatal("Failed to parse:", err)
337		}
338	}
339}
340
341func TestDepPrint(t *testing.T) {
342	testCases := []struct {
343		name   string
344		input  Deps
345		output string
346	}{
347		{
348			name: "Empty",
349			input: Deps{
350				Output: "a",
351			},
352			output: "a:",
353		},
354		{
355			name: "Basic",
356			input: Deps{
357				Output: "a",
358				Inputs: []string{"b", "c"},
359			},
360			output: "a: b c",
361		},
362		{
363			name: "Escapes",
364			input: Deps{
365				Output: `\!\@#$\%\^\&\`,
366			},
367			output: `\\!\\@\#$$\\%\\^\\&\\:`,
368		},
369		{
370			name: "Spaces",
371			input: Deps{
372				Output: "a b",
373				Inputs: []string{"c d", "e f "},
374			},
375			output: `a\ b: c\ d e\ f\ `,
376		},
377		{
378			name: "SpecialChars",
379			input: Deps{
380				Output: "C:/Program Files (x86)/Microsoft crtdefs.h",
381				Inputs: []string{
382					"en@quot.header~",
383					"t+t-x!1",
384					"openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif",
385					"Fu\303\244ball",
386				},
387			},
388			output: `C\:/Program\ Files\ (x86)/Microsoft\ crtdefs.h: en@quot.header~ t+t-x!1 openldap/slapd.d/cnconfig/cnschema/cn{0}core.ldif Fu` + "\303\244ball",
389		},
390	}
391
392	for _, tc := range testCases {
393		t.Run(tc.name, func(t *testing.T) {
394			out := tc.input.Print()
395			outStr := string(out)
396			want := tc.output + "\n"
397
398			if outStr != want {
399				t.Errorf("output doesn't match:\nwant:%q\n got:%q", want, outStr)
400			}
401		})
402	}
403}
404