Skip to content

Commit

Permalink
Test
Browse files Browse the repository at this point in the history
  • Loading branch information
fmeum committed Jan 7, 2025
1 parent fa48bc6 commit fbf6884
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ public void changeOutputMode_notInvalidateActions() throws Exception {
}

@Test
public void outputSymlinkHandledGracefully() throws Exception {
public void outputSymlinkCreatedWithToplevel() throws Exception {
// Dangling symlink would require developer mode to be enabled in the CI environment.
assumeFalse(OS.getCurrent() == OS.WINDOWS);

Expand Down Expand Up @@ -271,10 +271,186 @@ def _impl(ctx):
my_rule(name = "hello")
""");

setDownloadToplevel();
buildTarget("//a:hello");

Path outputPath = getOutputPath("a/hello");
assertThat(outputPath.stat(Symlinks.NOFOLLOW).isSymbolicLink()).isTrue();
assertThat(outputPath.readSymbolicLink()).isEqualTo(PathFragment.create("hello"));
}

@Test
public void outputSymlinkNotCreatedWithMinimal() throws Exception {
write(
"a/defs.bzl",
"""
def _impl(ctx):
out = ctx.actions.declare_symlink(ctx.label.name)
ctx.actions.run_shell(
inputs = [],
outputs = [out],
command = "ln -s hello $1",
arguments = [out.path],
)
return DefaultInfo(files = depset([out]))
my_rule = rule(
implementation = _impl,
)
""");

write(
"a/BUILD",
"""
load(":defs.bzl", "my_rule")
my_rule(name = "hello")
""");

buildTarget("//a:hello");

assertOutputsDoNotExist("//a:hello");
}

private void writeSymlinkProducerConsumeRules() throws Exception {
write(
"a/b/producer_consumer.bzl",
"""
def _producer_impl(ctx):
dangling = ctx.actions.declare_symlink(ctx.label.name + "_dangling")
ctx.actions.run_shell(
inputs = [],
outputs = [dangling],
command = "ln -s dangling $1",
arguments = [dangling.path],
)
not_dangling = ctx.actions.declare_symlink(ctx.label.name + "_not_dangling")
relative_target_path = not_dangling.path.count("/") * "../" + ctx.file.target.path
ctx.actions.run_shell(
inputs = [],
outputs = [not_dangling],
command = "ln -s $2 $1",
arguments = [not_dangling.path, relative_target_path],
)
return [
DefaultInfo(files = depset([dangling, not_dangling])),
OutputGroupInfo(
dangling = depset([dangling]),
not_dangling = depset([not_dangling]),
target = depset([ctx.file.target]),
),
]
producer = rule(
implementation = _producer_impl,
attrs = {
"target": attr.label(allow_single_file = True),
},
)
def _consumer_impl(ctx):
outputs = ctx.attr.producer[OutputGroupInfo]
target = outputs.target.to_list()[0]
dangling = outputs.dangling.to_list()[0]
not_dangling = outputs.not_dangling.to_list()[0]
out = ctx.actions.declare_file(ctx.label.name)
ctx.actions.run_shell(
inputs = [dangling, not_dangling],
outputs = [out],
command = \"""
[[ $(readlink $1) == "dangling" ]]
[[ $(readlink $2) == */$3 ]]
touch $4
\""",
arguments = [dangling.path, not_dangling.path, target.basename, out.path],
)
return DefaultInfo(files = depset([out]))
consumer = rule(
implementation = _consumer_impl,
attrs = {
"producer": attr.label(),
},
)
""");
}

@Test
public void inputSymlinkNotCreatedForRemoteAction() throws Exception {
writeSymlinkProducerConsumeRules();

write(
"a/b/BUILD",
"""
load(":producer_consumer.bzl", "producer", "consumer")
genrule(
name = "greeting",
srcs = [],
outs = ["greeting.txt"],
cmd = "echo 'hello world' > $@",
)
producer(
name = "producer",
target = ":greeting",
)
consumer(
name = "consumer",
producer = ":producer",
)
""");

write("a/b/greeting.in", "hello world");

buildTarget("//a/b:consumer");

assertOutputsDoNotExist("//a/b:greeting");
assertOutputsDoNotExist("//a/b:producer");
assertOutputsDoNotExist("//a/b:consumer");
}

@Test
public void inputSymlinkCreatedForLocalAction() throws Exception {
writeSymlinkProducerConsumeRules();

write(
"a/b/BUILD",
"""
load(":producer_consumer.bzl", "producer", "consumer")
genrule(
name = "greeting",
srcs = [],
outs = ["greeting.txt"],
cmd = "echo 'hello world' > $@",
)
producer(
name = "producer",
target = ":greeting",
)
consumer(
name = "consumer",
producer = ":producer",
tags = ["no-remote"],
)
""");

write("a/b/greeting.in", "hello world");

buildTarget("//a/b:consumer");

Path greetingPath = getOutputPath("a/b/greeting.txt");
assertThat(greetingPath.exists(Symlinks.NOFOLLOW)).isFalse();
Path danglingPath = getOutputPath("a/b/producer_dangling");
assertThat(danglingPath.readSymbolicLink()).isEqualTo(PathFragment.create("dangling"));
Path notDanglingPath = getOutputPath("a/b/producer_not_dangling");
assertThat(notDanglingPath.getParentDirectory().getRelative(notDanglingPath.readSymbolicLink()))
.isEqualTo(greetingPath);
assertOnlyOutputContent("//a/b:consumer", "consumer", "");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.google.devtools.build.lib.util.io.RecordingOutErr;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.Symlinks;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -1798,7 +1799,7 @@ protected void assertOutputsDoNotExist(String target) throws Exception {
for (Artifact output : getArtifacts(target)) {
assertWithMessage(
"output %s for target %s should not exist", output.getExecPathString(), target)
.that(output.getPath().exists())
.that(output.getPath().exists(Symlinks.NOFOLLOW))
.isFalse();
}
}
Expand All @@ -1813,7 +1814,7 @@ protected Path getOutputPath(String binRelativePath) {

protected void assertOutputDoesNotExist(String binRelativePath) {
Path output = getOutputPath(binRelativePath);
assertThat(output.exists()).isFalse();
assertThat(output.exists(Symlinks.NOFOLLOW)).isFalse();
}

protected void assertOnlyOutputContent(String target, String filename, String content)
Expand All @@ -1834,9 +1835,11 @@ protected void assertValidOutputFile(String binRelativePath, String content) thr

protected void assertSymlink(String binRelativeLinkPath, PathFragment targetPath)
throws Exception {
Path output = getOutputPath(binRelativeLinkPath);
// On Windows, symlinks might be implemented as a file copy.
if (OS.getCurrent() != OS.WINDOWS) {
Path output = getOutputPath(binRelativeLinkPath);
if (OS.getCurrent() == OS.WINDOWS) {
assertThat(output.exists()).isTrue();
} else {
assertThat(output.isSymbolicLink()).isTrue();
assertThat(output.readSymbolicLink()).isEqualTo(targetPath);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,8 @@ public void downloadOutputs_relativeFileSymlink_success() throws Exception {
FakeSpawnExecutionContext context = newSpawnExecutionContext(spawn);
RemoteExecutionService service = newRemoteExecutionService();
RemoteAction action = service.buildRemoteAction(spawn, context);
when(remoteOutputChecker.shouldDownloadOutput(ArgumentMatchers.<PathFragment>any()))
.thenReturn(true);

// Doesn't check for dangling links, hence download succeeds.
service.downloadOutputs(action, result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ function test_downloads_toplevel_dangling_symlink_output() {

bazel build \
--remote_executor=grpc://localhost:${worker_port} \
--remote_download_minimal \
--remote_download_toplevel \
//pkg:sym >& $TEST_log || fail "Expected build of //pkg:sym to succeed"

if [[ "$(readlink bazel-bin/pkg/sym)" != "target.txt" ]]; then
Expand Down

0 comments on commit fbf6884

Please sign in to comment.