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