/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.beam.fn.harness.test; import com.google.common.util.concurrent.ForwardingExecutorService; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; /** * A {@link TestRule} that validates that all submitted tasks finished and were completed. This * allows for testing that tasks have exercised the appropriate shutdown logic. */ public class TestExecutors { public static TestExecutorService from(Supplier<ExecutorService> executorServiceSuppler) { return new FromSupplier(executorServiceSuppler); } /** A union of the {@link ExecutorService} and {@link TestRule} interfaces. */ public interface TestExecutorService extends ExecutorService, TestRule {} private static class FromSupplier extends ForwardingExecutorService implements TestExecutorService { private final Supplier<ExecutorService> executorServiceSupplier; private ExecutorService delegate; private FromSupplier(Supplier<ExecutorService> executorServiceSupplier) { this.executorServiceSupplier = executorServiceSupplier; } @Override public Statement apply(Statement statement, Description arg1) { return new Statement() { @Override public void evaluate() throws Throwable { Throwable thrown = null; delegate = executorServiceSupplier.get(); try { statement.evaluate(); } catch (Throwable t) { thrown = t; } shutdown(); if (!awaitTermination(5, TimeUnit.SECONDS)) { shutdownNow(); IllegalStateException e = new IllegalStateException("Test executor failed to shutdown cleanly."); if (thrown != null) { thrown.addSuppressed(e); } else { thrown = e; } } if (thrown != null) { throw thrown; } } }; } @Override protected ExecutorService delegate() { return delegate; } } }