diff options
16 files changed, 741 insertions, 23 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java index 29e598659d..924bffd339 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelMain.java @@ -48,6 +48,7 @@ public final class BazelMain { com.google.devtools.build.lib.standalone.StandaloneModule.class, com.google.devtools.build.lib.sandbox.SandboxModule.class, com.google.devtools.build.lib.runtime.BuildSummaryStatsModule.class, + com.google.devtools.build.lib.runtime.BuildEventStreamerModule.class, com.google.devtools.build.lib.bazel.rules.BazelRulesModule.class); public static void main(String[] args) { diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BUILD b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BUILD index 618abca496..11b6c22ccc 100644 --- a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BUILD +++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BUILD @@ -11,6 +11,8 @@ java_library( deps = [ "//src/main/java/com/google/devtools/build/lib:buildeventstream", "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto", + "//src/main/java/com/google/devtools/common/options", + "//third_party:guava", "//third_party/protobuf", ], ) diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java new file mode 100644 index 0000000000..c0f341e2eb --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransport.java @@ -0,0 +1,47 @@ +// Copyright 2016 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.buildeventstream.transports; + +import com.google.devtools.build.lib.buildeventstream.BuildEvent; +import com.google.devtools.build.lib.buildeventstream.BuildEventTransport; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * A simple {@link BuildEventTransport} that writes varint delimited binary representation of event + * {@link BuildEvent} protocol-buffers to a file. Files written by this Transport can be read by + * successive calls of {code BuildEvent.Builder#mergeDelimitedFrom(InputStream)} (or the static + * method {@code BuildEvent.parseDelimitedFrom(InputStream)}). + */ +public final class BinaryFormatFileTransport implements BuildEventTransport { + private final BufferedOutputStream out; + + public BinaryFormatFileTransport(String path) throws IOException { + this.out = new BufferedOutputStream(new FileOutputStream(new File(path))); + } + + @Override + public synchronized void sendBuildEvent(BuildEvent event) throws IOException { + event.asStreamProto().writeDelimitedTo(out); + out.flush(); + } + + @Override + public void close() throws IOException { + out.close(); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BuildEventStreamOptions.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BuildEventStreamOptions.java new file mode 100644 index 0000000000..c4cb96a36d --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BuildEventStreamOptions.java @@ -0,0 +1,48 @@ +// Copyright 2016 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.buildeventstream.transports; + +import com.google.devtools.common.options.Option; +import com.google.devtools.common.options.OptionsBase; + +/** Options used to configure BuildEventStreamer and its BuildEventTransports. */ +public class BuildEventStreamOptions extends OptionsBase { + + @Option( + name = "experimental_build_event_text_file", + defaultValue = "", + category = "hidden", + help = "If non-empty, write a textual representation of the build event protocol to that file" + ) + public String buildEventTextFile; + + @Option( + name = "experimental_build_event_binary_file", + defaultValue = "", + category = "hidden", + help = + "If non-empty, write a varint delimited binary representation of representation of the" + + " build event protocol to that file." + ) + public String buildEventBinaryFile; + + public String getBuildEventTextFile() { + return buildEventTextFile; + } + + public String getBuildEventBinaryFile() { + return buildEventBinaryFile; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BuildEventTransportFactory.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BuildEventTransportFactory.java new file mode 100644 index 0000000000..f456b189d6 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/transports/BuildEventTransportFactory.java @@ -0,0 +1,74 @@ +// Copyright 2016 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.buildeventstream.transports; + +import static com.google.common.base.Strings.isNullOrEmpty; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +import com.google.devtools.build.lib.buildeventstream.BuildEventTransport; +import java.io.IOException; + +/** Factory used to create a Set of BuildEventTransports from BuildEventStreamOptions. */ +public enum BuildEventTransportFactory { + TEXT_TRANSPORT { + @Override + protected boolean enabled(BuildEventStreamOptions options) { + return !isNullOrEmpty(options.getBuildEventTextFile()); + } + + @Override + protected BuildEventTransport create(BuildEventStreamOptions options) throws IOException { + return new TextFormatFileTransport(options.getBuildEventTextFile()); + } + }, + + BINARY_TRANSPORT { + @Override + protected boolean enabled(BuildEventStreamOptions options) { + return !isNullOrEmpty(options.getBuildEventBinaryFile()); + } + + @Override + protected BuildEventTransport create(BuildEventStreamOptions options) throws IOException { + return new BinaryFormatFileTransport(options.getBuildEventBinaryFile()); + } + }; + + /** + * Creates a {@link ImmutableSet} of {@link BuildEventTransport} based on the specified {@link + * BuildEventStreamOptions}. + * + * @param options Options used configure and create the returned BuildEventTransports. + * @return A {@link ImmutableSet} of BuildEventTransports. This set may be empty. + * @throws IOException Exception propagated from a {@link BuildEventTransport} creation failure. + */ + public static ImmutableSet<BuildEventTransport> createFromOptions(BuildEventStreamOptions options) + throws IOException { + Builder<BuildEventTransport> buildEventTransportsBuilder = ImmutableSet.builder(); + for (BuildEventTransportFactory transportFactory : BuildEventTransportFactory.values()) { + if (transportFactory.enabled(options)) { + buildEventTransportsBuilder.add(transportFactory.create(options)); + } + } + return buildEventTransportsBuilder.build(); + } + + /** Returns true if this factory BuildEventTransport is enabled by the specified options. */ + protected abstract boolean enabled(BuildEventStreamOptions options); + + /** Creates a BuildEventTransport from the specified options. */ + protected abstract BuildEventTransport create(BuildEventStreamOptions options) throws IOException; +} diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java index 87adbd7374..c16994f0d4 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java @@ -27,8 +27,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.ListMultimap; import com.google.common.io.Flushables; -import com.google.devtools.build.lib.buildeventstream.BuildEventTransport; -import com.google.devtools.build.lib.buildeventstream.transports.TextFormatFileTransport; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.Reporter; @@ -455,18 +453,6 @@ public class BlazeCommandDispatcher { Reporter reporter = env.getReporter(); reporter.addHandler(handler); env.getEventBus().register(handler); - if (eventHandlerOptions.buildEventTextFile.length() > 0) { - try { - BuildEventStreamer streamer = - new BuildEventStreamer( - ImmutableSet.<BuildEventTransport>of( - new TextFormatFileTransport(eventHandlerOptions.buildEventTextFile))); - reporter.addHandler(streamer); - env.getEventBus().register(streamer); - } catch (IOException e) { - return ExitCode.LOCAL_ENVIRONMENTAL_ERROR.getNumericExitCode(); - } - } // We register an ANSI-allowing handler associated with {@code handler} so that ANSI control // codes can be re-introduced later even if blaze is invoked with --color=no. This is useful diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandEventHandler.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandEventHandler.java index 7ab613fc3f..601f37f5fd 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandEventHandler.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandEventHandler.java @@ -168,15 +168,6 @@ public class BlazeCommandEventHandler implements EventHandler { ) public int experimentalUiActionsShown; - - @Option( - name = "experimental_build_event_text_file", - defaultValue = "", - category = "hidden", - help = "If non-empty, write a textual representation of the build event protocol to that file" - ) - public String buildEventTextFile; - public boolean useColor() { return useColorEnum == UseColor.YES || (useColorEnum == UseColor.AUTO && isATty); } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java index 819e2f3c32..812d223449 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamer.java @@ -14,6 +14,8 @@ package com.google.devtools.build.lib.runtime; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; import com.google.devtools.build.lib.actions.ActionExecutedEvent; @@ -156,4 +158,9 @@ public class BuildEventStreamer implements EventHandler { post(event); } + + @VisibleForTesting + ImmutableSet<BuildEventTransport> getTransports() { + return ImmutableSet.copyOf(transports); + } } diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModule.java b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModule.java new file mode 100644 index 0000000000..b405c79b38 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModule.java @@ -0,0 +1,79 @@ +// Copyright 2016 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.runtime; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static com.google.devtools.build.lib.buildeventstream.transports.BuildEventTransportFactory.createFromOptions; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.buildeventstream.BuildEventTransport; +import com.google.devtools.build.lib.buildeventstream.transports.BuildEventStreamOptions; +import com.google.devtools.build.lib.util.AbruptExitException; +import com.google.devtools.build.lib.util.ExitCode; +import com.google.devtools.common.options.OptionsBase; +import com.google.devtools.common.options.OptionsProvider; +import java.io.IOException; + + +/** Module responsible for configuring BuildEventStreamer and transports. */ +public class BuildEventStreamerModule extends BlazeModule { + + private CommandEnvironment commandEnvironment; + + @Override + public Iterable<Class<? extends OptionsBase>> getCommandOptions(Command command) { + return ImmutableList.<Class<? extends OptionsBase>>of(BuildEventStreamOptions.class); + } + + @Override + public void beforeCommand(Command command, CommandEnvironment commandEnvironment) + throws AbruptExitException { + this.commandEnvironment = commandEnvironment; + } + + @Override + public void handleOptions(OptionsProvider optionsProvider) { + checkState(commandEnvironment != null, "Methods called out of order"); + Optional<BuildEventStreamer> streamer = + tryCreateStreamer(optionsProvider, commandEnvironment.getBlazeModuleEnvironment()); + if (streamer.isPresent()) { + commandEnvironment.getReporter().addHandler(streamer.get()); + commandEnvironment.getEventBus().register(streamer.get()); + } + } + + @VisibleForTesting + Optional<BuildEventStreamer> tryCreateStreamer( + OptionsProvider optionsProvider, ModuleEnvironment moduleEnvironment) { + try { + BuildEventStreamOptions besOptions = + checkNotNull( + optionsProvider.getOptions(BuildEventStreamOptions.class), + "Could not get BuildEventStreamOptions"); + ImmutableSet<BuildEventTransport> buildEventTransports = createFromOptions(besOptions); + if (!buildEventTransports.isEmpty()) { + BuildEventStreamer streamer = new BuildEventStreamer(buildEventTransports); + return Optional.of(streamer); + } + } catch (IOException e) { + moduleEnvironment.exit(new AbruptExitException(ExitCode.LOCAL_ENVIRONMENTAL_ERROR, e)); + } + return Optional.absent(); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD index f7ae7d599a..4c1f7b226d 100644 --- a/src/test/java/com/google/devtools/build/lib/BUILD +++ b/src/test/java/com/google/devtools/build/lib/BUILD @@ -27,6 +27,7 @@ filegroup( "//src/test/java/com/google/devtools/build/lib/skyframe:srcs", "//src/test/java/com/google/devtools/build/lib/rules/repository:srcs", "//src/test/java/com/google/devtools/build/lib/bazel/repository:srcs", + "//src/test/java/com/google/devtools/build/lib/buildeventstream/transports:srcs", "//src/test/java/com/google/devtools/build/lib/buildtool:srcs", ], visibility = ["//src:__pkg__"], @@ -1000,6 +1001,7 @@ java_test( "//src/main/java/com/google/devtools/build/lib:vfs", "//src/main/java/com/google/devtools/build/lib/actions", "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto", + "//src/main/java/com/google/devtools/build/lib/buildeventstream/transports", "//src/main/java/com/google/devtools/common/options", "//src/main/protobuf:invocation_policy_java_proto", "//src/main/protobuf:test_status_java_proto", diff --git a/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BUILD b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BUILD new file mode 100644 index 0000000000..6472d8aa21 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BUILD @@ -0,0 +1,24 @@ +filegroup( + name = "srcs", + srcs = glob(["**"]), + visibility = ["//src/test/java/com/google/devtools/build/lib:__pkg__"], +) + +java_test( + name = "BuildEventTransportTest", + srcs = glob(["*.java"]), + test_class = "com.google.devtools.build.lib.AllTests", + runtime_deps = ["//src/test/java/com/google/devtools/build/lib:test_runner"], + deps = [ + "//src/main/java/com/google/devtools/build/lib:buildeventstream", + "//src/main/java/com/google/devtools/build/lib:runtime", + "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto", + "//src/main/java/com/google/devtools/build/lib/buildeventstream/transports", + "//src/test/java/com/google/devtools/build/lib:packages_testutil", + "//third_party:guava", + "//third_party:junit4", + "//third_party:mockito", + "//third_party:truth", + "//third_party/protobuf", + ], +) diff --git a/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransportTest.java b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransportTest.java new file mode 100644 index 0000000000..e241e8410c --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BinaryFormatFileTransportTest.java @@ -0,0 +1,90 @@ +// Copyright 2016 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.buildeventstream.transports; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.google.devtools.build.lib.buildeventstream.BuildEvent; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildStarted; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.Progress; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.TargetComplete; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +/** Tests {@link BinaryFormatFileTransport}. **/ +@RunWith(JUnit4.class) +public class BinaryFormatFileTransportTest { + + @Rule public TemporaryFolder tmp = new TemporaryFolder(); + + @Mock public BuildEvent buildEvent; + + @Before + public void initMocks() { + MockitoAnnotations.initMocks(this); + } + + @After + public void validateMocks() { + Mockito.validateMockitoUsage(); + } + + @Test + public void testCreatesFileAndWritesProtoTextFormat() throws IOException { + File output = tmp.newFile(); + + BuildEventStreamProtos.BuildEvent started = + BuildEventStreamProtos.BuildEvent.newBuilder() + .setStarted(BuildStarted.newBuilder().setCommand("build")) + .build(); + when(buildEvent.asStreamProto()).thenReturn(started); + BinaryFormatFileTransport transport = new BinaryFormatFileTransport(output.getAbsolutePath()); + transport.sendBuildEvent(buildEvent); + + BuildEventStreamProtos.BuildEvent progress = + BuildEventStreamProtos.BuildEvent.newBuilder().setProgress(Progress.newBuilder()).build(); + when(buildEvent.asStreamProto()).thenReturn(progress); + transport.sendBuildEvent(buildEvent); + + BuildEventStreamProtos.BuildEvent completed = + BuildEventStreamProtos.BuildEvent.newBuilder() + .setCompleted(TargetComplete.newBuilder().setSuccess(true)) + .build(); + when(buildEvent.asStreamProto()).thenReturn(completed); + transport.sendBuildEvent(buildEvent); + + transport.close(); + try (InputStream in = new FileInputStream(output)) { + assertThat(BuildEventStreamProtos.BuildEvent.parseDelimitedFrom(in)).isEqualTo(started); + assertThat(BuildEventStreamProtos.BuildEvent.parseDelimitedFrom(in)).isEqualTo(progress); + assertThat(BuildEventStreamProtos.BuildEvent.parseDelimitedFrom(in)).isEqualTo(completed); + assertThat(in.available()).isEqualTo(0); + } + } +} diff --git a/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BuildEventTransportFactoryTest.java b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BuildEventTransportFactoryTest.java new file mode 100644 index 0000000000..8d4aa520db --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/BuildEventTransportFactoryTest.java @@ -0,0 +1,130 @@ +// Copyright 2016 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.buildeventstream.transports; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.google.common.base.Function; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.buildeventstream.BuildEvent; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildStarted; +import com.google.devtools.build.lib.buildeventstream.BuildEventTransport; +import java.io.File; +import java.io.IOException; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +/** Tests {@link BuildEventTransportFactory}. **/ +@RunWith(JUnit4.class) +public class BuildEventTransportFactoryTest { + + private static final Function<Object, Class<?>> GET_CLASS = + new Function<Object, Class<?>>() { + @Override + public Class<?> apply(Object o) { + return o.getClass(); + } + }; + + private static final BuildEventStreamProtos.BuildEvent BUILD_EVENT_AS_PROTO = + BuildEventStreamProtos.BuildEvent.newBuilder() + .setStarted(BuildStarted.newBuilder().setCommand("build")) + .build(); + + @Rule public TemporaryFolder tmp = new TemporaryFolder(); + + @Mock public BuildEventStreamOptions options; + + @Mock public BuildEvent buildEvent; + + @Before + public void before() { + MockitoAnnotations.initMocks(this); + when(buildEvent.asStreamProto()).thenReturn(BUILD_EVENT_AS_PROTO); + } + + @After + public void validateMocks() { + Mockito.validateMockitoUsage(); + } + + @Test + public void testCreatesTextFormatFileTransport() throws IOException { + File textFile = tmp.newFile(); + when(options.getBuildEventTextFile()).thenReturn(textFile.getAbsolutePath()); + when(options.getBuildEventBinaryFile()).thenReturn(""); + ImmutableSet<BuildEventTransport> transports = + BuildEventTransportFactory.createFromOptions(options); + assertThat(FluentIterable.from(transports).transform(GET_CLASS)) + .containsExactly(TextFormatFileTransport.class); + sendEventsAndClose(buildEvent, transports); + assertThat(textFile.exists()).isTrue(); + } + + @Test + public void testCreatesBinaryFormatFileTransport() throws IOException { + File binaryFile = tmp.newFile(); + when(options.getBuildEventTextFile()).thenReturn(""); + when(options.getBuildEventBinaryFile()).thenReturn(binaryFile.getAbsolutePath()); + ImmutableSet<BuildEventTransport> transports = + BuildEventTransportFactory.createFromOptions(options); + assertThat(FluentIterable.from(transports).transform(GET_CLASS)) + .containsExactly(BinaryFormatFileTransport.class); + sendEventsAndClose(buildEvent, transports); + assertThat(binaryFile.exists()).isTrue(); + } + + @Test + public void testCreatesAllTransports() throws IOException { + File textFile = tmp.newFile(); + File binaryFile = tmp.newFile(); + when(options.getBuildEventTextFile()).thenReturn(textFile.getAbsolutePath()); + when(options.getBuildEventBinaryFile()).thenReturn(binaryFile.getAbsolutePath()); + ImmutableSet<BuildEventTransport> transports = + BuildEventTransportFactory.createFromOptions(options); + assertThat(FluentIterable.from(transports).transform(GET_CLASS)) + .containsExactly(TextFormatFileTransport.class, BinaryFormatFileTransport.class); + sendEventsAndClose(buildEvent, transports); + assertThat(textFile.exists()).isTrue(); + assertThat(binaryFile.exists()).isTrue(); + } + + @Test + public void testCreatesNoTransports() throws IOException { + when(options.getBuildEventTextFile()).thenReturn(""); + ImmutableSet<BuildEventTransport> transports = + BuildEventTransportFactory.createFromOptions(options); + assertThat(transports).isEmpty(); + } + + private void sendEventsAndClose(BuildEvent event, Iterable<BuildEventTransport> transports) + throws IOException{ + for (BuildEventTransport transport : transports) { + transport.sendBuildEvent(event); + transport.close(); + } + } +} diff --git a/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransportTest.java b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransportTest.java new file mode 100644 index 0000000000..798e8557c8 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/buildeventstream/transports/TextFormatFileTransportTest.java @@ -0,0 +1,91 @@ +// Copyright 2016 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.buildeventstream.transports; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.google.common.base.Joiner; +import com.google.common.io.Files; +import com.google.devtools.build.lib.buildeventstream.BuildEvent; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildStarted; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.Progress; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.TargetComplete; +import com.google.protobuf.TextFormat; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +/** Tests {@link TextFormatFileTransport}. **/ +@RunWith(JUnit4.class) +public class TextFormatFileTransportTest { + + @Rule public TemporaryFolder tmp = new TemporaryFolder(); + + @Mock public BuildEvent buildEvent; + + @Before + public void initMocks() { + MockitoAnnotations.initMocks(this); + } + + @After + public void validateMocks() { + Mockito.validateMockitoUsage(); + } + + @Test + public void testCreatesFileAndWritesProtoTextFormat() throws IOException { + File output = tmp.newFile(); + + BuildEventStreamProtos.BuildEvent started = + BuildEventStreamProtos.BuildEvent.newBuilder() + .setStarted(BuildStarted.newBuilder().setCommand("build")) + .build(); + when(buildEvent.asStreamProto()).thenReturn(started); + TextFormatFileTransport transport = new TextFormatFileTransport(output.getAbsolutePath()); + transport.sendBuildEvent(buildEvent); + + BuildEventStreamProtos.BuildEvent progress = + BuildEventStreamProtos.BuildEvent.newBuilder().setProgress(Progress.newBuilder()).build(); + when(buildEvent.asStreamProto()).thenReturn(progress); + transport.sendBuildEvent(buildEvent); + + BuildEventStreamProtos.BuildEvent completed = + BuildEventStreamProtos.BuildEvent.newBuilder() + .setCompleted(TargetComplete.newBuilder().setSuccess(true)) + .build(); + when(buildEvent.asStreamProto()).thenReturn(completed); + transport.sendBuildEvent(buildEvent); + + transport.close(); + String contents = + Joiner.on(System.lineSeparator()).join(Files.readLines(output, StandardCharsets.UTF_8)); + assertThat(contents).contains(TextFormat.printToString(started)); + assertThat(contents).contains(TextFormat.printToString(progress)); + assertThat(contents).contains(TextFormat.printToString(completed)); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModuleTest.java b/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModuleTest.java new file mode 100644 index 0000000000..ad5a87c745 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/runtime/BuildEventStreamerModuleTest.java @@ -0,0 +1,136 @@ +// Copyright 2016 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.runtime; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.collect.FluentIterable; +import com.google.devtools.build.lib.buildeventstream.transports.BinaryFormatFileTransport; +import com.google.devtools.build.lib.buildeventstream.transports.BuildEventStreamOptions; +import com.google.devtools.build.lib.buildeventstream.transports.TextFormatFileTransport; +import com.google.devtools.build.lib.runtime.BlazeModule.ModuleEnvironment; +import com.google.devtools.common.options.OptionsBase; +import com.google.devtools.common.options.OptionsParser; +import com.google.devtools.common.options.OptionsProvider; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +/** Tests {@link BuildEventStreamerModule}. **/ +@RunWith(JUnit4.class) +public class BuildEventStreamerModuleTest { + + private static final Function<Object, Class<?>> GET_CLASS = + new Function<Object, Class<?>>() { + @Override + public Class<?> apply(Object o) { + return o.getClass(); + } + }; + + @Rule public TemporaryFolder tmp = new TemporaryFolder(); + + @Mock public BuildEventStreamOptions options; + + @Mock public OptionsProvider optionsProvider; + + @Mock public ModuleEnvironment moduleEnvironment; + + @Mock public Command command; + + @Before + public void initMocks() { + MockitoAnnotations.initMocks(this); + } + + @After + public void validateMocks() { + Mockito.validateMockitoUsage(); + } + + @Test + public void testReturnsBuildEventStreamerOptions() throws Exception { + BuildEventStreamerModule module = new BuildEventStreamerModule(); + Iterable<Class<? extends OptionsBase>> commandOptions = module.getCommandOptions(command); + assertThat(commandOptions).isNotEmpty(); + OptionsParser optionsParser = OptionsParser.newOptionsParser(commandOptions); + optionsParser.parse( + "--experimental_build_event_text_file", "/tmp/foo.txt", + "--experimental_build_event_binary_file", "/tmp/foo.bin"); + BuildEventStreamOptions options = optionsParser.getOptions(BuildEventStreamOptions.class); + assertThat(options.getBuildEventTextFile()).isEqualTo("/tmp/foo.txt"); + assertThat(options.getBuildEventBinaryFile()).isEqualTo("/tmp/foo.bin"); + } + + @Test + public void testCreatesStreamerForTextFormatFileTransport() throws Exception { + when(optionsProvider.getOptions(BuildEventStreamOptions.class)).thenReturn(options); + when(options.getBuildEventTextFile()).thenReturn(tmp.newFile().getAbsolutePath()); + + BuildEventStreamerModule module = new BuildEventStreamerModule(); + Optional<BuildEventStreamer> buildEventStreamer = + module.tryCreateStreamer(optionsProvider, moduleEnvironment); + assertThat(buildEventStreamer.get()).isInstanceOf(BuildEventStreamer.class); + assertThat(FluentIterable.from(buildEventStreamer.get().getTransports()).transform(GET_CLASS)) + .containsExactly(TextFormatFileTransport.class); + } + + @Test + public void testCreatesStreamerForBinaryFormatFileTransport() throws Exception { + when(optionsProvider.getOptions(BuildEventStreamOptions.class)).thenReturn(options); + when(options.getBuildEventBinaryFile()).thenReturn(tmp.newFile().getAbsolutePath()); + + BuildEventStreamerModule module = new BuildEventStreamerModule(); + Optional<BuildEventStreamer> buildEventStreamer = + module.tryCreateStreamer(optionsProvider, moduleEnvironment); + assertThat(buildEventStreamer.get()).isInstanceOf(BuildEventStreamer.class); + assertThat(FluentIterable.from(buildEventStreamer.get().getTransports()).transform(GET_CLASS)) + .containsExactly(BinaryFormatFileTransport.class); + } + + @Test + public void testCreatesStreamerForAllTransports() throws Exception { + when(optionsProvider.getOptions(BuildEventStreamOptions.class)).thenReturn(options); + when(options.getBuildEventTextFile()).thenReturn(tmp.newFile().getAbsolutePath()); + when(options.getBuildEventBinaryFile()).thenReturn(tmp.newFile().getAbsolutePath()); + + BuildEventStreamerModule module = new BuildEventStreamerModule(); + Optional<BuildEventStreamer> buildEventStreamer = + module.tryCreateStreamer(optionsProvider, moduleEnvironment); + assertThat(buildEventStreamer.get()).isInstanceOf(BuildEventStreamer.class); + assertThat(FluentIterable.from(buildEventStreamer.get().getTransports()).transform(GET_CLASS)) + .containsExactly(TextFormatFileTransport.class, BinaryFormatFileTransport.class); + } + + @Test + public void testDoesNotCreatesStreamerWithoutTransports() throws Exception { + when(optionsProvider.getOptions(BuildEventStreamOptions.class)).thenReturn(options); + + BuildEventStreamerModule module = new BuildEventStreamerModule(); + Optional<BuildEventStreamer> buildEventStreamer = + module.tryCreateStreamer(optionsProvider, moduleEnvironment); + assertThat(buildEventStreamer.isPresent()).isFalse(); + } +} diff --git a/src/test/shell/integration/build_event_stream_test.sh b/src/test/shell/integration/build_event_stream_test.sh index 2d8c246b9d..5cc89bb599 100755 --- a/src/test/shell/integration/build_event_stream_test.sh +++ b/src/test/shell/integration/build_event_stream_test.sh @@ -76,4 +76,14 @@ function test_test_summary() { expect_not_log 'aborted' } +function test_multiple_transports() { + # Verifies usage of multiple build event transports at the same time + bazel test \ + --experimental_build_event_text_file=test_multiple_transports.txt \ + --experimental_build_event_binary_file=test_multiple_transports.bin \ + pkg:suite || fail "bazel test failed" + [ -f test_multiple_transports.txt ] || fail "Missing expected file test_multiple_transports.txt" + [ -f test_multiple_transports.bin ] || fail "Missing expected file test_multiple_transports.bin" +} + run_suite "Integration tests for the build event stream" |