| // 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. |
| |
| package com.google.devtools.build.lib.rules.python; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.google.devtools.build.lib.rules.python.PythonTestUtils.assumesDefaultIsPY2; |
| import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; |
| |
| import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
| import com.google.devtools.build.lib.analysis.util.ConfigurationTestCase; |
| import com.google.devtools.common.options.OptionsParsingException; |
| import com.google.devtools.common.options.TriState; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Tests for {@link PythonOptions} and {@link PythonConfiguration}. */ |
| @RunWith(JUnit4.class) |
| public class PythonConfigurationTest extends ConfigurationTestCase { |
| |
| private PythonOptions parsePythonOptions(String... cmdline) throws Exception { |
| BuildConfiguration config = create(cmdline); |
| return config.getOptions().get(PythonOptions.class); |
| } |
| |
| @Test |
| public void invalidTargetPythonValue_NotATargetValue() { |
| OptionsParsingException expected = |
| assertThrows(OptionsParsingException.class, () -> create("--force_python=PY2AND3")); |
| assertThat(expected).hasMessageThat().contains("Not a valid Python major version"); |
| } |
| |
| @Test |
| public void invalidTargetPythonValue_UnknownValue() { |
| OptionsParsingException expected = |
| assertThrows( |
| OptionsParsingException.class, |
| () -> create("--force_python=BEETLEJUICE")); |
| assertThat(expected).hasMessageThat().contains("Not a valid Python major version"); |
| } |
| |
| @Test |
| public void oldVersionFlagGatedByIncompatibleFlag() throws Exception { |
| create("--incompatible_remove_old_python_version_api=false", "--force_python=PY2"); |
| checkError( |
| "`--force_python` is disabled by `--incompatible_remove_old_python_version_api`", |
| "--incompatible_remove_old_python_version_api=true", |
| "--force_python=PY2"); |
| } |
| |
| @Test |
| public void py3IsDefaultFlagRequiresNewSemanticsFlag() throws Exception { |
| checkError( |
| "cannot enable `--incompatible_py3_is_default` without also enabling " |
| + "`--incompatible_allow_python_version_transitions`", |
| "--incompatible_allow_python_version_transitions=false", |
| "--incompatible_py3_is_default=true"); |
| } |
| |
| @Test |
| public void getDefaultPythonVersion() throws Exception { |
| // --incompatible_py3_is_default requires --incompatible_allow_python_version_transitions |
| PythonOptions withoutPy3IsDefaultOpts = |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=true", |
| "--incompatible_py3_is_default=false"); |
| PythonOptions withPy3IsDefaultOpts = |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=true", |
| "--incompatible_py3_is_default=true"); |
| assertThat(withoutPy3IsDefaultOpts.getDefaultPythonVersion()).isEqualTo(PythonVersion.PY2); |
| assertThat(withPy3IsDefaultOpts.getDefaultPythonVersion()).isEqualTo(PythonVersion.PY3); |
| } |
| |
| @Test |
| public void getPythonVersion_FallBackOnDefaultPythonVersion() throws Exception { |
| // Run it twice with two different values for the incompatible flag to confirm it's actually |
| // reading getDefaultPythonVersion() and not some other source of default values. Note that |
| // --incompatible_py3_is_default requires --incompatible_allow_python_version_transitions. |
| PythonOptions py2Opts = |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=true", |
| "--incompatible_py3_is_default=false"); |
| PythonOptions py3Opts = |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=true", |
| "--incompatible_py3_is_default=true"); |
| assertThat(py2Opts.getPythonVersion()).isEqualTo(PythonVersion.PY2); |
| assertThat(py3Opts.getPythonVersion()).isEqualTo(PythonVersion.PY3); |
| } |
| |
| @Test |
| public void getPythonVersion_NewFlagTakesPrecedence() throws Exception { |
| assumesDefaultIsPY2(); |
| // --force_python is superseded by --python_version. |
| PythonOptions opts = |
| parsePythonOptions( |
| "--incompatible_remove_old_python_version_api=false", |
| "--force_python=PY2", |
| "--python_version=PY3"); |
| assertThat(opts.getPythonVersion()).isEqualTo(PythonVersion.PY3); |
| } |
| |
| @Test |
| public void getPythonVersion_FallBackOnOldFlag() throws Exception { |
| assumesDefaultIsPY2(); |
| // --force_python is used because --python_version is absent. |
| PythonOptions opts = |
| parsePythonOptions( |
| "--incompatible_remove_old_python_version_api=false", "--force_python=PY3"); |
| assertThat(opts.getPythonVersion()).isEqualTo(PythonVersion.PY3); |
| } |
| |
| @Test |
| public void canTransitionPythonVersion_OldSemantics_Yes() throws Exception { |
| assumesDefaultIsPY2(); |
| PythonOptions opts = |
| parsePythonOptions("--incompatible_allow_python_version_transitions=false"); |
| assertThat(opts.canTransitionPythonVersion(PythonVersion.PY3)).isTrue(); |
| } |
| |
| @Test |
| public void canTransitionPythonVersion_OldSemantics_NoBecauseAlreadySet() throws Exception { |
| assumesDefaultIsPY2(); |
| PythonOptions optsWithOldFlag = |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=false", |
| "--incompatible_remove_old_python_version_api=false", |
| "--force_python=PY2"); |
| PythonOptions optsWithNewFlag = |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=false", "--python_version=PY2"); |
| assertThat(optsWithOldFlag.canTransitionPythonVersion(PythonVersion.PY3)).isFalse(); |
| assertThat(optsWithNewFlag.canTransitionPythonVersion(PythonVersion.PY3)).isFalse(); |
| } |
| |
| @Test |
| public void canTransitionPythonVersion_OldSemantics_NoBecauseNewValueSameAsDefault() |
| throws Exception { |
| assumesDefaultIsPY2(); |
| PythonOptions opts = |
| parsePythonOptions("--incompatible_allow_python_version_transitions=false"); |
| assertThat(opts.canTransitionPythonVersion(PythonVersion.PY2)).isFalse(); |
| } |
| |
| @Test |
| public void canTransitionPythonVersion_NewSemantics_Yes() throws Exception { |
| PythonOptions opts = |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=true", "--python_version=PY3"); |
| assertThat(opts.canTransitionPythonVersion(PythonVersion.PY2)).isTrue(); |
| } |
| |
| @Test |
| public void canTransitionPythonVersion_NewSemantics_NoBecauseSameAsCurrent() throws Exception { |
| PythonOptions opts = |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=true", |
| // Set --force_python too, or else we fall into the "make --force_python consistent" |
| // case. |
| "--incompatible_remove_old_python_version_api=false", |
| "--force_python=PY3", |
| "--python_version=PY3"); |
| assertThat(opts.canTransitionPythonVersion(PythonVersion.PY3)).isFalse(); |
| } |
| |
| @Test |
| public void canTransitionPythonVersion_NewApi_NoEvenWhenForcePythonDisagrees() throws Exception { |
| PythonOptions opts = |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=true", |
| "--incompatible_remove_old_python_version_api=false", |
| // Test that even though --force_python's value isn't in sync, we don't transition |
| // because getPythonVersion() would be unaffected by the transition. |
| "--force_python=PY2", |
| "--python_version=PY3"); |
| assertThat(opts.canTransitionPythonVersion(PythonVersion.PY3)).isFalse(); |
| } |
| |
| @Test |
| public void setPythonVersion_OldApiEnabled() throws Exception { |
| PythonOptions opts = |
| parsePythonOptions( |
| "--incompatible_remove_old_python_version_api=false", |
| "--force_python=PY2", |
| "--python_version=PY2"); |
| opts.setPythonVersion(PythonVersion.PY3); |
| assertThat(opts.forcePython).isEqualTo(PythonVersion.PY3); |
| assertThat(opts.pythonVersion).isEqualTo(PythonVersion.PY3); |
| } |
| |
| @Test |
| public void setPythonVersion_OldApiDisabled() throws Exception { |
| PythonOptions opts = |
| parsePythonOptions( |
| "--incompatible_remove_old_python_version_api=true", "--python_version=PY2"); |
| opts.setPythonVersion(PythonVersion.PY3); |
| assertThat(opts.forcePython).isNull(); |
| assertThat(opts.pythonVersion).isEqualTo(PythonVersion.PY3); |
| } |
| |
| @Test |
| public void getHost_CopiesMostValues() throws Exception { |
| PythonOptions opts = |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=true", |
| "--incompatible_remove_old_python_version_api=true", |
| "--incompatible_py3_is_default=true", |
| "--incompatible_py2_outputs_are_suffixed=true", |
| "--build_python_zip=true", |
| "--incompatible_disallow_legacy_py_provider=true", |
| "--incompatible_use_python_toolchains=true"); |
| PythonOptions hostOpts = (PythonOptions) opts.getHost(); |
| assertThat(hostOpts.incompatibleAllowPythonVersionTransitions).isTrue(); |
| assertThat(hostOpts.incompatibleRemoveOldPythonVersionApi).isTrue(); |
| assertThat(hostOpts.incompatiblePy3IsDefault).isTrue(); |
| assertThat(hostOpts.incompatiblePy2OutputsAreSuffixed).isTrue(); |
| assertThat(hostOpts.buildPythonZip).isEqualTo(TriState.YES); |
| assertThat(hostOpts.incompatibleDisallowLegacyPyProvider).isTrue(); |
| assertThat(hostOpts.incompatibleUsePythonToolchains).isTrue(); |
| } |
| |
| @Test |
| public void getHost_AppliesHostForcePython() throws Exception { |
| assumesDefaultIsPY2(); |
| PythonOptions optsWithForcePythonFlag = |
| parsePythonOptions( |
| "--incompatible_remove_old_python_version_api=false", |
| "--force_python=PY2", |
| "--host_force_python=PY3"); |
| PythonOptions optsWithPythonVersionFlag = |
| parsePythonOptions("--python_version=PY2", "--host_force_python=PY3"); |
| PythonOptions optsWithPy3IsDefaultFlag = |
| // --incompatible_py3_is_default requires --incompatible_allow_python_version_transitions |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=true", |
| "--incompatible_py3_is_default=true", |
| // It's more interesting to set the incompatible flag true and force host to PY2, than |
| // it is to set the flag false and force host to PY3. |
| "--host_force_python=PY2"); |
| PythonOptions hostOptsWithForcePythonFlag = (PythonOptions) optsWithForcePythonFlag.getHost(); |
| PythonOptions hostOptsWithPythonVersionFlag = |
| (PythonOptions) optsWithPythonVersionFlag.getHost(); |
| PythonOptions hostOptsWithPy3IsDefaultFlag = (PythonOptions) optsWithPy3IsDefaultFlag.getHost(); |
| assertThat(hostOptsWithForcePythonFlag.getPythonVersion()).isEqualTo(PythonVersion.PY3); |
| assertThat(hostOptsWithPythonVersionFlag.getPythonVersion()).isEqualTo(PythonVersion.PY3); |
| assertThat(hostOptsWithPy3IsDefaultFlag.getPythonVersion()).isEqualTo(PythonVersion.PY2); |
| } |
| |
| @Test |
| public void getHost_Py3IsDefaultFlagChangesHost() throws Exception { |
| assumesDefaultIsPY2(); |
| PythonOptions opts = |
| // --incompatible_py3_is_default requires --incompatible_allow_python_version_transitions |
| parsePythonOptions( |
| "--incompatible_allow_python_version_transitions=true", |
| "--incompatible_py3_is_default=true"); |
| PythonOptions hostOpts = (PythonOptions) opts.getHost(); |
| assertThat(hostOpts.getPythonVersion()).isEqualTo(PythonVersion.PY3); |
| } |
| |
| @Test |
| public void getNormalized_OldSemantics() throws Exception { |
| PythonOptions opts = |
| parsePythonOptions("--incompatible_allow_python_version_transitions=false"); |
| PythonOptions normalizedOpts = (PythonOptions) opts.getNormalized(); |
| assertThat(normalizedOpts.pythonVersion).isNull(); |
| } |
| |
| @Test |
| public void getNormalized_NewSemantics() throws Exception { |
| assumesDefaultIsPY2(); |
| PythonOptions opts = parsePythonOptions("--incompatible_allow_python_version_transitions=true"); |
| PythonOptions normalizedOpts = (PythonOptions) opts.getNormalized(); |
| assertThat(normalizedOpts.pythonVersion).isEqualTo(PythonVersion.PY2); |
| } |
| } |