blob: 986166bfe39c7921643377ac1d2b7c9b99bcf4d0 [file] [log] [blame]
// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.skyframe;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import com.google.devtools.build.lib.skyframe.ActionExecutionInactivityWatchdog.InactivityMonitor;
import com.google.devtools.build.lib.skyframe.ActionExecutionInactivityWatchdog.InactivityReporter;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for ActionExecutionInactivityWatchdog. */
@RunWith(JUnit4.class)
public class ActionExecutionInactivityWatchdogTest {
private void assertInactivityWatchdogReports(final boolean shouldReport) throws Exception {
// The monitor implementation below is a state machine. This variable indicates which state
// it is in.
final int[] monitorState = new int[] {0};
// Object that the test thread will wait on.
final Object monitorFinishedIndicator = new Object();
// Reported number of action completions in each call to waitForNextCompletion.
final int[] actionCompletions = new int[] {1, 0, 3, 0, 0, 0, 0, 2};
// Simulated delay of action completions in each call to waitForNextCompletion.
final int[] waits = new int[] {5, 10, 3, 10, 30, 60, 60, 1};
// Log of all Sleep.sleep and InactivityMonitor.waitForNextCompletion calls.
final List<String> sleepsAndWaits = new ArrayList<>();
// Mock monitor for this test.
InactivityMonitor monitor =
new InactivityMonitor() {
@Override
public int waitForNextCompletion(int timeoutSeconds) throws InterruptedException {
// Simulate the following sequence of events (see actionCompletions):
// 1. return in 5s (within timeout), 1 action completed; caller will sleep
// 2. return in 10s (after timeout), 0 action completed; caller will wait
// 3. return in 3s (within timeout), 3 actions completed (this is possible, since the
// waiting (thread doesn't necessarily wake up immediately); caller will sleep
// 4. return in 10s (after timeout), 0 action completed; caller will wait 30s
// 5. return in 30s (after timeout), 0 action completed still; caller will wait 60s
// 6. return in 60s (after timeout), 0 action completed still; caller will wait 60s
// 7. return in 60s (after timeout), 0 action completed still; caller will wait 60s
// 8. return in 1s (within timeout), 2 actions completed; caller will sleep, but we
// won't record that, because monitorState reached its maximum
synchronized (monitorFinishedIndicator) {
if (monitorState[0] >= actionCompletions.length) {
// Notify the test thread that the test is over.
monitorFinishedIndicator.notify();
return 1;
} else {
int index = monitorState[0];
sleepsAndWaits.add("wait:" + waits[index]);
++monitorState[0];
return actionCompletions[index];
}
}
}
@Override
public boolean hasStarted() {
return true;
}
@Override
public int getPending() {
int index = monitorState[0];
if (index >= actionCompletions.length) {
return 0;
}
int result = actionCompletions[index];
while (result == 0) {
++index;
result = actionCompletions[index];
}
return result;
}
};
final boolean[] didReportInactivity = new boolean[] {false};
InactivityReporter reporter =
new InactivityReporter() {
@Override
public void maybeReportInactivity() {
if (shouldReport) {
didReportInactivity[0] = true;
}
}
};
// Mock sleep object; just logs how much the caller's thread would've slept.
ActionExecutionInactivityWatchdog.Sleep sleep =
new ActionExecutionInactivityWatchdog.Sleep() {
@Override
public void sleep(int durationMilliseconds) throws InterruptedException {
if (monitorState[0] < actionCompletions.length) {
sleepsAndWaits.add("sleep:" + durationMilliseconds);
}
}
};
ActionExecutionInactivityWatchdog watchdog =
new ActionExecutionInactivityWatchdog(monitor, reporter, 0, sleep);
try {
synchronized (monitorFinishedIndicator) {
watchdog.start();
long startTime = System.currentTimeMillis();
boolean done = false;
while (!done) {
try {
monitorFinishedIndicator.wait(5000);
done = true;
assertWithMessage("test didn't finish under 5 seconds")
.that(System.currentTimeMillis() - startTime)
.isLessThan(5000L);
} catch (InterruptedException ie) {
// so-called Spurious Wakeup; ignore
}
}
}
} finally {
watchdog.stop();
}
assertThat(didReportInactivity[0]).isEqualTo(shouldReport);
assertThat(sleepsAndWaits)
.containsExactly(
"wait:5",
"sleep:1000",
"wait:10",
"wait:3",
"sleep:1000",
"wait:10",
"wait:30",
"wait:60",
"wait:60",
"wait:1")
.inOrder();
}
@Test
public void testInactivityWatchdogReportsWhenItShould() throws Exception {
assertInactivityWatchdogReports(true);
}
@Test
public void testInactivityWatchdogDoesNotReportWhenItShouldNot() throws Exception {
assertInactivityWatchdogReports(false);
}
}