• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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