Export more testing infrastructure. PiperOrigin-RevId: 533158991 Change-Id: Ic1ebfa3dd2048ecc1b879867af6843d8dfe6326e
diff --git a/defs.bzl b/defs.bzl index 8abd100..0bfb9a8 100644 --- a/defs.bzl +++ b/defs.bzl
@@ -14,6 +14,7 @@ """Workspace setup macro for rules_android.""" +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") @@ -22,6 +23,8 @@ def rules_android_workspace(): """ Sets up workspace dependencies for rules_android.""" + bazel_skylib_workspace() + protobuf_deps() maven_install(
diff --git a/kokoro/presubmit/kokoro_presubmit.sh b/kokoro/presubmit/kokoro_presubmit.sh index c7ec3cb..b90b12c 100644 --- a/kokoro/presubmit/kokoro_presubmit.sh +++ b/kokoro/presubmit/kokoro_presubmit.sh
@@ -61,6 +61,7 @@ "--verbose_failures" "--experimental_google_legacy_api" "--experimental_enable_android_migration_apis" + "--build_tests_only" ) # Go to rules_android workspace and run relevant tests. @@ -71,7 +72,8 @@ "$bazel" aquery 'deps(...)' 2>&1 > /dev/null "$bazel" test "${COMMON_ARGS[@]}" //src/common/golang/... \ - //src/tools/ak/... + //src/tools/ak/... \ + //test/... # Go to basic app workspace in the source tree cd "${KOKORO_ARTIFACTS_DIR}/git/rules_android/examples/basicapp"
diff --git a/test/utils/BUILD b/test/utils/BUILD new file mode 100644 index 0000000..81cb750 --- /dev/null +++ b/test/utils/BUILD
@@ -0,0 +1,27 @@ +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + +package( + default_applicable_licenses = ["//:license"], + default_visibility = ["//visibility:private"], +) + +licenses(["notice"]) + +exports_files( + ["lib.bzl"], + visibility = [ + "//test:__subpackages__", + ], +) + +filegroup( + name = "testing", + srcs = glob(["*"]), + visibility = ["//test:__pkg__"], +) + +bzl_library( + name = "bzl", + srcs = glob(["*.bzl"]), + visibility = ["//tools/build_defs/android:__subpackages__"], +)
diff --git a/test/utils/asserts.bzl b/test/utils/asserts.bzl new file mode 100644 index 0000000..a1e5a1d --- /dev/null +++ b/test/utils/asserts.bzl
@@ -0,0 +1,610 @@ +# Copyright 2020 The Bazel Authors. 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. + +"""Bazel testing library asserts.""" + +load( + "//rules:providers.bzl", + "ResourcesNodeInfo", + "StarlarkAndroidResourcesInfo", +) + +_ATTRS = dict( + expected_default_info = attr.string_list_dict(), + expected_java_info = attr.string_list_dict(), + expected_proguard_spec_provider = attr.string_list_dict(), + expected_starlark_android_resources_info = attr.label(), + expected_output_group_info = attr.string_list_dict(), + expected_native_libs_info = attr.label(), +) + +def _expected_resources_node_info_impl(ctx): + return [ + ResourcesNodeInfo( + label = ctx.attr.label.label, + assets = ctx.files.assets, + assets_dir = ctx.attr.assets_dir, + assets_symbols = ctx.attr.assets_symbols if ctx.attr.assets_symbols else None, + compiled_assets = ctx.attr.compiled_assets if ctx.attr.compiled_assets else None, + compiled_resources = ctx.attr.compiled_resources if ctx.attr.compiled_resources else None, + r_txt = ctx.attr.r_txt if ctx.attr.r_txt else None, + manifest = ctx.attr.manifest if ctx.attr.manifest else None, + exports_manifest = ctx.attr.exports_manifest, + ), + ] + +_expected_resources_node_info = rule( + implementation = _expected_resources_node_info_impl, + attrs = dict( + label = attr.label(), + assets = attr.label_list(allow_files = True), + assets_dir = attr.string(), + assets_symbols = attr.string(), + compiled_assets = attr.string(), + compiled_resources = attr.string(), + r_txt = attr.string(), + manifest = attr.string(), + exports_manifest = attr.bool(default = False), + ), +) + +def ExpectedResourcesNodeInfo( + label, + assets = [], + assets_dir = "", + assets_symbols = None, + compiled_assets = None, + compiled_resources = None, + r_txt = None, + manifest = None, + exports_manifest = False, + name = "unused"): # appease linter + name = label + str(assets) + assets_dir + str(assets_symbols) + str(compiled_resources) + str(exports_manifest) + name = ":" + "".join([c for c in name.elems() if c != ":"]) + + _expected_resources_node_info( + name = name[1:], + label = label, + assets = assets, + assets_dir = assets_dir, + assets_symbols = assets_symbols, + compiled_assets = compiled_assets, + compiled_resources = compiled_resources, + r_txt = r_txt, + manifest = manifest, + exports_manifest = exports_manifest, + ) + return name + +def _expected_starlark_android_resources_info_impl(ctx): + return [ + StarlarkAndroidResourcesInfo( + direct_resources_nodes = [node[ResourcesNodeInfo] for node in ctx.attr.direct_resources_nodes], + transitive_resources_nodes = [node[ResourcesNodeInfo] for node in ctx.attr.transitive_resources_nodes], + transitive_assets = ctx.attr.transitive_assets, + transitive_assets_symbols = ctx.attr.transitive_assets_symbols, + transitive_compiled_resources = ctx.attr.transitive_compiled_resources, + packages_to_r_txts = ctx.attr.packages_to_r_txts, + ), + ] + +_expected_starlark_android_resources_info = rule( + implementation = _expected_starlark_android_resources_info_impl, + attrs = dict( + direct_resources_nodes = attr.label_list( + providers = [ResourcesNodeInfo], + ), + transitive_resources_nodes = attr.label_list( + providers = [ResourcesNodeInfo], + ), + transitive_assets = attr.string_list(), + transitive_assets_symbols = attr.string_list(), + transitive_compiled_resources = attr.string_list(), + packages_to_r_txts = attr.string_list_dict(), + ), +) + +def ExpectedStarlarkAndroidResourcesInfo( + direct_resources_nodes = None, + transitive_resources_nodes = [], + transitive_assets = [], + transitive_assets_symbols = [], + transitive_compiled_resources = [], + packages_to_r_txts = {}, + name = "unused"): # appease linter + name = (str(direct_resources_nodes) + str(transitive_resources_nodes) + str(transitive_assets) + + str(transitive_assets_symbols) + str(transitive_compiled_resources)) + name = ":" + "".join([c for c in name.elems() if c not in [":", "\\"]]) + _expected_starlark_android_resources_info( + name = name[1:], + direct_resources_nodes = direct_resources_nodes, + transitive_resources_nodes = transitive_resources_nodes, + transitive_assets = transitive_assets, + transitive_assets_symbols = transitive_assets_symbols, + transitive_compiled_resources = transitive_compiled_resources, + packages_to_r_txts = packages_to_r_txts, + ) + return name + +def _build_expected_resources_node_info(string): + parts = string.split(":") + if len(parts) != 5: + fail("Error: malformed resources_node_info string: %s" % string) + return dict( + label = parts[0], + assets = parts[1].split(",") if parts[1] else [], + assets_dir = parts[2], + assets_symbols = parts[3], + compiled_resources = parts[4], + ) + +def _expected_android_binary_native_libs_info_impl(ctx): + return _ExpectedAndroidBinaryNativeInfo( + transitive_native_libs = ctx.attr.transitive_native_libs, + native_libs_name = ctx.attr.native_libs_name, + native_libs = ctx.attr.native_libs, + ) + +_expected_android_binary_native_libs_info = rule( + implementation = _expected_android_binary_native_libs_info_impl, + attrs = { + "transitive_native_libs": attr.string_list(), + "native_libs_name": attr.string(), + "native_libs": attr.string_list_dict(), + }, +) + +def ExpectedAndroidBinaryNativeLibsInfo(**kwargs): + name = "".join([str(kwargs[param]) for param in kwargs]) + name = ":" + "".join([c for c in name.elems() if c not in [" ", "[", "]", ":", "\\", "{", "\""]]) + _expected_android_binary_native_libs_info(name = name[1:], **kwargs) + return name + +_ExpectedAndroidBinaryNativeInfo = provider( + "Test provider to compare native deps info", + fields = ["native_libs", "native_libs_name", "transitive_native_libs"], +) + +def _assert_native_libs_info(expected, actual): + expected = expected[_ExpectedAndroidBinaryNativeInfo] + if expected.native_libs_name: + _assert_file( + expected.native_libs_name, + actual.native_libs_name, + "AndroidBinaryNativeInfo.native_libs_name", + ) + for config in expected.native_libs: + if config not in actual.native_libs: + fail("Error for AndroidBinaryNativeInfo.native_libs: expected key %s was not found" % config) + _assert_files( + expected.native_libs[config], + actual.native_libs[config].to_list(), + "AndroidBinaryNativeInfo.native_libs." + config, + ) + _assert_files( + expected.transitive_native_libs, + actual.transitive_native_libs.to_list(), + "AndroidBinaryNativeInfo.transitive_native_libs", + ) + +def _assert_files(expected_file_names, actual_files, error_msg_field_name): + """Asserts that expected file names and actual list of files is equal. + + Args: + expected_file_names: The expected names of file basenames (no path), + actual_files: The actual list of files produced. + error_msg_field_name: The field the actual list of files is from. + """ + actual_file_names = [f.basename for f in actual_files] + if sorted(actual_file_names) == sorted(expected_file_names): + return + fail("""Error for %s, expected and actual file names are not the same: +expected file names: %s +actual files: %s +""" % (error_msg_field_name, expected_file_names, actual_files)) + +def _assert_file_objects(expected_files, actual_files, error_msg_field_name): + if sorted([f.basename for f in expected_files]) == sorted([f.basename for f in actual_files]): + return + fail("""Error for %s, expected and actual file names are not the same: +expected file names: %s +actual files: %s +""" % (error_msg_field_name, expected_files, actual_files)) + +def _assert_file_depset(expected_file_paths, actual_depset, error_msg_field_name, ignore_label_prefix = ""): + """Asserts that expected file short_paths and actual depset of files is equal. + + Args: + expected_file_paths: The expected file short_paths in depset order. + actual_depset: The actual depset produced. + error_msg_field_name: The field the actual depset is from. + ignore_label_prefix: Path prefix to ignore on actual file short_paths. + """ + actual_paths = [] # = [f.short_path for f in actual_depset.to_list()] + for f in actual_depset.to_list(): + path = f.short_path + if path.startswith(ignore_label_prefix): + path = path[len(ignore_label_prefix):] + actual_paths.append(path) + + if len(expected_file_paths) != len(actual_paths): + fail("""Error for %s, expected %d items, got %d items +expected: %s +actual: %s""" % ( + error_msg_field_name, + len(expected_file_paths), + len(actual_paths), + expected_file_paths, + actual_paths, + )) + for i in range(len(expected_file_paths)): + if expected_file_paths[i] != actual_paths[i]: + fail("""Error for %s, actual file depset ordering does not match expected ordering: +expected ordering: %s +actual ordering: %s +""" % (error_msg_field_name, expected_file_paths, actual_paths)) + +def _assert_empty(contents, error_msg_field_name): + """Asserts that the given is empty.""" + if len(contents) == 0: + return + fail("Error %s is not empty: %s" % (error_msg_field_name, contents)) + +def _assert_none(content, error_msg_field_name): + """Asserts that the given is None.""" + if content == None: + return + fail("Error %s is not None: %s" % (error_msg_field_name, content)) + +def _assert_java_info(expected, actual): + """Asserts that expected matches actual JavaInfo. + + Args: + expected: A dict containing fields of a JavaInfo that are compared against + the actual given JavaInfo. + actual: A JavaInfo. + """ + for key in expected.keys(): + if not hasattr(actual, key): + fail("Actual JavaInfo does not have attribute %s:\n%s" % (key, actual)) + actual_attr = getattr(actual, key) + expected_attr = expected[key] + + # files based asserts. + if key in [ + "compile_jars", + "runtime_output_jars", + "source_jars", + "transitive_compile_time_jars", + "transitive_runtime_jars", + "transitive_source_jars", + ]: + files = \ + actual_attr if type(actual_attr) == "list" else actual_attr.to_list() + _assert_files(expected_attr, files, "JavaInfo.%s" % key) + else: + fail("Error validation of JavaInfo.%s not implemented." % key) + +def _assert_default_info( + expected, + actual): + """Asserts that the DefaultInfo contains the expected values.""" + if not expected: + return + + # DefaultInfo.data_runfiles Assertions + _assert_empty( + actual.data_runfiles.empty_filenames.to_list(), + "DefaultInfo.data_runfiles.empty_filenames", + ) + _assert_files( + expected["runfiles"], + actual.data_runfiles.files.to_list(), + "DefaultInfo.data_runfiles.files", + ) + _assert_empty( + actual.data_runfiles.symlinks.to_list(), + "DefaultInfo.data_runfiles.symlinks", + ) + + # DefaultInfo.default_runfile Assertions + _assert_empty( + actual.default_runfiles.empty_filenames.to_list(), + "DefaultInfo.default_runfiles.empty_filenames", + ) + _assert_files( + expected["runfiles"], + actual.default_runfiles.files.to_list(), + "DefaultInfo.default_runfiles.files", + ) + _assert_empty( + actual.default_runfiles.symlinks.to_list(), + "DefaultInfo.default_runfiles.symlinks", + ) + + # DefaultInfo.files Assertion + _assert_files( + expected["files"], + actual.files.to_list(), + "DefaultInfo.files", + ) + + # DefaultInfo.files_to_run Assertions + _assert_none( + actual.files_to_run.executable, + "DefaultInfo.files_to_run.executable", + ) + _assert_none( + actual.files_to_run.runfiles_manifest, + "DefaultInfo.files_to_run.runfiles_manifest", + ) + +def _assert_proguard_spec_provider(expected, actual): + """Asserts that expected matches actual ProguardSpecProvider. + + Args: + expected: A dict containing fields of a ProguardSpecProvider that are + compared against the actual given ProguardSpecProvider. + actual: A ProguardSpecProvider. + """ + for key in expected.keys(): + if not hasattr(actual, key): + fail("Actual ProguardSpecProvider does not have attribute %s:\n%s" % (key, actual)) + actual_attr = getattr(actual, key) + expected_attr = expected[key] + if key in ["specs"]: + _assert_files( + expected_attr, + actual_attr.to_list(), + "ProguardSpecProvider.%s" % key, + ) + else: + fail("Error validation of ProguardSpecProvider.%s not implemented." % key) + +def _assert_string(expected, actual, error_msg): + if type(actual) != "string": + fail("Error for %s, actual value not of type string, got %s" % (error_msg, type(actual))) + if actual != expected: + fail("""Error for %s, expected and actual values are not the same: +expected value: %s +actual value: %s +""" % (error_msg, expected, actual)) + +def _assert_file(expected, actual, error_msg_field_name): + if actual == None and expected == None: + return + + if actual == None and expected != None: + fail("Error at %s, expected %s but got None" % (error_msg_field_name, expected)) + + if type(actual) != "File": + fail("Error at %s, expected a File but got %s" % (error_msg_field_name, type(actual))) + + if actual != None and expected == None: + fail("Error at %s, expected None but got %s" % (error_msg_field_name, actual.short_path)) + + ignore_label_prefix = actual.owner.package + "/" + actual_path = actual.short_path + if actual_path.startswith(ignore_label_prefix): + actual_path = actual_path[len(ignore_label_prefix):] + _assert_string(expected, actual_path, error_msg_field_name) + +def _assert_resources_node_info(expected, actual): + if type(actual.label) != "Label": + fail("Error for ResourcesNodeInfo.label, expected type Label, actual type is %s" % type(actual.label)) + _assert_string(expected.label.name, actual.label.name, "ResourcesNodeInfo.label.name") + + if type(actual.assets) != "depset": + fail("Error for ResourcesNodeInfo.assets, expected type depset, actual type is %s" % type(actual.assets)) + + # TODO(djwhang): Align _assert_file_objects and _assert_file_depset to work + # in a similar manner. For now, we will just call to_list() as this field + # was list prior to this change. + _assert_file_objects(expected.assets, actual.assets.to_list(), "ResourcesNodeInfo.assets") + + _assert_string(expected.assets_dir, actual.assets_dir, "ResourcesNodeInfo.assets_dir") + + _assert_file( + expected.assets_symbols, + actual.assets_symbols, + "ResourcesNodeInfo.assets_symbols", + ) + + _assert_file( + expected.compiled_assets, + actual.compiled_assets, + "ResourcesNodeInfo.compiled_assets", + ) + + _assert_file( + expected.compiled_resources, + actual.compiled_resources, + "ResourcesNodeInfo.compiled_resources", + ) + + _assert_file( + expected.r_txt, + actual.r_txt, + "ResourcesNodeInfo.r_txt", + ) + + _assert_file( + expected.manifest, + actual.manifest, + "ResourcesNodeInfo.manifest", + ) + + if type(actual.exports_manifest) != "bool": + fail("Error for ResourcesNodeInfo.exports_manifest, expected type bool, actual type is %s" % type(actual.exports_manifest)) + if expected.exports_manifest != actual.exports_manifest: + fail("""Error for ResourcesNodeInfo.exports_manifest, expected and actual values are not the same: +expected value: %s +actual value: %s +""" % (expected.exports_manifest, actual.exports_manifest)) + +def _assert_resources_node_info_depset(expected_resources_node_infos, actual_depset, error_msg): + actual_resources_node_infos = actual_depset.to_list() + if len(expected_resources_node_infos) != len(actual_resources_node_infos): + fail( + "Error for StarlarkAndroidResourcesInfo.%s, expected size of list to be %d, got %d:\nExpected: %s\nActual: %s" % + ( + error_msg, + len(expected_resources_node_infos), + len(actual_resources_node_infos), + [node.label for node in expected_resources_node_infos], + [node.label for node in actual_resources_node_infos], + ), + ) + for i in range(len(actual_resources_node_infos)): + _assert_resources_node_info(expected_resources_node_infos[i], actual_resources_node_infos[i]) + +def _assert_starlark_android_resources_info(expected, actual, label_under_test): + _assert_resources_node_info_depset( + expected.direct_resources_nodes, + actual.direct_resources_nodes, + "direct_resources_nodes", + ) + + _assert_resources_node_info_depset( + expected.transitive_resources_nodes, + actual.transitive_resources_nodes, + "transitive_resources_nodes", + ) + + # Use the package from the target under test to shrink actual paths being compared down to the + # name of the target. + ignore_label_prefix = label_under_test.package + "/" + + _assert_file_depset( + expected.transitive_assets, + actual.transitive_assets, + "StarlarkAndroidResourcesInfo.transitive_assets", + ignore_label_prefix, + ) + _assert_file_depset( + expected.transitive_assets_symbols, + actual.transitive_assets_symbols, + "StarlarkAndroidResourcesInfo.transitive_assets_symbols", + ignore_label_prefix, + ) + _assert_file_depset( + expected.transitive_compiled_resources, + actual.transitive_compiled_resources, + "StarlarkAndroidResourcesInfo.transitive_compiled_resources", + ignore_label_prefix, + ) + for pkg, value in expected.packages_to_r_txts.items(): + if pkg in actual.packages_to_r_txts: + _assert_file_depset( + value, + actual.packages_to_r_txts[pkg], + "StarlarkAndroidResourcesInfo.packages_to_r_txts[%s]" % pkg, + ignore_label_prefix, + ) + else: + fail("Error for StarlarkAndroidResourceInfo.packages_to_r_txts, expected key %s was not found" % pkg) + +_R_CLASS_ATTRS = dict( + _r_class_check = attr.label( + default = "//test/utils/java/com/google:RClassChecker_deploy.jar", + executable = True, + allow_files = True, + cfg = "exec", + ), + expected_r_class_fields = attr.string_list(), +) + +def _assert_output_group_info(expected, actual): + for key in expected: + actual_attr = getattr(actual, key, None) + if actual_attr == None: # both empty depset and list will fail. + fail("%s is not defined in OutputGroupInfo: %s" % (key, actual)) + _assert_files( + expected[key], + actual_attr.to_list(), + "OutputGroupInfo." + key, + ) + +def _is_suffix_sublist(full, suffixes): + """Returns whether suffixes is a sublist of suffixes of full.""" + for (fi, _) in enumerate(full): + sublist_match = True + for (si, sv) in enumerate(suffixes): + if (fi + si >= len(full)) or not full[fi + si].endswith(sv): + sublist_match = False + break + if sublist_match: + return True + return False + +def _check_actions(inspect, actions): + for mnemonic, expected_argvs in inspect.items(): + # Action mnemonic is not unique, even in the context of a target, hence + # it is necessary to find all actions and compare argv. If there are no + # matches among the actions that match the mnemonic, fail and present + # all the possible actions that could have matched. + mnemonic_matching_actions = [] + mnemonic_match = False + for _, value in actions.by_file.items(): + if mnemonic != value.mnemonic: + continue + mnemonic_match = True + + if _is_suffix_sublist(value.argv, expected_argvs): + # When there is a match, clear the actions stored for displaying + # an error messaage. + mnemonic_matching_actions = [] + break + else: + mnemonic_matching_actions.append(value) + + if not mnemonic_match: + fail("%s action not found." % mnemonic) + if mnemonic_matching_actions: + # If there are mnemonic_matching_actions, then the argvs did not + # align. Fail but show the other actions that were created. + error_message = ( + "%s with the following argv not found: %s\nSimilar actions:\n" % + (mnemonic, expected_argvs) + ) + for i, action in enumerate(mnemonic_matching_actions): + error_message += ( + "%d. Progress Message: %s\n Argv: %s\n\n" % + (i + 1, action, action.argv) + ) + fail(error_message) + +_ACTIONS_ATTRS = dict( + inspect_actions = attr.string_list_dict(), +) + +asserts = struct( + provider = struct( + attrs = _ATTRS, + default_info = _assert_default_info, + java_info = _assert_java_info, + proguard_spec_provider = _assert_proguard_spec_provider, + starlark_android_resources_info = _assert_starlark_android_resources_info, + output_group_info = _assert_output_group_info, + native_libs_info = _assert_native_libs_info, + ), + files = _assert_files, + r_class = struct( + attrs = _R_CLASS_ATTRS, + ), + actions = struct( + attrs = _ACTIONS_ATTRS, + check_actions = _check_actions, + ), +)
diff --git a/test/utils/file.bzl b/test/utils/file.bzl new file mode 100644 index 0000000..e019542 --- /dev/null +++ b/test/utils/file.bzl
@@ -0,0 +1,44 @@ +# Copyright 2018 The Bazel Authors. 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. + +"""Bazel lib that provides on-the-fly data generation helpers for testing.""" + +def _create( + name = None, + contents = "", + executable = False): + target_name = "gen_" + name.replace(".", "_") + native.genrule( + name = target_name, + cmd = """ +cat > $@ <<MAKE_FILE_EOM +%s +MAKE_FILE_EOM +""" % contents, + outs = [name], + executable = executable, + ) + return name + +def _create_mock_file(path, is_directory = False): + return struct( + path = path, + dirname = path.rpartition("/")[0], + is_directory = is_directory, + ) + +file = struct( + create = _create, + create_mock_file = _create_mock_file, +)
diff --git a/test/utils/lib.bzl b/test/utils/lib.bzl new file mode 100644 index 0000000..fef1281 --- /dev/null +++ b/test/utils/lib.bzl
@@ -0,0 +1,51 @@ +# Copyright 2018 The Bazel Authors. 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. + +"""Bazel Android testing libs.""" + +load( + ":file.bzl", + _file = "file", +) +load( + ":unittest.bzl", + _analysistest = "analysistest", + _unittest = "unittest", +) +load( + "@bazel_skylib//lib:unittest.bzl", + _asserts = "asserts", +) + +file = _file + +unittest = _unittest + +analysistest = _analysistest + +asserts = _asserts + +def _failure_test_impl(ctx): + env = analysistest.begin(ctx) + if ctx.attr.expected_error_msg != "": + asserts.expect_failure(env, ctx.attr.expected_error_msg) + return analysistest.end(env) + +failure_test = analysistest.make( + _failure_test_impl, + expect_failure = True, + attrs = dict( + expected_error_msg = attr.string(), + ), +)
diff --git a/test/utils/unittest.bzl b/test/utils/unittest.bzl new file mode 100644 index 0000000..8526902 --- /dev/null +++ b/test/utils/unittest.bzl
@@ -0,0 +1,108 @@ +# Copyright 2018 The Bazel Authors. 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. + +"""Bazel lib that provides test helpers for testing.""" + +load(":file.bzl", _file = "file") +load( + "@bazel_skylib//lib:unittest.bzl", + _analysistest = "analysistest", + _unittest = "unittest", +) + +TestInfo = provider( + doc = "Provides a test a suggested set of attributes.", + fields = { + "name": "The name of the test.", + "prefix": "The prefix used to isolate artifact and target names.", + }, +) + +def _prefix(prefix, name): + """Prepends the given prefix to the given name.""" + return "%s-%s" % (prefix, name) + +def _prefix_from_test_info(test_info, name): + """Prepends the prefix of a TestInfo to the given name.""" + return _prefix(test_info.prefix, name) + +def _test_suite( + name = None, + test_scenarios = None): + """Creates a test suite containing the list of test targets. + + Args: + name: Name of the test suite, also used as part of a prefix for naming. + test_scenarios: A list of methods, that setup and the test. Each scenario + method should accept a TestInfo provider. + """ + test_targets = [] + for scenario_name, make_scenario in test_scenarios.items(): + test_prefix = _prefix(name, scenario_name) + test_info = TestInfo( + prefix = test_prefix, + name = test_prefix + "_test", + ) + make_scenario(test_info) + test_targets.append(test_info.name) + + native.test_suite( + name = name, + tests = test_targets, + ) + +def _fake_java_library(name): + class_name = "".join( + [part.title() for part in name.replace("-", "_").split("_")], + ) + native.java_library( + name = name, + srcs = [_file.create( + class_name + ".java", + contents = """@SuppressWarnings("DefaultPackage") +class %s{}""" % class_name, + )], + ) + +def _fake_jar(name): + if not name.endswith(".jar"): + fail("fake_jar method requires name to end with '.jar'") + _fake_java_library(name[:-4]) + return name + +def _fake_executable(name): + return _file.create(name, contents = "echo %s" % name, executable = True) + +def _analysis_test_error(message, *args): + return [ + AnalysisTestResultInfo( + success = False, + message = message % args, + ), + ] + +analysistest = _analysistest + +unittest = struct( + # Forward through unittest methods through the current unittest. + analysis_test_error = _analysis_test_error, + begin = _unittest.begin, + end = _unittest.end, + fake_executable = _fake_executable, + fake_jar = _fake_jar, + fake_java_library = _fake_java_library, + make = _unittest.make, + prefix = _prefix_from_test_info, + test_suite = _test_suite, +)