blob: 1953e17441c1357cc63a40e02e3ced6def5c5462 [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;
35import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
36import java.io.IOException;
37import java.nio.charset.StandardCharsets;
38import org.junit.Before;
39import org.junit.Test;
40import org.junit.runner.RunWith;
41import org.junit.runners.JUnit4;
42import org.mockito.Mock;
43import org.mockito.MockitoAnnotations;
44
45/** Tests for {@link RemoteActionFileSystem} */
46@RunWith(JUnit4.class)
47public class RemoteActionFileSystemTest {
48
49 private static final DigestHashFunction HASH_FUNCTION = DigestHashFunction.SHA256;
50
51 @Mock private RemoteActionInputFetcher inputFetcher;
52 private FileSystem fs;
53 private Path execRoot;
54 private ArtifactRoot outputRoot;
55
56 @Before
57 public void setUp() throws IOException {
58 MockitoAnnotations.initMocks(this);
59 fs = new InMemoryFileSystem(new JavaClock(), HASH_FUNCTION);
60 execRoot = fs.getPath("/exec");
61 outputRoot = ArtifactRoot.asDerivedRoot(execRoot, execRoot.getRelative("out"));
62 outputRoot.getRoot().asPath().createDirectoryAndParents();
63 }
64
65 @Test
66 public void testGetInputStream() throws Exception {
67 // arrange
68 ActionInputMap inputs = new ActionInputMap(2);
69 Artifact remoteArtifact = createRemoteArtifact("remote-file", "remote contents", inputs);
70 Artifact localArtifact = createLocalArtifact("local-file", "local contents", inputs);
71 FileSystem actionFs = newRemoteActionFileSystem(inputs);
72 doAnswer(
73 invocationOnMock -> {
74 FileSystemUtils.writeContent(
75 remoteArtifact.getPath(), StandardCharsets.UTF_8, "remote contents");
76 return Futures.immediateFuture(null);
77 })
78 .when(inputFetcher)
79 .downloadFile(eq(remoteArtifact.getPath()), eq(inputs.getMetadata(remoteArtifact)));
80
81 // act
82 Path remoteActionFsPath = actionFs.getPath(remoteArtifact.getPath().asFragment());
83 String actualRemoteContents =
84 FileSystemUtils.readContent(remoteActionFsPath, StandardCharsets.UTF_8);
85
86 // assert
87 Path localActionFsPath = actionFs.getPath(localArtifact.getPath().asFragment());
88 String actualLocalContents =
89 FileSystemUtils.readContent(localActionFsPath, StandardCharsets.UTF_8);
cpovirka4d3da62019-05-02 14:27:33 -070090 assertThat(remoteActionFsPath.getFileSystem()).isSameInstanceAs(actionFs);
buchgr357cb1e2019-04-03 06:15:02 -070091 assertThat(actualRemoteContents).isEqualTo("remote contents");
92 assertThat(actualLocalContents).isEqualTo("local contents");
93 verify(inputFetcher)
94 .downloadFile(eq(remoteArtifact.getPath()), eq(inputs.getMetadata(remoteArtifact)));
95 verifyNoMoreInteractions(inputFetcher);
96 }
97
98 @Test
99 public void testCreateSymbolicLink() throws InterruptedException, IOException {
100 // arrange
101 ActionInputMap inputs = new ActionInputMap(1);
102 Artifact remoteArtifact = createRemoteArtifact("remote-file", "remote contents", inputs);
103 Path symlink = outputRoot.getRoot().getRelative("symlink");
104 FileSystem actionFs = newRemoteActionFileSystem(inputs);
105 doAnswer(
106 invocationOnMock -> {
107 FileSystemUtils.writeContent(
108 remoteArtifact.getPath(), StandardCharsets.UTF_8, "remote contents");
109 return Futures.immediateFuture(null);
110 })
111 .when(inputFetcher)
112 .downloadFile(eq(remoteArtifact.getPath()), eq(inputs.getMetadata(remoteArtifact)));
113
114 // act
115 Path symlinkActionFs = actionFs.getPath(symlink.getPathString());
116 symlinkActionFs.createSymbolicLink(actionFs.getPath(remoteArtifact.getPath().asFragment()));
117
118 // assert
cpovirka4d3da62019-05-02 14:27:33 -0700119 assertThat(symlinkActionFs.getFileSystem()).isSameInstanceAs(actionFs);
buchgr357cb1e2019-04-03 06:15:02 -0700120 verify(inputFetcher)
121 .downloadFile(eq(remoteArtifact.getPath()), eq(inputs.getMetadata(remoteArtifact)));
122 String symlinkTargetContents =
123 FileSystemUtils.readContent(symlinkActionFs, StandardCharsets.UTF_8);
124 assertThat(symlinkTargetContents).isEqualTo("remote contents");
125 verifyNoMoreInteractions(inputFetcher);
126 }
127
128 private FileSystem newRemoteActionFileSystem(ActionInputMap inputs) {
129 return new RemoteActionFileSystem(
130 fs,
131 execRoot.asFragment(),
132 outputRoot.getRoot().asPath().relativeTo(execRoot).getPathString(),
133 inputs,
134 inputFetcher);
135 }
136
137 /** Returns a remote artifact and puts its metadata into the action input map. */
138 private Artifact createRemoteArtifact(
139 String pathFragment, String contents, ActionInputMap inputs) {
140 Path p = outputRoot.getRoot().asPath().getRelative(pathFragment);
janakraea05602019-05-22 15:41:29 -0700141 Artifact a = ActionsTestUtil.createArtifact(outputRoot, p);
buchgr357cb1e2019-04-03 06:15:02 -0700142 byte[] b = contents.getBytes(StandardCharsets.UTF_8);
143 HashCode h = HASH_FUNCTION.getHashFunction().hashBytes(b);
144 FileArtifactValue f =
145 new RemoteFileArtifactValue(h.asBytes(), b.length, /* locationIndex= */ 1);
146 inputs.putWithNoDepOwner(a, f);
147 return a;
148 }
149
150 /** Returns a local artifact and puts its metadata into the action input map. */
151 private Artifact createLocalArtifact(String pathFragment, String contents, ActionInputMap inputs)
152 throws IOException {
153 Path p = outputRoot.getRoot().asPath().getRelative(pathFragment);
154 FileSystemUtils.writeContent(p, StandardCharsets.UTF_8, contents);
janakraea05602019-05-22 15:41:29 -0700155 Artifact a = ActionsTestUtil.createArtifact(outputRoot, p);
buchgr357cb1e2019-04-03 06:15:02 -0700156 inputs.putWithNoDepOwner(a, FileArtifactValue.create(a.getPath(), /* isShareable= */ true));
157 return a;
158 }
159}