blob: 4dc05e2f6ca5b422fc2a910d361ba42d80bed4f6 [file] [log] [blame]
buchgr357cb1e2019-04-03 06:15:02 -07001// 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.
14package com.google.devtools.build.lib.remote;
15
16import static com.google.common.truth.Truth.assertThat;
17import static org.mockito.ArgumentMatchers.eq;
18import static org.mockito.Mockito.doAnswer;
19import static org.mockito.Mockito.verify;
20import static org.mockito.Mockito.verifyNoMoreInteractions;
21
22import com.google.common.hash.HashCode;
23import com.google.common.util.concurrent.Futures;
24import com.google.devtools.build.lib.actions.ActionInputMap;
25import com.google.devtools.build.lib.actions.Artifact;
26import com.google.devtools.build.lib.actions.ArtifactRoot;
27import com.google.devtools.build.lib.actions.FileArtifactValue;
28import com.google.devtools.build.lib.actions.FileArtifactValue.RemoteFileArtifactValue;
janakraea05602019-05-22 15:41:29 -070029import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
buchgr357cb1e2019-04-03 06:15:02 -070030import com.google.devtools.build.lib.clock.JavaClock;
31import com.google.devtools.build.lib.vfs.DigestHashFunction;
32import com.google.devtools.build.lib.vfs.FileSystem;
33import com.google.devtools.build.lib.vfs.FileSystemUtils;
34import com.google.devtools.build.lib.vfs.Path;
lberki96ab6732019-08-01 05:54:42 -070035import com.google.devtools.build.lib.vfs.Symlinks;
buchgr357cb1e2019-04-03 06:15:02 -070036import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
37import java.io.IOException;
38import java.nio.charset.StandardCharsets;
39import org.junit.Before;
40import org.junit.Test;
41import org.junit.runner.RunWith;
42import org.junit.runners.JUnit4;
43import org.mockito.Mock;
44import org.mockito.MockitoAnnotations;
45
46/** Tests for {@link RemoteActionFileSystem} */
47@RunWith(JUnit4.class)
48public class RemoteActionFileSystemTest {
49
50 private static final DigestHashFunction HASH_FUNCTION = DigestHashFunction.SHA256;
51
52 @Mock private RemoteActionInputFetcher inputFetcher;
53 private FileSystem fs;
54 private Path execRoot;
55 private ArtifactRoot outputRoot;
56
57 @Before
58 public void setUp() throws IOException {
59 MockitoAnnotations.initMocks(this);
60 fs = new InMemoryFileSystem(new JavaClock(), HASH_FUNCTION);
61 execRoot = fs.getPath("/exec");
Googler147c2872021-01-21 08:47:48 -080062 outputRoot = ArtifactRoot.asDerivedRoot(execRoot, false, "out");
buchgr357cb1e2019-04-03 06:15:02 -070063 outputRoot.getRoot().asPath().createDirectoryAndParents();
64 }
65
66 @Test
67 public void testGetInputStream() throws Exception {
68 // arrange
69 ActionInputMap inputs = new ActionInputMap(2);
70 Artifact remoteArtifact = createRemoteArtifact("remote-file", "remote contents", inputs);
71 Artifact localArtifact = createLocalArtifact("local-file", "local contents", inputs);
72 FileSystem actionFs = newRemoteActionFileSystem(inputs);
73 doAnswer(
74 invocationOnMock -> {
75 FileSystemUtils.writeContent(
76 remoteArtifact.getPath(), StandardCharsets.UTF_8, "remote contents");
77 return Futures.immediateFuture(null);
78 })
79 .when(inputFetcher)
80 .downloadFile(eq(remoteArtifact.getPath()), eq(inputs.getMetadata(remoteArtifact)));
81
82 // act
83 Path remoteActionFsPath = actionFs.getPath(remoteArtifact.getPath().asFragment());
84 String actualRemoteContents =
85 FileSystemUtils.readContent(remoteActionFsPath, StandardCharsets.UTF_8);
86
87 // assert
88 Path localActionFsPath = actionFs.getPath(localArtifact.getPath().asFragment());
89 String actualLocalContents =
90 FileSystemUtils.readContent(localActionFsPath, StandardCharsets.UTF_8);
cpovirka4d3da62019-05-02 14:27:33 -070091 assertThat(remoteActionFsPath.getFileSystem()).isSameInstanceAs(actionFs);
buchgr357cb1e2019-04-03 06:15:02 -070092 assertThat(actualRemoteContents).isEqualTo("remote contents");
93 assertThat(actualLocalContents).isEqualTo("local contents");
94 verify(inputFetcher)
95 .downloadFile(eq(remoteArtifact.getPath()), eq(inputs.getMetadata(remoteArtifact)));
96 verifyNoMoreInteractions(inputFetcher);
97 }
98
99 @Test
100 public void testCreateSymbolicLink() throws InterruptedException, IOException {
101 // arrange
102 ActionInputMap inputs = new ActionInputMap(1);
103 Artifact remoteArtifact = createRemoteArtifact("remote-file", "remote contents", inputs);
104 Path symlink = outputRoot.getRoot().getRelative("symlink");
105 FileSystem actionFs = newRemoteActionFileSystem(inputs);
106 doAnswer(
107 invocationOnMock -> {
108 FileSystemUtils.writeContent(
109 remoteArtifact.getPath(), StandardCharsets.UTF_8, "remote contents");
110 return Futures.immediateFuture(null);
111 })
112 .when(inputFetcher)
113 .downloadFile(eq(remoteArtifact.getPath()), eq(inputs.getMetadata(remoteArtifact)));
114
115 // act
116 Path symlinkActionFs = actionFs.getPath(symlink.getPathString());
117 symlinkActionFs.createSymbolicLink(actionFs.getPath(remoteArtifact.getPath().asFragment()));
118
119 // assert
cpovirka4d3da62019-05-02 14:27:33 -0700120 assertThat(symlinkActionFs.getFileSystem()).isSameInstanceAs(actionFs);
buchgr357cb1e2019-04-03 06:15:02 -0700121 verify(inputFetcher)
122 .downloadFile(eq(remoteArtifact.getPath()), eq(inputs.getMetadata(remoteArtifact)));
123 String symlinkTargetContents =
124 FileSystemUtils.readContent(symlinkActionFs, StandardCharsets.UTF_8);
125 assertThat(symlinkTargetContents).isEqualTo("remote contents");
126 verifyNoMoreInteractions(inputFetcher);
127 }
128
Jakob Buchgraber5c02b922019-08-29 05:31:04 -0700129 @Test
130 public void testDeleteRemoteFile() throws Exception {
131 // arrange
132 ActionInputMap inputs = new ActionInputMap(1);
133 Artifact remoteArtifact = createRemoteArtifact("remote-file", "remote contents", inputs);
134 FileSystem actionFs = newRemoteActionFileSystem(inputs);
135
136 // act
137 boolean success = actionFs.delete(actionFs.getPath(remoteArtifact.getPath().getPathString()));
138
139 // assert
140 assertThat(success).isTrue();
141 }
142
143 @Test
144 public void testDeleteLocalFile() throws Exception {
145 // arrange
146 ActionInputMap inputs = new ActionInputMap(0);
147 FileSystem actionFs = newRemoteActionFileSystem(inputs);
148 Path filePath = actionFs.getPath(execRoot.getPathString()).getChild("local-file");
149 FileSystemUtils.writeContent(filePath, StandardCharsets.UTF_8, "local contents");
150
151 // act
152 boolean success = actionFs.delete(actionFs.getPath(filePath.getPathString()));
153
154 // assert
155 assertThat(success).isTrue();
156 }
157
buchgr357cb1e2019-04-03 06:15:02 -0700158 private FileSystem newRemoteActionFileSystem(ActionInputMap inputs) {
159 return new RemoteActionFileSystem(
160 fs,
161 execRoot.asFragment(),
162 outputRoot.getRoot().asPath().relativeTo(execRoot).getPathString(),
163 inputs,
164 inputFetcher);
165 }
166
167 /** Returns a remote artifact and puts its metadata into the action input map. */
168 private Artifact createRemoteArtifact(
169 String pathFragment, String contents, ActionInputMap inputs) {
170 Path p = outputRoot.getRoot().asPath().getRelative(pathFragment);
janakraea05602019-05-22 15:41:29 -0700171 Artifact a = ActionsTestUtil.createArtifact(outputRoot, p);
buchgr357cb1e2019-04-03 06:15:02 -0700172 byte[] b = contents.getBytes(StandardCharsets.UTF_8);
173 HashCode h = HASH_FUNCTION.getHashFunction().hashBytes(b);
174 FileArtifactValue f =
George Gensure3ef8fb92020-05-06 09:49:48 -0700175 new RemoteFileArtifactValue(h.asBytes(), b.length, /* locationIndex= */ 1, "action-id");
buchgr357cb1e2019-04-03 06:15:02 -0700176 inputs.putWithNoDepOwner(a, f);
177 return a;
178 }
179
180 /** Returns a local artifact and puts its metadata into the action input map. */
181 private Artifact createLocalArtifact(String pathFragment, String contents, ActionInputMap inputs)
182 throws IOException {
183 Path p = outputRoot.getRoot().asPath().getRelative(pathFragment);
184 FileSystemUtils.writeContent(p, StandardCharsets.UTF_8, contents);
janakraea05602019-05-22 15:41:29 -0700185 Artifact a = ActionsTestUtil.createArtifact(outputRoot, p);
lberki96ab6732019-08-01 05:54:42 -0700186 Path path = a.getPath();
187 // Caution: there's a race condition between stating the file and computing the
188 // digest. We need to stat first, since we're using the stat to detect changes.
189 // We follow symlinks here to be consistent with getDigest.
lberki812e6fe2019-07-25 07:50:55 -0700190 inputs.putWithNoDepOwner(
lberki96ab6732019-08-01 05:54:42 -0700191 a, FileArtifactValue.createFromStat(path, path.stat(Symlinks.FOLLOW), true));
buchgr357cb1e2019-04-03 06:15:02 -0700192 return a;
193 }
194}