blob: 531f25fb4f09b469e94b59a8b427eb980f110e6b [file] [log] [blame]
Kristina Chodorow335f0672015-11-16 23:19:13 +00001// Copyright 2015 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.skyframe;
16
17import static com.google.common.truth.Truth.assertThat;
jcater83130f42019-04-30 14:29:28 -070018import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
Kristina Chodorow335f0672015-11-16 23:19:13 +000019
20import com.google.common.collect.Sets;
21import com.google.devtools.build.lib.actions.Artifact;
22import com.google.devtools.build.lib.actions.BuildFailedException;
23import com.google.devtools.build.lib.actions.util.TestAction;
24import com.google.devtools.build.lib.testutil.BlazeTestUtils;
25import com.google.devtools.build.lib.vfs.FileSystemUtils;
Googler2633a592017-05-19 13:54:06 +020026import java.util.Collection;
27import java.util.Collections;
Florian Weikert92b22362015-12-03 10:17:18 +000028import org.junit.Test;
29import org.junit.runner.RunWith;
30import org.junit.runners.JUnit4;
31
Kristina Chodorow335f0672015-11-16 23:19:13 +000032/**
33 * Test suite for TimestampBuilder.
34 *
35 */
Florian Weikert92b22362015-12-03 10:17:18 +000036@RunWith(JUnit4.class)
Kristina Chodorow335f0672015-11-16 23:19:13 +000037public class TimestampBuilderTest extends TimestampBuilderTestCase {
38
Florian Weikert92b22362015-12-03 10:17:18 +000039 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +000040 public void testAmnesiacBuilderAlwaysRebuilds() throws Exception {
41 // [action] -> hello
42 Artifact hello = createDerivedArtifact("hello");
43 Button button = createActionButton(emptySet, Sets.newHashSet(hello));
44
45 button.pressed = false;
46 buildArtifacts(amnesiacBuilder(), hello);
lberkiaea56b32017-05-30 12:35:33 +020047 assertThat(button.pressed).isTrue(); // built
Kristina Chodorow335f0672015-11-16 23:19:13 +000048
49 button.pressed = false;
50 buildArtifacts(amnesiacBuilder(), hello);
lberkiaea56b32017-05-30 12:35:33 +020051 assertThat(button.pressed).isTrue(); // rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +000052 }
53
54 // If we re-use the same builder (even an "amnesiac" builder), it remembers
55 // which Actions it has already visited, and doesn't revisit them, even if
56 // they would otherwise be rebuilt.
57 //
58 // That is, Builders conflate traversal and dependency analysis, and don't
59 // revisit a node (traversal) even if it needs to be rebuilt (dependency
60 // analysis). We might want to separate these aspects.
Florian Weikert92b22362015-12-03 10:17:18 +000061 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +000062 public void testBuilderDoesntRevisitActions() throws Exception {
63 // [action] -> hello
64 Artifact hello = createDerivedArtifact("hello");
65 Counter counter = createActionCounter(emptySet, Sets.newHashSet(hello));
66
67 Builder amnesiacBuilder = amnesiacBuilder();
68
69 counter.count = 0;
70 buildArtifacts(amnesiacBuilder, hello, hello);
lberkiaea56b32017-05-30 12:35:33 +020071 assertThat(counter.count).isEqualTo(1); // built only once
Kristina Chodorow335f0672015-11-16 23:19:13 +000072 }
73
Florian Weikert92b22362015-12-03 10:17:18 +000074 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +000075 public void testBuildingExistingSourcefileSuceeds() throws Exception {
76 Artifact hello = createSourceArtifact("hello");
77 BlazeTestUtils.makeEmptyFile(hello.getPath());
78 buildArtifacts(cachingBuilder(), hello);
79 }
80
Florian Weikert92b22362015-12-03 10:17:18 +000081 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +000082 public void testBuildingNonexistentSourcefileFails() throws Exception {
83 reporter.removeHandler(failFastHandler);
84 Artifact hello = createSourceArtifact("hello");
jcater83130f42019-04-30 14:29:28 -070085 BuildFailedException e =
86 assertThrows(
87 "Expected input file to be missing",
88 BuildFailedException.class,
89 () -> buildArtifacts(cachingBuilder(), hello));
90 assertThat(e).hasMessageThat().isEqualTo("missing input file '" + hello.getPath() + "'");
Kristina Chodorow335f0672015-11-16 23:19:13 +000091 }
92
Florian Weikert92b22362015-12-03 10:17:18 +000093 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +000094 public void testCachingBuilderCachesUntilReset() throws Exception {
95 // [action] -> hello
96 Artifact hello = createDerivedArtifact("hello");
97 Button button = createActionButton(emptySet, Sets.newHashSet(hello));
98
99 button.pressed = false;
100 buildArtifacts(cachingBuilder(), hello);
lberkiaea56b32017-05-30 12:35:33 +0200101 assertThat(button.pressed).isTrue(); // built
Kristina Chodorow335f0672015-11-16 23:19:13 +0000102
103 button.pressed = false;
104 buildArtifacts(cachingBuilder(), hello);
lberkiaea56b32017-05-30 12:35:33 +0200105 assertThat(button.pressed).isFalse(); // not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000106
107 inMemoryCache.reset();
108
109 button.pressed = false;
110 buildArtifacts(cachingBuilder(), hello);
lberkiaea56b32017-05-30 12:35:33 +0200111 assertThat(button.pressed).isTrue(); // rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000112 }
113
Florian Weikert92b22362015-12-03 10:17:18 +0000114 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +0000115 public void testUnneededInputs() throws Exception {
116 Artifact hello = createSourceArtifact("hello");
117 BlazeTestUtils.makeEmptyFile(hello.getPath());
118 Artifact optional = createSourceArtifact("hello.optional");
119 Artifact goodbye = createDerivedArtifact("goodbye");
120 Button button = createActionButton(Sets.newHashSet(hello, optional), Sets.newHashSet(goodbye));
121
122 button.pressed = false;
123 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200124 assertThat(button.pressed).isTrue(); // built
Kristina Chodorow335f0672015-11-16 23:19:13 +0000125
126 button.pressed = false;
127 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200128 assertThat(button.pressed).isFalse(); // not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000129
130 BlazeTestUtils.makeEmptyFile(optional.getPath());
131
132 button.pressed = false;
133 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200134 assertThat(button.pressed).isTrue(); // built
Kristina Chodorow335f0672015-11-16 23:19:13 +0000135
136 button.pressed = false;
137 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200138 assertThat(button.pressed).isFalse(); // not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000139
140 optional.getPath().delete();
141
142 button.pressed = false;
143 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200144 assertThat(button.pressed).isTrue(); // built
Kristina Chodorow335f0672015-11-16 23:19:13 +0000145
146 button.pressed = false;
147 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200148 assertThat(button.pressed).isFalse(); // not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000149 }
150
Florian Weikert92b22362015-12-03 10:17:18 +0000151 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +0000152 public void testModifyingInputCausesActionReexecution() throws Exception {
153 // hello -> [action] -> goodbye
154 Artifact hello = createSourceArtifact("hello");
155 BlazeTestUtils.makeEmptyFile(hello.getPath());
156 Artifact goodbye = createDerivedArtifact("goodbye");
157 Button button = createActionButton(Sets.newHashSet(hello), Sets.newHashSet(goodbye));
158
159 button.pressed = false;
160 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200161 assertThat(button.pressed).isTrue(); // built
Kristina Chodorow335f0672015-11-16 23:19:13 +0000162
163 button.pressed = false;
164 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200165 assertThat(button.pressed).isFalse(); // not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000166
Janak Ramakrishnan08b0f7f2016-07-13 17:00:59 +0000167 hello.getPath().setWritable(true);
168 FileSystemUtils.writeContentAsLatin1(hello.getPath(), "new content");
Kristina Chodorow335f0672015-11-16 23:19:13 +0000169
170 button.pressed = false;
171 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200172 assertThat(button.pressed).isTrue(); // rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000173
174 button.pressed = false;
175 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200176 assertThat(button.pressed).isFalse(); // not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000177 }
178
Florian Weikert92b22362015-12-03 10:17:18 +0000179 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +0000180 public void testOnlyModifyingInputContentCausesReexecution() throws Exception {
181 // hello -> [action] -> goodbye
182 Artifact hello = createSourceArtifact("hello");
183 // touch file to create the directory structure
184 BlazeTestUtils.makeEmptyFile(hello.getPath());
185 FileSystemUtils.writeContentAsLatin1(hello.getPath(), "content1");
186
187 Artifact goodbye = createDerivedArtifact("goodbye");
188 Button button = createActionButton(Sets.newHashSet(hello), Sets.newHashSet(goodbye));
189
190 button.pressed = false;
191 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200192 assertThat(button.pressed).isTrue(); // built
Kristina Chodorow335f0672015-11-16 23:19:13 +0000193
194 button.pressed = false;
195 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200196 assertThat(button.pressed).isFalse(); // not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000197
198 FileSystemUtils.touchFile(hello.getPath());
199
200 button.pressed = false;
201 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200202 assertThat(button.pressed).isFalse(); // still not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000203
204 FileSystemUtils.writeContentAsLatin1(hello.getPath(), "content2");
205
206 button.pressed = false;
207 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200208 assertThat(button.pressed).isTrue(); // rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000209
210 button.pressed = false;
211 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200212 assertThat(button.pressed).isFalse(); // not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000213 }
214
Florian Weikert92b22362015-12-03 10:17:18 +0000215 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +0000216 public void testModifyingOutputCausesActionReexecution() throws Exception {
217 // [action] -> hello
218 Artifact hello = createDerivedArtifact("hello");
219 Button button = createActionButton(emptySet, Sets.newHashSet(hello));
220
221 button.pressed = false;
222 buildArtifacts(cachingBuilder(), hello);
lberkiaea56b32017-05-30 12:35:33 +0200223 assertThat(button.pressed).isTrue(); // built
Kristina Chodorow335f0672015-11-16 23:19:13 +0000224
225 button.pressed = false;
226 buildArtifacts(cachingBuilder(), hello);
lberkiaea56b32017-05-30 12:35:33 +0200227 assertThat(button.pressed).isFalse(); // not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000228
Janak Ramakrishnan08b0f7f2016-07-13 17:00:59 +0000229 // Changing the *output* file 'hello' causes 'action' to re-execute, to make things consistent
230 // again.
231 hello.getPath().setWritable(true);
232 FileSystemUtils.writeContentAsLatin1(hello.getPath(), "new content");
Kristina Chodorow335f0672015-11-16 23:19:13 +0000233
234 button.pressed = false;
235 buildArtifacts(cachingBuilder(), hello);
lberkiaea56b32017-05-30 12:35:33 +0200236 assertThat(button.pressed).isTrue(); // rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000237
238 button.pressed = false;
239 buildArtifacts(cachingBuilder(), hello);
lberkiaea56b32017-05-30 12:35:33 +0200240 assertThat(button.pressed).isFalse(); // not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000241 }
242
Florian Weikert92b22362015-12-03 10:17:18 +0000243 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +0000244 public void testBuildingTransitivePrerequisites() throws Exception {
245 // hello -> [action1] -> wazuup -> [action2] -> goodbye
246 Artifact hello = createSourceArtifact("hello");
247 BlazeTestUtils.makeEmptyFile(hello.getPath());
248 Artifact wazuup = createDerivedArtifact("wazuup");
Janak Ramakrishnan08b0f7f2016-07-13 17:00:59 +0000249 Button button1 = new Button();
250 registerAction(new CopyingAction(button1, hello, wazuup));
Kristina Chodorow335f0672015-11-16 23:19:13 +0000251 Artifact goodbye = createDerivedArtifact("goodbye");
252 Button button2 = createActionButton(Sets.newHashSet(wazuup), Sets.newHashSet(goodbye));
253
254 button1.pressed = button2.pressed = false;
255 buildArtifacts(cachingBuilder(), wazuup);
lberkiaea56b32017-05-30 12:35:33 +0200256 assertThat(button1.pressed).isTrue(); // built wazuup
257 assertThat(button2.pressed).isFalse(); // goodbye not built
Kristina Chodorow335f0672015-11-16 23:19:13 +0000258
259 button1.pressed = button2.pressed = false;
260 buildArtifacts(cachingBuilder(), wazuup);
lberkiaea56b32017-05-30 12:35:33 +0200261 assertThat(button1.pressed).isFalse(); // wazuup not rebuilt
262 assertThat(button2.pressed).isFalse(); // goodbye not built
Kristina Chodorow335f0672015-11-16 23:19:13 +0000263
264 button1.pressed = button2.pressed = false;
265 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200266 assertThat(button1.pressed).isFalse(); // wazuup not rebuilt
267 assertThat(button2.pressed).isTrue(); // built goodbye
Kristina Chodorow335f0672015-11-16 23:19:13 +0000268
269 button1.pressed = button2.pressed = false;
270 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200271 assertThat(button1.pressed).isFalse(); // wazuup not rebuilt
272 assertThat(button2.pressed).isFalse(); // goodbye not rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000273
Janak Ramakrishnan08b0f7f2016-07-13 17:00:59 +0000274 hello.getPath().setWritable(true);
275 FileSystemUtils.writeContentAsLatin1(hello.getPath(), "new content");
Kristina Chodorow335f0672015-11-16 23:19:13 +0000276
277 button1.pressed = button2.pressed = false;
278 buildArtifacts(cachingBuilder(), goodbye);
lberkiaea56b32017-05-30 12:35:33 +0200279 assertThat(button1.pressed).isTrue(); // hello rebuilt
280 assertThat(button2.pressed).isTrue(); // goodbye rebuilt
Kristina Chodorow335f0672015-11-16 23:19:13 +0000281 }
282
Florian Weikert92b22362015-12-03 10:17:18 +0000283 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +0000284 public void testWillNotRebuildActionsWithEmptyListOfInputsSpuriously()
285 throws Exception {
286
287 Artifact anOutputFile = createDerivedArtifact("anOutputFile");
288 Artifact anotherOutputFile = createDerivedArtifact("anotherOutputFile");
289 Collection<Artifact> noInputs = Collections.emptySet();
290
291 Button aButton = createActionButton(noInputs, Sets.newHashSet(anOutputFile));
292 Button anotherButton = createActionButton(noInputs,
293 Sets.newHashSet(anotherOutputFile));
294
295 buildArtifacts(cachingBuilder(), anOutputFile, anotherOutputFile);
296
lberkiaea56b32017-05-30 12:35:33 +0200297 assertThat(aButton.pressed).isTrue();
298 assertThat(anotherButton.pressed).isTrue();
Kristina Chodorow335f0672015-11-16 23:19:13 +0000299
300 aButton.pressed = anotherButton.pressed = false;
301
302 buildArtifacts(cachingBuilder(), anOutputFile, anotherOutputFile);
303
lberkiaea56b32017-05-30 12:35:33 +0200304 assertThat(aButton.pressed).isFalse();
305 assertThat(anotherButton.pressed).isFalse();
Kristina Chodorow335f0672015-11-16 23:19:13 +0000306 }
307
Florian Weikert92b22362015-12-03 10:17:18 +0000308 @Test
Kristina Chodorow335f0672015-11-16 23:19:13 +0000309 public void testMissingSourceFileIsAnError() throws Exception {
310 // A missing input to an action must be treated as an error because there's
311 // a risk that the action that consumes it will succeed, but with a
312 // different behavior (imagine that it globs over the directory, for
313 // example). It's not ok to simply try the action and let the action
314 // report "input file not found".
315 //
316 // (However, there are exceptions to this principle: C++ compilation
317 // actions may depend on non-existent headers from stale .d files. We need
318 // to allow the action to proceed to execution in this case.)
319
320 reporter.removeHandler(failFastHandler);
321 Artifact in = createSourceArtifact("in"); // doesn't exist
322 Artifact out = createDerivedArtifact("out");
323
324 registerAction(new TestAction(TestAction.NO_EFFECT, Collections.singleton(in),
325 Collections.singleton(out)));
326
jcater83130f42019-04-30 14:29:28 -0700327 BuildFailedException e =
328 assertThrows(BuildFailedException.class, () -> buildArtifacts(amnesiacBuilder(), out));
329 assertThat(e).hasMessageThat().contains("1 input file(s) do not exist");
Kristina Chodorow335f0672015-11-16 23:19:13 +0000330 }
331}