| // Copyright 2014 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.events.util; |
| |
| import com.google.common.eventbus.EventBus; |
| import com.google.devtools.build.lib.events.Event; |
| import com.google.devtools.build.lib.events.EventCollector; |
| import com.google.devtools.build.lib.events.EventHandler; |
| import com.google.devtools.build.lib.events.EventKind; |
| import com.google.devtools.build.lib.events.PrintingEventHandler; |
| import com.google.devtools.build.lib.events.Reporter; |
| import com.google.devtools.build.lib.testutil.MoreAsserts; |
| import com.google.devtools.build.lib.util.io.OutErr; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * An apparatus for reporting / collecting events. |
| */ |
| public final class EventCollectionApparatus { |
| private EventCollector eventCollector; |
| private Reporter reporter; |
| private PrintingEventHandler printingEventHandler; |
| |
| private boolean failFast; |
| private List<EventHandler> handlers = new ArrayList<>(); |
| |
| /** |
| * Determine which events the {@link #collector()} created by this apparatus |
| * will collect. Default: {@link EventKind#ERRORS_AND_WARNINGS}. |
| */ |
| public EventCollectionApparatus(Set<EventKind> mask) { |
| eventCollector = new EventCollector(mask); |
| printingEventHandler = new PrintingEventHandler(EventKind.ERRORS_AND_WARNINGS_AND_OUTPUT); |
| reporter = new Reporter(new EventBus(), eventCollector, printingEventHandler); |
| this.setFailFast(true); |
| } |
| |
| public EventCollectionApparatus() { |
| this(EventKind.ERRORS_WARNINGS_AND_INFO); |
| } |
| |
| public void clear() { |
| eventCollector.clear(); |
| } |
| |
| public void initExternal(Reporter reporter) { |
| // TODO(ulfjack): Changes to the EventCollectionApparatus are not reflected in the external |
| // reporter, i.e., this is a one-shot change. Maybe we should store the external reporter here? |
| reporter.addHandler(eventCollector); |
| reporter.addHandler(printingEventHandler); |
| for (EventHandler handler : handlers) { |
| reporter.addHandler(handler); |
| } |
| if (failFast) { |
| reporter.addHandler(FAIL_FAST_HANDLER); |
| } |
| } |
| |
| /** |
| * Determine whether the {#link reporter()} created by this apparatus will |
| * fail fast, that is, throw an exception whenever we encounter an event of |
| * matching {@link EventKind#ERRORS_AND_WARNINGS}. |
| * Default: {@code true}. |
| */ |
| public void setFailFast(boolean failFast) { |
| this.failFast = failFast; |
| if (failFast) { |
| reporter.addHandler(FAIL_FAST_HANDLER); |
| } else { |
| reporter.removeHandler(FAIL_FAST_HANDLER); |
| } |
| } |
| |
| public void addHandler(EventHandler eventHandler) { |
| reporter.addHandler(eventHandler); |
| handlers.add(eventHandler); |
| } |
| |
| /** An exception thrown by {@link #FAIL_FAST_HANDLER}. */ |
| // TODO(bazel-team): Possibly extend RuntimeException instead of IllegalArgumentException. |
| public static class FailFastException extends IllegalArgumentException { |
| public FailFastException(String s) { |
| super(s); |
| } |
| } |
| |
| /** |
| * A handler that immediately throws {@link FailFastException} whenever an error or warning |
| * occurs. |
| * |
| * <p>We do not reuse an existing unchecked exception type, because callers (e.g., test |
| * assertions) need to be able to distinguish between organically occurring exceptions and |
| * exceptions thrown by this handler. |
| */ |
| private static final EventHandler FAIL_FAST_HANDLER = |
| new EventHandler() { |
| @Override |
| public void handle(Event event) { |
| if (EventKind.ERRORS_AND_WARNINGS.contains(event.getKind())) { |
| throw new FailFastException(event.toString()); |
| } |
| } |
| }; |
| |
| /** |
| * @return the event reporter for this apparatus |
| */ |
| public Reporter reporter() { |
| return reporter; |
| } |
| |
| /** |
| * @return the event collector for this apparatus. |
| */ |
| public EventCollector collector() { |
| return eventCollector; |
| } |
| |
| public Iterable<Event> infos() { |
| return eventCollector.filtered(EventKind.INFO); |
| } |
| |
| public Iterable<Event> errors() { |
| return eventCollector.filtered(EventKind.ERROR); |
| } |
| |
| public Iterable<Event> warnings() { |
| return eventCollector.filtered(EventKind.WARNING); |
| } |
| |
| /** |
| * Redirects all output to the specified OutErr stream pair. |
| * Returns the previous OutErr. |
| */ |
| public OutErr setOutErr(OutErr outErr) { |
| return printingEventHandler.setOutErr(outErr); |
| } |
| |
| /** |
| * Utility method: Asserts that the {@link #collector()} has not collected |
| * any warnings or errors. |
| */ |
| public void assertNoWarningsOrErrors() { |
| MoreAsserts.assertNoEvents(warnings()); |
| MoreAsserts.assertNoEvents(errors()); |
| } |
| |
| /** |
| * Utility method: Assert that the {@link #collector()} has received an |
| * info message with the {@code expectedMessage}. |
| */ |
| public Event assertContainsInfo(String expectedMessage) { |
| return MoreAsserts.assertContainsEvent(eventCollector, expectedMessage, EventKind.INFO); |
| } |
| |
| /** |
| * Utility method: Assert that the {@link #collector()} has received an |
| * error with the {@code expectedMessage}. |
| */ |
| public Event assertContainsError(String expectedMessage) { |
| return MoreAsserts.assertContainsEvent(eventCollector, expectedMessage, EventKind.ERROR); |
| } |
| |
| /** |
| * Utility method: Assert that the {@link #collector()} has received a |
| * warning with the {@code expectedMessage}. |
| */ |
| public Event assertContainsWarning(String expectedMessage) { |
| return MoreAsserts.assertContainsEvent(eventCollector, expectedMessage, EventKind.WARNING); |
| } |
| |
| /** |
| * Utility method: Assert that the {@link #collector()} has received a |
| * debug message with the {@code expectedMessage}. |
| */ |
| public Event assertContainsDebug(String expectedMessage) { |
| return MoreAsserts.assertContainsEvent(eventCollector, expectedMessage, EventKind.DEBUG); |
| } |
| |
| /** |
| * Utility method: Assert that the {@link #collector()} has received an event of the given type |
| * and with the {@code expectedMessage}. |
| */ |
| public Event assertContainsEvent(EventKind kind, String expectedMessage) { |
| return MoreAsserts.assertContainsEvent(eventCollector, expectedMessage, kind); |
| } |
| |
| public List<Event> assertContainsEventWithFrequency(String expectedMessage, |
| int expectedFrequency) { |
| return MoreAsserts.assertContainsEventWithFrequency(eventCollector, expectedMessage, |
| expectedFrequency); |
| } |
| |
| /** |
| * Utility method: Assert that the {@link #collector()} has received an |
| * event with the {@code expectedMessage} in quotes. |
| */ |
| |
| public Event assertContainsEventWithWordsInQuotes(String... words) { |
| return MoreAsserts.assertContainsEventWithWordsInQuotes( |
| eventCollector, words); |
| } |
| |
| public void assertDoesNotContainEvent(String unexpectedEvent) { |
| MoreAsserts.assertDoesNotContainEvent(eventCollector, unexpectedEvent); |
| } |
| |
| public void assertContainsEventsInOrder(String... expectedMessages) { |
| MoreAsserts.assertContainsEventsInOrder(eventCollector, expectedMessages); |
| } |
| } |