| // 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; |
| } |
| } |