1# Copyright 2022 Google LLC. 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"""Kotlin kt_jvm_library rule tests.""" 16 17load("//kotlin:jvm_library.bzl", "kt_jvm_library") 18load("//tests/analysis:util.bzl", "ONLY_FOR_ANALYSIS_TEST_TAGS", "create_file", "get_action", "get_arg") 19load("@bazel_skylib//lib:sets.bzl", "sets") 20load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") 21load(":assert_failure_test.bzl", "assert_failure_test") 22load("//:visibility.bzl", "RULES_KOTLIN") 23 24_DEFAULT_LIST = ["__default__"] 25 26def _test_impl(ctx): 27 env = analysistest.begin(ctx) 28 actions = analysistest.target_actions(env) 29 actual = ctx.attr.target_under_test 30 expected = ctx.attr.expected 31 32 asserts.true( 33 env, 34 JavaInfo in actual, 35 "kt_jvm_library did not produce JavaInfo provider.", 36 ) 37 asserts.true( 38 env, 39 ProguardSpecProvider in actual, 40 "Expected a ProguardSpecProvider provider.", 41 ) 42 43 if "data" in expected: 44 expected_data = expected["data"] 45 actual_data = _extract_data_runfiles(actual) 46 47 asserts.new_set_equals( 48 env, 49 sets.make(expected_data), 50 sets.make(actual_data), 51 """ 52 FAIL: kt_jvm_library did not produce the expected data dependencies. 53 EXPECTED: %s 54 ACTUAL: %s 55 """ % (expected_data, actual_data), 56 ) 57 58 expected_exports = [] 59 for target in ctx.attr.expected_exports: 60 asserts.equals( 61 env, 62 1, 63 len(target[JavaInfo].full_compile_jars.to_list()), 64 "Not a single compile-time Jar: %s" % target.label, 65 ) 66 expected_exports.extend(target[JavaInfo].full_compile_jars.to_list()) 67 actual_exports = actual[JavaInfo].full_compile_jars.to_list() 68 69 # TODO: fail if there are *un*expected exports, maybe by making sure 70 # that the actual exports are exactly the expected ones plus the Jar(s) 71 # produced by this JavaInfo. 72 for expected_export in expected_exports: 73 asserts.true( 74 env, 75 expected_export in actual_exports, 76 """ 77 kt_jvm_library did not export %s 78 actual: %s 79 """ % (expected_export, actual_exports), 80 ) 81 82 asserts.equals( 83 env, 84 ctx.attr.expected_exported_processor_classes, 85 actual[JavaInfo].plugins.processor_classes.to_list(), 86 ) 87 88 kt_2_java_compile = get_action(actions, "Kt2JavaCompile") 89 90 if kt_2_java_compile: 91 asserts.true( 92 env, 93 kt_2_java_compile.outputs.to_list()[0].basename.endswith(".jar"), 94 "Expected first output to be a JAR (this affects the param file name).", 95 ) 96 97 if ctx.attr.expected_friend_jar_names != _DEFAULT_LIST: 98 friend_paths_arg = get_arg(kt_2_java_compile, "-Xfriend-paths=") 99 friend_jar_names = [p.rsplit("/", 1)[1] for p in friend_paths_arg.split(",")] if friend_paths_arg else [] 100 asserts.set_equals(env, sets.make(ctx.attr.expected_friend_jar_names), sets.make(friend_jar_names)) 101 102 asserts.equals( 103 env, 104 ctx.attr.expect_neverlink, 105 len(actual[JavaInfo].transitive_runtime_jars.to_list()) == 0, 106 "Mismatch: Expected transitive_runtime_jars iff (neverlink == False)", 107 ) 108 109 return analysistest.end(env) 110 111_test = analysistest.make( 112 impl = _test_impl, 113 attrs = dict( 114 expected = attr.string_list_dict(), 115 expected_exports = attr.label_list(), 116 expected_exported_processor_classes = attr.string_list( 117 doc = "Annotation processors reported as to be run on depending targets", 118 ), 119 expected_processor_classes = attr.string_list( 120 doc = "Annotation processors reported as run on the given target", 121 ), 122 expected_friend_jar_names = attr.string_list( 123 doc = "Names of all -Xfriend-paths= JARs", 124 default = _DEFAULT_LIST, 125 ), 126 expect_processor_classpath = attr.bool(), 127 expect_neverlink = attr.bool(), 128 ), 129) 130 131jvm_library_test = _test 132 133def _coverage_test_impl(ctx): 134 env = analysistest.begin(ctx) 135 target_under_test = analysistest.target_under_test(env) 136 instrumented_files_info = target_under_test[InstrumentedFilesInfo] 137 instrumented_files = instrumented_files_info.instrumented_files.to_list() 138 asserts.equals( 139 env, 140 ctx.attr.expected_instrumented_file_basenames, 141 [file.basename for file in instrumented_files], 142 ) 143 return analysistest.end(env) 144 145_coverage_test = analysistest.make( 146 impl = _coverage_test_impl, 147 attrs = { 148 "expected_instrumented_file_basenames": attr.string_list(), 149 }, 150 config_settings = { 151 "//command_line_option:collect_code_coverage": "1", 152 "//command_line_option:instrument_test_targets": "1", 153 "//command_line_option:instrumentation_filter": "+tests/analysis[:/]", 154 }, 155) 156 157def _extract_data_runfiles(target): 158 return [f.basename for f in target[DefaultInfo].data_runfiles.files.to_list()] 159 160def _test_kt_jvm_library_with_proguard_specs(): 161 test_name = "kt_jvm_library_with_proguard_specs_test" 162 create_file( 163 name = test_name + "/Salutations.kt", 164 content = """ 165package test 166 167fun greeting(): String = "Hello World!" 168""", 169 ) 170 create_file( 171 name = test_name + "/salutations.pgcfg", 172 content = """ 173-keep class * { 174 *** greeting(); 175} 176""", 177 ) 178 kt_jvm_library( 179 name = test_name + "_tut", 180 srcs = [ 181 test_name + "/Salutations.kt", 182 ], 183 proguard_specs = [ 184 test_name + "/salutations.pgcfg", 185 ], 186 ) 187 _test( 188 name = test_name, 189 target_under_test = test_name + "_tut", 190 ) 191 return test_name 192 193def _test_kt_jvm_library_with_resources(): 194 test_name = "kt_jvm_library_with_resources_test" 195 create_file( 196 name = test_name + "/Salutations.kt", 197 content = """ 198package test 199 200fun greeting(): String = "Hello World!" 201""", 202 ) 203 create_file( 204 name = test_name + "/salutations.txt", 205 content = """ 206Hi! 207""", 208 ) 209 kt_jvm_library( 210 name = test_name + "_tut", 211 srcs = [ 212 test_name + "/Salutations.kt", 213 "testinputs/Foo.java", 214 ], 215 resources = [ 216 test_name + "/salutations.txt", 217 ], 218 ) 219 _test( 220 name = test_name, 221 target_under_test = test_name + "_tut", 222 ) 223 return test_name 224 225def _test_kt_jvm_library_with_plugin(): 226 test_name = "kt_jvm_library_with_plugin_test" 227 create_file( 228 name = test_name + "/Salutations.kt", 229 content = """ 230package test 231 232fun greeting(): String = "Hello World!" 233""", 234 ) 235 236 kt_jvm_library( 237 name = test_name + "_tut", 238 srcs = [ 239 test_name + "/Salutations.kt", 240 ], 241 # Need a working plugin so it can run for the test. 242 plugins = ["//bazel:auto_value_plugin"], 243 ) 244 245 _test( 246 name = test_name, 247 target_under_test = test_name + "_tut", 248 expected_processor_classes = ["com.google.auto.value.processor.AutoValueProcessor"], 249 expect_processor_classpath = True, 250 ) 251 return test_name 252 253def _test_kt_jvm_library_no_kt_srcs_with_plugin(): 254 test_name = "kt_jvm_library_no_kt_srcs_with_plugin_test" 255 native.java_plugin( 256 name = "%s_plugin" % test_name, 257 processor_class = test_name, 258 srcs = ["testinputs/Foo.java"], # induce processor_classpath 259 ) 260 kt_jvm_library( 261 name = test_name + "_tut", 262 srcs = ["testinputs/Bar.java"], 263 plugins = [":%s_plugin" % test_name], 264 tags = ONLY_FOR_ANALYSIS_TEST_TAGS, 265 ) 266 _test( 267 name = test_name, 268 target_under_test = test_name + "_tut", 269 expected_processor_classes = [test_name], 270 expect_processor_classpath = True, 271 ) 272 return test_name 273 274def _test_kt_jvm_library_with_non_processor_plugin(): 275 test_name = "kt_jvm_library_with_non_processor_plugin_test" 276 create_file( 277 name = test_name + "/Salutations.kt", 278 content = """ 279package test 280 281fun greeting(): String = "Hello World!" 282""", 283 ) 284 285 native.java_plugin( 286 # no processor_class 287 name = "%s_plugin" % test_name, 288 srcs = ["testinputs/Foo.java"], 289 ) 290 kt_jvm_library( 291 name = test_name + "_tut", 292 srcs = [ 293 test_name + "/Salutations.kt", 294 ], 295 plugins = [":%s_plugin" % test_name], 296 ) 297 298 _test( 299 name = test_name, 300 target_under_test = test_name + "_tut", 301 expected_processor_classes = [], # no processor class so no processing 302 expect_processor_classpath = True, # expect java_plugin's Jar 303 ) 304 return test_name 305 306def _test_kt_jvm_library_with_exported_plugin(): 307 test_name = "kt_jvm_library_with_exported_plugin_test" 308 create_file( 309 name = test_name + "/Salutations.kt", 310 content = """ 311package test 312 313fun greeting(): String = "Hello World!" 314""", 315 ) 316 native.java_plugin( 317 name = "%s_plugin" % test_name, 318 processor_class = test_name, 319 ) 320 kt_jvm_library( 321 name = test_name + "_tut", 322 srcs = [ 323 test_name + "/Salutations.kt", 324 ], 325 exported_plugins = [":%s_plugin" % test_name], 326 ) 327 328 _test( 329 name = test_name, 330 target_under_test = test_name + "_tut", 331 expected_exported_processor_classes = [test_name], 332 expected_processor_classes = [], # exported plugin should *not* run on _tut itself 333 ) 334 return test_name 335 336def _test_kt_jvm_library_dep_on_exported_plugin(): 337 test_name = "kt_jvm_library_dep_on_exported_plugin_test" 338 create_file( 339 name = test_name + "/Salutations.kt", 340 content = """ 341package test 342 343fun greeting(): String = "Hello World!" 344""", 345 ) 346 347 native.java_plugin( 348 name = "%s_plugin" % test_name, 349 processor_class = test_name, 350 srcs = ["testinputs/Foo.java"], # induce processor_classpath 351 ) 352 kt_jvm_library( 353 name = "%s_exports_plugin" % test_name, 354 srcs = [test_name + "/Salutations.kt"], 355 exported_plugins = [":%s_plugin" % test_name], 356 ) 357 kt_jvm_library( 358 name = test_name + "_tut", 359 srcs = [ 360 test_name + "/Salutations.kt", 361 ], 362 deps = [":%s_exports_plugin" % test_name], 363 tags = ONLY_FOR_ANALYSIS_TEST_TAGS, 364 ) 365 366 _test( 367 name = test_name, 368 target_under_test = test_name + "_tut", 369 expected_processor_classes = [test_name], 370 expect_processor_classpath = True, 371 ) 372 return test_name 373 374def _test_kt_jvm_library_java_dep_on_exported_plugin(): 375 test_name = "kt_jvm_library_java_dep_on_exported_plugin_test" 376 create_file( 377 name = test_name + "/Salutations.kt", 378 content = """ 379package test 380 381fun greeting(): String = "Hello World!" 382""", 383 ) 384 native.java_plugin( 385 name = "%s_plugin" % test_name, 386 processor_class = test_name, 387 srcs = ["testinputs/Foo.java"], # induce processor_classpath 388 ) 389 native.java_library( 390 name = "%s_exports_plugin" % test_name, 391 exported_plugins = [":%s_plugin" % test_name], 392 ) 393 kt_jvm_library( 394 name = test_name + "_tut", 395 srcs = [ 396 test_name + "/Salutations.kt", 397 ], 398 deps = [":%s_exports_plugin" % test_name], 399 tags = ONLY_FOR_ANALYSIS_TEST_TAGS, 400 ) 401 402 _test( 403 name = test_name, 404 target_under_test = test_name + "_tut", 405 expected_processor_classes = [test_name], 406 expect_processor_classpath = True, 407 ) 408 return test_name 409 410def _test_kt_jvm_library_with_exports(): 411 test_name = "kt_jvm_library_with_exports_test" 412 create_file( 413 name = test_name + "/Salutations.kt", 414 content = """ 415package test 416 417fun greeting(): String = "Hello World!" 418""", 419 ) 420 kt_jvm_library( 421 name = test_name + "_exp", 422 srcs = [test_name + "/Salutations.kt"], 423 ) 424 native.java_library( 425 name = test_name + "_javaexp", 426 srcs = ["testinputs/Foo.java"], # need file here so we get a Jar 427 ) 428 kt_jvm_library( 429 name = test_name + "_tut", 430 srcs = [ 431 test_name + "/Salutations.kt", 432 ], 433 exports = [ 434 ":%s_exp" % test_name, 435 ":%s_javaexp" % test_name, 436 ], 437 ) 438 _test( 439 name = test_name, 440 target_under_test = test_name + "_tut", 441 expected_exports = [ 442 ":%s_exp" % test_name, 443 ":%s_javaexp" % test_name, 444 ], 445 ) 446 return test_name 447 448def _test_kt_jvm_library_with_export_that_exports_plugin(): 449 test_name = "kt_jvm_library_with_export_that_exports_plugin_test" 450 create_file( 451 name = test_name + "/Salutations.kt", 452 content = """ 453package test 454 455fun greeting(): String = "Hello World!" 456""", 457 ) 458 native.java_plugin( 459 name = "%s_plugin" % test_name, 460 processor_class = test_name, 461 srcs = ["testinputs/Foo.java"], # induce processor_classpath 462 ) 463 kt_jvm_library( 464 name = "%s_exports_plugin" % test_name, 465 exported_plugins = [":%s_plugin" % test_name], 466 srcs = [test_name + "/Salutations.kt"], 467 ) 468 kt_jvm_library( 469 name = test_name + "_tut", 470 srcs = [ 471 test_name + "/Salutations.kt", 472 ], 473 exports = [":%s_exports_plugin" % test_name], 474 ) 475 _test( 476 name = test_name, 477 target_under_test = test_name + "_tut", 478 expected_exports = [":%s_exports_plugin" % test_name], 479 expected_exported_processor_classes = [test_name], 480 ) 481 return test_name 482 483def _test_kt_jvm_library_with_java_export_that_exports_plugin(): 484 test_name = "kt_jvm_library_with_java_export_that_exports_plugin_test" 485 create_file( 486 name = test_name + "/Salutations.kt", 487 content = """ 488package test 489 490fun greeting(): String = "Hello World!" 491""", 492 ) 493 native.java_plugin( 494 name = "%s_plugin" % test_name, 495 processor_class = test_name, 496 srcs = ["testinputs/Foo.java"], # induce processor_classpath 497 ) 498 native.java_library( 499 name = "%s_exports_plugin" % test_name, 500 exported_plugins = [":%s_plugin" % test_name], 501 ) 502 kt_jvm_library( 503 name = test_name + "_tut", 504 srcs = [ 505 test_name + "/Salutations.kt", 506 ], 507 exports = [":%s_exports_plugin" % test_name], 508 ) 509 _test( 510 name = test_name, 511 target_under_test = test_name + "_tut", 512 expected_exports = [], # _exports_plugin has no compile/runtime Jars 513 expected_exported_processor_classes = [test_name], 514 ) 515 return test_name 516 517def _test_forbidden_nano_dep(): 518 test_name = "kt_jvm_library_forbidden_nano_test" 519 520 kt_jvm_library( 521 name = test_name + "_tut", 522 srcs = [test_name + "/Ignored.kt"], 523 deps = [test_name + "_fake_nano_proto_lib"], 524 tags = [ 525 "manual", 526 "nobuilder", 527 ], 528 ) 529 native.java_library( 530 name = test_name + "_fake_nano_proto_lib", 531 srcs = [], 532 tags = ["nano_proto_library"], 533 ) 534 assert_failure_test( 535 name = test_name, 536 target_under_test = test_name + "_tut", 537 msg_contains = test_name + "_fake_nano_proto_lib : nano_proto_library", 538 ) 539 return test_name 540 541def _test_forbidden_nano_export(): 542 test_name = "kt_jvm_library_forbidden_nano_export_test" 543 544 kt_jvm_library( 545 name = test_name + "_tut", 546 srcs = [test_name + "/Ignored.kt"], 547 deps = [test_name + "_export"], 548 tags = [ 549 "manual", 550 "nobuilder", 551 ], 552 ) 553 native.java_library( 554 name = test_name + "_export", 555 exports = [test_name + "_fake_nano_proto_lib"], 556 ) 557 native.java_library( 558 name = test_name + "_fake_nano_proto_lib", 559 srcs = [], 560 tags = ["nano_proto_library"], 561 ) 562 assert_failure_test( 563 name = test_name, 564 target_under_test = test_name + "_tut", 565 msg_contains = test_name + "_fake_nano_proto_lib : nano_proto_library", 566 ) 567 return test_name 568 569def _test_kt_jvm_library_with_no_sources(): 570 test_name = "kt_jvm_library_with_no_sources_test" 571 572 kt_jvm_library( 573 name = test_name + "_tut", 574 tags = [ 575 "manual", 576 "nobuilder", 577 ], 578 ) 579 tut_label = str(Label("//tests/analysis:kt_jvm_library_with_no_sources_test_tut")) 580 assert_failure_test( 581 name = test_name, 582 target_under_test = test_name + "_tut", 583 msg_contains = "One of {srcs, common_srcs, exports, exported_plugins} of target " + tut_label + " must be non empty", 584 ) 585 return test_name 586 587def _test_kt_jvm_library_coverage(): 588 test_name = "kt_jvm_library_coverage" 589 kt_jvm_library( 590 name = test_name + "_tut", 591 srcs = ["testinputs/Srcs.kt"], 592 common_srcs = ["testinputs/CommonSrcs.kt"], 593 deps = [":{}_deps".format(test_name)], 594 runtime_deps = [":{}_runtime_deps".format(test_name)], 595 data = [":{}_data".format(test_name)], 596 resources = [":{}_resources".format(test_name)], 597 testonly = True, 598 ) 599 native.java_library( 600 name = test_name + "_deps", 601 srcs = ["testinputs/Deps.java"], 602 testonly = True, 603 ) 604 native.java_library( 605 name = test_name + "_runtime_deps", 606 srcs = ["testinputs/RuntimeDeps.java"], 607 testonly = True, 608 ) 609 native.java_binary( 610 name = test_name + "_data", 611 main_class = "Data", 612 srcs = ["testinputs/Data.java"], 613 testonly = True, 614 ) 615 native.java_binary( 616 name = test_name + "_resources", 617 main_class = "Resources", 618 srcs = ["testinputs/Resources.java"], 619 testonly = True, 620 ) 621 _coverage_test( 622 name = test_name, 623 target_under_test = test_name + "_tut", 624 expected_instrumented_file_basenames = [ 625 "Data.java", 626 "Deps.java", 627 "Resources.java", 628 "RuntimeDeps.java", 629 "Srcs.kt", 630 "CommonSrcs.kt", 631 ], 632 ) 633 return test_name 634 635def test_suite(name): 636 native.test_suite( 637 name = name, 638 tests = [ 639 _test_forbidden_nano_dep(), 640 _test_forbidden_nano_export(), 641 _test_kt_jvm_library_dep_on_exported_plugin(), 642 _test_kt_jvm_library_java_dep_on_exported_plugin(), 643 _test_kt_jvm_library_no_kt_srcs_with_plugin(), 644 _test_kt_jvm_library_with_export_that_exports_plugin(), 645 _test_kt_jvm_library_with_exported_plugin(), 646 _test_kt_jvm_library_with_exports(), 647 _test_kt_jvm_library_with_java_export_that_exports_plugin(), 648 _test_kt_jvm_library_with_no_sources(), 649 _test_kt_jvm_library_with_non_processor_plugin(), 650 _test_kt_jvm_library_with_plugin(), 651 _test_kt_jvm_library_with_proguard_specs(), 652 _test_kt_jvm_library_with_resources(), 653 _test_kt_jvm_library_coverage(), 654 ], 655 ) 656