17850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com/* 27850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Copyright (C) 2010 The Android Open Source Project 37850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * 47850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Licensed under the Apache License, Version 2.0 (the "License"); 57850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * you may not use this file except in compliance with the License. 67850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * You may obtain a copy of the License at 77850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * 87850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * http://www.apache.org/licenses/LICENSE-2.0 97850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * 107850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Unless required by applicable law or agreed to in writing, software 117850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * distributed under the License is distributed on an "AS IS" BASIS, 127850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * See the License for the specific language governing permissions and 147850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * limitations under the License. 157850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 167850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 177850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.compackage vogar; 187850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 19d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.comimport com.google.common.collect.Lists; 208918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.comimport java.util.Collection; 215b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.comimport java.util.Collections; 22d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.comimport java.util.Date; 235884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.comimport java.util.HashMap; 247850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.comimport java.util.List; 255884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.comimport java.util.Map; 265b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.comimport vogar.util.MarkResetConsole; 277850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 287850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com/** 295884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * Controls, formats and emits output to the command line. This class emits 305884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * output in two modes: 315884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * <ul> 325884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * <li><strong>Streaming</strong> output prints as it is received, but cannot 335884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * support multiple concurrent output streams. 345884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * <li><strong>Multiplexing</strong> buffers output until it is complete and 355884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * then prints it completely. 365884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * </ul> 377850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 38b5c5c44d0c0a01c278cdac68ae23646682eb8ef7jessewilson@google.compublic abstract class Console implements Log { 39dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com static final long DAY_MILLIS = 1000 * 60 * 60 * 24; 40dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com static final long HOUR_MILLIS = 1000 * 60 * 60; 41dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com static final long WARNING_HOURS = 12; 42dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com static final long FAILURE_HOURS = 48; 43dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com 44d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com private boolean useColor; 450942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com private boolean ansi; 4688b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com private boolean verbose; 475884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com protected String indent; 485884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com protected CurrentLine currentLine = CurrentLine.NEW; 495b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com protected final MarkResetConsole out = new MarkResetConsole(System.out); 505b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com protected MarkResetConsole.Mark currentVerboseMark; 515b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com protected MarkResetConsole.Mark currentStreamMark; 527850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 53400bee347dd7464ecc17dc24c82f59c59645ff44jessewilson@google.com private Console() {} 54027ca289d7c7b21501a9d14e69f10735033f57a0elliott.n.hughes@gmail.com 55027ca289d7c7b21501a9d14e69f10735033f57a0elliott.n.hughes@gmail.com public void setIndent(String indent) { 567850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com this.indent = indent; 57027ca289d7c7b21501a9d14e69f10735033f57a0elliott.n.hughes@gmail.com } 58027ca289d7c7b21501a9d14e69f10735033f57a0elliott.n.hughes@gmail.com 595ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray public void setUseColor( 605ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray boolean useColor, int passColor, int skipColor, int failColor, int warnColor) { 61d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com this.useColor = useColor; 6250bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com Color.PASS.setCode(passColor); 635ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray Color.SKIP.setCode(skipColor); 6450bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com Color.FAIL.setCode(failColor); 655ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray Color.WARN.setCode(warnColor); 663617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com Color.COMMENT.setCode(34); 677850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 687850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 690942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com public void setAnsi(boolean ansi) { 700942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com this.ansi = ansi; 710942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com } 720942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com 7388b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com public void setVerbose(boolean verbose) { 7488b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com this.verbose = verbose; 7588b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com } 7688b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com 77f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com public boolean isVerbose() { 78f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com return verbose; 79f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com } 80f83be5e4273263df2bb9ef609946b911695b3996jessewilson@google.com 81ba1c2cee7506480a0a239e2d99a944d08fc47be5jessewilson@google.com public synchronized void verbose(String s) { 825b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com /* 830942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com * terminal does't support overwriting output, so don't print 840942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com * verbose message unless requested. 850942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com */ 860942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com if (!verbose && !ansi) { 870942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com return; 880942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com } 890942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com /* 9072bce7bb658ad68388f7b3adfadaa71979cb88c9jessewilson@google.com * When writing verbose output in the middle of streamed output, keep 9172bce7bb658ad68388f7b3adfadaa71979cb88c9jessewilson@google.com * the streamed mark location. That way we can remove the verbose output 9272bce7bb658ad68388f7b3adfadaa71979cb88c9jessewilson@google.com * later without losing our position mid-line in the streamed output. 935b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com */ 9472bce7bb658ad68388f7b3adfadaa71979cb88c9jessewilson@google.com MarkResetConsole.Mark savedStreamMark = currentLine == CurrentLine.STREAMED_OUTPUT 9572bce7bb658ad68388f7b3adfadaa71979cb88c9jessewilson@google.com ? out.mark() 9672bce7bb658ad68388f7b3adfadaa71979cb88c9jessewilson@google.com : currentStreamMark; 9772bce7bb658ad68388f7b3adfadaa71979cb88c9jessewilson@google.com newLine(); 9872bce7bb658ad68388f7b3adfadaa71979cb88c9jessewilson@google.com currentStreamMark = savedStreamMark; 995b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com 1005b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com currentVerboseMark = out.mark(); 1015b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print(s); 10288b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com currentLine = CurrentLine.VERBOSE; 103027ca289d7c7b21501a9d14e69f10735033f57a0elliott.n.hughes@gmail.com } 104027ca289d7c7b21501a9d14e69f10735033f57a0elliott.n.hughes@gmail.com 1053dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com public synchronized void warn(String message) { 1065b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com warn(message, Collections.<String>emptyList()); 1073dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com } 1083dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com 1093dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com /** 1103dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com * Warns, and also puts a list of strings afterwards. 1113dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com */ 1123dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com public synchronized void warn(String message, List<String> list) { 1133dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com newLine(); 1145b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println(colorString("Warning: " + message, Color.WARN)); 1153dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com for (String item : list) { 1165b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println(colorString(indent + item, Color.WARN)); 1173dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com } 1183dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com } 1193dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com 120ba1c2cee7506480a0a239e2d99a944d08fc47be5jessewilson@google.com public synchronized void info(String s) { 121400bee347dd7464ecc17dc24c82f59c59645ff44jessewilson@google.com newLine(); 1225b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println(s); 123027ca289d7c7b21501a9d14e69f10735033f57a0elliott.n.hughes@gmail.com } 124027ca289d7c7b21501a9d14e69f10735033f57a0elliott.n.hughes@gmail.com 125ba1c2cee7506480a0a239e2d99a944d08fc47be5jessewilson@google.com public synchronized void info(String message, Throwable throwable) { 1267850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com newLine(); 1275b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println(message); 128400bee347dd7464ecc17dc24c82f59c59645ff44jessewilson@google.com throwable.printStackTrace(System.out); 1297850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1307850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1317850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 1325884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * Begins streaming output for the named action. 1337850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 1345884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com public void action(String name) {} 1357850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1365884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com /** 1375884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * Begins streaming output for the named outcome. 1385884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com */ 1395884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com public void outcome(String name) {} 1407850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1417850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 1427850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Appends the action output immediately to the stream when streaming is on, 1437850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * or to a buffer when streaming is off. Buffered output will be held and 1447850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * printed only if the outcome is unsuccessful. 1457850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 1465884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com public abstract void streamOutput(String outcomeName, String output); 1477850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1487850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 1493617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com * Hook to flush anything streamed via {@link #streamOutput}. 1503617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com */ 1513617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com protected void flushBufferedOutput(String outcomeName) {} 1523617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com 1533617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com /** 1547850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Writes the action's outcome. 1557850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 1563617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com public synchronized void printResult( 1573617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com String outcomeName, Result result, ResultValue resultValue, Expectation expectation) { 1583617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com // when the result is interesting, include the description and bug number 1593617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com if (result != Result.SUCCESS || resultValue != ResultValue.OK) { 1603617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com if (!expectation.getDescription().isEmpty()) { 1613617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com streamOutput(outcomeName, "\n" + colorString(expectation.getDescription(), Color.COMMENT)); 1623617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com } 1633617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com if (expectation.getBug() != -1) { 1643617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com streamOutput(outcomeName, "\n" + colorString("http://b/" + expectation.getBug(), Color.COMMENT)); 1653617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com } 1663617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com } 1673617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com 1683617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com flushBufferedOutput(outcomeName); 1693617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com 1705884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com if (currentLine == CurrentLine.NAME) { 1715b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print(" "); 1725884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } else { 1732b3ec15cc85c52d60bebc7587cdb5604cf79cec6jessewilson@google.com newLine(); // TODO: backup the cursor up to the name if there's no streaming output 1742b3ec15cc85c52d60bebc7587cdb5604cf79cec6jessewilson@google.com out.print(indent + outcomeName + " "); 1755884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 1767850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1775884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com if (resultValue == ResultValue.OK) { 1785b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println(colorString("OK (" + result + ")", Color.PASS)); 1791d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com } else if (resultValue == ResultValue.FAIL) { 1805b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println(colorString("FAIL (" + result + ")", Color.FAIL)); 1811d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com } else if (resultValue == ResultValue.IGNORE) { 1825b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println(colorString("SKIP (" + result + ")", Color.WARN)); 1837850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1847850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1857850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com currentLine = CurrentLine.NEW; 1867850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 1877850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 1888918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com public synchronized void summarizeOutcomes(Collection<AnnotatedOutcome> annotatedOutcomes) { 1898918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com List<AnnotatedOutcome> annotatedOutcomesSorted = 1908918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com AnnotatedOutcome.ORDER_BY_NAME.sortedCopy(annotatedOutcomes); 191d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 192d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com List<String> failures = Lists.newArrayList(); 193d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com List<String> skips = Lists.newArrayList(); 194d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com List<String> successes = Lists.newArrayList(); 1955ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray List<String> warnings = Lists.newArrayList(); 196d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 197d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com // figure out whether each outcome is noteworthy, and add a message to the appropriate list 1988918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com for (AnnotatedOutcome annotatedOutcome : annotatedOutcomesSorted) { 199d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com if (!annotatedOutcome.isNoteworthy()) { 200d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com continue; 201d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 202d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 203d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com Color color; 204d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com List<String> list; 2058918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com ResultValue resultValue = annotatedOutcome.getResultValue(); 2068918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com if (resultValue == ResultValue.OK) { 20750bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com color = Color.PASS; 208d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com list = successes; 2098918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com } else if (resultValue == ResultValue.FAIL) { 21050bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com color = Color.FAIL; 211d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com list = failures; 2125ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray } else if (resultValue == ResultValue.WARNING) { 21350bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com color = Color.WARN; 2145ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray list = warnings; 2155ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray } else { 2165ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray color = Color.SKIP; 217d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com list = skips; 218d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 219d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 220dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com Long lastRun = annotatedOutcome.lastRun(null); 221d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com String timestamp; 222dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com if (lastRun == null) { 223dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com timestamp = colorString("unknown", Color.WARN); 224d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } else { 225dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com timestamp = formatElapsedTime(new Date().getTime() - lastRun); 226d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 227d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 228d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com String brokeThisMessage = ""; 2293a4c5dbace966faa26dcbc04e40470efad37ef01jsharpe@google.com ResultValue mostRecentResultValue = annotatedOutcome.getMostRecentResultValue(null); 2303a4c5dbace966faa26dcbc04e40470efad37ef01jsharpe@google.com if (mostRecentResultValue != null && resultValue != mostRecentResultValue) { 231d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com if (resultValue == ResultValue.OK) { 232dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com brokeThisMessage = colorString(" (you might have fixed this)", Color.WARN); 233d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } else { 234dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com brokeThisMessage = colorString(" (you might have broken this)", Color.WARN); 235d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 2363a4c5dbace966faa26dcbc04e40470efad37ef01jsharpe@google.com } else if (mostRecentResultValue == null) { 23750bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com brokeThisMessage = colorString(" (no test history available)", Color.WARN); 238d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 239d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 240b5c5c44d0c0a01c278cdac68ae23646682eb8ef7jessewilson@google.com List<ResultValue> previousResultValues = annotatedOutcome.getPreviousResultValues(); 2418918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com int numPreviousResultValues = previousResultValues.size(); 2428918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com int numResultValuesToShow = Math.min(10, numPreviousResultValues); 243b5c5c44d0c0a01c278cdac68ae23646682eb8ef7jessewilson@google.com List<ResultValue> previousResultValuesToShow = previousResultValues.subList( 244b5c5c44d0c0a01c278cdac68ae23646682eb8ef7jessewilson@google.com numPreviousResultValues - numResultValuesToShow, numPreviousResultValues); 245d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 2468918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com StringBuilder sb = new StringBuilder(); 2478918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com sb.append(indent); 2488918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com sb.append(colorString(annotatedOutcome.getOutcome().getName(), color)); 249d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com if (!previousResultValuesToShow.isEmpty()) { 250dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com sb.append(String.format(" [last %d: %s] [last run: %s]", 2518918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com previousResultValuesToShow.size(), 2528918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com generateSparkLine(previousResultValuesToShow), 2538918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com timestamp)); 254d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 2558918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com sb.append(brokeThisMessage); 2568918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com list.add(sb.toString()); 257d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 258d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 2593dafbce3b8d025f465ebf0a4c6d018c7dc31654ejsharpe@google.com newLine(); 260d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com if (!successes.isEmpty()) { 2615b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println("Success summary:"); 262d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com for (String success : successes) { 2635b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println(success); 264d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 265d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 266d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com if (!failures.isEmpty()) { 2675b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println("Failure summary:"); 268d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com for (String failure : failures) { 2695b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println(failure); 270d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 271d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 272d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com if (!skips.isEmpty()) { 2735b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println("Skips summary:"); 274d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com for (String skip : skips) { 2755b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.println(skip); 276d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 2777850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 2785ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray if (!warnings.isEmpty()) { 2795ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray out.println("Warnings summary:"); 2805ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray for (String warning : warnings) { 2815ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray out.println(warning); 2825ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray } 2835ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray } 2847850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 2857850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 286dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com private String formatElapsedTime(long elapsedTime) { 287dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com if (elapsedTime < 0) { 288dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com throw new IllegalArgumentException("non-negative elapsed times only"); 289dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com } 290dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com 291dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com String formatted; 292dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com if (elapsedTime >= DAY_MILLIS) { 293dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com long days = elapsedTime / DAY_MILLIS; 294dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com formatted = String.format("%d days ago", days); 295dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com } else if (elapsedTime >= HOUR_MILLIS) { 296dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com long hours = elapsedTime / HOUR_MILLIS; 297dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com formatted = String.format("%d hours ago", hours); 298dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com } else { 299dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com formatted = "less than an hour ago"; 300dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com } 301dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com 302dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com Color color = elapsedTimeWarningColor(elapsedTime); 303dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com return colorString(formatted, color); 304dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com } 305dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com 306dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com private Color elapsedTimeWarningColor(long elapsedTime) { 307dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com if (elapsedTime < WARNING_HOURS * HOUR_MILLIS) { 308dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com return Color.PASS; 309dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com } else if (elapsedTime < FAILURE_HOURS * HOUR_MILLIS) { 310dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com return Color.WARN; 311dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com } else { 312dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com return Color.FAIL; 313dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com } 314dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com } 315dfebab2a6f40fde48ee696eb0be85ef399891924jsharpe@google.com 316d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com private String generateSparkLine(List<ResultValue> resultValues) { 317d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com StringBuilder sb = new StringBuilder(); 318d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com for (ResultValue resultValue : resultValues) { 319d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com if (resultValue == ResultValue.OK) { 32050bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com sb.append(colorString("\u2713", Color.PASS)); 321d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } else if (resultValue == ResultValue.FAIL) { 32250bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com sb.append(colorString("X", Color.FAIL)); 323d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } else { 32450bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com sb.append(colorString("-", Color.WARN)); 325d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 3261d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com } 327d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com return sb.toString(); 3281d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com } 3291d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com 3307850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 3317850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Prints the action output with appropriate indentation. 3327850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 3334878be6187c09b4b65fed8c99f3aedb9b613d59cjessewilson@google.com public synchronized void streamOutput(CharSequence streamedOutput) { 3343ad417cb75663abaa5d34573d520a57a42ee68d6jessewilson@google.com if (streamedOutput.length() == 0) { 3353ad417cb75663abaa5d34573d520a57a42ee68d6jessewilson@google.com return; 3363ad417cb75663abaa5d34573d520a57a42ee68d6jessewilson@google.com } 3373ad417cb75663abaa5d34573d520a57a42ee68d6jessewilson@google.com 3385884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com String[] lines = messageToLines(streamedOutput.toString()); 3397850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 3400942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com if (currentLine == CurrentLine.VERBOSE && currentStreamMark != null && ansi) { 3415b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com currentStreamMark.reset(); 3425b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com currentStreamMark = null; 3435b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com } else if (currentLine != CurrentLine.STREAMED_OUTPUT) { 3447850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com newLine(); 3455b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print(indent); 3465b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print(indent); 3477850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 3485b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print(lines[0]); 3497850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com currentLine = CurrentLine.STREAMED_OUTPUT; 3507850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 3517850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com for (int i = 1; i < lines.length; i++) { 3527850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com newLine(); 3537850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 3547850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com if (lines[i].length() > 0) { 3555b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print(indent); 3565b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print(indent); 3575b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print(lines[i]); 3587850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com currentLine = CurrentLine.STREAMED_OUTPUT; 3597850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 3607850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 3617850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 3627850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 3637850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 3647850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Inserts a linebreak if necessary. 3657850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 3665884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com protected void newLine() { 3675b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com currentStreamMark = null; 3685b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com 3690942355a74d759fb2e50a002b6b0b93430f07d72bdc@google.com if (currentLine == CurrentLine.VERBOSE && !verbose && ansi) { 3705b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com /* 3715b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com * Verbose means we leave all verbose output on the screen. 3725b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com * Otherwise we overwrite verbose output when new output arrives. 3735b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com */ 3745b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com currentVerboseMark.reset(); 3755b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com } else if (currentLine != CurrentLine.NEW) { 3765b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print("\n"); 3777850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 3787850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 3797850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com currentLine = CurrentLine.NEW; 3807850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 3817850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 3827850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 3837850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Status of a currently-in-progress line of output. 3847850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 3857850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com enum CurrentLine { 3867850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 3877850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 3887850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * The line is blank. 3897850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 3907850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com NEW, 3917850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 3927850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 3937850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * The line contains streamed application output. Additional streamed 3947850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * output may be appended without additional line separators or 3957850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * indentation. 3967850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 3977850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com STREAMED_OUTPUT, 3987850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 3997850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 4007850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * The line contains the name of an action or outcome. The outcome's 4017850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * result (such as "OK") can be appended without additional line 4027850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * separators or indentation. 4037850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 4047850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com NAME, 40588b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com 40688b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com /** 40788b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com * The line contains verbose output, and may be overwritten. 40888b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com */ 40988b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com VERBOSE, 4107850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 4117850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 4127850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com /** 4137850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com * Returns an array containing the lines of the given text. 4147850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com */ 4157850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com private String[] messageToLines(String message) { 4167850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com // pass Integer.MAX_VALUE so split doesn't trim trailing empty strings. 4177850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com return message.split("\r\n|\r|\n", Integer.MAX_VALUE); 4187850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 4197850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com 420d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com private enum Color { 4215ad56966d0c66ab4b733fe97c4b862f5f85711e8Nicolas Geoffray PASS, FAIL, SKIP, WARN, COMMENT; 4228918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com 42350bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com int code = 0; 424d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com 425d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com public int getCode() { 4268918b5cafd482363a48e0bc9ae0114028cda7e79jsharpe@google.com return code; 427d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com } 42850bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com 42950bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com public void setCode(int code) { 43050bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com this.code = code; 43150bd7c43bee5854a7a824265ec224c58c67c698bjsharpe@google.com } 4327850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com } 43388b369c017a9eeee0f074ff4be8506b8952c76e0elliott.n.hughes@gmail.com 434d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com protected String colorString(String message, Color color) { 435d806c4c900e08bf04e07b5c564f2f61d8c490731jsharpe@google.com return useColor ? ("\u001b[" + color.getCode() + ";1m" + message + "\u001b[0m") : message; 4361d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com } 4371d4fef707a383dc57285e6608ca290be48811a85jsharpe@google.com 4385884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com /** 4395884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * This console prints output as it's emitted. It supports at most one 4405884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * action at a time. 4415884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com */ 442b5c5c44d0c0a01c278cdac68ae23646682eb8ef7jessewilson@google.com static class StreamingConsole extends Console { 4435884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com private String currentName; 4445884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com 4455884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com @Override public synchronized void action(String name) { 4465884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com newLine(); 4475b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print("Action " + name); 4485884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com currentName = name; 4495884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com currentLine = CurrentLine.NAME; 4505884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 4515884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com 4525884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com /** 4535884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * Prints the beginning of the named outcome. 4545884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com */ 4555884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com @Override public synchronized void outcome(String name) { 4565884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com // if the outcome and action names are the same, omit the outcome name 4575884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com if (name.equals(currentName)) { 4585884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com return; 4595884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 4605884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com 4615884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com currentName = name; 462d59b17f5dc87d856a01714853d5296d387ff8dedjessewilson@google.com newLine(); 4635b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print(indent + name); 4645884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com currentLine = CurrentLine.NAME; 4655884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 4665884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com 4675884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com @Override public synchronized void streamOutput(String outcomeName, String output) { 4684878be6187c09b4b65fed8c99f3aedb9b613d59cjessewilson@google.com streamOutput(output); 4695884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 4705884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 4715884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com 4725884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com /** 4735884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * This console buffers output, only printing when a result is found. It 4745884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com * supports multiple concurrent actions. 4755884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com */ 476b5c5c44d0c0a01c278cdac68ae23646682eb8ef7jessewilson@google.com static class MultiplexingConsole extends Console { 4775884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com private final Map<String, StringBuilder> bufferedOutputByOutcome = new HashMap<String, StringBuilder>(); 4785884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com 4795884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com @Override public synchronized void streamOutput(String outcomeName, String output) { 4805884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com StringBuilder buffer = bufferedOutputByOutcome.get(outcomeName); 4815884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com if (buffer == null) { 4825884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com buffer = new StringBuilder(); 4835884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com bufferedOutputByOutcome.put(outcomeName, buffer); 4845884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 4855884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com 4865884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com buffer.append(output); 4875884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 4885884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com 4893617522d6678cd021a98b635b46e536ae14b45f3jessewilson@google.com @Override protected synchronized void flushBufferedOutput(String outcomeName) { 4905884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com newLine(); 4915b2080e28c579ae6e563143c6a1788846d8da2f1jessewilson@google.com out.print(indent + outcomeName); 4925884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com currentLine = CurrentLine.NAME; 4935884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com 4945884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com StringBuilder buffer = bufferedOutputByOutcome.remove(outcomeName); 4955884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com if (buffer != null) { 4964878be6187c09b4b65fed8c99f3aedb9b613d59cjessewilson@google.com streamOutput(buffer); 4975884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 4985884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 4995884b0e6bc3eec46f4a374254626a14d128179fajessewilson@google.com } 5007850f3f3da0099b76f09ed64d23e0a43ba4a5c76jessewilson@google.com} 501