diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 07db49e..c2c65ea 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -36,6 +36,10 @@ tasks: name: "Last Green Bazel" bazel: last_green <<: *common + # TODO: Remove once we test with Bazel 8+ + test_targets: + - "//..." + - "//test:xcode_config_test" macos_latest_head_deps: name: "Current LTS with Head Deps" diff --git a/test/BUILD b/test/BUILD index a5a0914..623a81f 100644 --- a/test/BUILD +++ b/test/BUILD @@ -2,23 +2,32 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@bazel_skylib//rules:build_test.bzl", "build_test") load("//rules:apple_genrule.bzl", "apple_genrule") load(":apple_support_test.bzl", "apple_support_test") +load(":available_xcodes_test.bzl", "available_xcodes_test") load(":binary_tests.bzl", "binary_test_suite") load(":linking_tests.bzl", "linking_test_suite") load(":starlark_apple_binary.bzl", "starlark_apple_binary") load(":universal_binary_test.bzl", "universal_binary_test") +load(":xcode_config_test.bzl", "xcode_config_test") load(":xcode_support_test.bzl", "xcode_support_test") +load(":xcode_version_test.bzl", "xcode_version_test") licenses(["notice"]) # Custom rules that test rule-context APIs. Check their implementations for more details. apple_support_test(name = "apple_support_test") -xcode_support_test(name = "xcode_support_test") +available_xcodes_test(name = "available_xcodes_test") binary_test_suite(name = "binary") linking_test_suite(name = "linking") +xcode_config_test(name = "xcode_config_test") + +xcode_support_test(name = "xcode_support_test") + +xcode_version_test(name = "xcode_version_test") + # Test to ensure the environment variable contract of apple_genrule. sh_test( name = "apple_genrule_test", @@ -51,6 +60,10 @@ bzl_library( deps = [ "//lib:apple_support", "//lib:xcode_support", + "//xcode:available_xcodes", + "//xcode:xcode_config", + "//xcode:xcode_version", + "@bazel_skylib//lib:unittest", ], ) diff --git a/test/available_xcodes_test.bzl b/test/available_xcodes_test.bzl new file mode 100644 index 0000000..49aa6ea --- /dev/null +++ b/test/available_xcodes_test.bzl @@ -0,0 +1,90 @@ +# Copyright 2024 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. + +"""Tests for the `available_xcodes` rule.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest") +load( + "@build_bazel_apple_support//xcode:available_xcodes.bzl", + "available_xcodes", +) +load( + "@build_bazel_apple_support//xcode:xcode_version.bzl", + "xcode_version", +) +load(":test_helpers.bzl", "FIXTURE_TAGS", "make_all_tests") + +visibility("private") + +# ------------------------------------------------------------------------------ + +def _read_version_from_providers(namer): + available_xcodes( + name = namer("my_xcodes"), + default = namer(":xcode_8"), + versions = [ + namer(":xcode_8"), + namer(":xcode_9"), + ], + tags = FIXTURE_TAGS, + ) + + xcode_version( + name = namer("xcode_8"), + default_ios_sdk_version = "9.0", + default_macos_sdk_version = "9.3", + default_tvos_sdk_version = "9.2", + default_watchos_sdk_version = "9.1", + version = "8", + tags = FIXTURE_TAGS, + ) + + xcode_version( + name = namer("xcode_9"), + default_ios_sdk_version = "10.0", + default_macos_sdk_version = "10.3", + default_tvos_sdk_version = "10.2", + default_watchos_sdk_version = "10.1", + version = "9", + tags = FIXTURE_TAGS, + ) + + _read_version_from_providers_test( + name = "read_version_from_providers", + target_under_test = namer("my_xcodes"), + ) + return ["read_version_from_providers"] + +def _read_version_from_providers_test_impl(ctx): + env = analysistest.begin(ctx) + + # TODO: b/311385128 - Add tests for the provider contents once we've moved + # the providers here. We can't test them yet because they are internal to + # built-in Starlark. + + return analysistest.end(env) + +_read_version_from_providers_test = analysistest.make( + _read_version_from_providers_test_impl, +) + +# ------------------------------------------------------------------------------ + +def available_xcodes_test(name): + make_all_tests( + name = name, + tests = [ + _read_version_from_providers, + ], + ) diff --git a/test/test_helpers.bzl b/test/test_helpers.bzl new file mode 100644 index 0000000..7917ffe --- /dev/null +++ b/test/test_helpers.bzl @@ -0,0 +1,98 @@ +# Copyright 2024 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. + +"""Common Starlark helpers used by apple_support tests.""" + +visibility(["//test/..."]) + +# Common tags used for all test fixtures to ensure that they don't build unless +# used as a dependency of a test. +FIXTURE_TAGS = [ + "manual", +] + +def make_unique_namer(*, prefix, index): + """Returns a function used to generate unique names in a package. + + When generating multiple test fixtures in a single `.bzl` file that contains + multiple test macros, you generally don't want to worry about ensuring that + all the fixture targets have unique names. This utility makes that easier by + returning a simple function that can be used to generate unique names based + on a prefix and index of the test being created. See `make_all_tests` for + how this is used in practice (most users will not need to call this + directly.) + + Notice that the returned function handles same-package label references + (beginning with `:`) correctly as well. + """ + + def namer(suffix): + if suffix.startswith(":"): + return ":{}__{}__{}".format(prefix, index, suffix[1:]) + return "{}__{}__{}".format(prefix, index, suffix) + + return namer + +def make_all_tests(*, name, tests, tags = []): + """Makes all of the tests defined by a list of test functions. + + This function simplifies the process of creating Starlark tests and the + corresponding test suite. It should be called from a test-creation macro + with the desired name of the test suite target, which will be used to create + a unique namer for fixtures created by the macro, and a list of other test + macros that each represents a test and its fixtures. + + Each entry in `tests` passed to this function should have the following + behavior: + + * It must take a single `namer` argument that will be a function returned + by `make_unique_namer` that the test should use to create unique names + for its fixture targets. + * It must return a list of names of the test targets (not fixtures, just + actual tests) that it created so that they can be added to the test + suite. + + ```build + def some_test_macro(name): + make_all_tests( + name = name, + tests = [ + test1, + test2, + ], + ) + + def test1(namer): + some_target( + name = namer("foo"), + some_label = namer(":bar") + ) + some_test( + name = "test1", + target_under_test = namer(":foo"), + ) + return ["test1"] + ``` + """ + native.test_suite( + name = name, + tests = [ + returned_test + for index, test_creator in enumerate(tests) + for returned_test in test_creator( + namer = make_unique_namer(prefix = name, index = index + 1), + ) + ], + tags = tags, + ) diff --git a/test/xcode_config_test.bzl b/test/xcode_config_test.bzl new file mode 100644 index 0000000..cae4c12 --- /dev/null +++ b/test/xcode_config_test.bzl @@ -0,0 +1,634 @@ +# Copyright 2024 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. + +"""Tests for the `xcode_config` rule.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load( + "@build_bazel_apple_support//xcode:available_xcodes.bzl", + "available_xcodes", +) +load( + "@build_bazel_apple_support//xcode:xcode_config.bzl", + "xcode_config", +) +load( + "@build_bazel_apple_support//xcode:xcode_version.bzl", + "xcode_version", +) +load(":test_helpers.bzl", "FIXTURE_TAGS", "make_all_tests") + +visibility("private") + +# ------------------------------------------------------------------------------ + +def _mutual_and_explicit_xcodes_fails(namer): + xcode_config( + name = namer("foo"), + default = namer(":version512"), + local_versions = namer(":local"), + remote_versions = namer(":remote"), + versions = [ + namer(":version512"), + namer(":version84"), + ], + tags = FIXTURE_TAGS, + ) + + xcode_version( + name = namer("version512"), + aliases = [ + "5", + "5.1", + ], + version = "5.1.2", + tags = FIXTURE_TAGS, + ) + + xcode_version( + name = namer("version84"), + version = "8.4", + tags = FIXTURE_TAGS, + ) + + available_xcodes( + name = namer("remote"), + default = namer(":version512"), + versions = [namer(":version512")], + tags = FIXTURE_TAGS, + ) + + available_xcodes( + name = namer("local"), + default = namer(":version84"), + versions = [namer(":version84")], + tags = FIXTURE_TAGS, + ) + + _mutual_and_explicit_xcodes_fails_test( + name = "mutual_and_explicit_xcodes_fails", + target_under_test = namer("foo"), + # TODO: Remove once we test with Bazel 8+ + tags = ["manual"], + ) + return ["mutual_and_explicit_xcodes_fails"] + +def _mutual_and_explicit_xcodes_fails_test_impl(ctx): + env = analysistest.begin(ctx) + asserts.expect_failure(env, "'versions' may not be set if '[local,remote]_versions' is set.") + return analysistest.end(env) + +_mutual_and_explicit_xcodes_fails_test = analysistest.make( + _mutual_and_explicit_xcodes_fails_test_impl, + expect_failure = True, +) + +# ------------------------------------------------------------------------------ + +def _mutual_and_default_xcodes_fails(namer): + xcode_config( + name = namer("foo"), + default = namer(":version512"), + local_versions = namer(":local"), + remote_versions = namer(":remote"), + tags = FIXTURE_TAGS, + ) + + xcode_version( + name = namer("version512"), + aliases = [ + "5", + "5.1", + ], + version = "5.1.2", + tags = FIXTURE_TAGS, + ) + + xcode_version( + name = namer("version84"), + version = "8.4", + tags = FIXTURE_TAGS, + ) + + available_xcodes( + name = namer("remote"), + default = namer(":version512"), + versions = [namer(":version512")], + tags = FIXTURE_TAGS, + ) + + available_xcodes( + name = namer("local"), + default = namer(":version84"), + versions = [namer(":version84")], + tags = FIXTURE_TAGS, + ) + + _mutual_and_default_xcodes_fails_test( + name = "mutual_and_default_xcodes_fails", + target_under_test = namer("foo"), + # TODO: Remove once we test with Bazel 8+ + tags = ["manual"], + ) + return ["mutual_and_default_xcodes_fails"] + +def _mutual_and_default_xcodes_fails_test_impl(ctx): + env = analysistest.begin(ctx) + asserts.expect_failure(env, "'default' may not be set if '[local,remote]_versions' is set.") + return analysistest.end(env) + +_mutual_and_default_xcodes_fails_test = analysistest.make( + _mutual_and_default_xcodes_fails_test_impl, + expect_failure = True, +) + +# ------------------------------------------------------------------------------ + +def _no_local_xcodes_fails(namer): + xcode_config( + name = namer("foo"), + remote_versions = namer(":remote"), + tags = FIXTURE_TAGS, + ) + + xcode_version( + name = namer("version512"), + aliases = [ + "5", + "5.1", + ], + version = "5.1.2", + tags = FIXTURE_TAGS, + ) + + available_xcodes( + name = namer("remote"), + default = namer(":version512"), + versions = [namer(":version512")], + tags = FIXTURE_TAGS, + ) + + _no_local_xcodes_fails_test( + name = "no_local_xcodes_fails", + target_under_test = namer("foo"), + ) + return ["no_local_xcodes_fails"] + +def _no_local_xcodes_fails_test_impl(ctx): + env = analysistest.begin(ctx) + asserts.expect_failure(env, "if 'remote_versions' are set, you must also set 'local_versions'") + return analysistest.end(env) + +_no_local_xcodes_fails_test = analysistest.make( + _no_local_xcodes_fails_test_impl, + expect_failure = True, +) + +# ------------------------------------------------------------------------------ + +def _accepts_flag_for_mutually_available(namer): + _make_xcode_fixtures( + namer = namer, + xcode_config_name = "accepts_flag_for_mutually_available__foo", + remote_versions = [ + struct(name = "version512", version = "5.1.2", is_default = True), + struct(name = "version84", version = "8.4"), + ], + local_versions = [ + struct(name = "version84", version = "8.4", is_default = True), + ], + ) + + _accepts_flag_for_mutually_available_test( + name = "accepts_flag_for_mutually_available", + target_under_test = "accepts_flag_for_mutually_available__foo", + ) + return ["accepts_flag_for_mutually_available"] + +def _accepts_flag_for_mutually_available_test_impl(ctx): + env = analysistest.begin(ctx) + + target_under_test = analysistest.target_under_test(env) + xcode_version_info = target_under_test[apple_common.XcodeVersionConfig] + + asserts.equals(env, "8.4", str(xcode_version_info.xcode_version())) + asserts.equals(env, "both", xcode_version_info.availability()) + asserts.true(env, "requires-darwin" in xcode_version_info.execution_info()) + asserts.true(env, "supports-xcode-requirements-set" in xcode_version_info.execution_info()) + + return analysistest.end(env) + +_accepts_flag_for_mutually_available_test = analysistest.make( + _accepts_flag_for_mutually_available_test_impl, + config_settings = { + "//command_line_option:xcode_version": "8.4", + "//command_line_option:xcode_version_config": "@build_bazel_apple_support//test:accepts_flag_for_mutually_available__foo", + }, +) + +# ------------------------------------------------------------------------------ + +def _prefers_flag_over_mutually_available(namer): + _make_xcode_fixtures( + namer = namer, + xcode_config_name = "prefers_flag_over_mutually_available__foo", + remote_versions = [ + struct(name = "version512", version = "5.1.2", is_default = True), + struct(name = "version84", version = "8.4"), + ], + local_versions = [ + struct(name = "version84", version = "8.4", is_default = True), + ], + ) + + _prefers_flag_over_mutually_available_test( + name = "prefers_flag_over_mutually_available", + target_under_test = "prefers_flag_over_mutually_available__foo", + ) + return ["prefers_flag_over_mutually_available"] + +def _prefers_flag_over_mutually_available_test_impl(ctx): + env = analysistest.begin(ctx) + + target_under_test = analysistest.target_under_test(env) + xcode_version_info = target_under_test[apple_common.XcodeVersionConfig] + + asserts.equals(env, "5.1.2", str(xcode_version_info.xcode_version())) + asserts.equals(env, "remote", xcode_version_info.availability()) + asserts.true(env, "requires-darwin" in xcode_version_info.execution_info()) + asserts.true(env, "no-local" in xcode_version_info.execution_info()) + asserts.true(env, "supports-xcode-requirements-set" in xcode_version_info.execution_info()) + + return analysistest.end(env) + +_prefers_flag_over_mutually_available_test = analysistest.make( + _prefers_flag_over_mutually_available_test_impl, + config_settings = { + "//command_line_option:xcode_version": "5.1.2", + "//command_line_option:xcode_version_config": "@build_bazel_apple_support//test:prefers_flag_over_mutually_available__foo", + }, +) + +# ------------------------------------------------------------------------------ + +def _warn_with_explicit_local_only_version(namer): + _make_xcode_fixtures( + namer = namer, + xcode_config_name = "warn_with_explicit_local_only_version__foo", + remote_versions = [ + struct(name = "version512", version = "5.1.2", is_default = True), + ], + local_versions = [ + struct(name = "version84", version = "8.4", is_default = True), + ], + ) + + _warn_with_explicit_local_only_version_test( + name = "warn_with_explicit_local_only_version", + target_under_test = "warn_with_explicit_local_only_version__foo", + ) + return ["warn_with_explicit_local_only_version"] + +def _warn_with_explicit_local_only_version_test_impl(ctx): + env = analysistest.begin(ctx) + + target_under_test = analysistest.target_under_test(env) + xcode_version_info = target_under_test[apple_common.XcodeVersionConfig] + + # TODO: b/311385128 - Once we move the rules to apple_support, hack up + # something that would let us actually test the warning messages. We can't + # test `print`. + + asserts.equals(env, "8.4", str(xcode_version_info.xcode_version())) + asserts.equals(env, "local", xcode_version_info.availability()) + asserts.true(env, "requires-darwin" in xcode_version_info.execution_info()) + asserts.true(env, "no-remote" in xcode_version_info.execution_info()) + asserts.true(env, "supports-xcode-requirements-set" in xcode_version_info.execution_info()) + + return analysistest.end(env) + +_warn_with_explicit_local_only_version_test = analysistest.make( + _warn_with_explicit_local_only_version_test_impl, + config_settings = { + "//command_line_option:xcode_version": "8.4", + "//command_line_option:xcode_version_config": "@build_bazel_apple_support//test:warn_with_explicit_local_only_version__foo", + }, +) + +# ------------------------------------------------------------------------------ + +def _prefer_local_default_if_no_mutual_no_flag_different_main_version(namer): + _make_xcode_fixtures( + namer = namer, + xcode_config_name = "prefer_local_default_if_no_mutual_no_flag_different_main_version__foo", + remote_versions = [ + struct(name = "version512", version = "5.1.2", is_default = True), + ], + local_versions = [ + struct(name = "version84", version = "8.4", is_default = True), + ], + ) + + _prefer_local_default_if_no_mutual_no_flag_different_main_version_test( + name = "prefer_local_default_if_no_mutual_no_flag_different_main_version", + target_under_test = "prefer_local_default_if_no_mutual_no_flag_different_main_version__foo", + ) + return ["prefer_local_default_if_no_mutual_no_flag_different_main_version"] + +def _prefer_local_default_if_no_mutual_no_flag_different_main_version_test_impl(ctx): + env = analysistest.begin(ctx) + + target_under_test = analysistest.target_under_test(env) + xcode_version_info = target_under_test[apple_common.XcodeVersionConfig] + + # TODO: b/311385128 - Once we move the rules to apple_support, hack up + # something that would let us actually test the warning messages. We can't + # test `print`. + + asserts.equals(env, "8.4", str(xcode_version_info.xcode_version())) + asserts.equals(env, "local", xcode_version_info.availability()) + asserts.true(env, "requires-darwin" in xcode_version_info.execution_info()) + asserts.true(env, "no-remote" in xcode_version_info.execution_info()) + asserts.true(env, "supports-xcode-requirements-set" in xcode_version_info.execution_info()) + + return analysistest.end(env) + +_prefer_local_default_if_no_mutual_no_flag_different_main_version_test = analysistest.make( + _prefer_local_default_if_no_mutual_no_flag_different_main_version_test_impl, + config_settings = { + "//command_line_option:xcode_version_config": "@build_bazel_apple_support//test:prefer_local_default_if_no_mutual_no_flag_different_main_version__foo", + }, +) + +# ------------------------------------------------------------------------------ + +def _prefer_local_default_if_no_mutual_no_flag_different_build_alias(namer): + _make_xcode_fixtures( + namer = namer, + xcode_config_name = "prefer_local_default_if_no_mutual_no_flag_different_build_alias__foo", + remote_versions = [ + struct(name = "version10", version = "10.0", is_default = True, aliases = ["10.0.0.101ff", "10.0"]), + ], + local_versions = [ + struct(name = "version10.0.0.10C504", version = "10.0.0.10C504", is_default = True, aliases = ["10.0.0.10C504", "10.0"]), + ], + ) + + _prefer_local_default_if_no_mutual_no_flag_different_build_alias_test( + name = "prefer_local_default_if_no_mutual_no_flag_different_build_alias", + target_under_test = "prefer_local_default_if_no_mutual_no_flag_different_build_alias__foo", + ) + return ["prefer_local_default_if_no_mutual_no_flag_different_build_alias"] + +def _prefer_local_default_if_no_mutual_no_flag_different_build_alias_test_impl(ctx): + env = analysistest.begin(ctx) + + target_under_test = analysistest.target_under_test(env) + xcode_version_info = target_under_test[apple_common.XcodeVersionConfig] + + # TODO: b/311385128 - Once we move the rules to apple_support, hack up + # something that would let us actually test the warning messages. We can't + # test `print`. + + asserts.equals(env, "10.0.0.10C504", str(xcode_version_info.xcode_version())) + asserts.equals(env, "local", xcode_version_info.availability()) + asserts.true(env, "requires-darwin" in xcode_version_info.execution_info()) + asserts.true(env, "no-remote" in xcode_version_info.execution_info()) + asserts.true(env, "supports-xcode-requirements-set" in xcode_version_info.execution_info()) + + return analysistest.end(env) + +_prefer_local_default_if_no_mutual_no_flag_different_build_alias_test = analysistest.make( + _prefer_local_default_if_no_mutual_no_flag_different_build_alias_test_impl, + config_settings = { + "//command_line_option:xcode_version_config": "@build_bazel_apple_support//test:prefer_local_default_if_no_mutual_no_flag_different_build_alias__foo", + }, +) + +# ------------------------------------------------------------------------------ + +def _prefer_local_default_if_no_mutual_no_flag_different_full_version(namer): + _make_xcode_fixtures( + namer = namer, + xcode_config_name = "prefer_local_default_if_no_mutual_no_flag_different_full_version__foo", + remote_versions = [ + struct(name = "version10", version = "10.0.0.101ff", is_default = True, aliases = ["10.0", "10.0.0.101ff"]), + ], + local_versions = [ + struct(name = "version10.0.0.10C504", version = "10.0.0.10C504", is_default = True, aliases = ["10.0.0.10C504", "10.0"]), + ], + ) + + _prefer_local_default_if_no_mutual_no_flag_different_full_version_test( + name = "prefer_local_default_if_no_mutual_no_flag_different_full_version", + target_under_test = "prefer_local_default_if_no_mutual_no_flag_different_full_version__foo", + ) + return ["prefer_local_default_if_no_mutual_no_flag_different_full_version"] + +def _prefer_local_default_if_no_mutual_no_flag_different_full_version_test_impl(ctx): + env = analysistest.begin(ctx) + + target_under_test = analysistest.target_under_test(env) + xcode_version_info = target_under_test[apple_common.XcodeVersionConfig] + + # TODO: b/311385128 - Once we move the rules to apple_support, hack up + # something that would let us actually test the warning messages. We can't + # test `print`. + + asserts.equals(env, "10.0.0.10C504", str(xcode_version_info.xcode_version())) + asserts.equals(env, "local", xcode_version_info.availability()) + asserts.true(env, "requires-darwin" in xcode_version_info.execution_info()) + asserts.true(env, "no-remote" in xcode_version_info.execution_info()) + asserts.true(env, "supports-xcode-requirements-set" in xcode_version_info.execution_info()) + + return analysistest.end(env) + +_prefer_local_default_if_no_mutual_no_flag_different_full_version_test = analysistest.make( + _prefer_local_default_if_no_mutual_no_flag_different_full_version_test_impl, + config_settings = { + "//command_line_option:xcode_version_config": "@build_bazel_apple_support//test:prefer_local_default_if_no_mutual_no_flag_different_full_version__foo", + }, +) + +# ------------------------------------------------------------------------------ + +def _choose_newest_mutual_xcode(namer): + _make_xcode_fixtures( + namer = namer, + xcode_config_name = "choose_newest_mutual_xcode__foo", + remote_versions = [ + struct(name = "version92", version = "9.2", is_default = True), + struct(name = "version10", version = "10", aliases = ["10.0.0.10C504"]), + struct(name = "version84", version = "8.4"), + ], + local_versions = [ + struct(name = "version9", version = "9", is_default = True), + struct(name = "version84", version = "8.4"), + struct(name = "version10.0.0.10C504", version = "10.0.0.10C504", aliases = ["10.0"]), + ], + ) + + _choose_newest_mutual_xcode_test( + name = "choose_newest_mutual_xcode", + target_under_test = "choose_newest_mutual_xcode__foo", + ) + return ["choose_newest_mutual_xcode"] + +def _choose_newest_mutual_xcode_test_impl(ctx): + env = analysistest.begin(ctx) + + target_under_test = analysistest.target_under_test(env) + xcode_version_info = target_under_test[apple_common.XcodeVersionConfig] + + # TODO: b/311385128 - Once we move the rules to apple_support, hack up + # something that would let us actually test the warning messages. We can't + # test `print`. + + asserts.equals(env, "10", str(xcode_version_info.xcode_version())) + asserts.equals(env, "both", xcode_version_info.availability()) + asserts.true(env, "requires-darwin" in xcode_version_info.execution_info()) + asserts.true(env, "supports-xcode-requirements-set" in xcode_version_info.execution_info()) + + return analysistest.end(env) + +_choose_newest_mutual_xcode_test = analysistest.make( + _choose_newest_mutual_xcode_test_impl, + config_settings = { + "//command_line_option:xcode_version_config": "@build_bazel_apple_support//test:choose_newest_mutual_xcode__foo", + }, +) + +# ------------------------------------------------------------------------------ + +def _invalid_xcode_from_mutual_throws(namer): + _make_xcode_fixtures( + namer = namer, + xcode_config_name = "invalid_xcode_from_mutual_throws__foo", + remote_versions = [ + struct(name = "version512", version = "5.1.2", is_default = True), + struct(name = "version84", version = "8.4"), + ], + local_versions = [ + struct(name = "version84", version = "8.4", is_default = True), + ], + ) + + _invalid_xcode_from_mutual_throws_test( + name = "invalid_xcode_from_mutual_throws", + target_under_test = "invalid_xcode_from_mutual_throws__foo", + # TODO: Remove once we test with Bazel 8+ + tags = ["manual"], + ) + return ["invalid_xcode_from_mutual_throws"] + +def _invalid_xcode_from_mutual_throws_test_impl(ctx): + env = analysistest.begin(ctx) + asserts.expect_failure(env, "--xcode_version=6 specified, but '6' is not an available Xcode version. Locally available versions: [8.4]. Remotely available versions: [5.1.2, 8.4].") + return analysistest.end(env) + +_invalid_xcode_from_mutual_throws_test = analysistest.make( + _invalid_xcode_from_mutual_throws_test_impl, + config_settings = { + "//command_line_option:xcode_version": "6", + "//command_line_option:xcode_version_config": "@build_bazel_apple_support//test:invalid_xcode_from_mutual_throws__foo", + }, + expect_failure = True, +) + +# ------------------------------------------------------------------------------ + +def _make_xcode_fixtures( + *, + namer, + xcode_config_name, + remote_versions = [], + local_versions = []): + """Helper function to splat out fixtures used by multiple tests.""" + all_versions = {} + + remote_default_label = None + for version_info in remote_versions: + version_name = version_info.name + all_versions[version_name] = version_info + if getattr(version_info, "is_default", False): + if remote_default_label: + fail("Only one remote version may be the default") + remote_default_label = version_name + + local_default_label = None + for version_info in local_versions: + version_name = version_info.name + all_versions[version_name] = version_info + if getattr(version_info, "is_default", False): + if local_default_label: + fail("Only one local version may be the default") + local_default_label = version_name + + for version_name, version in all_versions.items(): + xcode_version( + name = namer(version.name), + version = version.version, + aliases = getattr(version, "aliases", []), + tags = FIXTURE_TAGS, + ) + + if local_versions or remote_versions: + if local_versions: + available_xcodes( + name = namer("local"), + default = namer(local_default_label), + versions = [namer(version.name) for version in local_versions], + tags = FIXTURE_TAGS, + ) + if remote_versions: + available_xcodes( + name = namer("remote"), + default = namer(remote_default_label), + versions = [namer(version.name) for version in remote_versions], + tags = FIXTURE_TAGS, + ) + xcode_config( + name = xcode_config_name, + local_versions = namer("local"), + remote_versions = namer("remote"), + ) + +def xcode_config_test(name): + make_all_tests( + name = name, + tests = [ + _mutual_and_explicit_xcodes_fails, + _mutual_and_default_xcodes_fails, + _no_local_xcodes_fails, + _accepts_flag_for_mutually_available, + _prefers_flag_over_mutually_available, + _warn_with_explicit_local_only_version, + _prefer_local_default_if_no_mutual_no_flag_different_main_version, + _prefer_local_default_if_no_mutual_no_flag_different_build_alias, + _prefer_local_default_if_no_mutual_no_flag_different_full_version, + _choose_newest_mutual_xcode, + _invalid_xcode_from_mutual_throws, + ], + # TODO: Remove once we test with Bazel 8+ + tags = ["manual"], + ) + + # TODO: b/311385128 - The following tests from `XcodeConfigTest.java` + # couldn't be migrated to Starlark, because they need to set an + # `--experimental_*` flag, which is not supported in Starlark transitions: + # + # * testPreferMutual_choosesLocalDefaultOverNewest + # * testPreferMutualXcodeFalseOverridesMutual + # * testLocalDefaultCanBeMutuallyAvailable + # * testPreferLocalDefaultOverDifferentBuild diff --git a/test/xcode_version_test.bzl b/test/xcode_version_test.bzl new file mode 100644 index 0000000..19b38b2 --- /dev/null +++ b/test/xcode_version_test.bzl @@ -0,0 +1,73 @@ +# Copyright 2024 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. + +"""Tests for the `xcode_version` rule.""" + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load( + "@build_bazel_apple_support//xcode:xcode_version.bzl", + "xcode_version", +) +load(":test_helpers.bzl", "FIXTURE_TAGS", "make_all_tests") + +visibility("private") + +# ------------------------------------------------------------------------------ + +def _read_version_from_providers(namer): + xcode_version( + name = namer("my_xcode"), + default_ios_sdk_version = "9.0", + default_macos_sdk_version = "9.3", + default_tvos_sdk_version = "9.2", + default_visionos_sdk_version = "9.4", + default_watchos_sdk_version = "9.1", + version = "8", + tags = FIXTURE_TAGS, + ) + + _read_version_from_provider_test( + name = "read_version_from_provider", + target_under_test = namer("my_xcode"), + ) + return ["read_version_from_provider"] + +def _read_version_from_provider_test_impl(ctx): + env = analysistest.begin(ctx) + + target_under_test = analysistest.target_under_test(env) + xcode_properties = target_under_test[apple_common.XcodeProperties] + + asserts.equals(env, "8", xcode_properties.xcode_version) + asserts.equals(env, "9.0", xcode_properties.default_ios_sdk_version) + asserts.equals(env, "9.1", xcode_properties.default_watchos_sdk_version) + asserts.equals(env, "9.2", xcode_properties.default_tvos_sdk_version) + asserts.equals(env, "9.3", xcode_properties.default_macos_sdk_version) + asserts.equals(env, "9.4", xcode_properties.default_visionos_sdk_version) + + return analysistest.end(env) + +_read_version_from_provider_test = analysistest.make( + _read_version_from_provider_test_impl, +) + +# ------------------------------------------------------------------------------ + +def xcode_version_test(name): + make_all_tests( + name = name, + tests = [ + _read_version_from_providers, + ], + )