blob: a6eafa382a2b95c257eb4c2af4bbb2edcdb7c752 [file] [log] [blame]
Michajlo Matijkiwaa058282015-09-28 22:13:27 +00001// Copyright 2014 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.skyframe;
15
16import static com.google.common.truth.Truth.assertThat;
17
18import com.google.common.collect.ImmutableList;
19import com.google.common.collect.Iterables;
20import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
21import com.google.devtools.build.lib.collect.nestedset.Order;
22import com.google.devtools.build.skyframe.SkyFunctionException.ReifiedSkyFunctionException;
janakrbfdad902017-05-03 21:38:28 +020023import java.io.IOException;
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000024import org.junit.Test;
25import org.junit.runner.RunWith;
26import org.junit.runners.JUnit4;
27
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000028/** Tests for the non-trivial creation logic of {@link ErrorInfo}. */
29@RunWith(JUnit4.class)
30public class ErrorInfoTest {
31
32 /** Dummy SkyFunctionException implementation for the sake of testing. */
33 private static class DummySkyFunctionException extends SkyFunctionException {
34 private final boolean isCatastrophic;
35
36 public DummySkyFunctionException(Exception cause, boolean isTransient,
37 boolean isCatastrophic) {
38 super(cause, isTransient ? Transience.TRANSIENT : Transience.PERSISTENT);
39 this.isCatastrophic = isCatastrophic;
40 }
41
42 @Override
43 public boolean isCatastrophic() {
44 return isCatastrophic;
45 }
46 }
47
Nathan Harmata97731682015-12-09 23:36:22 +000048 private void runTestFromException(boolean isDirectlyTransient, boolean isTransitivelyTransient) {
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000049 Exception exception = new IOException("ehhhhh");
janakr5fb2a482018-03-02 17:48:57 -080050 SkyKey causeOfException = GraphTester.toSkyKey("CAUSE, 1234");
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000051 DummySkyFunctionException dummyException =
Nathan Harmata97731682015-12-09 23:36:22 +000052 new DummySkyFunctionException(exception, isDirectlyTransient, /*isCatastrophic=*/ false);
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000053
54 ErrorInfo errorInfo = ErrorInfo.fromException(
Nathan Harmata97731682015-12-09 23:36:22 +000055 new ReifiedSkyFunctionException(dummyException, causeOfException),
56 isTransitivelyTransient);
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000057
58 assertThat(errorInfo.getRootCauses()).containsExactly(causeOfException);
59 assertThat(errorInfo.getException()).isSameAs(exception);
60 assertThat(errorInfo.getRootCauseOfException()).isSameAs(causeOfException);
61 assertThat(errorInfo.getCycleInfo()).isEmpty();
nharmatabea67e92017-06-16 00:26:27 +020062 assertThat(errorInfo.isDirectlyTransient()).isEqualTo(isDirectlyTransient);
63 assertThat(errorInfo.isTransitivelyTransient()).isEqualTo(
64 isDirectlyTransient || isTransitivelyTransient);
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000065 assertThat(errorInfo.isCatastrophic()).isFalse();
66 }
67
68 @Test
Nathan Harmata97731682015-12-09 23:36:22 +000069 public void testFromException_NonTransient() {
70 runTestFromException(/*isDirectlyTransient=*/ false, /*isTransitivelyTransient= */ false);
71 }
72
73 @Test
74 public void testFromException_DirectlyTransient() {
75 runTestFromException(/*isDirectlyTransient=*/ true, /*isTransitivelyTransient= */ false);
76 }
77
78 @Test
79 public void testFromException_TransitivelyTransient() {
80 runTestFromException(/*isDirectlyTransient=*/ false, /*isTransitivelyTransient= */ true);
81 }
82
83 @Test
84 public void testFromException_DirectlyAndTransitivelyTransient() {
85 runTestFromException(/*isDirectlyTransient=*/ true, /*isTransitivelyTransient= */ true);
86 }
87
88 @Test
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000089 public void testFromCycle() {
Janak Ramakrishnanf745e992016-03-03 08:08:50 +000090 CycleInfo cycle =
91 new CycleInfo(
janakr5fb2a482018-03-02 17:48:57 -080092 ImmutableList.of(GraphTester.toSkyKey("PATH, 1234")),
93 ImmutableList.of(GraphTester.toSkyKey("CYCLE, 4321")));
Michajlo Matijkiwaa058282015-09-28 22:13:27 +000094
95 ErrorInfo errorInfo = ErrorInfo.fromCycle(cycle);
96
97 assertThat(errorInfo.getRootCauses()).isEmpty();
98 assertThat(errorInfo.getException()).isNull();
99 assertThat(errorInfo.getRootCauseOfException()).isNull();
nharmatabea67e92017-06-16 00:26:27 +0200100 assertThat(errorInfo.isTransitivelyTransient()).isFalse();
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000101 assertThat(errorInfo.isCatastrophic()).isFalse();
102 }
103
104 @Test
105 public void testFromChildErrors() {
Janak Ramakrishnanf745e992016-03-03 08:08:50 +0000106 CycleInfo cycle =
107 new CycleInfo(
janakr5fb2a482018-03-02 17:48:57 -0800108 ImmutableList.of(GraphTester.toSkyKey("PATH, 1234")),
109 ImmutableList.of(GraphTester.toSkyKey("CYCLE, 4321")));
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000110 ErrorInfo cycleErrorInfo = ErrorInfo.fromCycle(cycle);
111
112 Exception exception1 = new IOException("ehhhhh");
janakr5fb2a482018-03-02 17:48:57 -0800113 SkyKey causeOfException1 = GraphTester.toSkyKey("CAUSE1, 1234");
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000114 DummySkyFunctionException dummyException1 =
115 new DummySkyFunctionException(exception1, /*isTransient=*/ true, /*isCatastrophic=*/ false);
116 ErrorInfo exceptionErrorInfo1 = ErrorInfo.fromException(
Nathan Harmata97731682015-12-09 23:36:22 +0000117 new ReifiedSkyFunctionException(dummyException1, causeOfException1),
118 /*isTransitivelyTransient=*/ false);
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000119
120 // N.B this ErrorInfo will be catastrophic.
121 Exception exception2 = new IOException("blahhhhh");
janakr5fb2a482018-03-02 17:48:57 -0800122 SkyKey causeOfException2 = GraphTester.toSkyKey("CAUSE2, 5678");
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000123 DummySkyFunctionException dummyException2 =
Nathan Harmata97731682015-12-09 23:36:22 +0000124 new DummySkyFunctionException(exception2, /*isTransient=*/ false, /*isCatastrophic=*/ true);
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000125 ErrorInfo exceptionErrorInfo2 = ErrorInfo.fromException(
Nathan Harmata97731682015-12-09 23:36:22 +0000126 new ReifiedSkyFunctionException(dummyException2, causeOfException2),
127 /*isTransitivelyTransient=*/ false);
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000128
janakr5fb2a482018-03-02 17:48:57 -0800129 SkyKey currentKey = GraphTester.toSkyKey("CURRENT, 9876");
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000130
131 ErrorInfo errorInfo = ErrorInfo.fromChildErrors(
132 currentKey, ImmutableList.of(cycleErrorInfo, exceptionErrorInfo1, exceptionErrorInfo2));
133
134 assertThat(errorInfo.getRootCauses()).containsExactly(causeOfException1, causeOfException2);
135
136 // For simplicity we test the current implementation detail that we choose the first non-null
137 // (exception, cause) pair that we encounter. This isn't necessarily a requirement of the
138 // interface, but it makes the test convenient and is a way to document the current behavior.
139 assertThat(errorInfo.getException()).isSameAs(exception1);
140 assertThat(errorInfo.getRootCauseOfException()).isSameAs(causeOfException1);
141
142 assertThat(errorInfo.getCycleInfo()).containsExactly(
143 new CycleInfo(
144 ImmutableList.of(currentKey, Iterables.getOnlyElement(cycle.getPathToCycle())),
145 cycle.getCycle()));
nharmatabea67e92017-06-16 00:26:27 +0200146 assertThat(errorInfo.isTransitivelyTransient()).isTrue();
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000147 assertThat(errorInfo.isCatastrophic()).isTrue();
148 }
149
150 @Test
151 public void testCannotCreateErrorInfoWithoutExceptionOrCycle() {
152 try {
153 new ErrorInfo(
154 NestedSetBuilder.<SkyKey>emptySet(Order.COMPILE_ORDER),
155 /*exception=*/ null,
156 /*rootCauseOfException=*/ null,
157 ImmutableList.<CycleInfo>of(),
158 false,
nharmatabea67e92017-06-16 00:26:27 +0200159 false,
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000160 false);
161 } catch (IllegalStateException e) {
162 // Brittle, but confirms we failed for the right reason.
163 assertThat(e)
diamondmad04da62019-03-19 09:54:50 -0700164 .hasMessageThat()
165 .isEqualTo("At least one of exception and cycles must be non-null/empty, respectively");
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000166 }
167 }
168
169 @Test
170 public void testCannotCreateErrorInfoWithExceptionButNoRootCause() {
171 try {
172 new ErrorInfo(
173 NestedSetBuilder.<SkyKey>emptySet(Order.COMPILE_ORDER),
174 new IOException("foo"),
175 /*rootCauseOfException=*/ null,
176 ImmutableList.<CycleInfo>of(),
177 false,
nharmatabea67e92017-06-16 00:26:27 +0200178 false,
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000179 false);
180 } catch (IllegalStateException e) {
181 // Brittle, but confirms we failed for the right reason.
lberkiaea56b32017-05-30 12:35:33 +0200182 assertThat(e)
183 .hasMessageThat()
Michajlo Matijkiwaa058282015-09-28 22:13:27 +0000184 .startsWith("exception and rootCauseOfException must both be null or non-null");
185 }
186 }
187}