blob: d51e6e11ca977b83c4d7415c673e5326db88c2c8 [file] [log] [blame] [edit]
# Copyright 2022 Google LLC. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the License);
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Kotlin kt_jvm_library rule tests."""
load("//kotlin:jvm_library.bzl", "kt_jvm_library")
load("//tests/analysis:util.bzl", "ONLY_FOR_ANALYSIS_TEST_TAGS", "create_file", "get_action", "get_arg")
load("@bazel_skylib//lib:sets.bzl", "sets")
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
load(":assert_failure_test.bzl", "assert_failure_test")
load("//:visibility.bzl", "RULES_KOTLIN")
_DEFAULT_LIST = ["__default__"]
def _test_impl(ctx):
env = analysistest.begin(ctx)
actions = analysistest.target_actions(env)
actual = ctx.attr.target_under_test
expected = ctx.attr.expected
asserts.true(
env,
JavaInfo in actual,
"kt_jvm_library did not produce JavaInfo provider.",
)
asserts.true(
env,
ProguardSpecProvider in actual,
"Expected a ProguardSpecProvider provider.",
)
if "data" in expected:
expected_data = expected["data"]
actual_data = _extract_data_runfiles(actual)
asserts.new_set_equals(
env,
sets.make(expected_data),
sets.make(actual_data),
"""
FAIL: kt_jvm_library did not produce the expected data dependencies.
EXPECTED: %s
ACTUAL: %s
""" % (expected_data, actual_data),
)
expected_exports = []
for target in ctx.attr.expected_exports:
asserts.equals(
env,
1,
len(target[JavaInfo].full_compile_jars.to_list()),
"Not a single compile-time Jar: %s" % target.label,
)
expected_exports.extend(target[JavaInfo].full_compile_jars.to_list())
actual_exports = actual[JavaInfo].full_compile_jars.to_list()
# TODO: fail if there are *un*expected exports, maybe by making sure
# that the actual exports are exactly the expected ones plus the Jar(s)
# produced by this JavaInfo.
for expected_export in expected_exports:
asserts.true(
env,
expected_export in actual_exports,
"""
kt_jvm_library did not export %s
actual: %s
""" % (expected_export, actual_exports),
)
asserts.equals(
env,
ctx.attr.expected_exported_processor_classes,
actual[JavaInfo].plugins.processor_classes.to_list(),
)
kt_2_java_compile = get_action(actions, "Kt2JavaCompile")
if kt_2_java_compile:
asserts.true(
env,
kt_2_java_compile.outputs.to_list()[0].basename.endswith(".jar"),
"Expected first output to be a JAR (this affects the param file name).",
)
if ctx.attr.expected_friend_jar_names != _DEFAULT_LIST:
friend_paths_arg = get_arg(kt_2_java_compile, "-Xfriend-paths=")
friend_jar_names = [p.rsplit("/", 1)[1] for p in friend_paths_arg.split(",")] if friend_paths_arg else []
asserts.set_equals(env, sets.make(ctx.attr.expected_friend_jar_names), sets.make(friend_jar_names))
asserts.equals(
env,
ctx.attr.expect_neverlink,
len(actual[JavaInfo].transitive_runtime_jars.to_list()) == 0,
"Mismatch: Expected transitive_runtime_jars iff (neverlink == False)",
)
return analysistest.end(env)
_test = analysistest.make(
impl = _test_impl,
attrs = dict(
expected = attr.string_list_dict(),
expected_exports = attr.label_list(),
expected_exported_processor_classes = attr.string_list(
doc = "Annotation processors reported as to be run on depending targets",
),
expected_processor_classes = attr.string_list(
doc = "Annotation processors reported as run on the given target",
),
expected_friend_jar_names = attr.string_list(
doc = "Names of all -Xfriend-paths= JARs",
default = _DEFAULT_LIST,
),
expect_processor_classpath = attr.bool(),
expect_neverlink = attr.bool(),
),
)
jvm_library_test = _test
def _coverage_test_impl(ctx):
env = analysistest.begin(ctx)
target_under_test = analysistest.target_under_test(env)
instrumented_files_info = target_under_test[InstrumentedFilesInfo]
instrumented_files = instrumented_files_info.instrumented_files.to_list()
asserts.equals(
env,
ctx.attr.expected_instrumented_file_basenames,
[file.basename for file in instrumented_files],
)
return analysistest.end(env)
_coverage_test = analysistest.make(
impl = _coverage_test_impl,
attrs = {
"expected_instrumented_file_basenames": attr.string_list(),
},
config_settings = {
"//command_line_option:collect_code_coverage": "1",
"//command_line_option:instrument_test_targets": "1",
"//command_line_option:instrumentation_filter": "+tests/analysis[:/]",
},
)
def _extract_data_runfiles(target):
return [f.basename for f in target[DefaultInfo].data_runfiles.files.to_list()]
def _test_kt_jvm_library_with_proguard_specs():
test_name = "kt_jvm_library_with_proguard_specs_test"
create_file(
name = test_name + "/Salutations.kt",
content = """
package test
fun greeting(): String = "Hello World!"
""",
)
create_file(
name = test_name + "/salutations.pgcfg",
content = """
-keep class * {
*** greeting();
}
""",
)
kt_jvm_library(
name = test_name + "_tut",
srcs = [
test_name + "/Salutations.kt",
],
proguard_specs = [
test_name + "/salutations.pgcfg",
],
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
)
return test_name
def _test_kt_jvm_library_with_resources():
test_name = "kt_jvm_library_with_resources_test"
create_file(
name = test_name + "/Salutations.kt",
content = """
package test
fun greeting(): String = "Hello World!"
""",
)
create_file(
name = test_name + "/salutations.txt",
content = """
Hi!
""",
)
kt_jvm_library(
name = test_name + "_tut",
srcs = [
test_name + "/Salutations.kt",
"testinputs/Foo.java",
],
resources = [
test_name + "/salutations.txt",
],
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
)
return test_name
def _test_kt_jvm_library_with_plugin():
test_name = "kt_jvm_library_with_plugin_test"
create_file(
name = test_name + "/Salutations.kt",
content = """
package test
fun greeting(): String = "Hello World!"
""",
)
kt_jvm_library(
name = test_name + "_tut",
srcs = [
test_name + "/Salutations.kt",
],
# Need a working plugin so it can run for the test.
plugins = ["//bazel:auto_value_plugin"],
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
expected_processor_classes = ["com.google.auto.value.processor.AutoValueProcessor"],
expect_processor_classpath = True,
)
return test_name
def _test_kt_jvm_library_no_kt_srcs_with_plugin():
test_name = "kt_jvm_library_no_kt_srcs_with_plugin_test"
native.java_plugin(
name = "%s_plugin" % test_name,
processor_class = test_name,
srcs = ["testinputs/Foo.java"], # induce processor_classpath
)
kt_jvm_library(
name = test_name + "_tut",
srcs = ["testinputs/Bar.java"],
plugins = [":%s_plugin" % test_name],
tags = ONLY_FOR_ANALYSIS_TEST_TAGS,
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
expected_processor_classes = [test_name],
expect_processor_classpath = True,
)
return test_name
def _test_kt_jvm_library_with_non_processor_plugin():
test_name = "kt_jvm_library_with_non_processor_plugin_test"
create_file(
name = test_name + "/Salutations.kt",
content = """
package test
fun greeting(): String = "Hello World!"
""",
)
native.java_plugin(
# no processor_class
name = "%s_plugin" % test_name,
srcs = ["testinputs/Foo.java"],
)
kt_jvm_library(
name = test_name + "_tut",
srcs = [
test_name + "/Salutations.kt",
],
plugins = [":%s_plugin" % test_name],
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
expected_processor_classes = [], # no processor class so no processing
expect_processor_classpath = True, # expect java_plugin's Jar
)
return test_name
def _test_kt_jvm_library_with_exported_plugin():
test_name = "kt_jvm_library_with_exported_plugin_test"
create_file(
name = test_name + "/Salutations.kt",
content = """
package test
fun greeting(): String = "Hello World!"
""",
)
native.java_plugin(
name = "%s_plugin" % test_name,
processor_class = test_name,
)
kt_jvm_library(
name = test_name + "_tut",
srcs = [
test_name + "/Salutations.kt",
],
exported_plugins = [":%s_plugin" % test_name],
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
expected_exported_processor_classes = [test_name],
expected_processor_classes = [], # exported plugin should *not* run on _tut itself
)
return test_name
def _test_kt_jvm_library_dep_on_exported_plugin():
test_name = "kt_jvm_library_dep_on_exported_plugin_test"
create_file(
name = test_name + "/Salutations.kt",
content = """
package test
fun greeting(): String = "Hello World!"
""",
)
native.java_plugin(
name = "%s_plugin" % test_name,
processor_class = test_name,
srcs = ["testinputs/Foo.java"], # induce processor_classpath
)
kt_jvm_library(
name = "%s_exports_plugin" % test_name,
srcs = [test_name + "/Salutations.kt"],
exported_plugins = [":%s_plugin" % test_name],
)
kt_jvm_library(
name = test_name + "_tut",
srcs = [
test_name + "/Salutations.kt",
],
deps = [":%s_exports_plugin" % test_name],
tags = ONLY_FOR_ANALYSIS_TEST_TAGS,
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
expected_processor_classes = [test_name],
expect_processor_classpath = True,
)
return test_name
def _test_kt_jvm_library_java_dep_on_exported_plugin():
test_name = "kt_jvm_library_java_dep_on_exported_plugin_test"
create_file(
name = test_name + "/Salutations.kt",
content = """
package test
fun greeting(): String = "Hello World!"
""",
)
native.java_plugin(
name = "%s_plugin" % test_name,
processor_class = test_name,
srcs = ["testinputs/Foo.java"], # induce processor_classpath
)
native.java_library(
name = "%s_exports_plugin" % test_name,
exported_plugins = [":%s_plugin" % test_name],
)
kt_jvm_library(
name = test_name + "_tut",
srcs = [
test_name + "/Salutations.kt",
],
deps = [":%s_exports_plugin" % test_name],
tags = ONLY_FOR_ANALYSIS_TEST_TAGS,
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
expected_processor_classes = [test_name],
expect_processor_classpath = True,
)
return test_name
def _test_kt_jvm_library_with_exports():
test_name = "kt_jvm_library_with_exports_test"
create_file(
name = test_name + "/Salutations.kt",
content = """
package test
fun greeting(): String = "Hello World!"
""",
)
kt_jvm_library(
name = test_name + "_exp",
srcs = [test_name + "/Salutations.kt"],
)
native.java_library(
name = test_name + "_javaexp",
srcs = ["testinputs/Foo.java"], # need file here so we get a Jar
)
kt_jvm_library(
name = test_name + "_tut",
srcs = [
test_name + "/Salutations.kt",
],
exports = [
":%s_exp" % test_name,
":%s_javaexp" % test_name,
],
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
expected_exports = [
":%s_exp" % test_name,
":%s_javaexp" % test_name,
],
)
return test_name
def _test_kt_jvm_library_with_export_that_exports_plugin():
test_name = "kt_jvm_library_with_export_that_exports_plugin_test"
create_file(
name = test_name + "/Salutations.kt",
content = """
package test
fun greeting(): String = "Hello World!"
""",
)
native.java_plugin(
name = "%s_plugin" % test_name,
processor_class = test_name,
srcs = ["testinputs/Foo.java"], # induce processor_classpath
)
kt_jvm_library(
name = "%s_exports_plugin" % test_name,
exported_plugins = [":%s_plugin" % test_name],
srcs = [test_name + "/Salutations.kt"],
)
kt_jvm_library(
name = test_name + "_tut",
srcs = [
test_name + "/Salutations.kt",
],
exports = [":%s_exports_plugin" % test_name],
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
expected_exports = [":%s_exports_plugin" % test_name],
expected_exported_processor_classes = [test_name],
)
return test_name
def _test_kt_jvm_library_with_java_export_that_exports_plugin():
test_name = "kt_jvm_library_with_java_export_that_exports_plugin_test"
create_file(
name = test_name + "/Salutations.kt",
content = """
package test
fun greeting(): String = "Hello World!"
""",
)
native.java_plugin(
name = "%s_plugin" % test_name,
processor_class = test_name,
srcs = ["testinputs/Foo.java"], # induce processor_classpath
)
native.java_library(
name = "%s_exports_plugin" % test_name,
exported_plugins = [":%s_plugin" % test_name],
)
kt_jvm_library(
name = test_name + "_tut",
srcs = [
test_name + "/Salutations.kt",
],
exports = [":%s_exports_plugin" % test_name],
)
_test(
name = test_name,
target_under_test = test_name + "_tut",
expected_exports = [], # _exports_plugin has no compile/runtime Jars
expected_exported_processor_classes = [test_name],
)
return test_name
def _test_forbidden_nano_dep():
test_name = "kt_jvm_library_forbidden_nano_test"
kt_jvm_library(
name = test_name + "_tut",
srcs = [test_name + "/Ignored.kt"],
deps = [test_name + "_fake_nano_proto_lib"],
tags = [
"manual",
"nobuilder",
],
)
native.java_library(
name = test_name + "_fake_nano_proto_lib",
srcs = [],
tags = ["nano_proto_library"],
)
assert_failure_test(
name = test_name,
target_under_test = test_name + "_tut",
msg_contains = test_name + "_fake_nano_proto_lib : nano_proto_library",
)
return test_name
def _test_forbidden_nano_export():
test_name = "kt_jvm_library_forbidden_nano_export_test"
kt_jvm_library(
name = test_name + "_tut",
srcs = [test_name + "/Ignored.kt"],
deps = [test_name + "_export"],
tags = [
"manual",
"nobuilder",
],
)
native.java_library(
name = test_name + "_export",
exports = [test_name + "_fake_nano_proto_lib"],
)
native.java_library(
name = test_name + "_fake_nano_proto_lib",
srcs = [],
tags = ["nano_proto_library"],
)
assert_failure_test(
name = test_name,
target_under_test = test_name + "_tut",
msg_contains = test_name + "_fake_nano_proto_lib : nano_proto_library",
)
return test_name
def _test_kt_jvm_library_with_no_sources():
test_name = "kt_jvm_library_with_no_sources_test"
kt_jvm_library(
name = test_name + "_tut",
tags = [
"manual",
"nobuilder",
],
)
tut_label = str(Label("//tests/analysis:kt_jvm_library_with_no_sources_test_tut"))
assert_failure_test(
name = test_name,
target_under_test = test_name + "_tut",
msg_contains = "One of {srcs, common_srcs, exports, exported_plugins} of target " + tut_label + " must be non empty",
)
return test_name
def _test_kt_jvm_library_coverage():
test_name = "kt_jvm_library_coverage"
kt_jvm_library(
name = test_name + "_tut",
srcs = ["testinputs/Srcs.kt"],
common_srcs = ["testinputs/CommonSrcs.kt"],
deps = [":{}_deps".format(test_name)],
runtime_deps = [":{}_runtime_deps".format(test_name)],
data = [":{}_data".format(test_name)],
resources = [":{}_resources".format(test_name)],
testonly = True,
)
native.java_library(
name = test_name + "_deps",
srcs = ["testinputs/Deps.java"],
testonly = True,
)
native.java_library(
name = test_name + "_runtime_deps",
srcs = ["testinputs/RuntimeDeps.java"],
testonly = True,
)
native.java_binary(
name = test_name + "_data",
main_class = "Data",
srcs = ["testinputs/Data.java"],
testonly = True,
)
native.java_binary(
name = test_name + "_resources",
main_class = "Resources",
srcs = ["testinputs/Resources.java"],
testonly = True,
)
_coverage_test(
name = test_name,
target_under_test = test_name + "_tut",
expected_instrumented_file_basenames = [
"Data.java",
"Deps.java",
"Resources.java",
"RuntimeDeps.java",
"Srcs.kt",
"CommonSrcs.kt",
],
)
return test_name
def test_suite(name):
native.test_suite(
name = name,
tests = [
_test_forbidden_nano_dep(),
_test_forbidden_nano_export(),
_test_kt_jvm_library_dep_on_exported_plugin(),
_test_kt_jvm_library_java_dep_on_exported_plugin(),
_test_kt_jvm_library_no_kt_srcs_with_plugin(),
_test_kt_jvm_library_with_export_that_exports_plugin(),
_test_kt_jvm_library_with_exported_plugin(),
_test_kt_jvm_library_with_exports(),
_test_kt_jvm_library_with_java_export_that_exports_plugin(),
_test_kt_jvm_library_with_no_sources(),
_test_kt_jvm_library_with_non_processor_plugin(),
_test_kt_jvm_library_with_plugin(),
_test_kt_jvm_library_with_proguard_specs(),
_test_kt_jvm_library_with_resources(),
_test_kt_jvm_library_coverage(),
],
)