blob: 839a4bb64dd615cf4c7a984eaea9cd4ddd0719c5 [file] [log] [blame]
ulfjack8f075d22019-11-26 02:48:42 -08001// Copyright 2019 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.exec;
16
17import static com.google.common.truth.Truth.assertThat;
ulfjack112d41f2019-11-28 00:33:14 -080018import static com.google.common.truth.Truth.assertWithMessage;
ulfjack8f075d22019-11-26 02:48:42 -080019import static org.mockito.ArgumentMatchers.any;
ulfjack1a80bb82019-11-26 07:52:35 -080020import static org.mockito.Mockito.doAnswer;
ulfjack8f075d22019-11-26 02:48:42 -080021import static org.mockito.Mockito.mock;
ulfjack112d41f2019-11-28 00:33:14 -080022import static org.mockito.Mockito.never;
ulfjack8f075d22019-11-26 02:48:42 -080023import static org.mockito.Mockito.times;
24import static org.mockito.Mockito.verify;
25import static org.mockito.Mockito.when;
26
27import com.google.common.collect.ImmutableList;
28import com.google.devtools.build.lib.actions.ActionEnvironment;
29import com.google.devtools.build.lib.actions.ActionExecutionContext;
30import com.google.devtools.build.lib.actions.Artifact;
31import com.google.devtools.build.lib.actions.ArtifactPathResolver;
32import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
33import com.google.devtools.build.lib.analysis.Runfiles;
34import com.google.devtools.build.lib.analysis.actions.SymlinkTreeAction;
35import com.google.devtools.build.lib.analysis.actions.SymlinkTreeActionContext;
36import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
37import com.google.devtools.build.lib.events.StoredEventHandler;
ulfjack112d41f2019-11-28 00:33:14 -080038import com.google.devtools.build.lib.vfs.FileSystemUtils;
ulfjack8f075d22019-11-26 02:48:42 -080039import com.google.devtools.build.lib.vfs.OutputService;
ulfjack112d41f2019-11-28 00:33:14 -080040import com.google.devtools.build.lib.vfs.Path;
ulfjack8f075d22019-11-26 02:48:42 -080041import com.google.devtools.build.lib.vfs.PathFragment;
ulfjack112d41f2019-11-28 00:33:14 -080042import com.google.devtools.build.lib.vfs.Symlinks;
ulfjack8f075d22019-11-26 02:48:42 -080043import java.util.Map;
44import org.junit.Test;
45import org.junit.runner.RunWith;
46import org.junit.runners.JUnit4;
47import org.mockito.ArgumentCaptor;
48
49/** Unit tests for {@link SymlinkTreeStrategy}. */
50@RunWith(JUnit4.class)
51public final class SymlinkTreeStrategyTest extends BuildViewTestCase {
52 @Test
53 public void testArtifactToPathConversion() {
54 Artifact artifact = getBinArtifactWithNoOwner("dir/foo");
ulfjack1a80bb82019-11-26 07:52:35 -080055 assertThat(SymlinkTreeStrategy.TO_PATH.apply(artifact))
56 .isEqualTo(artifact.getPath().asFragment());
ulfjack8f075d22019-11-26 02:48:42 -080057 assertThat(SymlinkTreeStrategy.TO_PATH.apply(null)).isEqualTo(null);
58 }
59
60 @Test
61 public void outputServiceInteraction() throws Exception {
62 ActionExecutionContext context = mock(ActionExecutionContext.class);
63 OutputService outputService = mock(OutputService.class);
64 StoredEventHandler eventHandler = new StoredEventHandler();
65
66 when(context.getContext(SymlinkTreeActionContext.class))
67 .thenReturn(new SymlinkTreeStrategy(outputService, null));
68 when(context.getInputPath(any())).thenAnswer((i) -> ((Artifact) i.getArgument(0)).getPath());
69 when(context.getPathResolver()).thenReturn(ArtifactPathResolver.IDENTITY);
70 when(context.getEventHandler()).thenReturn(eventHandler);
71 when(outputService.canCreateSymlinkTree()).thenReturn(true);
72
73 Artifact inputManifest = getBinArtifactWithNoOwner("dir/manifest.in");
ulfjack112d41f2019-11-28 00:33:14 -080074 Artifact outputManifest = getBinArtifactWithNoOwner("dir.runfiles/MANIFEST");
ulfjack8f075d22019-11-26 02:48:42 -080075 Artifact runfile = getBinArtifactWithNoOwner("dir/runfile");
ulfjack1a80bb82019-11-26 07:52:35 -080076 doAnswer(
77 (i) -> {
78 outputManifest.getPath().getParentDirectory().createDirectoryAndParents();
79 return null;
80 })
81 .when(outputService)
82 .createSymlinkTree(any(), any());
ulfjack8f075d22019-11-26 02:48:42 -080083
84 Runfiles runfiles =
85 new Runfiles.Builder("TESTING", false)
86 .setEmptyFilesSupplier((paths) -> ImmutableList.of(PathFragment.create("dir/empty")))
87 .addArtifact(runfile)
88 .build();
89 SymlinkTreeAction action =
90 new SymlinkTreeAction(
91 ActionsTestUtil.NULL_ACTION_OWNER,
92 inputManifest,
93 runfiles,
94 outputManifest,
fellya0e7b672020-01-29 11:31:07 -080095 /*filesetRoot=*/ null,
ulfjack8f075d22019-11-26 02:48:42 -080096 ActionEnvironment.EMPTY,
ulfjack112d41f2019-11-28 00:33:14 -080097 /*enableRunfiles=*/ true,
ulfjackef1a9da2019-11-29 12:00:03 -080098 /*inprocessSymlinkCreation=*/ false,
99 /*skipRunfilesManifests=*/ false);
ulfjack8f075d22019-11-26 02:48:42 -0800100
101 action.execute(context);
102
103 @SuppressWarnings("unchecked")
ulfjack1a80bb82019-11-26 07:52:35 -0800104 ArgumentCaptor<Map<PathFragment, PathFragment>> capture = ArgumentCaptor.forClass(Map.class);
105 verify(outputService, times(1)).createSymlinkTree(capture.capture(), any());
ulfjack8f075d22019-11-26 02:48:42 -0800106 assertThat(capture.getValue())
107 .containsExactly(
108 PathFragment.create("TESTING/dir/runfile"),
ulfjack1a80bb82019-11-26 07:52:35 -0800109 runfile.getPath().asFragment(),
ulfjack8f075d22019-11-26 02:48:42 -0800110 PathFragment.create("TESTING/dir/empty"),
111 null);
112 }
ulfjack112d41f2019-11-28 00:33:14 -0800113
114 @Test
115 public void inprocessSymlinkCreation() throws Exception {
116 ActionExecutionContext context = mock(ActionExecutionContext.class);
117 OutputService outputService = mock(OutputService.class);
118 StoredEventHandler eventHandler = new StoredEventHandler();
119
120 when(context.getContext(SymlinkTreeActionContext.class))
121 .thenReturn(new SymlinkTreeStrategy(outputService, null));
122 when(context.getInputPath(any())).thenAnswer((i) -> ((Artifact) i.getArgument(0)).getPath());
123 when(context.getEventHandler()).thenReturn(eventHandler);
124 when(outputService.canCreateSymlinkTree()).thenReturn(false);
125
126 Artifact inputManifest = getBinArtifactWithNoOwner("dir/manifest.in");
127 Artifact outputManifest = getBinArtifactWithNoOwner("dir.runfiles/MANIFEST");
128 Artifact runfile = getBinArtifactWithNoOwner("dir/runfile");
129
130 Runfiles runfiles =
131 new Runfiles.Builder("TESTING", false)
132 .setEmptyFilesSupplier((paths) -> ImmutableList.of(PathFragment.create("dir/empty")))
133 .addArtifact(runfile)
134 .build();
135 SymlinkTreeAction action =
136 new SymlinkTreeAction(
137 ActionsTestUtil.NULL_ACTION_OWNER,
138 inputManifest,
139 runfiles,
140 outputManifest,
fellya0e7b672020-01-29 11:31:07 -0800141 /*filesetRoot=*/ null,
ulfjack112d41f2019-11-28 00:33:14 -0800142 ActionEnvironment.EMPTY,
143 /*enableRunfiles=*/ true,
ulfjackef1a9da2019-11-29 12:00:03 -0800144 /*inprocessSymlinkCreation=*/ true,
145 /*skipRunfilesManifests*/ false);
ulfjack112d41f2019-11-28 00:33:14 -0800146
147 action.execute(context);
148 // Check that the OutputService is not used.
149 verify(outputService, never()).createSymlinkTree(any(), any());
150
151 Path p = outputManifest.getPath().getParentDirectory().getRelative("TESTING/dir/runfile");
152 assertWithMessage("Path %s expected to exist", p).that(p.exists(Symlinks.NOFOLLOW)).isTrue();
153 assertWithMessage("Path %s expected to be a symlink", p).that(p.isSymbolicLink()).isTrue();
154 assertThat(p.readSymbolicLink()).isEqualTo(runfile.getPath().asFragment());
155 Path q = outputManifest.getPath().getParentDirectory().getRelative("TESTING/dir/empty");
156 assertWithMessage("Path %s expected to be a file", q).that(q.isFile()).isTrue();
157 assertThat(FileSystemUtils.readContent(q)).isEmpty();
158 }
ulfjack8f075d22019-11-26 02:48:42 -0800159}