| // Copyright 2012 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.testing.junit.runner.internal.junit4; |
| |
| import com.google.testing.junit.junit4.runner.RunNotifierWrapper; |
| import javax.inject.Inject; |
| import javax.inject.Singleton; |
| import org.junit.runner.Description; |
| import org.junit.runner.Request; |
| import org.junit.runner.Runner; |
| import org.junit.runner.notification.RunNotifier; |
| import org.junit.runner.notification.StoppedByUserException; |
| |
| /** |
| * Creates requests that can be cancelled. |
| */ |
| @Singleton |
| public class CancellableRequestFactory { |
| private boolean requestCreated; |
| private volatile ThreadSafeRunNotifier currentNotifier; |
| private volatile boolean cancelRequested = false; |
| |
| @Inject |
| public CancellableRequestFactory() {} |
| |
| /** |
| * Creates a request that can be cancelled. Can only be called once. |
| * |
| * @param delegate request to wrap |
| */ |
| public Request createRequest(Request delegate) { |
| if (requestCreated) { |
| throw new IllegalStateException("a request was already created"); |
| } |
| requestCreated = true; |
| |
| return new MemoizingRequest(delegate) { |
| @Override |
| Runner createRunner(Request delegate) { |
| return new CancellableRunner(delegate.getRunner()); |
| } |
| }; |
| } |
| |
| /** |
| * Cancels the request created by this request factory. |
| */ |
| public void cancelRun() { |
| cancelRequested = true; |
| RunNotifier notifier = currentNotifier; |
| if (notifier != null) { |
| notifier.pleaseStop(); |
| } |
| } |
| |
| |
| private class CancellableRunner extends Runner { |
| private final Runner delegate; |
| |
| public CancellableRunner(Runner delegate) { |
| this.delegate = delegate; |
| } |
| |
| @Override |
| public Description getDescription() { |
| return delegate.getDescription(); |
| } |
| |
| @Override |
| public void run(RunNotifier notifier) { |
| currentNotifier = new ThreadSafeRunNotifier(notifier); |
| if (cancelRequested) { |
| currentNotifier.pleaseStop(); |
| } |
| |
| try { |
| delegate.run(currentNotifier); |
| } catch (StoppedByUserException e) { |
| if (cancelRequested) { |
| throw new RuntimeException("Test run interrupted", e); |
| } |
| throw e; |
| } |
| } |
| } |
| |
| |
| private static class ThreadSafeRunNotifier extends RunNotifierWrapper { |
| private volatile boolean stopRequested; |
| |
| public ThreadSafeRunNotifier(RunNotifier delegate) { |
| super(delegate); |
| } |
| |
| /** |
| * {@inheritDoc}<p> |
| * |
| * The implementation is almost an exact copy of the version in |
| * {@code RunNotifier} but is thread-safe. |
| */ |
| @Override |
| public void fireTestStarted(Description description) throws StoppedByUserException { |
| if (stopRequested) { |
| throw new StoppedByUserException(); |
| } |
| getDelegate().fireTestStarted(description); |
| } |
| |
| /** |
| * {@inheritDoc}<p> |
| * |
| * This method is thread-safe. |
| */ |
| @Override |
| public void pleaseStop() { |
| stopRequested = true; |
| } |
| } |
| } |