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