From 1100e32e615f6afbc535b2e049c601d281d0ff7d Mon Sep 17 00:00:00 2001 From: Tiago Quelhas Date: Thu, 3 Oct 2024 21:54:32 +0100 Subject: [PATCH] [7.4.0] Teach ExecLogParser how to parse all three execution log formats. (#23824) The format is auto-detected from the first few bytes of the input file. PiperOrigin-RevId: 680915789 Change-Id: I6f1b4a2334912f25a8c41761a970e8f75265cc13 --- .../com/google/devtools/build/lib/exec/BUILD | 1 - .../build/lib/exec/SpawnLogReconstructor.java | 7 +- src/tools/execlog/README.md | 8 +- .../com/google/devtools/build/execlog/BUILD | 3 + .../devtools/build/execlog/ExecLogParser.java | 104 +++++++--- .../com/google/devtools/build/execlog/BUILD | 3 + .../build/execlog/ExecLogParserTest.java | 191 +++++++++++------- 7 files changed, 208 insertions(+), 109 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/exec/BUILD b/src/main/java/com/google/devtools/build/lib/exec/BUILD index 1dc4fed16b7f64..a7cf2df662778a 100644 --- a/src/main/java/com/google/devtools/build/lib/exec/BUILD +++ b/src/main/java/com/google/devtools/build/lib/exec/BUILD @@ -359,7 +359,6 @@ java_library( ":spawn_strategy_registry", "//src/main/java/com/google/devtools/build/lib/actions", "//src/main/protobuf:failure_details_java_proto", - "//third_party:flogger", "//third_party:guava", ], ) diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnLogReconstructor.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnLogReconstructor.java index 56f1b5f188675a..cdfd07784e96eb 100644 --- a/src/main/java/com/google/devtools/build/lib/exec/SpawnLogReconstructor.java +++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnLogReconstructor.java @@ -166,8 +166,11 @@ private SpawnExec reconstructSpawnExec(ExecLogEntry.Spawn entry) throws IOExcept .setRemotable(entry.getRemotable()) .setCacheable(entry.getCacheable()) .setRemoteCacheable(entry.getRemoteCacheable()) - .setTimeoutMillis(entry.getTimeoutMillis()) - .setMetrics(entry.getMetrics()); + .setTimeoutMillis(entry.getTimeoutMillis()); + + if (entry.hasMetrics()) { + builder.setMetrics(entry.getMetrics()); + } if (entry.hasPlatform()) { builder.setPlatform(entry.getPlatform()); diff --git a/src/tools/execlog/README.md b/src/tools/execlog/README.md index b532b2132f08a2..c98418c0dcbdf9 100644 --- a/src/tools/execlog/README.md +++ b/src/tools/execlog/README.md @@ -1,12 +1,14 @@ # Execution Log Parser -This tool is used to inspect and parse the Bazel execution logs. +This tool is used to inspect and parse the Bazel execution logs. Currently +supported formats are `binary`, `json`, and `compact`. + To generate the execution log, run e.g.: bazel build \ - --execution_log_binary_file=/tmp/exec.log :hello_world + --execution_log_compact_file=/tmp/exec.log :hello_world -Then build the parser and run it. +Then build the parser and run it: bazel build src/tools/execlog:parser bazel-bin/src/tools/execlog/parser --log_path=/tmp/exec.log diff --git a/src/tools/execlog/src/main/java/com/google/devtools/build/execlog/BUILD b/src/tools/execlog/src/main/java/com/google/devtools/build/execlog/BUILD index 13da4011cc8050..c4e015c03fad7d 100644 --- a/src/tools/execlog/src/main/java/com/google/devtools/build/execlog/BUILD +++ b/src/tools/execlog/src/main/java/com/google/devtools/build/execlog/BUILD @@ -18,9 +18,12 @@ java_library( "ParserOptions.java", ], deps = [ + "//src/main/java/com/google/devtools/build/lib/exec:spawn_log_context_utils", + "//src/main/java/com/google/devtools/build/lib/util/io", "//src/main/java/com/google/devtools/common/options", "//src/main/protobuf:spawn_java_proto", "//third_party:guava", + "//third_party:jsr305", ], ) diff --git a/src/tools/execlog/src/main/java/com/google/devtools/build/execlog/ExecLogParser.java b/src/tools/execlog/src/main/java/com/google/devtools/build/execlog/ExecLogParser.java index cbc37283e57e03..f3a39a6bfba1ba 100644 --- a/src/tools/execlog/src/main/java/com/google/devtools/build/execlog/ExecLogParser.java +++ b/src/tools/execlog/src/main/java/com/google/devtools/build/execlog/ExecLogParser.java @@ -17,6 +17,10 @@ import com.google.common.annotations.VisibleForTesting; import com.google.devtools.build.lib.exec.Protos.SpawnExec; +import com.google.devtools.build.lib.exec.SpawnLogReconstructor; +import com.google.devtools.build.lib.util.io.MessageInputStream; +import com.google.devtools.build.lib.util.io.MessageInputStreamWrapper.BinaryInputStreamWrapper; +import com.google.devtools.build.lib.util.io.MessageInputStreamWrapper.JsonInputStreamWrapper; import com.google.devtools.common.options.OptionsParser; import java.io.BufferedWriter; import java.io.FileInputStream; @@ -32,42 +36,78 @@ import java.util.Map; import java.util.PriorityQueue; import java.util.Queue; +import javax.annotation.Nullable; -/** - * A tool to inspect and parse the Bazel execution log. - */ -final class ExecLogParser { +/** A tool to inspect and parse the Bazel execution log. */ +public final class ExecLogParser { + private ExecLogParser() {} - static final String DELIMITER = "\n---------------------------------------------------------\n"; + private static final String DELIMITER = + "\n---------------------------------------------------------\n"; + + private static byte[] readFirstFourBytes(String path) throws IOException { + try (InputStream in = new FileInputStream(path)) { + return in.readNBytes(4); + } + } @VisibleForTesting - interface Parser { - SpawnExec getNext() throws IOException; + static MessageInputStream getMessageInputStream(String path) throws IOException { + byte[] b = readFirstFourBytes(path); + if (b.length == 4 + && b[0] == 0x28 + && b[1] == (byte) 0xb5 + && b[2] == 0x2f + && b[3] == (byte) 0xfd) { + // Looks like a compact file (zstd-compressed). + // This is definitely not a JSON file (the first byte is not '{') and definitely not a + // binary file (the first byte would indicate the size of the first message, and the + // second byte would indicate an invalid wire type). + return new SpawnLogReconstructor(new FileInputStream(path)); + } + if (b.length >= 2 && b[0] == '{' && b[1] == '\n') { + // Looks like a JSON file. + // This is definitely not a compact file (the first byte is not 0x28) and definitely not a + // binary file (the first byte would indicate the size of the first message, and the + // second byte would indicate a field with number 1 and wire type I32, which doesn't match + // the proto definition). + return new JsonInputStreamWrapper<>( + new FileInputStream(path), SpawnExec.getDefaultInstance()); + } + // Otherwise assume it's a binary file. + return new BinaryInputStreamWrapper<>( + new FileInputStream(path), SpawnExec.getDefaultInstance()); } @VisibleForTesting - static class FilteringLogParser implements Parser { - final InputStream in; + static class FilteredStream implements MessageInputStream { + final MessageInputStream in; final String restrictToRunner; - FilteringLogParser(InputStream in, String restrictToRunner) { + FilteredStream(MessageInputStream in, String restrictToRunner) { this.in = in; this.restrictToRunner = restrictToRunner; } @Override - public SpawnExec getNext() throws IOException { + @Nullable + public SpawnExec read() throws IOException { SpawnExec ex; - // Find the next record whose runner matches - while ((ex = SpawnExec.parseDelimitedFrom(in)) != null) { + while ((ex = in.read()) != null) { if (restrictToRunner == null || restrictToRunner.equals(ex.getRunner())) { return ex; } } return null; } + + @Override + public void close() throws IOException { + in.close(); + } } + @Nullable static String getFirstOutput(SpawnExec e) { if (e.getListedOutputsCount() > 0) { return e.getListedOutputs(0); @@ -76,7 +116,7 @@ static String getFirstOutput(SpawnExec e) { } @VisibleForTesting - static class ReorderingParser implements Parser { + static class OrderedStream implements MessageInputStream { public static class Golden { // A map of positions of actions in the first file. @@ -116,11 +156,13 @@ public Element(int position, SpawnExec element) { } } + private final MessageInputStream in; private final Golden golden; - ReorderingParser(Golden golden, Parser input) throws IOException { + OrderedStream(Golden golden, MessageInputStream in) throws IOException { + this.in = in; this.golden = golden; - processInputFile(input); + processInputFile(); } // actions from input that appear in golden, indexed by their position in the golden. @@ -128,12 +170,12 @@ public Element(int position, SpawnExec element) { // actions in input that are not in the golden, in order received. Queue uniqueActions; - private void processInputFile(Parser input) throws IOException { + private void processInputFile() throws IOException { sameActions = new PriorityQueue<>((e1, e2) -> (e1.position - e2.position)); uniqueActions = new ArrayDeque<>(); SpawnExec ex; - while ((ex = input.getNext()) != null) { + while ((ex = in.read()) != null) { int position = golden.positionFor(ex); if (position >= 0) { sameActions.add(new Element(position, ex)); @@ -144,20 +186,26 @@ private void processInputFile(Parser input) throws IOException { } @Override - public SpawnExec getNext() { + public SpawnExec read() { if (sameActions.isEmpty()) { return uniqueActions.poll(); } return sameActions.remove().element; } + + @Override + public void close() throws IOException { + in.close(); + } } - public static void output(Parser p, OutputStream outStream, ReorderingParser.Golden golden) + public static void output( + MessageInputStream in, OutputStream outStream, OrderedStream.Golden golden) throws IOException { PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outStream, UTF_8)), true); SpawnExec ex; - while ((ex = p.getNext()) != null) { + while ((ex = in.read()) != null) { out.println(ex); out.println(DELIMITER); if (golden != null) { @@ -211,13 +259,13 @@ public static void main(String[] args) throws Exception { } } - ReorderingParser.Golden golden = null; + OrderedStream.Golden golden = null; if (secondPath != null) { - golden = new ReorderingParser.Golden(); + golden = new OrderedStream.Golden(); } - try (InputStream input = new FileInputStream(logPath)) { - Parser parser = new FilteringLogParser(input, options.restrictToRunner); + try (MessageInputStream input = getMessageInputStream(logPath)) { + FilteredStream parser = new FilteredStream(input, options.restrictToRunner); if (output1 == null) { output(parser, System.out, golden); @@ -229,12 +277,12 @@ public static void main(String[] args) throws Exception { } if (secondPath != null) { - try (InputStream file2 = new FileInputStream(secondPath); + try (MessageInputStream file2 = getMessageInputStream(secondPath); OutputStream output = new FileOutputStream(output2)) { - Parser parser = new FilteringLogParser(file2, options.restrictToRunner); + MessageInputStream parser = new FilteredStream(file2, options.restrictToRunner); // ReorderingParser will read the whole golden on initialization, // so it is safe to close after. - parser = new ReorderingParser(golden, parser); + parser = new OrderedStream(golden, parser); output(parser, output, null); } } diff --git a/src/tools/execlog/test/main/java/com/google/devtools/build/execlog/BUILD b/src/tools/execlog/test/main/java/com/google/devtools/build/execlog/BUILD index 0ca42a0e092b0a..a618f2f6f62f4b 100644 --- a/src/tools/execlog/test/main/java/com/google/devtools/build/execlog/BUILD +++ b/src/tools/execlog/test/main/java/com/google/devtools/build/execlog/BUILD @@ -22,9 +22,12 @@ java_test( srcs = ["ExecLogParserTest.java"], test_class = "com.google.devtools.build.execlog.ExecLogParserTest", deps = [ + "//src/main/java/com/google/devtools/build/lib/exec:spawn_log_context_utils", + "//src/main/java/com/google/devtools/build/lib/util/io", "//src/main/protobuf:spawn_java_proto", "//src/tools/execlog/src/main/java/com/google/devtools/build/execlog:parser", "//third_party:junit4", "//third_party:truth", + "@zstd-jni", ], ) diff --git a/src/tools/execlog/test/main/java/com/google/devtools/build/execlog/ExecLogParserTest.java b/src/tools/execlog/test/main/java/com/google/devtools/build/execlog/ExecLogParserTest.java index dd9b93975c24c5..13fe83908e88a8 100644 --- a/src/tools/execlog/test/main/java/com/google/devtools/build/execlog/ExecLogParserTest.java +++ b/src/tools/execlog/test/main/java/com/google/devtools/build/execlog/ExecLogParserTest.java @@ -15,13 +15,18 @@ import static com.google.common.truth.Truth.assertThat; -import com.google.devtools.build.execlog.ExecLogParser.FilteringLogParser; -import com.google.devtools.build.execlog.ExecLogParser.Parser; -import com.google.devtools.build.execlog.ExecLogParser.ReorderingParser; +import com.github.luben.zstd.ZstdOutputStream; +import com.google.devtools.build.execlog.ExecLogParser.FilteredStream; +import com.google.devtools.build.execlog.ExecLogParser.OrderedStream; +import com.google.devtools.build.lib.exec.Protos.ExecLogEntry; import com.google.devtools.build.lib.exec.Protos.SpawnExec; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; +import com.google.devtools.build.lib.exec.SpawnLogReconstructor; +import com.google.devtools.build.lib.util.io.MessageInputStream; +import com.google.devtools.build.lib.util.io.MessageInputStreamWrapper.BinaryInputStreamWrapper; +import com.google.devtools.build.lib.util.io.MessageInputStreamWrapper.JsonInputStreamWrapper; +import com.google.devtools.build.lib.util.io.MessageOutputStreamWrapper.BinaryOutputStreamWrapper; +import com.google.devtools.build.lib.util.io.MessageOutputStreamWrapper.JsonOutputStreamWrapper; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -33,149 +38,186 @@ @RunWith(JUnit4.class) public final class ExecLogParserTest { - private InputStream toInputStream(List list) throws Exception { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - for (SpawnExec spawnExec : list) { - spawnExec.writeDelimitedTo(bos); + @Test + public void detectCompactFormat() throws Exception { + var path = Files.createTempFile("compact", ".tmp"); + try (var out = new ZstdOutputStream(Files.newOutputStream(path))) { + ExecLogEntry.newBuilder() + .setInvocation(ExecLogEntry.Invocation.newBuilder().setHashFunctionName("SHA256")) + .build() + .writeDelimitedTo(out); + ExecLogEntry.newBuilder() + .setSpawn(ExecLogEntry.Spawn.newBuilder().addArgs("/bin/true")) + .build() + .writeDelimitedTo(out); } - return new ByteArrayInputStream(bos.toByteArray()); + try (var stream = ExecLogParser.getMessageInputStream(path.toString())) { + assertThat(stream).isInstanceOf(SpawnLogReconstructor.class); + assertThat(stream.read()) + .isEqualTo(SpawnExec.newBuilder().addCommandArgs("/bin/true").build()); + } } @Test - public void getNextEmpty() throws Exception { - FilteringLogParser p = new FilteringLogParser(toInputStream(new ArrayList()), null); - assertThat(p.getNext()).isNull(); + public void detectJsonFormat() throws Exception { + var path = Files.createTempFile("json", ".tmp"); + try (var out = new JsonOutputStreamWrapper(Files.newOutputStream(path))) { + out.write(SpawnExec.newBuilder().addCommandArgs("/bin/true").build()); + } + + try (var stream = ExecLogParser.getMessageInputStream(path.toString())) { + assertThat(stream).isInstanceOf(JsonInputStreamWrapper.class); + assertThat(stream.read()) + .isEqualTo(SpawnExec.newBuilder().addCommandArgs("/bin/true").build()); + } } @Test - public void getNextEmptyWithRunner() throws Exception { - FilteringLogParser p = - new FilteringLogParser(toInputStream(new ArrayList()), "local"); - assertThat(p.getNext()).isNull(); + public void detectBinaryFormat() throws Exception { + var path = Files.createTempFile("binary", ".tmp"); + try (var out = new BinaryOutputStreamWrapper(Files.newOutputStream(path))) { + out.write(SpawnExec.newBuilder().addCommandArgs("/bin/true").build()); + } + + try (var stream = ExecLogParser.getMessageInputStream(path.toString())) { + assertThat(stream).isInstanceOf(BinaryInputStreamWrapper.class); + assertThat(stream.read()) + .isEqualTo(SpawnExec.newBuilder().addCommandArgs("/bin/true").build()); + } } @Test - public void getNextSingleSpawn() throws Exception { + public void readEmpty() throws Exception { + FilteredStream p = new FilteredStream(new FakeStream(new ArrayList()), null); + assertThat(p.read()).isNull(); + } + + @Test + public void readEmptyWithRunner() throws Exception { + FilteredStream p = new FilteredStream(new FakeStream(new ArrayList()), "local"); + assertThat(p.read()).isNull(); + } + + @Test + public void readSingleSpawn() throws Exception { SpawnExec e = SpawnExec.newBuilder().setRunner("runs").addCommandArgs("command").build(); - FilteringLogParser p = new FilteringLogParser(toInputStream(Arrays.asList(e)), null); - assertThat(p.getNext()).isEqualTo(e); - assertThat(p.getNext()).isNull(); + FilteredStream p = new FilteredStream(new FakeStream(Arrays.asList(e)), null); + assertThat(p.read()).isEqualTo(e); + assertThat(p.read()).isNull(); } @Test - public void getNextSingleSpawnRunnerMatch() throws Exception { + public void readSingleSpawnRunnerMatch() throws Exception { SpawnExec e = SpawnExec.newBuilder().setRunner("runs").addCommandArgs("command").build(); - FilteringLogParser p = new FilteringLogParser(toInputStream(Arrays.asList(e)), "runs"); - assertThat(p.getNext()).isEqualTo(e); - assertThat(p.getNext()).isNull(); + FilteredStream p = new FilteredStream(new FakeStream(Arrays.asList(e)), "runs"); + assertThat(p.read()).isEqualTo(e); + assertThat(p.read()).isNull(); } @Test - public void getNextSingleSpawnRunnerNoMatch() throws Exception { + public void readSingleSpawnRunnerNoMatch() throws Exception { SpawnExec e = SpawnExec.newBuilder().setRunner("runs").addCommandArgs("command").build(); - FilteringLogParser p = new FilteringLogParser(toInputStream(Arrays.asList(e)), "run"); - assertThat(p.getNext()).isNull(); + FilteredStream p = new FilteredStream(new FakeStream(Arrays.asList(e)), "run"); + assertThat(p.read()).isNull(); } @Test - public void getNextManyMatches() throws Exception { + public void readManyMatches() throws Exception { SpawnExec e = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com1").build(); SpawnExec e2 = SpawnExec.newBuilder().setRunner("r").addCommandArgs("com2").build(); SpawnExec e3 = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com3").build(); SpawnExec e4 = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com4").build(); SpawnExec e5 = SpawnExec.newBuilder().setRunner("ru").addCommandArgs("com5").build(); - FilteringLogParser p = - new FilteringLogParser(toInputStream(Arrays.asList(e, e2, e3, e4, e5)), "run1"); - assertThat(p.getNext()).isEqualTo(e); - assertThat(p.getNext()).isEqualTo(e3); - assertThat(p.getNext()).isEqualTo(e4); - assertThat(p.getNext()).isNull(); + FilteredStream p = new FilteredStream(new FakeStream(Arrays.asList(e, e2, e3, e4, e5)), "run1"); + assertThat(p.read()).isEqualTo(e); + assertThat(p.read()).isEqualTo(e3); + assertThat(p.read()).isEqualTo(e4); + assertThat(p.read()).isNull(); } @Test - public void getNextManyMatches1() throws Exception { + public void readManyMatches1() throws Exception { SpawnExec e = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com1").build(); SpawnExec e2 = SpawnExec.newBuilder().setRunner("r").addCommandArgs("com2").build(); SpawnExec e3 = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com3").build(); SpawnExec e4 = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com4").build(); SpawnExec e5 = SpawnExec.newBuilder().setRunner("ru").addCommandArgs("com5").build(); - FilteringLogParser p = - new FilteringLogParser(toInputStream(Arrays.asList(e, e2, e3, e4, e5)), "r"); - assertThat(p.getNext()).isEqualTo(e2); - assertThat(p.getNext()).isNull(); + FilteredStream p = new FilteredStream(new FakeStream(Arrays.asList(e, e2, e3, e4, e5)), "r"); + assertThat(p.read()).isEqualTo(e2); + assertThat(p.read()).isNull(); } @Test - public void getNextManyMatches2() throws Exception { + public void readManyMatches2() throws Exception { SpawnExec e = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com1").build(); SpawnExec e2 = SpawnExec.newBuilder().setRunner("r").addCommandArgs("com2").build(); SpawnExec e3 = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com3").build(); SpawnExec e4 = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com4").build(); SpawnExec e5 = SpawnExec.newBuilder().setRunner("ru").addCommandArgs("com5").build(); - FilteringLogParser p = - new FilteringLogParser(toInputStream(Arrays.asList(e, e2, e3, e4, e5)), "ru"); - assertThat(p.getNext()).isEqualTo(e5); - assertThat(p.getNext()).isNull(); + FilteredStream p = new FilteredStream(new FakeStream(Arrays.asList(e, e2, e3, e4, e5)), "ru"); + assertThat(p.read()).isEqualTo(e5); + assertThat(p.read()).isNull(); } @Test - public void getNextManyButNoMatch() throws Exception { + public void readManyButNoMatch() throws Exception { SpawnExec e = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com1").build(); SpawnExec e2 = SpawnExec.newBuilder().setRunner("r").addCommandArgs("com2").build(); SpawnExec e3 = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com3").build(); SpawnExec e4 = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com4").build(); SpawnExec e5 = SpawnExec.newBuilder().setRunner("ru").addCommandArgs("com5").build(); - FilteringLogParser p = - new FilteringLogParser(toInputStream(Arrays.asList(e, e2, e3, e4, e5)), "none"); - assertThat(p.getNext()).isNull(); + FilteredStream p = new FilteredStream(new FakeStream(Arrays.asList(e, e2, e3, e4, e5)), "none"); + assertThat(p.read()).isNull(); } @Test - public void getNextManyNoMatcher() throws Exception { + public void readManyNoMatcher() throws Exception { SpawnExec e = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com1").build(); SpawnExec e2 = SpawnExec.newBuilder().setRunner("r").addCommandArgs("com2").build(); SpawnExec e3 = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com3").build(); SpawnExec e4 = SpawnExec.newBuilder().setRunner("run1").addCommandArgs("com4").build(); SpawnExec e5 = SpawnExec.newBuilder().setRunner("ru").addCommandArgs("com5").build(); - FilteringLogParser p = - new FilteringLogParser(toInputStream(Arrays.asList(e, e2, e3, e4, e5)), null); - assertThat(p.getNext()).isEqualTo(e); - assertThat(p.getNext()).isEqualTo(e2); - assertThat(p.getNext()).isEqualTo(e3); - assertThat(p.getNext()).isEqualTo(e4); - assertThat(p.getNext()).isEqualTo(e5); - assertThat(p.getNext()).isNull(); + FilteredStream p = new FilteredStream(new FakeStream(Arrays.asList(e, e2, e3, e4, e5)), null); + assertThat(p.read()).isEqualTo(e); + assertThat(p.read()).isEqualTo(e2); + assertThat(p.read()).isEqualTo(e3); + assertThat(p.read()).isEqualTo(e4); + assertThat(p.read()).isEqualTo(e5); + assertThat(p.read()).isNull(); } - private static class FakeParser implements Parser { + private static class FakeStream implements MessageInputStream { List inputs; int i; - public FakeParser(List ex) { + public FakeStream(List ex) { this.inputs = ex; i = 0; } @Override - public SpawnExec getNext() { + public SpawnExec read() { if (i >= inputs.size()) { return null; } return inputs.get(i++); } + + @Override + public void close() {} }; - public static FakeParser fakeParserFromStrings(List strings) { + public static FakeStream fakeParserFromStrings(List strings) { ArrayList ins = new ArrayList<>(strings.size()); for (String s : strings) { ins.add(SpawnExec.newBuilder().addCommandArgs(s).addListedOutputs(s).build()); } - return new FakeParser(ins); + return new FakeStream(ins); } - public static ReorderingParser.Golden getGolden(List keys) { - ReorderingParser.Golden result = new ReorderingParser.Golden(); + public static OrderedStream.Golden getGolden(List keys) { + OrderedStream.Golden result = new OrderedStream.Golden(); for (String s : keys) { SpawnExec e = SpawnExec.newBuilder().addListedOutputs(s).build(); result.addSpawnExec(e); @@ -185,12 +227,12 @@ public static ReorderingParser.Golden getGolden(List keys) { public void test(List golden, List input, List expectedOutput) throws Exception { - ReorderingParser p = new ReorderingParser(getGolden(golden), fakeParserFromStrings(input)); + OrderedStream p = new OrderedStream(getGolden(golden), fakeParserFromStrings(input)); List got = new ArrayList<>(); SpawnExec ex; - while ((ex = p.getNext()) != null) { + while ((ex = p.read()) != null) { assertThat(ex.getCommandArgsCount()).isEqualTo(1); got.add(ex.getCommandArgs(0)); } @@ -299,14 +341,13 @@ public void reorderPreservesInformation() throws Exception { SpawnExec a = SpawnExec.newBuilder().addListedOutputs("a").addCommandArgs("acom").build(); SpawnExec b = SpawnExec.newBuilder().addListedOutputs("b").addCommandArgs("bcom").build(); SpawnExec c = SpawnExec.newBuilder().addListedOutputs("c").addCommandArgs("ccom").build(); - Parser input = new FakeParser(Arrays.asList(c, b, a)); + MessageInputStream input = new FakeStream(Arrays.asList(c, b, a)); - ReorderingParser p = new ReorderingParser(getGolden(golden), input); + OrderedStream p = new OrderedStream(getGolden(golden), input); - assertThat(p.getNext()).isEqualTo(a); - assertThat(p.getNext()).isEqualTo(b); - assertThat(p.getNext()).isEqualTo(c); - assertThat(p.getNext()).isNull(); + assertThat(p.read()).isEqualTo(a); + assertThat(p.read()).isEqualTo(b); + assertThat(p.read()).isEqualTo(c); + assertThat(p.read()).isNull(); } } -