blob: 80a3edd8d88ff9e0d24fb38e741dcf51e8e6a2d5 [file] [log] [blame]
// Copyright 2024 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.server;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Uninterruptibles;
import java.time.Duration;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Test for {@link IdleTaskManager}. */
@RunWith(JUnit4.class)
public class IdleTaskManagerTest {
@Test
public void registeredTask_taskSuccessful() throws Exception {
CountDownLatch taskRunning = new CountDownLatch(1);
AtomicBoolean taskDone = new AtomicBoolean();
IdleTask task =
makeTask(
"task",
() -> {
taskRunning.countDown();
Uninterruptibles.sleepUninterruptibly(Duration.ofMillis(200));
taskDone.set(true);
});
IdleTaskManager manager = new IdleTaskManager(ImmutableList.of(task));
manager.idle();
taskRunning.await(); // wait for task to start
ImmutableList<IdleTask.Result> stats = manager.busy();
assertThat(taskDone.get()).isTrue();
assertThat(stats.stream().map(s -> new IdleTask.Result(s.name(), s.status(), Duration.ZERO)))
.containsExactly(new IdleTask.Result("task", IdleTask.Status.SUCCESS, Duration.ZERO));
}
@Test
public void registeredTask_taskFailed() throws Exception {
CountDownLatch taskRunning = new CountDownLatch(1);
AtomicBoolean taskDone = new AtomicBoolean();
IdleTask task =
makeTask(
"task",
() -> {
taskRunning.countDown();
Uninterruptibles.sleepUninterruptibly(Duration.ofMillis(200));
try {
throw new IdleTaskException(new RuntimeException("failed"));
} finally {
taskDone.set(true);
}
});
IdleTaskManager manager = new IdleTaskManager(ImmutableList.of(task));
manager.idle();
taskRunning.await(); // wait for task to start
ImmutableList<IdleTask.Result> stats = manager.busy();
assertThat(taskDone.get()).isTrue();
assertThat(stats.stream().map(s -> new IdleTask.Result(s.name(), s.status(), Duration.ZERO)))
.containsExactly(new IdleTask.Result("task", IdleTask.Status.FAILURE, Duration.ZERO));
}
@Test
public void registeredTask_taskInterrupted() throws Exception {
CountDownLatch taskRunning = new CountDownLatch(1);
AtomicBoolean taskInterrupted = new AtomicBoolean();
IdleTask task =
makeTask(
"task",
() -> {
taskRunning.countDown();
try {
Thread.sleep(Duration.ofDays(1));
} catch (InterruptedException e) {
taskInterrupted.set(true);
throw e;
}
});
IdleTaskManager manager = new IdleTaskManager(ImmutableList.of(task));
manager.idle();
taskRunning.await(); // wait for task to start
ImmutableList<IdleTask.Result> stats = manager.busy();
assertThat(taskInterrupted.get()).isTrue();
assertThat(stats.stream().map(s -> new IdleTask.Result(s.name(), s.status(), Duration.ZERO)))
.containsExactly(new IdleTask.Result("task", IdleTask.Status.INTERRUPTED, Duration.ZERO));
}
@Test
public void registeredTask_taskNotStarted() throws Exception {
AtomicBoolean taskStarted = new AtomicBoolean();
IdleTask task = makeTask("task", Duration.ofDays(1), () -> taskStarted.set(true));
IdleTaskManager manager = new IdleTaskManager(ImmutableList.of(task));
manager.idle();
Thread.sleep(Duration.ofMillis(200)); // make it more likely that a bug will be caught
ImmutableList<IdleTask.Result> stats = manager.busy();
assertThat(taskStarted.get()).isFalse();
assertThat(stats)
.containsExactly(new IdleTask.Result("task", IdleTask.Status.NOT_STARTED, Duration.ZERO));
}
@Test
public void registeredTask_multipleTasks() throws Exception {
AtomicBoolean taskRunning = new AtomicBoolean(false);
AtomicBoolean concurrentTasksDetected = new AtomicBoolean(false);
CountDownLatch finishedTasks = new CountDownLatch(3);
ImmutableList<IdleTask> tasks =
ImmutableList.of(
makeTask("a", () -> runTask(taskRunning, concurrentTasksDetected, finishedTasks)),
makeTask("b", () -> runTask(taskRunning, concurrentTasksDetected, finishedTasks)),
makeTask("c", () -> runTask(taskRunning, concurrentTasksDetected, finishedTasks)));
IdleTaskManager manager = new IdleTaskManager(tasks);
manager.idle();
finishedTasks.await();
ImmutableList<IdleTask.Result> stats = manager.busy();
assertThat(concurrentTasksDetected.get()).isFalse();
assertThat(stats.stream().map(s -> new IdleTask.Result(s.name(), s.status(), Duration.ZERO)))
.containsExactly(
new IdleTask.Result("a", IdleTask.Status.SUCCESS, Duration.ZERO),
new IdleTask.Result("b", IdleTask.Status.SUCCESS, Duration.ZERO),
new IdleTask.Result("c", IdleTask.Status.SUCCESS, Duration.ZERO))
.inOrder();
}
private static final void runTask(
AtomicBoolean taskRunning,
AtomicBoolean concurrentTasksDetected,
CountDownLatch finishedTasks)
throws InterruptedException {
if (!taskRunning.compareAndSet(false, true)) {
concurrentTasksDetected.set(true);
}
Thread.sleep(Duration.ofMillis(200)); // make it more likely that a bug will be caught
finishedTasks.countDown();
if (!taskRunning.compareAndSet(true, false)) {
concurrentTasksDetected.set(true);
}
}
private static IdleTask makeTask(String name, IdleTaskRunnable runnable) {
return makeTask(name, Duration.ZERO, runnable);
}
private static IdleTask makeTask(String name, Duration delay, IdleTaskRunnable runnable) {
return new IdleTask() {
@Override
public String displayName() {
return name;
}
@Override
public Duration delay() {
return delay;
}
@Override
public void run() throws IdleTaskException, InterruptedException {
runnable.run();
}
};
}
private interface IdleTaskRunnable {
void run() throws IdleTaskException, InterruptedException;
}
}