// Copyright 2014 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.bazel.repository.skylark;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.CharStreams;
import com.google.devtools.build.lib.bazel.repository.downloader.HttpDownloader;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClass;
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException;
import com.google.devtools.build.lib.syntax.Argument.Passed;
import com.google.devtools.build.lib.syntax.BuiltinFunction;
import com.google.devtools.build.lib.syntax.FuncallExpression;
import com.google.devtools.build.lib.syntax.Identifier;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.testutil.Scratch;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.skyframe.SkyFunction;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mockito;
/**
* Unit tests for complex function of SkylarkRepositoryContext.
*/
@RunWith(JUnit4.class)
public class SkylarkRepositoryContextTest {
private Scratch scratch;
private Path outputDirectory;
private Path workspaceFile;
private SkylarkRepositoryContext context;
@Before
public void setUp() throws Exception {
scratch = new Scratch("/");
outputDirectory = scratch.dir("/outputDir");
workspaceFile = scratch.file("/WORKSPACE");
}
protected static RuleClass buildRuleClass(Attribute... attributes) {
RuleClass.Builder ruleClassBuilder =
new RuleClass.Builder("test", RuleClassType.WORKSPACE, true);
for (Attribute attr : attributes) {
ruleClassBuilder.addOrOverrideAttribute(attr);
}
ruleClassBuilder.setWorkspaceOnly();
ruleClassBuilder.setConfiguredTargetFunction(new BuiltinFunction("test") {});
return ruleClassBuilder.build();
}
protected void setUpContextForRule(Map<String, Object> kwargs, Attribute... attributes)
throws Exception {
Package.Builder packageBuilder = Package.newExternalPackageBuilder(
Package.Builder.DefaultHelper.INSTANCE, workspaceFile, "runfiles");
FuncallExpression ast =
new FuncallExpression(new Identifier("test"), ImmutableList.<Passed>of());
ast.setLocation(Location.BUILTIN);
Rule rule =
packageBuilder
.externalPackageData()
.createAndAddRepositoryRule(
packageBuilder, buildRuleClass(attributes), null, kwargs, ast);
HttpDownloader downloader = Mockito.mock(HttpDownloader.class);
context =
new SkylarkRepositoryContext(
rule,
outputDirectory,
Mockito.mock(SkyFunction.Environment.class),
ImmutableMap.of("FOO", "BAR"),
downloader, new HashMap<String, String>());
}
protected void setUpContexForRule(String name) throws Exception {
setUpContextForRule(ImmutableMap.<String, Object>of("name", name));
}
@Test
public void testAttr() throws Exception {
setUpContextForRule(
ImmutableMap.<String, Object>of("name", "test", "foo", "bar"),
Attribute.attr("foo", Type.STRING).build());
assertThat(context.getAttr().getKeys()).contains("foo");
assertThat(context.getAttr().getValue("foo")).isEqualTo("bar");
}
@Test
public void testWhich() throws Exception {
setUpContexForRule("test");
SkylarkRepositoryContext.setPathEnvironment("/bin", "/path/sbin", ".");
scratch.file("/bin/true").setExecutable(true);
scratch.file("/path/sbin/true").setExecutable(true);
scratch.file("/path/sbin/false").setExecutable(true);
scratch.file("/path/bin/undef").setExecutable(true);
scratch.file("/path/bin/def").setExecutable(true);
scratch.file("/bin/undef");
assertThat(context.which("anything")).isNull();
assertThat(context.which("def")).isNull();
assertThat(context.which("undef")).isNull();
assertThat(context.which("true").toString()).isEqualTo("/bin/true");
assertThat(context.which("false").toString()).isEqualTo("/path/sbin/false");
}
@Test
public void testFile() throws Exception {
setUpContexForRule("test");
context.createFile(context.path("foobar"), "", true);
context.createFile(context.path("foo/bar"), "foobar", true);
context.createFile(context.path("bar/foo/bar"), "", true);
testOutputFile(outputDirectory.getChild("foobar"), "");
testOutputFile(outputDirectory.getRelative("foo/bar"), "foobar");
testOutputFile(outputDirectory.getRelative("bar/foo/bar"), "");
try {
context.createFile(context.path("/absolute"), "", true);
fail("Expected error on creating path outside of the repository directory");
} catch (RepositoryFunctionException ex) {
assertThat(ex.getCause().getMessage())
.isEqualTo("Cannot write outside of the repository directory for path /absolute");
}
try {
context.createFile(context.path("../somepath"), "", true);
fail("Expected error on creating path outside of the repository directory");
} catch (RepositoryFunctionException ex) {
assertThat(ex.getCause().getMessage())
.isEqualTo("Cannot write outside of the repository directory for path /somepath");
}
try {
context.createFile(context.path("foo/../../somepath"), "", true);
fail("Expected error on creating path outside of the repository directory");
} catch (RepositoryFunctionException ex) {
assertThat(ex.getCause().getMessage())
.isEqualTo("Cannot write outside of the repository directory for path /somepath");
}
}
@Test
public void testSymlink() throws Exception {
setUpContexForRule("test");
context.createFile(context.path("foo"), "foobar", true);
context.symlink(context.path("foo"), context.path("bar"));
testOutputFile(outputDirectory.getChild("bar"), "foobar");
assertThat(context.path("bar").realpath()).isEqualTo(context.path("foo"));
}
private void testOutputFile(Path path, String content) throws IOException {
assertThat(path.exists()).isTrue();
assertThat(
CharStreams.toString(
new InputStreamReader(path.getInputStream(), StandardCharsets.UTF_8)))
.isEqualTo(content);
}
@Test
public void testDirectoryListing() throws Exception {
setUpContexForRule("test");
scratch.file("/my/folder/a");
scratch.file("/my/folder/b");
scratch.file("/my/folder/c");
assertThat(context.path("/my/folder").readdir()).containsExactly(
context.path("/my/folder/a"), context.path("/my/folder/b"), context.path("/my/folder/c"));
}
}