1// Copyright 2017 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 java
16
17import (
18	"strconv"
19	"strings"
20	"testing"
21
22	"android/soong/android"
23)
24
25func TestKotlin(t *testing.T) {
26	ctx, _ := testJava(t, `
27		java_library {
28			name: "foo",
29			srcs: ["a.java", "b.kt"],
30		}
31
32		java_library {
33			name: "bar",
34			srcs: ["b.kt"],
35			libs: ["foo"],
36			static_libs: ["baz"],
37		}
38
39		java_library {
40			name: "baz",
41			srcs: ["c.java"],
42		}
43		`)
44
45	kotlinStdlib := ctx.ModuleForTests("kotlin-stdlib", "android_common").
46		Output("turbine-combined/kotlin-stdlib.jar").Output
47	kotlinStdlibJdk7 := ctx.ModuleForTests("kotlin-stdlib-jdk7", "android_common").
48		Output("turbine-combined/kotlin-stdlib-jdk7.jar").Output
49	kotlinStdlibJdk8 := ctx.ModuleForTests("kotlin-stdlib-jdk8", "android_common").
50		Output("turbine-combined/kotlin-stdlib-jdk8.jar").Output
51	kotlinAnnotations := ctx.ModuleForTests("kotlin-annotations", "android_common").
52		Output("turbine-combined/kotlin-annotations.jar").Output
53
54	fooKotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
55	fooJavac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
56	fooJar := ctx.ModuleForTests("foo", "android_common").Output("combined/foo.jar")
57	fooHeaderJar := ctx.ModuleForTests("foo", "android_common").Output("turbine-combined/foo.jar")
58
59	fooKotlincClasses := fooKotlinc.Output
60	fooKotlincHeaderClasses := fooKotlinc.ImplicitOutput
61
62	if len(fooKotlinc.Inputs) != 2 || fooKotlinc.Inputs[0].String() != "a.java" ||
63		fooKotlinc.Inputs[1].String() != "b.kt" {
64		t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, fooKotlinc.Inputs)
65	}
66
67	if len(fooJavac.Inputs) != 1 || fooJavac.Inputs[0].String() != "a.java" {
68		t.Errorf(`foo inputs %v != ["a.java"]`, fooJavac.Inputs)
69	}
70
71	if !strings.Contains(fooJavac.Args["classpath"], fooKotlincHeaderClasses.String()) {
72		t.Errorf("foo classpath %v does not contain %q",
73			fooJavac.Args["classpath"], fooKotlincHeaderClasses.String())
74	}
75
76	if !inList(fooKotlincClasses.String(), fooJar.Inputs.Strings()) {
77		t.Errorf("foo jar inputs %v does not contain %q",
78			fooJar.Inputs.Strings(), fooKotlincClasses.String())
79	}
80
81	if !inList(kotlinStdlib.String(), fooJar.Inputs.Strings()) {
82		t.Errorf("foo jar inputs %v does not contain %v",
83			fooJar.Inputs.Strings(), kotlinStdlib.String())
84	}
85
86	if !inList(kotlinStdlibJdk7.String(), fooJar.Inputs.Strings()) {
87		t.Errorf("foo jar inputs %v does not contain %v",
88			fooJar.Inputs.Strings(), kotlinStdlibJdk7.String())
89	}
90
91	if !inList(kotlinStdlibJdk8.String(), fooJar.Inputs.Strings()) {
92		t.Errorf("foo jar inputs %v does not contain %v",
93			fooJar.Inputs.Strings(), kotlinStdlibJdk8.String())
94	}
95
96	if !inList(kotlinAnnotations.String(), fooJar.Inputs.Strings()) {
97		t.Errorf("foo jar inputs %v does not contain %v",
98			fooJar.Inputs.Strings(), kotlinAnnotations.String())
99	}
100
101	if !inList(fooKotlincHeaderClasses.String(), fooHeaderJar.Inputs.Strings()) {
102		t.Errorf("foo header jar inputs %v does not contain %q",
103			fooHeaderJar.Inputs.Strings(), fooKotlincHeaderClasses.String())
104	}
105
106	bazHeaderJar := ctx.ModuleForTests("baz", "android_common").Output("turbine-combined/baz.jar")
107	barKotlinc := ctx.ModuleForTests("bar", "android_common").Rule("kotlinc")
108
109	if len(barKotlinc.Inputs) != 1 || barKotlinc.Inputs[0].String() != "b.kt" {
110		t.Errorf(`bar kotlinc inputs %v != ["b.kt"]`, barKotlinc.Inputs)
111	}
112
113	if !inList(fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
114		t.Errorf(`expected %q in bar implicits %v`,
115			fooHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
116	}
117
118	if !inList(bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings()) {
119		t.Errorf(`expected %q in bar implicits %v`,
120			bazHeaderJar.Output.String(), barKotlinc.Implicits.Strings())
121	}
122}
123
124func TestKapt(t *testing.T) {
125	bp := `
126		java_library {
127			name: "foo",
128			srcs: ["a.java", "b.kt"],
129			plugins: ["bar", "baz"],
130			errorprone: {
131				extra_check_modules: ["my_check"],
132			},
133		}
134
135		java_plugin {
136			name: "bar",
137			processor_class: "com.bar",
138			srcs: ["b.java"],
139		}
140
141		java_plugin {
142			name: "baz",
143			processor_class: "com.baz",
144			srcs: ["b.java"],
145		}
146
147		java_plugin {
148			name: "my_check",
149			srcs: ["b.java"],
150		}
151	`
152	t.Run("", func(t *testing.T) {
153		ctx, _ := testJava(t, bp)
154
155		buildOS := ctx.Config().BuildOS.String()
156
157		foo := ctx.ModuleForTests("foo", "android_common")
158		kaptStubs := foo.Rule("kapt")
159		turbineApt := foo.Description("turbine apt")
160		kotlinc := foo.Rule("kotlinc")
161		javac := foo.Rule("javac")
162
163		bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
164		baz := ctx.ModuleForTests("baz", buildOS+"_common").Rule("javac").Output.String()
165
166		// Test that the kotlin and java sources are passed to kapt and kotlinc
167		if len(kaptStubs.Inputs) != 2 || kaptStubs.Inputs[0].String() != "a.java" || kaptStubs.Inputs[1].String() != "b.kt" {
168			t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kaptStubs.Inputs)
169		}
170		if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" || kotlinc.Inputs[1].String() != "b.kt" {
171			t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs)
172		}
173
174		// Test that only the java sources are passed to turbine-apt and javac
175		if len(turbineApt.Inputs) != 1 || turbineApt.Inputs[0].String() != "a.java" {
176			t.Errorf(`foo turbine apt inputs %v != ["a.java"]`, turbineApt.Inputs)
177		}
178		if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
179			t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
180		}
181
182		// Test that the kapt stubs jar is a dependency of turbine-apt
183		if !inList(kaptStubs.Output.String(), turbineApt.Implicits.Strings()) {
184			t.Errorf("expected %q in turbine-apt implicits %v", kaptStubs.Output.String(), kotlinc.Implicits.Strings())
185		}
186
187		// Test that the turbine-apt srcjar is a dependency of kotlinc and javac rules
188		if !inList(turbineApt.Output.String(), kotlinc.Implicits.Strings()) {
189			t.Errorf("expected %q in kotlinc implicits %v", turbineApt.Output.String(), kotlinc.Implicits.Strings())
190		}
191		if !inList(turbineApt.Output.String(), javac.Implicits.Strings()) {
192			t.Errorf("expected %q in javac implicits %v", turbineApt.Output.String(), javac.Implicits.Strings())
193		}
194
195		// Test that the turbine-apt srcjar is extracted by the kotlinc and javac rules
196		if kotlinc.Args["srcJars"] != turbineApt.Output.String() {
197			t.Errorf("expected %q in kotlinc srcjars %v", turbineApt.Output.String(), kotlinc.Args["srcJars"])
198		}
199		if javac.Args["srcJars"] != turbineApt.Output.String() {
200			t.Errorf("expected %q in javac srcjars %v", turbineApt.Output.String(), kotlinc.Args["srcJars"])
201		}
202
203		// Test that the processors are passed to kapt
204		expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
205			" -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz
206		if kaptStubs.Args["kaptProcessorPath"] != expectedProcessorPath {
207			t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kaptStubs.Args["kaptProcessorPath"])
208		}
209		expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz"
210		if kaptStubs.Args["kaptProcessor"] != expectedProcessor {
211			t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kaptStubs.Args["kaptProcessor"])
212		}
213
214		// Test that the processors are passed to turbine-apt
215		expectedProcessorPath = "--processorpath " + bar + " " + baz
216		if !strings.Contains(turbineApt.Args["turbineFlags"], expectedProcessorPath) {
217			t.Errorf("expected turbine-apt processorpath %q, got %q", expectedProcessorPath, turbineApt.Args["turbineFlags"])
218		}
219		expectedProcessor = "--processors com.bar com.baz"
220		if !strings.Contains(turbineApt.Args["turbineFlags"], expectedProcessor) {
221			t.Errorf("expected turbine-apt processor %q, got %q", expectedProcessor, turbineApt.Args["turbineFlags"])
222		}
223
224		// Test that the processors are not passed to javac
225		if javac.Args["processorpath"] != "" {
226			t.Errorf("expected processorPath '', got %q", javac.Args["processorpath"])
227		}
228		if javac.Args["processor"] != "-proc:none" {
229			t.Errorf("expected processor '-proc:none', got %q", javac.Args["processor"])
230		}
231	})
232
233	t.Run("errorprone", func(t *testing.T) {
234		env := map[string]string{
235			"RUN_ERROR_PRONE": "true",
236		}
237
238		result := android.GroupFixturePreparers(
239			PrepareForTestWithJavaDefaultModules,
240			android.FixtureMergeEnv(env),
241		).RunTestWithBp(t, bp)
242
243		buildOS := result.Config.BuildOS.String()
244
245		kapt := result.ModuleForTests("foo", "android_common").Rule("kapt")
246		javac := result.ModuleForTests("foo", "android_common").Description("javac")
247		errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone")
248
249		bar := result.ModuleForTests("bar", buildOS+"_common").Description("javac").Output.String()
250		baz := result.ModuleForTests("baz", buildOS+"_common").Description("javac").Output.String()
251		myCheck := result.ModuleForTests("my_check", buildOS+"_common").Description("javac").Output.String()
252
253		// Test that the errorprone plugins are not passed to kapt
254		expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
255			" -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz
256		if kapt.Args["kaptProcessorPath"] != expectedProcessorPath {
257			t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"])
258		}
259		expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz"
260		if kapt.Args["kaptProcessor"] != expectedProcessor {
261			t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"])
262		}
263
264		// Test that the errorprone plugins are not passed to javac
265		if javac.Args["processorpath"] != "" {
266			t.Errorf("expected processorPath '', got %q", javac.Args["processorpath"])
267		}
268		if javac.Args["processor"] != "-proc:none" {
269			t.Errorf("expected processor '-proc:none', got %q", javac.Args["processor"])
270		}
271
272		// Test that the errorprone plugins are passed to errorprone
273		expectedProcessorPath = "-processorpath " + myCheck
274		if errorprone.Args["processorpath"] != expectedProcessorPath {
275			t.Errorf("expected processorpath %q, got %q", expectedProcessorPath, errorprone.Args["processorpath"])
276		}
277		if errorprone.Args["processor"] != "-proc:none" {
278			t.Errorf("expected processor '-proc:none', got %q", errorprone.Args["processor"])
279		}
280	})
281}
282
283func TestKaptEncodeFlags(t *testing.T) {
284	// Compares the kaptEncodeFlags against the results of the example implementation at
285	// https://kotlinlang.org/docs/reference/kapt.html#apjavac-options-encoding
286	tests := []struct {
287		in  [][2]string
288		out string
289	}{
290		{
291			// empty input
292			in:  [][2]string{},
293			out: "rO0ABXcEAAAAAA==",
294		},
295		{
296			// common input
297			in: [][2]string{
298				{"-source", "1.8"},
299				{"-target", "1.8"},
300			},
301			out: "rO0ABXcgAAAAAgAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjg=",
302		},
303		{
304			// input that serializes to a 255 byte block
305			in: [][2]string{
306				{"-source", "1.8"},
307				{"-target", "1.8"},
308				{"a", strings.Repeat("b", 218)},
309			},
310			out: "rO0ABXf/AAAAAwAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjgAAWEA2mJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJi",
311		},
312		{
313			// input that serializes to a 256 byte block
314			in: [][2]string{
315				{"-source", "1.8"},
316				{"-target", "1.8"},
317				{"a", strings.Repeat("b", 219)},
318			},
319			out: "rO0ABXoAAAEAAAAAAwAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjgAAWEA22JiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYg==",
320		},
321		{
322			// input that serializes to a 257 byte block
323			in: [][2]string{
324				{"-source", "1.8"},
325				{"-target", "1.8"},
326				{"a", strings.Repeat("b", 220)},
327			},
328			out: "rO0ABXoAAAEBAAAAAwAHLXNvdXJjZQADMS44AActdGFyZ2V0AAMxLjgAAWEA3GJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmI=",
329		},
330	}
331
332	for i, test := range tests {
333		t.Run(strconv.Itoa(i), func(t *testing.T) {
334			got := kaptEncodeFlags(test.in)
335			if got != test.out {
336				t.Errorf("\nwant %q\n got %q", test.out, got)
337			}
338		})
339	}
340}
341
342func TestKotlinCompose(t *testing.T) {
343	result := android.GroupFixturePreparers(
344		PrepareForTestWithJavaDefaultModules,
345	).RunTestWithBp(t, `
346		java_library {
347			name: "androidx.compose.runtime_runtime",
348		}
349
350		java_library_host {
351			name: "androidx.compose.compiler_compiler-hosted",
352		}
353
354		java_library {
355			name: "withcompose",
356			srcs: ["a.kt"],
357			plugins: ["plugin"],
358			static_libs: ["androidx.compose.runtime_runtime"],
359		}
360
361		java_library {
362			name: "nocompose",
363			srcs: ["a.kt"],
364		}
365
366		java_plugin {
367			name: "plugin",
368		}
369	`)
370
371	buildOS := result.Config.BuildOS.String()
372
373	composeCompiler := result.ModuleForTests("androidx.compose.compiler_compiler-hosted", buildOS+"_common").Rule("combineJar").Output
374	withCompose := result.ModuleForTests("withcompose", "android_common")
375	noCompose := result.ModuleForTests("nocompose", "android_common")
376
377	android.AssertStringListContains(t, "missing compose compiler dependency",
378		withCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String())
379
380	android.AssertStringDoesContain(t, "missing compose compiler plugin",
381		withCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String())
382
383	android.AssertStringListContains(t, "missing kapt compose compiler dependency",
384		withCompose.Rule("kapt").Implicits.Strings(), composeCompiler.String())
385
386	android.AssertStringListDoesNotContain(t, "unexpected compose compiler dependency",
387		noCompose.Rule("kotlinc").Implicits.Strings(), composeCompiler.String())
388
389	android.AssertStringDoesNotContain(t, "unexpected compose compiler plugin",
390		noCompose.VariablesForTestsRelativeToTop()["kotlincFlags"], "-Xplugin="+composeCompiler.String())
391}
392