blob: 1b433bc5c9fc08b3d3ed7feaf7de35429d75ca39 [file] [log] [blame]
// Copyright 2019 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 org.junit.Assert.assertThrows;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.syntax.EvalException;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link PyProviderUtils}. */
@RunWith(JUnit4.class)
public class PyProviderUtilsTest extends BuildViewTestCase {
private static String join(String... lines) {
return String.join("\n", lines);
}
private static void assertThrowsEvalExceptionContaining(
ThrowingRunnable runnable, String message) {
assertThat(assertThrows(EvalException.class, runnable)).hasMessageThat().contains(message);
}
/**
* Creates {@code //pkg:target} as an instance of a trivial rule having the given implementation
* function body.
*
* <p>The body is formed by joining the input strings with newlines, then inserting 4 spaces of
* indentation before the first line and each newline. This allows a single string argument to
* contain multiple pre-joined lines and still be indented correctly.
*/
private void declareTargetWithImplementation(String... input) throws Exception {
String indentedBody;
if (input.length == 0) {
indentedBody = " pass";
} else {
indentedBody = " " + String.join("\n", input).replace("\n", "\n ");
}
scratch.file(
"pkg/rules.bzl",
"def _myrule_impl(ctx):",
indentedBody,
"myrule = rule(",
" implementation = _myrule_impl,",
")");
scratch.file(
"pkg/BUILD", //
"load(':rules.bzl', 'myrule')", //
"myrule(", //
" name = 'target',", //
")");
}
/** Retrieves the target defined by {@link #declareTargetWithImplementation}. */
private ConfiguredTarget getTarget() throws Exception {
ConfiguredTarget target = getConfiguredTarget("//pkg:target");
assertThat(target).isNotNull();
return target;
}
@Test
public void getAndHasModernProvider_Present() throws Exception {
declareTargetWithImplementation( //
"return [PyInfo(transitive_sources=depset([]))]");
assertThat(PyProviderUtils.hasModernProvider(getTarget())).isTrue();
assertThat(PyProviderUtils.getModernProvider(getTarget())).isNotNull();
}
@Test
public void getAndHasModernProvider_Absent() throws Exception {
declareTargetWithImplementation( //
"return []");
assertThat(PyProviderUtils.hasModernProvider(getTarget())).isFalse();
assertThat(PyProviderUtils.getModernProvider(getTarget())).isNull();
}
@Test
public void getAndHasLegacyProvider_Present() throws Exception {
declareTargetWithImplementation( //
"return struct(py=struct())"); // Accessor doesn't actually check fields
assertThat(PyProviderUtils.hasLegacyProvider(getTarget())).isTrue();
assertThat(PyProviderUtils.getLegacyProvider(getTarget())).isNotNull();
}
@Test
public void getAndHasLegacyProvider_Absent() throws Exception {
declareTargetWithImplementation( //
"return []");
assertThat(PyProviderUtils.hasLegacyProvider(getTarget())).isFalse();
assertThrowsEvalExceptionContaining(
() -> PyProviderUtils.getLegacyProvider(getTarget()), "Target does not have 'py' provider");
}
@Test
public void getLegacyProvider_NotAStruct() throws Exception {
declareTargetWithImplementation( //
"return struct(py=123)");
assertThrowsEvalExceptionContaining(
() -> PyProviderUtils.getLegacyProvider(getTarget()), "'py' provider should be a struct");
}
private static final String TRANSITIVE_SOURCES_SETUP_CODE =
join(
"afile = ctx.actions.declare_file('a.py')",
"ctx.actions.write(output=afile, content='a')",
"bfile = ctx.actions.declare_file('b.py')",
"ctx.actions.write(output=bfile, content='b')",
"modern_info = PyInfo(transitive_sources = depset(direct=[afile]))",
"legacy_info = struct(transitive_sources = depset(direct=[bfile]))");
@Test
public void getTransitiveSources_ModernProvider() throws Exception {
declareTargetWithImplementation( //
TRANSITIVE_SOURCES_SETUP_CODE, //
"return [modern_info]");
assertThat(PyProviderUtils.getTransitiveSources(getTarget()).toList())
.containsExactly(getBinArtifact("a.py", getTarget()));
}
@Test
public void getTransitiveSources_LegacyProvider() throws Exception {
declareTargetWithImplementation( //
TRANSITIVE_SOURCES_SETUP_CODE, //
"return struct(py=legacy_info)");
assertThat(PyProviderUtils.getTransitiveSources(getTarget()).toList())
.containsExactly(getBinArtifact("b.py", getTarget()));
}
@Test
public void getTransitiveSources_BothProviders() throws Exception {
declareTargetWithImplementation( //
TRANSITIVE_SOURCES_SETUP_CODE, //
"return struct(py=legacy_info, providers=[modern_info])");
assertThat(PyProviderUtils.getTransitiveSources(getTarget()).toList())
.containsExactly(getBinArtifact("a.py", getTarget()));
}
@Test
public void getTransitiveSources_NoProvider() throws Exception {
declareTargetWithImplementation( //
TRANSITIVE_SOURCES_SETUP_CODE, //
"return [DefaultInfo(files=depset(direct=[afile]))]");
assertThat(PyProviderUtils.getTransitiveSources(getTarget()).toList())
.containsExactly(getBinArtifact("a.py", getTarget()));
}
private static final String USES_SHARED_LIBRARIES_SETUP_CODE =
join(
"modern_info = PyInfo(transitive_sources = depset([]), uses_shared_libraries=False)",
"legacy_info = struct(transitive_sources = depset([]), uses_shared_libraries=True)");
@Test
public void getUsesSharedLibraries_ModernProvider() throws Exception {
declareTargetWithImplementation( //
USES_SHARED_LIBRARIES_SETUP_CODE, //
"return [modern_info]");
assertThat(PyProviderUtils.getUsesSharedLibraries(getTarget())).isFalse();
}
@Test
public void getUsesSharedLibraries_LegacyProvider() throws Exception {
declareTargetWithImplementation( //
USES_SHARED_LIBRARIES_SETUP_CODE, //
"return struct(py=legacy_info)");
assertThat(PyProviderUtils.getUsesSharedLibraries(getTarget())).isTrue();
}
@Test
public void getUsesSharedLibraries_BothProviders() throws Exception {
declareTargetWithImplementation( //
USES_SHARED_LIBRARIES_SETUP_CODE, //
"return struct(py=legacy_info, providers=[modern_info])");
assertThat(PyProviderUtils.getUsesSharedLibraries(getTarget())).isFalse();
}
@Test
public void getUsesSharedLibraries_NoProvider() throws Exception {
declareTargetWithImplementation(
"afile = ctx.actions.declare_file('a.so')",
"ctx.actions.write(output=afile, content='a')",
"return [DefaultInfo(files=depset(direct=[afile]))]");
assertThat(PyProviderUtils.getUsesSharedLibraries(getTarget())).isTrue();
}
private static final String IMPORTS_SETUP_CODE =
join(
"modern_info = PyInfo(transitive_sources = depset([]), imports = depset(direct=['abc']))",
"legacy_info = struct(",
" transitive_sources = depset([]),",
" imports = depset(direct=['def']))");
@Test
public void getImports_ModernProvider() throws Exception {
declareTargetWithImplementation( //
IMPORTS_SETUP_CODE, //
"return [modern_info]");
assertThat(PyProviderUtils.getImports(getTarget()).toList()).containsExactly("abc");
}
@Test
public void getImports_LegacyProvider() throws Exception {
declareTargetWithImplementation( //
IMPORTS_SETUP_CODE, //
"return struct(py=legacy_info)");
assertThat(PyProviderUtils.getImports(getTarget()).toList()).containsExactly("def");
}
@Test
public void getImports_BothProviders() throws Exception {
declareTargetWithImplementation( //
IMPORTS_SETUP_CODE, //
"return struct(py=legacy_info, providers=[modern_info])");
assertThat(PyProviderUtils.getImports(getTarget()).toList()).containsExactly("abc");
}
@Test
public void getImports_NoProvider() throws Exception {
declareTargetWithImplementation( //
IMPORTS_SETUP_CODE, //
"return []");
assertThat(PyProviderUtils.getImports(getTarget()).toList()).isEmpty();
}
private static final String HAS_PY2_ONLY_SOURCES_SETUP_CODE =
join(
"modern_info = PyInfo(transitive_sources = depset([]), has_py2_only_sources = False)",
"legacy_info = struct(transitive_sources = depset([]), has_py2_only_sources = True)");
@Test
public void getHasPy2OnlySources_ModernProvider() throws Exception {
declareTargetWithImplementation( //
HAS_PY2_ONLY_SOURCES_SETUP_CODE, //
"return [modern_info]");
assertThat(PyProviderUtils.getHasPy2OnlySources(getTarget())).isFalse();
}
@Test
public void getHasPy2OnlySources_LegacyProvider() throws Exception {
declareTargetWithImplementation( //
HAS_PY2_ONLY_SOURCES_SETUP_CODE, //
"return struct(py=legacy_info)");
assertThat(PyProviderUtils.getHasPy2OnlySources(getTarget())).isTrue();
}
@Test
public void getHasPy2OnlySources_BothProviders() throws Exception {
declareTargetWithImplementation( //
HAS_PY2_ONLY_SOURCES_SETUP_CODE, //
"return struct(py=legacy_info, providers=[modern_info])");
assertThat(PyProviderUtils.getHasPy2OnlySources(getTarget())).isFalse();
}
@Test
public void getHasPy2OnlySources_NoProvider() throws Exception {
declareTargetWithImplementation( //
"return []");
assertThat(PyProviderUtils.getHasPy2OnlySources(getTarget())).isFalse();
}
private static final String HAS_PY3_ONLY_SOURCES_SETUP_CODE =
join(
"modern_info = PyInfo(transitive_sources = depset([]), has_py3_only_sources = False)",
"legacy_info = struct(transitive_sources = depset([]), has_py3_only_sources = True)");
@Test
public void getHasPy3OnlySources_ModernProvider() throws Exception {
declareTargetWithImplementation( //
HAS_PY3_ONLY_SOURCES_SETUP_CODE, //
"return [modern_info]");
assertThat(PyProviderUtils.getHasPy3OnlySources(getTarget())).isFalse();
}
@Test
public void getHasPy3OnlySources_LegacyProvider() throws Exception {
declareTargetWithImplementation( //
HAS_PY3_ONLY_SOURCES_SETUP_CODE, //
"return struct(py=legacy_info)");
assertThat(PyProviderUtils.getHasPy3OnlySources(getTarget())).isTrue();
}
@Test
public void getHasPy3OnlySources_BothProviders() throws Exception {
declareTargetWithImplementation( //
HAS_PY3_ONLY_SOURCES_SETUP_CODE, //
"return struct(py=legacy_info, providers=[modern_info])");
assertThat(PyProviderUtils.getHasPy3OnlySources(getTarget())).isFalse();
}
@Test
public void getHasPy3OnlySources_NoProvider() throws Exception {
declareTargetWithImplementation( //
"return []");
assertThat(PyProviderUtils.getHasPy3OnlySources(getTarget())).isFalse();
}
}