From 8f7f5e395c339edc6072763dfecc282ce48637f8 Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Sun, 19 Jul 2020 17:30:43 +0200 Subject: [PATCH 01/14] #185 Add RunListener.onFinalized listener. Inject START ActionNote after build finishes to trigger plugin's logic also on partial log output --- .../plugins/ansicolor/log/LogInjector.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/main/java/hudson/plugins/ansicolor/log/LogInjector.java diff --git a/src/main/java/hudson/plugins/ansicolor/log/LogInjector.java b/src/main/java/hudson/plugins/ansicolor/log/LogInjector.java new file mode 100644 index 0000000..c0b1e4c --- /dev/null +++ b/src/main/java/hudson/plugins/ansicolor/log/LogInjector.java @@ -0,0 +1,93 @@ +package hudson.plugins.ansicolor.log; + +import hudson.Extension; +import hudson.model.Run; +import hudson.model.listeners.RunListener; +import hudson.plugins.ansicolor.action.ActionNote; +import hudson.plugins.ansicolor.action.ColorizedAction; +import org.apache.commons.io.output.CountingOutputStream; + +import java.io.*; +import java.util.Optional; +import java.util.logging.Logger; + +public class LogInjector { + private static final Logger LOGGER = Logger.getLogger(LogInjector.class.getName()); + private static final int CONSOLE_TAIL_DEFAULT = 150; + private static final int BUFFER_SIZE = 64 * 1024; + + public void injectShortlogAction(File logFile, int injectionFromEnd, ActionNote actionNote) { + final File logTmp = new File(String.format("%s.ansicolor.tmp", logFile)); + final File logFileBkp = new File(String.format("%s.bkp", logFile)); + try { + try ( + BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(logFile)); + FileOutputStream outputStream = new FileOutputStream(logTmp); + ) { + final long injectionPoint = logFile.length() - injectionFromEnd * 1024; + final byte[] buf = new byte[BUFFER_SIZE]; + int read; + int totalRead = 0; + int i = 0; + boolean injected = false; + do { + read = inputStream.read(buf); + if (read != -1) { + i++; + if (!injected && totalRead + read >= injectionPoint) { + final BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); + final CountingOutputStream countingStream = new CountingOutputStream(bufferedStream); + actionNote.encodeTo(countingStream); + final long actionNoteLength = countingStream.getByteCount(); + // todo When (if doable) in the middle of another ActionNote render afterwards in order to preserve it + final int spot = (int) (injectionPoint - totalRead + actionNoteLength); + outputStream.write(buf, 0, spot); + bufferedStream.flush(); + outputStream.write(buf, spot, read - spot); + injected = true; + } else { + outputStream.write(buf, 0, read); + } + totalRead += read; + } + } while (read != -1); + } + + if (logFile.renameTo(logFileBkp) && logTmp.renameTo(logFile) && logFileBkp.delete()) { + LOGGER.fine("Substituted original log with the injected one."); + } else { + LOGGER.warning("Error while substituting log file"); + } + } catch (IOException e) { + if (logTmp.exists() && !logTmp.getPath().equals(logFile.getPath())) { + if (!logTmp.delete()) { + LOGGER.fine("Error while deleting tmp file"); + } + } + if (logFileBkp.exists()) { + if (!logFileBkp.delete()) { + LOGGER.fine("Error while deleting backup file"); + } + } + LOGGER.warning("Error while injecting shortlog action: " + e.getMessage()); + } + } + + @Extension + public static class InjectorRunListener extends RunListener> { + @Override + public void onFinalized(Run run) { + super.onFinalized(run); + // todo Find the actual last start action before tail + final Optional startAction = run.getActions(ColorizedAction.class).stream().filter(a -> a.getCommand().equals(ColorizedAction.Command.START)).findAny(); + if (startAction.isPresent()) { + final File logFile = new File(run.getRootDir(), "log"); + if (logFile.isFile()) { + final LogInjector logInjector = new LogInjector(); + final String consoleTail = System.getProperty("hudson.consoleTailKB"); + logInjector.injectShortlogAction(logFile, consoleTail != null ? Integer.parseInt(consoleTail) : CONSOLE_TAIL_DEFAULT, new ActionNote(startAction.get())); + } + } + } + } +} From c96b716f4ad79642789ee26326082a4cdb25bc3e Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Sun, 19 Jul 2020 21:55:09 +0200 Subject: [PATCH 02/14] #185 Multiply with longs - satisfy spotbugs --- src/main/java/hudson/plugins/ansicolor/log/LogInjector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/hudson/plugins/ansicolor/log/LogInjector.java b/src/main/java/hudson/plugins/ansicolor/log/LogInjector.java index c0b1e4c..39e73be 100644 --- a/src/main/java/hudson/plugins/ansicolor/log/LogInjector.java +++ b/src/main/java/hudson/plugins/ansicolor/log/LogInjector.java @@ -24,7 +24,7 @@ public void injectShortlogAction(File logFile, int injectionFromEnd, ActionNote BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(logFile)); FileOutputStream outputStream = new FileOutputStream(logTmp); ) { - final long injectionPoint = logFile.length() - injectionFromEnd * 1024; + final long injectionPoint = logFile.length() - injectionFromEnd * 1024L; final byte[] buf = new byte[BUFFER_SIZE]; int read; int totalRead = 0; From a634a557e5dc0eaf63333d126ae90bfe8b90b164 Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Mon, 27 Jul 2020 20:56:04 +0200 Subject: [PATCH 03/14] #185 Get rid of injecting metadata into log. Add another ColorizedAction to indicate shortlog ansicolor begin --- .../action/ShortlogActionCreator.java | 187 ++++++++++++++++++ .../plugins/ansicolor/log/LogInjector.java | 93 --------- 2 files changed, 187 insertions(+), 93 deletions(-) create mode 100644 src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java delete mode 100644 src/main/java/hudson/plugins/ansicolor/log/LogInjector.java diff --git a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java new file mode 100644 index 0000000..96e9e05 --- /dev/null +++ b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java @@ -0,0 +1,187 @@ +package hudson.plugins.ansicolor.action; + +import hudson.Extension; +import hudson.console.ConsoleNote; +import hudson.model.Run; +import hudson.model.listeners.RunListener; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class ShortlogActionCreator { + private static final Logger LOGGER = Logger.getLogger(ShortlogActionCreator.class.getName()); + private static final int CONSOLE_TAIL_DEFAULT = 150; + private static final int BUFFER_SIZE = 16 * 1024; + private static final byte[] EOL = System.lineSeparator().getBytes(); + + private final LineIdentifier lineIdentifier; + + public ShortlogActionCreator(LineIdentifier lineIdentifier) { + this.lineIdentifier = lineIdentifier; + } + + public ColorizedAction createActionForShortlog(File logFile, Map startActions, int beginFromEnd) { + final ActionContext shortlogStartAction = findStartActionAt(logFile, startActions.keySet(), beginFromEnd); + if (!shortlogStartAction.isEmpty()) { + return new ColorizedAction(lineIdentifier.hash(ConsoleNote.removeNotes(shortlogStartAction.line), 1), startActions.get(shortlogStartAction.serializedAction)); + } + return null; + } + + private ActionContext findStartActionAt(File logFile, Collection serializedActions, int bytesFromEnd) { + try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(logFile))) { + final long shortlogStart = logFile.length() - bytesFromEnd * 1024L; + final byte[] buf = new byte[BUFFER_SIZE]; + int read; + int totalRead = 0; + String currentStartAction = ""; + byte[] partialLine = new byte[]{}; + while ((read = inputStream.read(buf)) != -1) { + final String newAction = findActionInBuffer(serializedActions, buf); + if (!newAction.isEmpty()) { + currentStartAction = newAction; + } + if (totalRead + read >= shortlogStart) { +// final String s = System.lineSeparator(); +// final byte nl = (byte) '\n'; + final int startInBuff = shortlogStart > totalRead ? (int) (shortlogStart - totalRead) : 0; + final int eolPos = indexOfEol(buf, /*nl,*/ startInBuff); + if (eolPos != -1) { + return new ActionContext(currentStartAction, new String(partialLine) + new String(buf, startInBuff, eolPos - startInBuff + EOL.length)); + } else { + // line extends to the next buffer + partialLine = Arrays.copyOfRange(buf, startInBuff, buf.length - 1); + } + } + totalRead += read; + } + } catch (IOException e) { + LOGGER.warning("Cannot search log for actions: " + e.getMessage()); + } + +/* try ( + CountingInputStream inputStream = new CountingInputStream(new FileInputStream(logFile)); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream), BUFFER_SIZE); + ) { + String currentStartAction = ""; + final long shortlogStart = logFile.length() - bytesFromEnd * 1024L; + int posInBuff = 0; + int i = 0; + if (shortlogStart > 0) { + String line; + while ((line = reader.readLine()) != null) { + if (line.contains(ConsoleNote.PREAMBLE_STR)) { + final Optional startAction = serializedActions.stream().filter(line::contains).findFirst(); + if (startAction.isPresent()) { + currentStartAction = startAction.get(); + } + } + final long readBytes = inputStream.getByteCount(); + if (readBytes >= shortlogStart) { + i++; + final String logLine = line + "\n"; + posInBuff += logLine.length(); + +// final byte[] lineBytes = line.getBytes(); +// posInBuff += lineBytes.length + 1; // System.lineSeparator().length(); + + // !line does NOT have to start at (readBytes - BUFFER_SIZE)! + + final long shortlogStartBuff = shortlogStart - (readBytes - BUFFER_SIZE); + if (posInBuff >= shortlogStartBuff) { + return new ActionContext(currentStartAction, logLine.substring((int) (posInBuff - shortlogStartBuff - 21))); + +// return new ActionContext(currentStartAction, new String(Arrays.copyOfRange(lineBytes, (int) (readBytes - shortlogStart), lineBytes.length-1))); + } + } + } + } + } catch (IOException e) { + LOGGER.warning("Cannot search log for actions: " + e.getMessage()); + }*/ + return new ActionContext(); + } + + private String findActionInBuffer(Collection serializedActions, byte[] buf) { + int preamblePos = 0; + while (preamblePos < buf.length && (preamblePos = ConsoleNote.findPreamble(buf, preamblePos, buf.length - preamblePos)) != -1) { + final int begin = preamblePos; + final Optional startAction = serializedActions.stream().filter(sa -> buf.length - begin > sa.length() && sa.equals(new String(buf, begin, sa.length()))).findFirst(); + if (startAction.isPresent()) { + return startAction.get(); + } + preamblePos++; + } + return ""; + } + + + private int indexOfEol(byte[] buf, /*byte needle,*/ int after) { + for (int i = after; i < buf.length; i++) { + if (Arrays.equals(Arrays.copyOfRange(buf, i, i + EOL.length), EOL)) { + return i; + } +// if (buf[i] == needle) { +// return i; +// } + } + return -1; + } + + @Extension + public static class Listener extends RunListener> { + @Override + public void onFinalized(Run run) { + super.onFinalized(run); + Map startActions = run.getActions(ColorizedAction.class).stream() + .filter(a -> a.getCommand().equals(ColorizedAction.Command.START)) + .collect(Collectors.toMap(a -> { + try { + return new ActionNote(a).encode(); + } catch (IOException e) { + LOGGER.warning("Will not be able to identify all ColorizedActions: " + e.getMessage()); + } + return ""; + }, Function.identity())); + if (!startActions.isEmpty()) { + final File logFile = new File(run.getRootDir(), "log"); + if (logFile.isFile()) { + final ShortlogActionCreator shortlogActionCreator = new ShortlogActionCreator(new LineIdentifier()); + final String consoleTail = System.getProperty("hudson.consoleTailKB"); + final ColorizedAction action = shortlogActionCreator.createActionForShortlog(logFile, startActions, consoleTail != null ? Integer.parseInt(consoleTail) : CONSOLE_TAIL_DEFAULT); + if (action != null) { + run.addAction(action); + } + } + } + } + } + + private static class ActionContext { + private final String serializedAction; + private final String line; + + public ActionContext() { + this(null, null); + } + + public ActionContext(String serializedAction, String line) { + this.serializedAction = serializedAction; + this.line = line; + } + + public boolean isEmpty() { + return serializedAction == null && line == null; + } + } +} diff --git a/src/main/java/hudson/plugins/ansicolor/log/LogInjector.java b/src/main/java/hudson/plugins/ansicolor/log/LogInjector.java deleted file mode 100644 index 39e73be..0000000 --- a/src/main/java/hudson/plugins/ansicolor/log/LogInjector.java +++ /dev/null @@ -1,93 +0,0 @@ -package hudson.plugins.ansicolor.log; - -import hudson.Extension; -import hudson.model.Run; -import hudson.model.listeners.RunListener; -import hudson.plugins.ansicolor.action.ActionNote; -import hudson.plugins.ansicolor.action.ColorizedAction; -import org.apache.commons.io.output.CountingOutputStream; - -import java.io.*; -import java.util.Optional; -import java.util.logging.Logger; - -public class LogInjector { - private static final Logger LOGGER = Logger.getLogger(LogInjector.class.getName()); - private static final int CONSOLE_TAIL_DEFAULT = 150; - private static final int BUFFER_SIZE = 64 * 1024; - - public void injectShortlogAction(File logFile, int injectionFromEnd, ActionNote actionNote) { - final File logTmp = new File(String.format("%s.ansicolor.tmp", logFile)); - final File logFileBkp = new File(String.format("%s.bkp", logFile)); - try { - try ( - BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(logFile)); - FileOutputStream outputStream = new FileOutputStream(logTmp); - ) { - final long injectionPoint = logFile.length() - injectionFromEnd * 1024L; - final byte[] buf = new byte[BUFFER_SIZE]; - int read; - int totalRead = 0; - int i = 0; - boolean injected = false; - do { - read = inputStream.read(buf); - if (read != -1) { - i++; - if (!injected && totalRead + read >= injectionPoint) { - final BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); - final CountingOutputStream countingStream = new CountingOutputStream(bufferedStream); - actionNote.encodeTo(countingStream); - final long actionNoteLength = countingStream.getByteCount(); - // todo When (if doable) in the middle of another ActionNote render afterwards in order to preserve it - final int spot = (int) (injectionPoint - totalRead + actionNoteLength); - outputStream.write(buf, 0, spot); - bufferedStream.flush(); - outputStream.write(buf, spot, read - spot); - injected = true; - } else { - outputStream.write(buf, 0, read); - } - totalRead += read; - } - } while (read != -1); - } - - if (logFile.renameTo(logFileBkp) && logTmp.renameTo(logFile) && logFileBkp.delete()) { - LOGGER.fine("Substituted original log with the injected one."); - } else { - LOGGER.warning("Error while substituting log file"); - } - } catch (IOException e) { - if (logTmp.exists() && !logTmp.getPath().equals(logFile.getPath())) { - if (!logTmp.delete()) { - LOGGER.fine("Error while deleting tmp file"); - } - } - if (logFileBkp.exists()) { - if (!logFileBkp.delete()) { - LOGGER.fine("Error while deleting backup file"); - } - } - LOGGER.warning("Error while injecting shortlog action: " + e.getMessage()); - } - } - - @Extension - public static class InjectorRunListener extends RunListener> { - @Override - public void onFinalized(Run run) { - super.onFinalized(run); - // todo Find the actual last start action before tail - final Optional startAction = run.getActions(ColorizedAction.class).stream().filter(a -> a.getCommand().equals(ColorizedAction.Command.START)).findAny(); - if (startAction.isPresent()) { - final File logFile = new File(run.getRootDir(), "log"); - if (logFile.isFile()) { - final LogInjector logInjector = new LogInjector(); - final String consoleTail = System.getProperty("hudson.consoleTailKB"); - logInjector.injectShortlogAction(logFile, consoleTail != null ? Integer.parseInt(consoleTail) : CONSOLE_TAIL_DEFAULT, new ActionNote(startAction.get())); - } - } - } - } -} From 15be927ebec702ffd9e59a17ae345c1f2fe08b70 Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Mon, 27 Jul 2020 20:58:03 +0200 Subject: [PATCH 04/14] #185 Allow identifying ColorizedAction with a String which can be used more broadly and is backwards serializable --- .../ansicolor/action/ColorizedAction.java | 18 ++++++++++++++---- .../ansicolor/action/ActionNoteTest.java | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/hudson/plugins/ansicolor/action/ColorizedAction.java b/src/main/java/hudson/plugins/ansicolor/action/ColorizedAction.java index d1a44ab..a6e4969 100644 --- a/src/main/java/hudson/plugins/ansicolor/action/ColorizedAction.java +++ b/src/main/java/hudson/plugins/ansicolor/action/ColorizedAction.java @@ -41,7 +41,7 @@ public class ColorizedAction extends InvisibleAction { static final ColorizedAction CONTINUE = new ColorizedAction("", Command.CONTINUE); static final ColorizedAction IGNORE = new ColorizedAction("", Command.IGNORE); - private final UUID id; + private final String id; private final String colorMapName; @@ -55,12 +55,18 @@ public enum Command { } public ColorizedAction(String colorMapName, Command command) { - id = UUID.randomUUID(); + id = UUID.randomUUID().toString(); this.colorMapName = colorMapName == null || colorMapName.isEmpty() ? AnsiColorMap.DefaultName : colorMapName; this.command = command; } - public UUID getId() { + public ColorizedAction(String id, ColorizedAction other) { + this.id = id; + colorMapName = other.colorMapName; + command = other.command; + } + + public String getId() { return id; } @@ -79,8 +85,12 @@ public static ColorizedAction parseAction(MarkupText text, Run run) { final int from = actionIdOffset + TAG_ACTION_BEGIN.length() + 1; final int to = line.indexOf("\"", from); final String id = line.substring(from, to); - return run.getActions(ColorizedAction.class).stream().filter(a -> id.equals(a.getId().toString())).findAny().orElse(CONTINUE); + return run.getActions(ColorizedAction.class).stream().filter(a -> id.equals(a.getId())).findAny().orElse(CONTINUE); } return line.contains(TAG_PIPELINE_INTERNAL) ? IGNORE : CONTINUE; } + + public static ColorizedAction parseAction(String lineContent, long lineNo, Run run, LineIdentifier lineIdentifier) { + return run.getActions(ColorizedAction.class).stream().filter(a -> lineIdentifier.isEqual(lineContent, lineNo, a.id)).findAny().orElse(CONTINUE); + } } diff --git a/src/test/java/hudson/plugins/ansicolor/action/ActionNoteTest.java b/src/test/java/hudson/plugins/ansicolor/action/ActionNoteTest.java index 11ed4ba..8328e54 100644 --- a/src/test/java/hudson/plugins/ansicolor/action/ActionNoteTest.java +++ b/src/test/java/hudson/plugins/ansicolor/action/ActionNoteTest.java @@ -30,7 +30,7 @@ public class ActionNoteTest { @Before public void setUp() throws Exception { - when(colorizedAction.getId()).thenReturn(UUID); + when(colorizedAction.getId()).thenReturn(UUID.toString()); actionNote = new ActionNote(colorizedAction); } From a2208929a79c8e3a94d26a5b5f93f55b8a8afdac Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Mon, 27 Jul 2020 21:11:05 +0200 Subject: [PATCH 05/14] #185 Hash line content with its ordinal number to uniquely identify it --- .../ansicolor/action/LineIdentifier.java | 31 +++++++++++ .../action/ShortlogActionCreator.java | 52 +------------------ 2 files changed, 33 insertions(+), 50 deletions(-) create mode 100644 src/main/java/hudson/plugins/ansicolor/action/LineIdentifier.java diff --git a/src/main/java/hudson/plugins/ansicolor/action/LineIdentifier.java b/src/main/java/hudson/plugins/ansicolor/action/LineIdentifier.java new file mode 100644 index 0000000..11dc6e2 --- /dev/null +++ b/src/main/java/hudson/plugins/ansicolor/action/LineIdentifier.java @@ -0,0 +1,31 @@ +package hudson.plugins.ansicolor.action; + +import java.io.Serializable; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +public class LineIdentifier implements Serializable { + private static final String ALGORITHM = "SHA-256"; + private MessageDigest messageDigest; + + private MessageDigest getMessageDigest() { + if (messageDigest == null) { + try { + messageDigest = MessageDigest.getInstance(ALGORITHM); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Cannot get message digest", e); + } + } + return messageDigest; + } + + public String hash(String lineContent, long lineNo) { + final String key = String.join("|", lineContent, String.valueOf(lineNo)); + return Base64.getEncoder().encodeToString(getMessageDigest().digest(key.getBytes())); + } + + public boolean isEqual(String lineContent, long lineNo, String other) { + return hash(lineContent, lineNo).equals(other); + } +} diff --git a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java index 96e9e05..20e1ff7 100644 --- a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java +++ b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java @@ -9,7 +9,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Collection; import java.util.Map; @@ -52,10 +51,8 @@ private ActionContext findStartActionAt(File logFile, Collection seriali currentStartAction = newAction; } if (totalRead + read >= shortlogStart) { -// final String s = System.lineSeparator(); -// final byte nl = (byte) '\n'; final int startInBuff = shortlogStart > totalRead ? (int) (shortlogStart - totalRead) : 0; - final int eolPos = indexOfEol(buf, /*nl,*/ startInBuff); + final int eolPos = indexOfEol(buf, startInBuff); if (eolPos != -1) { return new ActionContext(currentStartAction, new String(partialLine) + new String(buf, startInBuff, eolPos - startInBuff + EOL.length)); } else { @@ -68,47 +65,6 @@ private ActionContext findStartActionAt(File logFile, Collection seriali } catch (IOException e) { LOGGER.warning("Cannot search log for actions: " + e.getMessage()); } - -/* try ( - CountingInputStream inputStream = new CountingInputStream(new FileInputStream(logFile)); - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream), BUFFER_SIZE); - ) { - String currentStartAction = ""; - final long shortlogStart = logFile.length() - bytesFromEnd * 1024L; - int posInBuff = 0; - int i = 0; - if (shortlogStart > 0) { - String line; - while ((line = reader.readLine()) != null) { - if (line.contains(ConsoleNote.PREAMBLE_STR)) { - final Optional startAction = serializedActions.stream().filter(line::contains).findFirst(); - if (startAction.isPresent()) { - currentStartAction = startAction.get(); - } - } - final long readBytes = inputStream.getByteCount(); - if (readBytes >= shortlogStart) { - i++; - final String logLine = line + "\n"; - posInBuff += logLine.length(); - -// final byte[] lineBytes = line.getBytes(); -// posInBuff += lineBytes.length + 1; // System.lineSeparator().length(); - - // !line does NOT have to start at (readBytes - BUFFER_SIZE)! - - final long shortlogStartBuff = shortlogStart - (readBytes - BUFFER_SIZE); - if (posInBuff >= shortlogStartBuff) { - return new ActionContext(currentStartAction, logLine.substring((int) (posInBuff - shortlogStartBuff - 21))); - -// return new ActionContext(currentStartAction, new String(Arrays.copyOfRange(lineBytes, (int) (readBytes - shortlogStart), lineBytes.length-1))); - } - } - } - } - } catch (IOException e) { - LOGGER.warning("Cannot search log for actions: " + e.getMessage()); - }*/ return new ActionContext(); } @@ -125,15 +81,11 @@ private String findActionInBuffer(Collection serializedActions, byte[] b return ""; } - - private int indexOfEol(byte[] buf, /*byte needle,*/ int after) { + private int indexOfEol(byte[] buf, int after) { for (int i = after; i < buf.length; i++) { if (Arrays.equals(Arrays.copyOfRange(buf, i, i + EOL.length), EOL)) { return i; } -// if (buf[i] == needle) { -// return i; -// } } return -1; } From 1f25f074eb0b53564550b25611122ae52c0887de Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Mon, 27 Jul 2020 21:13:07 +0200 Subject: [PATCH 06/14] #185 Identify first line while rendering log to allow shortlog coloring to kick-in --- .../ansicolor/ColorConsoleAnnotator.java | 26 ++++++++++++++++--- .../plugins/ansicolor/action/ActionNote.java | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/main/java/hudson/plugins/ansicolor/ColorConsoleAnnotator.java b/src/main/java/hudson/plugins/ansicolor/ColorConsoleAnnotator.java index 03ca147..aaf7b9a 100644 --- a/src/main/java/hudson/plugins/ansicolor/ColorConsoleAnnotator.java +++ b/src/main/java/hudson/plugins/ansicolor/ColorConsoleAnnotator.java @@ -31,6 +31,7 @@ import hudson.model.Queue; import hudson.model.Run; import hudson.plugins.ansicolor.action.ColorizedAction; +import hudson.plugins.ansicolor.action.LineIdentifier; import jenkins.model.Jenkins; import org.apache.commons.io.output.CountingOutputStream; import org.apache.commons.io.output.NullOutputStream; @@ -59,25 +60,38 @@ final class ColorConsoleAnnotator extends ConsoleAnnotator { private final String defaultColorMapName; + private final LineIdentifier lineIdentifier; + @CheckForNull private String colorMapName; @Nonnull private List openTags = Collections.emptyList(); - private ColorConsoleAnnotator(String defaultColorMapName) { + private long lineNo; + + private ColorConsoleAnnotator(String defaultColorMapName, LineIdentifier lineIdentifier, long startLineNo) { this.defaultColorMapName = defaultColorMapName; + this.lineIdentifier = lineIdentifier; + this.lineNo = startLineNo; } @Override public ConsoleAnnotator annotate(@Nonnull Object context, @Nonnull MarkupText text) { - final ColorizedAction colorizedAction = ColorizedAction.parseAction(text, runOf(context)); + lineNo++; + Run run = runOf(context); + if (run == null) { + return this; + } + final ColorizedAction colorizedAction = lineNo == 1 + ? ColorizedAction.parseAction(text.getText(), lineNo, run, lineIdentifier) + : ColorizedAction.parseAction(text, run); switch (colorizedAction.getCommand()) { case START: colorMapName = colorizedAction.getColorMapName(); break; case STOP: - return FACTORY.newInstance(context); + return FACTORY.newInstance(context, lineNo); case IGNORE: return this; default: @@ -212,7 +226,11 @@ public static final class Factory extends ConsoleAnnotatorFactory { @Override public ConsoleAnnotator newInstance(Object context) { - return new ColorConsoleAnnotator(Jenkins.get().getDescriptorByType(AnsiColorBuildWrapper.DescriptorImpl.class).getGlobalColorMapName()); + return newInstance(context, 0); + } + + private ConsoleAnnotator newInstance(Object context, long startLineNo) { + return new ColorConsoleAnnotator(Jenkins.get().getDescriptorByType(AnsiColorBuildWrapper.DescriptorImpl.class).getGlobalColorMapName(), new LineIdentifier(), startLineNo); } } } diff --git a/src/main/java/hudson/plugins/ansicolor/action/ActionNote.java b/src/main/java/hudson/plugins/ansicolor/action/ActionNote.java index b3d44a6..207e512 100644 --- a/src/main/java/hudson/plugins/ansicolor/action/ActionNote.java +++ b/src/main/java/hudson/plugins/ansicolor/action/ActionNote.java @@ -16,7 +16,7 @@ public class ActionNote extends ConsoleNote> { private final String actionId; public ActionNote(ColorizedAction action) { - actionId = action.getId().toString(); + actionId = action.getId(); } @Override From 28c81f909ba1f17abf247e44e3e6364df79d13c6 Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Mon, 27 Jul 2020 22:23:38 +0200 Subject: [PATCH 07/14] #185 Calculate hash based on line content. Test --- .../ansicolor/action/LineIdentifier.java | 7 +++-- .../action/ShortlogActionCreator.java | 13 +++++--- .../ansicolor/action/LineIdentifierTest.java | 31 +++++++++++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 src/test/java/hudson/plugins/ansicolor/action/LineIdentifierTest.java diff --git a/src/main/java/hudson/plugins/ansicolor/action/LineIdentifier.java b/src/main/java/hudson/plugins/ansicolor/action/LineIdentifier.java index 11dc6e2..062c402 100644 --- a/src/main/java/hudson/plugins/ansicolor/action/LineIdentifier.java +++ b/src/main/java/hudson/plugins/ansicolor/action/LineIdentifier.java @@ -5,9 +5,12 @@ import java.security.NoSuchAlgorithmException; import java.util.Base64; +import static java.nio.charset.StandardCharsets.UTF_8; + public class LineIdentifier implements Serializable { private static final String ALGORITHM = "SHA-256"; - private MessageDigest messageDigest; + private static final long serialVersionUID = 1; + private transient MessageDigest messageDigest; private MessageDigest getMessageDigest() { if (messageDigest == null) { @@ -22,7 +25,7 @@ private MessageDigest getMessageDigest() { public String hash(String lineContent, long lineNo) { final String key = String.join("|", lineContent, String.valueOf(lineNo)); - return Base64.getEncoder().encodeToString(getMessageDigest().digest(key.getBytes())); + return Base64.getEncoder().encodeToString(getMessageDigest().digest(key.getBytes(UTF_8))); } public boolean isEqual(String lineContent, long lineNo, String other) { diff --git a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java index 20e1ff7..2aaa041 100644 --- a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java +++ b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java @@ -9,6 +9,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.Map; @@ -17,11 +18,13 @@ import java.util.logging.Logger; import java.util.stream.Collectors; +import static java.nio.charset.StandardCharsets.UTF_8; + public class ShortlogActionCreator { private static final Logger LOGGER = Logger.getLogger(ShortlogActionCreator.class.getName()); private static final int CONSOLE_TAIL_DEFAULT = 150; private static final int BUFFER_SIZE = 16 * 1024; - private static final byte[] EOL = System.lineSeparator().getBytes(); + private static final byte[] EOL = System.lineSeparator().getBytes(UTF_8); private final LineIdentifier lineIdentifier; @@ -44,7 +47,7 @@ private ActionContext findStartActionAt(File logFile, Collection seriali int read; int totalRead = 0; String currentStartAction = ""; - byte[] partialLine = new byte[]{}; + String partialLine = ""; while ((read = inputStream.read(buf)) != -1) { final String newAction = findActionInBuffer(serializedActions, buf); if (!newAction.isEmpty()) { @@ -54,10 +57,10 @@ private ActionContext findStartActionAt(File logFile, Collection seriali final int startInBuff = shortlogStart > totalRead ? (int) (shortlogStart - totalRead) : 0; final int eolPos = indexOfEol(buf, startInBuff); if (eolPos != -1) { - return new ActionContext(currentStartAction, new String(partialLine) + new String(buf, startInBuff, eolPos - startInBuff + EOL.length)); + return new ActionContext(currentStartAction, partialLine + new String(buf, startInBuff, eolPos - startInBuff + EOL.length, UTF_8)); } else { // line extends to the next buffer - partialLine = Arrays.copyOfRange(buf, startInBuff, buf.length - 1); + partialLine = new String(Arrays.copyOfRange(buf, startInBuff, buf.length - 1), UTF_8); } } totalRead += read; @@ -72,7 +75,7 @@ private String findActionInBuffer(Collection serializedActions, byte[] b int preamblePos = 0; while (preamblePos < buf.length && (preamblePos = ConsoleNote.findPreamble(buf, preamblePos, buf.length - preamblePos)) != -1) { final int begin = preamblePos; - final Optional startAction = serializedActions.stream().filter(sa -> buf.length - begin > sa.length() && sa.equals(new String(buf, begin, sa.length()))).findFirst(); + final Optional startAction = serializedActions.stream().filter(sa -> buf.length - begin > sa.length() && sa.equals(new String(buf, begin, sa.length(), UTF_8))).findFirst(); if (startAction.isPresent()) { return startAction.get(); } diff --git a/src/test/java/hudson/plugins/ansicolor/action/LineIdentifierTest.java b/src/test/java/hudson/plugins/ansicolor/action/LineIdentifierTest.java new file mode 100644 index 0000000..3e439a7 --- /dev/null +++ b/src/test/java/hudson/plugins/ansicolor/action/LineIdentifierTest.java @@ -0,0 +1,31 @@ +package hudson.plugins.ansicolor.action; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class LineIdentifierTest { + + private LineIdentifier lineIdentifier; + + @Before + public void setUp() throws Exception { + lineIdentifier = new LineIdentifier(); + } + + @Test + public void canHashLine() { + assertEquals("ojq32twB56Mha38FSpsOvwxZDdkOKa/SveGHDC4tgHY=", lineIdentifier.hash("test line 123", 735)); + } + + @Test + public void canDetermineIsEqualPositive() { + assertTrue(lineIdentifier.isEqual("\u001B[92m\u001B[1mlightgreen bold \u001B[92m\u001B[22mlightgreen normal\u001B[0m", 67, "1mo5/lFK3s3+qaz1vK3o62k+EAJkd8Q0j3dMRH8Wkh4=")); + } + + @Test + public void canDetermineIsEqualNegative() { + assertFalse(lineIdentifier.isEqual("\u001B[92m\u001B[1mlightgreen bold \u001B[92m\u001B[22mlightgreen normal\u001B[0m", 67, "bogus")); + } +} From 723e74340d97b741f3e4e0239cc25d2d2f969c7b Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Mon, 27 Jul 2020 22:43:04 +0200 Subject: [PATCH 08/14] #185 Indicate shortlog ansicolor begin for logs longer than shortlog limit --- .../action/ShortlogActionCreator.java | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java index 2aaa041..733b494 100644 --- a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java +++ b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java @@ -9,7 +9,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; import java.util.Map; @@ -43,27 +42,29 @@ public ColorizedAction createActionForShortlog(File logFile, Map serializedActions, int bytesFromEnd) { try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(logFile))) { final long shortlogStart = logFile.length() - bytesFromEnd * 1024L; - final byte[] buf = new byte[BUFFER_SIZE]; - int read; - int totalRead = 0; - String currentStartAction = ""; - String partialLine = ""; - while ((read = inputStream.read(buf)) != -1) { - final String newAction = findActionInBuffer(serializedActions, buf); - if (!newAction.isEmpty()) { - currentStartAction = newAction; - } - if (totalRead + read >= shortlogStart) { - final int startInBuff = shortlogStart > totalRead ? (int) (shortlogStart - totalRead) : 0; - final int eolPos = indexOfEol(buf, startInBuff); - if (eolPos != -1) { - return new ActionContext(currentStartAction, partialLine + new String(buf, startInBuff, eolPos - startInBuff + EOL.length, UTF_8)); - } else { - // line extends to the next buffer - partialLine = new String(Arrays.copyOfRange(buf, startInBuff, buf.length - 1), UTF_8); + if (shortlogStart > 0) { + final byte[] buf = new byte[BUFFER_SIZE]; + int read; + int totalRead = 0; + String currentStartAction = ""; + String partialLine = ""; + while ((read = inputStream.read(buf)) != -1) { + final String newAction = findActionInBuffer(serializedActions, buf); + if (!newAction.isEmpty()) { + currentStartAction = newAction; + } + if (totalRead + read >= shortlogStart) { + final int startInBuff = shortlogStart > totalRead ? (int) (shortlogStart - totalRead) : 0; + final int eolPos = indexOfEol(buf, startInBuff); + if (eolPos != -1) { + return new ActionContext(currentStartAction, partialLine + new String(buf, startInBuff, eolPos - startInBuff + EOL.length, UTF_8)); + } else { + // line extends to the next buffer + partialLine = new String(Arrays.copyOfRange(buf, startInBuff, buf.length - 1), UTF_8); + } } + totalRead += read; } - totalRead += read; } } catch (IOException e) { LOGGER.warning("Cannot search log for actions: " + e.getMessage()); From 022c91aae9917d42265f44a884a2fd2c93ce49c2 Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Tue, 28 Jul 2020 22:46:05 +0200 Subject: [PATCH 09/14] #185 Allow specifying eol character(s). Implement tests for preparing additional action used in shortlog --- .gitattributes | 1 + .../action/ShortlogActionCreator.java | 11 ++-- .../action/ShortlogActionCreatorTest.java | 61 +++++++++++++++++++ .../testlog-crlf.log | 30 +++++++++ .../ShortlogActionCreatorTest/testlog.log | 30 +++++++++ 5 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 .gitattributes create mode 100644 src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java create mode 100644 src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-crlf.log create mode 100644 src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog.log diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ef07baf --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-crlf.log eol=crlf diff --git a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java index 733b494..c11af71 100644 --- a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java +++ b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java @@ -23,12 +23,13 @@ public class ShortlogActionCreator { private static final Logger LOGGER = Logger.getLogger(ShortlogActionCreator.class.getName()); private static final int CONSOLE_TAIL_DEFAULT = 150; private static final int BUFFER_SIZE = 16 * 1024; - private static final byte[] EOL = System.lineSeparator().getBytes(UTF_8); private final LineIdentifier lineIdentifier; + private final byte[] eol; - public ShortlogActionCreator(LineIdentifier lineIdentifier) { + public ShortlogActionCreator(LineIdentifier lineIdentifier, String eol) { this.lineIdentifier = lineIdentifier; + this.eol = eol.getBytes(UTF_8); } public ColorizedAction createActionForShortlog(File logFile, Map startActions, int beginFromEnd) { @@ -57,7 +58,7 @@ private ActionContext findStartActionAt(File logFile, Collection seriali final int startInBuff = shortlogStart > totalRead ? (int) (shortlogStart - totalRead) : 0; final int eolPos = indexOfEol(buf, startInBuff); if (eolPos != -1) { - return new ActionContext(currentStartAction, partialLine + new String(buf, startInBuff, eolPos - startInBuff + EOL.length, UTF_8)); + return new ActionContext(currentStartAction, partialLine + new String(buf, startInBuff, eolPos - startInBuff + eol.length, UTF_8)); } else { // line extends to the next buffer partialLine = new String(Arrays.copyOfRange(buf, startInBuff, buf.length - 1), UTF_8); @@ -87,7 +88,7 @@ private String findActionInBuffer(Collection serializedActions, byte[] b private int indexOfEol(byte[] buf, int after) { for (int i = after; i < buf.length; i++) { - if (Arrays.equals(Arrays.copyOfRange(buf, i, i + EOL.length), EOL)) { + if (Arrays.equals(Arrays.copyOfRange(buf, i, i + eol.length), eol)) { return i; } } @@ -112,7 +113,7 @@ public void onFinalized(Run run) { if (!startActions.isEmpty()) { final File logFile = new File(run.getRootDir(), "log"); if (logFile.isFile()) { - final ShortlogActionCreator shortlogActionCreator = new ShortlogActionCreator(new LineIdentifier()); + final ShortlogActionCreator shortlogActionCreator = new ShortlogActionCreator(new LineIdentifier(), System.lineSeparator()); final String consoleTail = System.getProperty("hudson.consoleTailKB"); final ColorizedAction action = shortlogActionCreator.createActionForShortlog(logFile, startActions, consoleTail != null ? Integer.parseInt(consoleTail) : CONSOLE_TAIL_DEFAULT); if (action != null) { diff --git a/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java b/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java new file mode 100644 index 0000000..4798531 --- /dev/null +++ b/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java @@ -0,0 +1,61 @@ +package hudson.plugins.ansicolor.action; + +import hudson.console.ConsoleNote; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; +import java.util.HashMap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ShortlogActionCreatorTest { + private ShortlogActionCreator shortlogActionCreator; + + @Mock + private LineIdentifier lineIdentifier; + + @Before + public void setUp() throws Exception { + shortlogActionCreator = new ShortlogActionCreator(lineIdentifier, "\n"); + } + + @Test + public void canCreateActionForShortlog() { + final String eol = "\n"; + canCreateActionForShortlog(shortlogActionCreator, eol, "testlog.log"); + } + + @Test + public void canCreateActionForShortlogForWindows() { + final String eol = "\r\n"; + canCreateActionForShortlog(new ShortlogActionCreator(lineIdentifier, eol), eol, "testlog-crlf.log"); + } + + private void canCreateActionForShortlog(ShortlogActionCreator shortlogActionCreator, String eol, String logFile) { + final String lineHash = "mock-line-hash"; + final ColorizedAction colorizedAction = new ColorizedAction("xterm", ColorizedAction.Command.START); + final String serializedNote = ""; + when(lineIdentifier.hash(eq("[Pipeline] echo" + eol), eq(1L))).thenReturn(lineHash); + + final File file = new File(getClass().getResource(String.join("/", "", getClass().getName().replace('.', '/'), logFile)).getFile()); + final HashMap startActions = new HashMap<>(); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("css", ColorizedAction.Command.START)); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("vga", ColorizedAction.Command.START)); + startActions.put(ConsoleNote.PREAMBLE_STR + serializedNote, colorizedAction); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("gnome-terminal", ColorizedAction.Command.START)); + + final ColorizedAction shortlogAction = shortlogActionCreator.createActionForShortlog(file, startActions, 3); + assertEquals(colorizedAction.getColorMapName(), shortlogAction.getColorMapName()); + assertEquals(colorizedAction.getCommand(), shortlogAction.getCommand()); + assertNotEquals(colorizedAction.getId(), shortlogAction.getId()); + assertEquals(lineHash, shortlogAction.getId()); + } +} diff --git a/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-crlf.log b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-crlf.log new file mode 100644 index 0000000..92c45d6 --- /dev/null +++ b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-crlf.log @@ -0,0 +1,30 @@ +Started by user unknown or anonymous +Running in Durability level: MAX_SURVIVABILITY +ha:////4HPR6yAvrg8Qw/J1EkAW+oRkK9sifK+1Yg1owCCMcljNAAAAoh+LCAAAAAAAAP9tjTEOwjAQBM8BClpKHuFItIiK1krDC0x8GCfWnbEdkooX8TX+gCESFVvtrLSa5wtWKcKBo5UdUu8otU4GP9jS5Mixv3geZcdn2TIl9igbHBs2eJyx4YwwR1SwULBGaj0nRzbDRnX6rmuvydanHMu2V1A5c4MHCFXMWcf8hSnC9jqYxPTz/BXAFEIGsfuclm8zQVqFvQAAAA==[Pipeline] Start of Pipeline +ha:////4EQtDedZSq5v6ZloVQtQLa+CDepKC0Xq9VIk43niZUUNAAAApR+LCAAAAAAAAP9tjTEOwjAUQ3+KOrAycohUghExsUZZOEFIQkgb/d8mKe3EibgadyBQiQlLlmxL1nu+oE4RjhQdby12HpP2vA+jK4lPFLtroIm3dOGaMFGwXNpJkrGnpUrKFhaxClYC1hZ1oOTRZdiIVt1VExS65pxj2Q4CKm8GeAAThZxVzN8yR9jeRpMIf5y/AJj7DGxXvP/86jduZBmjwAAAAA==[Pipeline] ansiColor +ha:////4LmN+Pw6n1NaJQ7ErOnt3MfVLo5kUJToDDJ6IHS1hLlyAAAApR+LCAAAAAAAAP9tjTEOwjAUQ3+KOrAycoh0gA0xsUZZOEFIQkgb/d8mKe3EibgadyBQiQlLlmxL1nu+oE4RjhQdby12HpP2vA+jK4lPFLtroIm3dOGaMFGwXNpJkrGnpUrKFhaxClYC1hZ1oOTRZdiIVt1VExS65pxj2Q4CKm8GeAAThZxVzN8yR9jeRpMIf5y/AJj7DGxXvP/86jfoP95RwAAAAA==[Pipeline] { +ha:////4LzXftQJX0F1z2gdncUl1iLgtfRtkFgsitFOoiuDpFpPAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGFTMj82QDi+REXUtzQ2Ndk+QUS91EY1MTXWNjyzRDE4NEYwNDEwBbu/A1rwAAAA== +ha:////4Hz6LRLFlcp0y5BZCTN29FoqS4u8Xl/n7YeG3BsPgopsAAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbaGWsbAmNJ0AWEZb8zwLrbuWJvJp3kLiJlZNMMm+a93rDOic4UbLcG+wdZu14DKOti0+U+lugiXu6ck2YKRguzSSpM+cFJRUDS1gDKwEbgzpQdmgLbIVXD9UGhba9lFS/o4DGdQM8gYlqLiqVL8wJdvexy4Q/z18BzLEA29ce4gdpL1fxvAAAAA==[Pipeline] echo +breaks +ha:////4HnB2r4VgzJqgqmrD4g18qXZX2NNIEYvTh5Gkci1D/q5AAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbGBtjZUtoPAGyiLDkfxZYdytP5NW8g8RNrJxkknnTvNcb1jnBiZLl3mDvMGvHYxhtXXyi1N8CTdzTlWvCTMFwaSZJnTkvKKkYWMIaWAnYGNSBskNbYCu8eqg2KLTtpaT6HQU0rhvgCUxUc1GpfGFOsLuPXSb8ef4KYI4F2L72ED+qfHravAAAAA==[Pipeline] echo + lightgreen and separate reset sgr  +ha:////4OYH6MKu52x3PonC1Ud5u7wMz6Wji2CZn9UwqAp4UR5CAAAAox+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbmFgYK1tC4wmQRYQl/7PAult5Iq/mHSRuYuUkk8yb5r3esM4JTpQs9wZ7h1k7HsNo6+ITpf4WaOKerlwTZgqGSzNJ6sx5QUnFwBLWwErAxqAOlB3aAlvh1UO1QaFtLyXV7yigcd0AT2CimotK5Qtzgt197DLhz/NXAHMswPa1h/gB601hw7wAAAA=[Pipeline] echo +works ok +ha:////4AgWlppQmatIvnCLDCZnR0046dt548KF4Fy1mnNPTMSuAAAAox+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbWJgYK1tC4wmQRYQl/7PAult5Iq/mHSRuYuUkk8yb5r3esM4JTpQs9wZ7h1k7HsNo6+ITpf4WaOKerlwTZgqGSzNJ6sx5QUnFwBLWwErAxqAOlB3aAlvh1UO1QaFtLyXV7yigcd0AT2CimotK5Qtzgt197DLhz/NXAHMswPa1h/gBJFH5RLwAAAA=[Pipeline] echo +lightgreen and combined reset sgr  +ha:////4HoFmWLKIkI9Tuz2n4mKzKuCIPjtwb+WA7Faafh6YRZXAAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTb2BhjZUtoPAGyiLDkfxZYdytP5NW8g8RNrJxkknnTvNcb1jnBiZLl3mDvMGvHYxhtXXyi1N8CTdzTlWvCTMFwaSZJnTkvKKkYWMIaWAnYGNSBskNbYCu8eqg2KLTtpaT6HQU0rhvgCUxUc1GpfGFOsLuPXSb8ef4KYI4F2L72ED9lYOJdvAAAAA==[Pipeline] echo +works ok +ha:////4D7Zgg25Qjm2MWdX0qwWsDuYIDqNO9EUW/ZSVggXMIZxAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIBiQ5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+S7bfcBVVhdU70AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal +ha:////4Ln4Ti1usH0TUFY6h4qFT8ZreUeRo7lDbzMqBX3EjTZfAAAAox+LCAAAAAAAAP9tjbEOgjAURS8YB1dHP6KExM04uTYsfkGFWgvNe9g+hMkv8tf8B4kkTt7pnrOc1xvrFHHk6FRrqfOUaq/6MLj5qZFjdw08qpYvqmZKHKyq7FhxY08LViwWy7IcK42NpTpw8uQEW92ahymCIVecJc7uoJH75o4nMj2XxUT5whSxuw1NYvp1/gYw9YJsL8jLsv8AFGlGSr0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal +ha:[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal  bold  +ha:////4KP1GBTrTdTRwr542XvhrlixmKr1h18H7yH7mpjCqRDSAAAApB+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIREg1CVLRWGl5gEmOcWHfBvpBUvIiv8QciIlGx1c4083pjmSKOHJ1qLLWeUuVVF3o3PTVwbK+BB9XwRVVMiYNVpR1Kru1pxpLFYl6WY6GxslQFTp6cYK0b8zBFMOSKs8TJHTRyX9/xRKanspgoXxgjNre+Tky/zt8Axk6Q7QT5dt99ABzjn4K9AAAA[Pipeline] echo +ha:////4D0d8D/qHeobVG5vhdYwbTDvY17TvoYDq8B0eZ7C09uyAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYQDiA5R0VppeIFxjHFi3QX7glPxIr7GH4iIRMVWO9PM6w2LFOFI0cnWYucxGS/7MLjpyUyxuwbKsqWLNISJgpW1zTU19jRjTWxhniigVLC0aAIlj45hpVr90FXQ6Kozx8kdFBS+ucMThJrKrCN/YYywvg1NIvx1/gZg7HuGcrfdMIj9B9mBdbK+AAAA[Pipeline] } +ha:////4KQeVjBBTsWoImddPCU9r94N4M9pBeonazxlGiHtR3/wAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGlSQL08SUpMRE3cTUZENdE2MLI90kMxMTXTMz42QjI4vUFAsTEwBd9k+WrwAAAA== +ha:////4MI76wcMq2dN05ewLG1CD7KwCRZtEJik/cVtVY3EpE2HAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYSjQImoaK00vMDExjix7oJ9wal4EV/jD0REomKrnWnm9YZVinCk6GRnsfeYWi+HMLr5yUyxvwbKsqOLbAkTBSsbmxsy9rRgQ2xhmSigVLC22AZKHh3DRnX6oaug0VVnjrM7KCi8ucMThJrLrCN/YYqwvY0mEf46fwMwDQNDuatrBrH/AMo9cRG+AAAA[Pipeline] // ansiColor +ha:////4H7BbEWee3fTq4xXyQx78tPkFuqShNpo39bJTP4b4nc7AAAAox+LCAAAAAAAAP9tjTESgjAQRT8yFraWHiKMtI6VbYbGE0SIMZDZxWQRKk/k1byDjMxY+av/XvNeb6xTxJGjU62lzlOqverD4OanRo7dNfCoWr6omilxsKqyY8WNPS1YsVgsy1bINTaW6sDJkxNsdWsepgiGXHGWOLuDxso3dzyR6bksJsoXpojdbWgS06/zN4Cp7wV5uS8FWfkBjHfWIb4AAAA=[Pipeline] End of Pipeline +Finished: SUCCESS diff --git a/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog.log b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog.log new file mode 100644 index 0000000..0168447 --- /dev/null +++ b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog.log @@ -0,0 +1,30 @@ +Started by user unknown or anonymous +Running in Durability level: MAX_SURVIVABILITY +ha:////4HPR6yAvrg8Qw/J1EkAW+oRkK9sifK+1Yg1owCCMcljNAAAAoh+LCAAAAAAAAP9tjTEOwjAQBM8BClpKHuFItIiK1krDC0x8GCfWnbEdkooX8TX+gCESFVvtrLSa5wtWKcKBo5UdUu8otU4GP9jS5Mixv3geZcdn2TIl9igbHBs2eJyx4YwwR1SwULBGaj0nRzbDRnX6rmuvydanHMu2V1A5c4MHCFXMWcf8hSnC9jqYxPTz/BXAFEIGsfuclm8zQVqFvQAAAA==[Pipeline] Start of Pipeline +ha:////4EQtDedZSq5v6ZloVQtQLa+CDepKC0Xq9VIk43niZUUNAAAApR+LCAAAAAAAAP9tjTEOwjAUQ3+KOrAycohUghExsUZZOEFIQkgb/d8mKe3EibgadyBQiQlLlmxL1nu+oE4RjhQdby12HpP2vA+jK4lPFLtroIm3dOGaMFGwXNpJkrGnpUrKFhaxClYC1hZ1oOTRZdiIVt1VExS65pxj2Q4CKm8GeAAThZxVzN8yR9jeRpMIf5y/AJj7DGxXvP/86jduZBmjwAAAAA==[Pipeline] ansiColor +ha:////4LmN+Pw6n1NaJQ7ErOnt3MfVLo5kUJToDDJ6IHS1hLlyAAAApR+LCAAAAAAAAP9tjTEOwjAUQ3+KOrAycoh0gA0xsUZZOEFIQkgb/d8mKe3EibgadyBQiQlLlmxL1nu+oE4RjhQdby12HpP2vA+jK4lPFLtroIm3dOGaMFGwXNpJkrGnpUrKFhaxClYC1hZ1oOTRZdiIVt1VExS65pxj2Q4CKm8GeAAThZxVzN8yR9jeRpMIf5y/AJj7DGxXvP/86jfoP95RwAAAAA==[Pipeline] { +ha:////4LzXftQJX0F1z2gdncUl1iLgtfRtkFgsitFOoiuDpFpPAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGFTMj82QDi+REXUtzQ2Ndk+QUS91EY1MTXWNjyzRDE4NEYwNDEwBbu/A1rwAAAA== +ha:////4Hz6LRLFlcp0y5BZCTN29FoqS4u8Xl/n7YeG3BsPgopsAAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbaGWsbAmNJ0AWEZb8zwLrbuWJvJp3kLiJlZNMMm+a93rDOic4UbLcG+wdZu14DKOti0+U+lugiXu6ck2YKRguzSSpM+cFJRUDS1gDKwEbgzpQdmgLbIVXD9UGhba9lFS/o4DGdQM8gYlqLiqVL8wJdvexy4Q/z18BzLEA29ce4gdpL1fxvAAAAA==[Pipeline] echo +breaks +ha:////4HnB2r4VgzJqgqmrD4g18qXZX2NNIEYvTh5Gkci1D/q5AAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbGBtjZUtoPAGyiLDkfxZYdytP5NW8g8RNrJxkknnTvNcb1jnBiZLl3mDvMGvHYxhtXXyi1N8CTdzTlWvCTMFwaSZJnTkvKKkYWMIaWAnYGNSBskNbYCu8eqg2KLTtpaT6HQU0rhvgCUxUc1GpfGFOsLuPXSb8ef4KYI4F2L72ED+qfHravAAAAA==[Pipeline] echo + lightgreen and separate reset sgr  +ha:////4OYH6MKu52x3PonC1Ud5u7wMz6Wji2CZn9UwqAp4UR5CAAAAox+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbmFgYK1tC4wmQRYQl/7PAult5Iq/mHSRuYuUkk8yb5r3esM4JTpQs9wZ7h1k7HsNo6+ITpf4WaOKerlwTZgqGSzNJ6sx5QUnFwBLWwErAxqAOlB3aAlvh1UO1QaFtLyXV7yigcd0AT2CimotK5Qtzgt197DLhz/NXAHMswPa1h/gB601hw7wAAAA=[Pipeline] echo +works ok +ha:////4AgWlppQmatIvnCLDCZnR0046dt548KF4Fy1mnNPTMSuAAAAox+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbWJgYK1tC4wmQRYQl/7PAult5Iq/mHSRuYuUkk8yb5r3esM4JTpQs9wZ7h1k7HsNo6+ITpf4WaOKerlwTZgqGSzNJ6sx5QUnFwBLWwErAxqAOlB3aAlvh1UO1QaFtLyXV7yigcd0AT2CimotK5Qtzgt197DLhz/NXAHMswPa1h/gBJFH5RLwAAAA=[Pipeline] echo +lightgreen and combined reset sgr  +ha:////4HoFmWLKIkI9Tuz2n4mKzKuCIPjtwb+WA7Faafh6YRZXAAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTb2BhjZUtoPAGyiLDkfxZYdytP5NW8g8RNrJxkknnTvNcb1jnBiZLl3mDvMGvHYxhtXXyi1N8CTdzTlWvCTMFwaSZJnTkvKKkYWMIaWAnYGNSBskNbYCu8eqg2KLTtpaT6HQU0rhvgCUxUc1GpfGFOsLuPXSb8ef4KYI4F2L72ED9lYOJdvAAAAA==[Pipeline] echo +works ok +ha:////4D7Zgg25Qjm2MWdX0qwWsDuYIDqNO9EUW/ZSVggXMIZxAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIBiQ5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+S7bfcBVVhdU70AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal +ha:////4Ln4Ti1usH0TUFY6h4qFT8ZreUeRo7lDbzMqBX3EjTZfAAAAox+LCAAAAAAAAP9tjbEOgjAURS8YB1dHP6KExM04uTYsfkGFWgvNe9g+hMkv8tf8B4kkTt7pnrOc1xvrFHHk6FRrqfOUaq/6MLj5qZFjdw08qpYvqmZKHKyq7FhxY08LViwWy7IcK42NpTpw8uQEW92ahymCIVecJc7uoJH75o4nMj2XxUT5whSxuw1NYvp1/gYw9YJsL8jLsv8AFGlGSr0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal +ha:[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal  bold lightgreen +ha:////4KP1GBTrTdTRwr542XvhrlixmKr1h18H7yH7mpjCqRDSAAAApB+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIREg1CVLRWGl5gEmOcWHfBvpBUvIiv8QciIlGx1c4083pjmSKOHJ1qLLWeUuVVF3o3PTVwbK+BB9XwRVVMiYNVpR1Kru1pxpLFYl6WY6GxslQFTp6cYK0b8zBFMOSKs8TJHTRyX9/xRKanspgoXxgjNre+Tky/zt8Axk6Q7QT5dt99ABzjn4K9AAAA[Pipeline] echo +ha:////4D0d8D/qHeobVG5vhdYwbTDvY17TvoYDq8B0eZ7C09uyAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYQDiA5R0VppeIFxjHFi3QX7glPxIr7GH4iIRMVWO9PM6w2LFOFI0cnWYucxGS/7MLjpyUyxuwbKsqWLNISJgpW1zTU19jRjTWxhniigVLC0aAIlj45hpVr90FXQ6Kozx8kdFBS+ucMThJrKrCN/YYywvg1NIvx1/gZg7HuGcrfdMIj9B9mBdbK+AAAA[Pipeline] } +ha:////4KQeVjBBTsWoImddPCU9r94N4M9pBeonazxlGiHtR3/wAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGlSQL08SUpMRE3cTUZENdE2MLI90kMxMTXTMz42QjI4vUFAsTEwBd9k+WrwAAAA== +ha:////4MI76wcMq2dN05ewLG1CD7KwCRZtEJik/cVtVY3EpE2HAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYSjQImoaK00vMDExjix7oJ9wal4EV/jD0REomKrnWnm9YZVinCk6GRnsfeYWi+HMLr5yUyxvwbKsqOLbAkTBSsbmxsy9rRgQ2xhmSigVLC22AZKHh3DRnX6oaug0VVnjrM7KCi8ucMThJrLrCN/YYqwvY0mEf46fwMwDQNDuatrBrH/AMo9cRG+AAAA[Pipeline] // ansiColor +ha:////4H7BbEWee3fTq4xXyQx78tPkFuqShNpo39bJTP4b4nc7AAAAox+LCAAAAAAAAP9tjTESgjAQRT8yFraWHiKMtI6VbYbGE0SIMZDZxWQRKk/k1byDjMxY+av/XvNeb6xTxJGjU62lzlOqverD4OanRo7dNfCoWr6omilxsKqyY8WNPS1YsVgsy1bINTaW6sDJkxNsdWsepgiGXHGWOLuDxso3dzyR6bksJsoXpojdbWgS06/zN4Cp7wV5uS8FWfkBjHfWIb4AAAA=[Pipeline] End of Pipeline +Finished: SUCCESS From debc7eec0ccd20143b15f620c716d05506af73cf Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Wed, 29 Jul 2020 22:34:20 +0200 Subject: [PATCH 10/14] #185 Take into account stop actions. Use the last action before shortlog position. Add tests --- .../action/ShortlogActionCreator.java | 52 +++++++-------- .../action/ShortlogActionCreatorTest.java | 64 ++++++++++++++++--- .../testlog-action-not-active.log | 31 +++++++++ .../testlog-crlf.log | 9 +-- .../testlog-no-notes.log | 30 +++++++++ .../ShortlogActionCreatorTest/testlog.log | 5 +- 6 files changed, 152 insertions(+), 39 deletions(-) create mode 100644 src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-action-not-active.log create mode 100644 src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-no-notes.log diff --git a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java index c11af71..478e4dc 100644 --- a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java +++ b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java @@ -11,8 +11,8 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collection; +import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.function.Function; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -32,33 +32,36 @@ public ShortlogActionCreator(LineIdentifier lineIdentifier, String eol) { this.eol = eol.getBytes(UTF_8); } - public ColorizedAction createActionForShortlog(File logFile, Map startActions, int beginFromEnd) { - final ActionContext shortlogStartAction = findStartActionAt(logFile, startActions.keySet(), beginFromEnd); - if (!shortlogStartAction.isEmpty()) { - return new ColorizedAction(lineIdentifier.hash(ConsoleNote.removeNotes(shortlogStartAction.line), 1), startActions.get(shortlogStartAction.serializedAction)); + public ColorizedAction createActionForShortlog(File logFile, Map actions, int shortlogLimit) { + final ActionContext lastAction = findLastActionBefore(logFile, actions.keySet(), shortlogLimit); + if (!lastAction.isEmpty()) { + final ColorizedAction colorizedAction = actions.get(lastAction.serializedAction); + if (ColorizedAction.Command.START.equals(colorizedAction.getCommand())) { + return new ColorizedAction(lineIdentifier.hash(ConsoleNote.removeNotes(lastAction.line), 1), colorizedAction); + } } return null; } - private ActionContext findStartActionAt(File logFile, Collection serializedActions, int bytesFromEnd) { + private ActionContext findLastActionBefore(File logFile, Collection serializedActions, int shortlogLimit) { try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(logFile))) { - final long shortlogStart = logFile.length() - bytesFromEnd * 1024L; + final long shortlogStart = logFile.length() - shortlogLimit * 1024L; if (shortlogStart > 0) { final byte[] buf = new byte[BUFFER_SIZE]; int read; int totalRead = 0; - String currentStartAction = ""; + String lastAction = ""; String partialLine = ""; while ((read = inputStream.read(buf)) != -1) { - final String newAction = findActionInBuffer(serializedActions, buf); - if (!newAction.isEmpty()) { - currentStartAction = newAction; + final int startInBuff = shortlogStart > totalRead ? (int) (shortlogStart - totalRead) : 0; + final String action = findLastAction(serializedActions, buf, startInBuff); + if (!action.isEmpty()) { + lastAction = action; } if (totalRead + read >= shortlogStart) { - final int startInBuff = shortlogStart > totalRead ? (int) (shortlogStart - totalRead) : 0; final int eolPos = indexOfEol(buf, startInBuff); - if (eolPos != -1) { - return new ActionContext(currentStartAction, partialLine + new String(buf, startInBuff, eolPos - startInBuff + eol.length, UTF_8)); + if (eolPos != -1 && !lastAction.isEmpty()) { + return new ActionContext(lastAction, partialLine + new String(buf, startInBuff, eolPos - startInBuff + eol.length, UTF_8)); } else { // line extends to the next buffer partialLine = new String(Arrays.copyOfRange(buf, startInBuff, buf.length - 1), UTF_8); @@ -73,17 +76,15 @@ private ActionContext findStartActionAt(File logFile, Collection seriali return new ActionContext(); } - private String findActionInBuffer(Collection serializedActions, byte[] buf) { + private String findLastAction(Collection serializedActions, byte[] buf, int maxPos) { + String lastAction = ""; int preamblePos = 0; - while (preamblePos < buf.length && (preamblePos = ConsoleNote.findPreamble(buf, preamblePos, buf.length - preamblePos)) != -1) { + while (preamblePos < maxPos && (preamblePos = ConsoleNote.findPreamble(buf, preamblePos, buf.length - preamblePos)) != -1) { final int begin = preamblePos; - final Optional startAction = serializedActions.stream().filter(sa -> buf.length - begin > sa.length() && sa.equals(new String(buf, begin, sa.length(), UTF_8))).findFirst(); - if (startAction.isPresent()) { - return startAction.get(); - } + lastAction = serializedActions.stream().filter(sa -> buf.length - begin > sa.length() && sa.equals(new String(buf, begin, sa.length(), UTF_8))).findFirst().orElse(lastAction); preamblePos++; } - return ""; + return lastAction; } private int indexOfEol(byte[] buf, int after) { @@ -100,8 +101,9 @@ public static class Listener extends RunListener> { @Override public void onFinalized(Run run) { super.onFinalized(run); - Map startActions = run.getActions(ColorizedAction.class).stream() - .filter(a -> a.getCommand().equals(ColorizedAction.Command.START)) + final List commands = Arrays.asList(ColorizedAction.Command.START, ColorizedAction.Command.STOP); + Map actions = run.getActions(ColorizedAction.class).stream() + .filter(a -> commands.contains(a.getCommand())) .collect(Collectors.toMap(a -> { try { return new ActionNote(a).encode(); @@ -110,12 +112,12 @@ public void onFinalized(Run run) { } return ""; }, Function.identity())); - if (!startActions.isEmpty()) { + if (!actions.isEmpty()) { final File logFile = new File(run.getRootDir(), "log"); if (logFile.isFile()) { final ShortlogActionCreator shortlogActionCreator = new ShortlogActionCreator(new LineIdentifier(), System.lineSeparator()); final String consoleTail = System.getProperty("hudson.consoleTailKB"); - final ColorizedAction action = shortlogActionCreator.createActionForShortlog(logFile, startActions, consoleTail != null ? Integer.parseInt(consoleTail) : CONSOLE_TAIL_DEFAULT); + final ColorizedAction action = shortlogActionCreator.createActionForShortlog(logFile, actions, consoleTail != null ? Integer.parseInt(consoleTail) : CONSOLE_TAIL_DEFAULT); if (action != null) { run.addAction(action); } diff --git a/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java b/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java index 4798531..14e4773 100644 --- a/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java +++ b/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java @@ -10,10 +10,9 @@ import java.io.File; import java.util.HashMap; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class ShortlogActionCreatorTest { @@ -42,20 +41,69 @@ public void canCreateActionForShortlogForWindows() { private void canCreateActionForShortlog(ShortlogActionCreator shortlogActionCreator, String eol, String logFile) { final String lineHash = "mock-line-hash"; final ColorizedAction colorizedAction = new ColorizedAction("xterm", ColorizedAction.Command.START); - final String serializedNote = ""; + final String serializedNote = ""; when(lineIdentifier.hash(eq("[Pipeline] echo" + eol), eq(1L))).thenReturn(lineHash); final File file = new File(getClass().getResource(String.join("/", "", getClass().getName().replace('.', '/'), logFile)).getFile()); final HashMap startActions = new HashMap<>(); - startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("css", ColorizedAction.Command.START)); - startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("vga", ColorizedAction.Command.START)); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("css", ColorizedAction.Command.START)); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("css", ColorizedAction.Command.STOP)); startActions.put(ConsoleNote.PREAMBLE_STR + serializedNote, colorizedAction); - startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("gnome-terminal", ColorizedAction.Command.START)); - + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("xterm", ColorizedAction.Command.STOP)); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("gnome-terminal", ColorizedAction.Command.START)); final ColorizedAction shortlogAction = shortlogActionCreator.createActionForShortlog(file, startActions, 3); assertEquals(colorizedAction.getColorMapName(), shortlogAction.getColorMapName()); assertEquals(colorizedAction.getCommand(), shortlogAction.getCommand()); assertNotEquals(colorizedAction.getId(), shortlogAction.getId()); assertEquals(lineHash, shortlogAction.getId()); } + + @Test + public void wontCreateActionForLogFileShorterThanShortlogLimit() { + final String serializedNote = ""; + final ColorizedAction colorizedAction = new ColorizedAction("xterm", ColorizedAction.Command.START); + final File file = new File(getClass().getResource(String.join("/", "", getClass().getName().replace('.', '/'), "testlog.log")).getFile()); + final HashMap startActions = new HashMap<>(); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("css", ColorizedAction.Command.START)); + startActions.put(ConsoleNote.PREAMBLE_STR + serializedNote, colorizedAction); + assertNull(shortlogActionCreator.createActionForShortlog(file, startActions, 256)); + verify(lineIdentifier, never()).hash(anyString(), anyLong()); + } + + @Test + public void wontCreateActionIfBuildHasNoStartActions() { + final File file = new File(getClass().getResource(String.join("/", "", getClass().getName().replace('.', '/'), "testlog.log")).getFile()); + assertNull(shortlogActionCreator.createActionForShortlog(file, new HashMap<>(), 3)); + verify(lineIdentifier, never()).hash(anyString(), anyLong()); + } + + @Test + public void wontCreateActionIfNoCorrespondingNotesArePresent() { + final HashMap startActions = new HashMap<>(); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("css", ColorizedAction.Command.START)); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("css", ColorizedAction.Command.STOP)); + final File file = new File(getClass().getResource(String.join("/", "", getClass().getName().replace('.', '/'), "testlog-no-notes.log")).getFile()); + assertNull(shortlogActionCreator.createActionForShortlog(file, startActions, 3)); + verify(lineIdentifier, never()).hash(anyString(), anyLong()); + } + + @Test + public void wontCreateActionIfNoLogFileIsPresent() { + final HashMap startActions = new HashMap<>(); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("css", ColorizedAction.Command.START)); + final File file = new File("non-existing.log"); + assertNull(shortlogActionCreator.createActionForShortlog(file, startActions, 3)); + verify(lineIdentifier, never()).hash(anyString(), anyLong()); + } + + @Test + public void wontCreateActionIfActionIsNotActiveAtShortlogLimit() { + final File file = new File(getClass().getResource(String.join("/", "", getClass().getName().replace('.', '/'), "testlog-action-not-active.log")).getFile()); + final HashMap startActions = new HashMap<>(); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("css", ColorizedAction.Command.START)); + startActions.put(ConsoleNote.PREAMBLE_STR + "", new ColorizedAction("css", ColorizedAction.Command.STOP)); + + assertNull(shortlogActionCreator.createActionForShortlog(file, startActions, 3)); + verify(lineIdentifier, never()).hash(anyString(), anyLong()); + } } diff --git a/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-action-not-active.log b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-action-not-active.log new file mode 100644 index 0000000..cc748c4 --- /dev/null +++ b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-action-not-active.log @@ -0,0 +1,31 @@ +Started by user unknown or anonymous +Running in Durability level: MAX_SURVIVABILITY +ha:////4HPR6yAvrg8Qw/J1EkAW+oRkK9sifK+1Yg1owCCMcljNAAAAoh+LCAAAAAAAAP9tjTEOwjAQBM8BClpKHuFItIiK1krDC0x8GCfWnbEdkooX8TX+gCESFVvtrLSa5wtWKcKBo5UdUu8otU4GP9jS5Mixv3geZcdn2TIl9igbHBs2eJyx4YwwR1SwULBGaj0nRzbDRnX6rmuvydanHMu2V1A5c4MHCFXMWcf8hSnC9jqYxPTz/BXAFEIGsfuclm8zQVqFvQAAAA==[Pipeline] Start of Pipeline +ha:////4EQtDedZSq5v6ZloVQtQLa+CDepKC0Xq9VIk43niZUUNAAAApR+LCAAAAAAAAP9tjTEOwjAUQ3+KOrAycohUghExsUZZOEFIQkgb/d8mKe3EibgadyBQiQlLlmxL1nu+oE4RjhQdby12HpP2vA+jK4lPFLtroIm3dOGaMFGwXNpJkrGnpUrKFhaxClYC1hZ1oOTRZdiIVt1VExS65pxj2Q4CKm8GeAAThZxVzN8yR9jeRpMIf5y/AJj7DGxXvP/86jduZBmjwAAAAA==[Pipeline] ansiColor +ha:////4LmN+Pw6n1NaJQ7ErOnt3MfVLo5kUJToDDJ6IHS1hLlyAAAApR+LCAAAAAAAAP9tjTEOwjAUQ3+KOrAycoh0gA0xsUZZOEFIQkgb/d8mKe3EibgadyBQiQlLlmxL1nu+oE4RjhQdby12HpP2vA+jK4lPFLtroIm3dOGaMFGwXNpJkrGnpUrKFhaxClYC1hZ1oOTRZdiIVt1VExS65pxj2Q4CKm8GeAAThZxVzN8yR9jeRpMIf5y/AJj7DGxXvP/86jfoP95RwAAAAA==[Pipeline] { +ha:////4LzXftQJX0F1z2gdncUl1iLgtfRtkFgsitFOoiuDpFpPAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGFTMj82QDi+REXUtzQ2Ndk+QUS91EY1MTXWNjyzRDE4NEYwNDEwBbu/A1rwAAAA== +ha:////4Hz6LRLFlcp0y5BZCTN29FoqS4u8Xl/n7YeG3BsPgopsAAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbaGWsbAmNJ0AWEZb8zwLrbuWJvJp3kLiJlZNMMm+a93rDOic4UbLcG+wdZu14DKOti0+U+lugiXu6ck2YKRguzSSpM+cFJRUDS1gDKwEbgzpQdmgLbIVXD9UGhba9lFS/o4DGdQM8gYlqLiqVL8wJdvexy4Q/z18BzLEA29ce4gdpL1fxvAAAAA==[Pipeline] echo +breaks +ha:////4HnB2r4VgzJqgqmrD4g18qXZX2NNIEYvTh5Gkci1D/q5AAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbGBtjZUtoPAGyiLDkfxZYdytP5NW8g8RNrJxkknnTvNcb1jnBiZLl3mDvMGvHYxhtXXyi1N8CTdzTlWvCTMFwaSZJnTkvKKkYWMIaWAnYGNSBskNbYCu8eqg2KLTtpaT6HQU0rhvgCUxUc1GpfGFOsLuPXSb8ef4KYI4F2L72ED+qfHravAAAAA==[Pipeline] echo + lightgreen and separate reset sgr  +ha:////4OYH6MKu52x3PonC1Ud5u7wMz6Wji2CZn9UwqAp4UR5CAAAAox+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbmFgYK1tC4wmQRYQl/7PAult5Iq/mHSRuYuUkk8yb5r3esM4JTpQs9wZ7h1k7HsNo6+ITpf4WaOKerlwTZgqGSzNJ6sx5QUnFwBLWwErAxqAOlB3aAlvh1UO1QaFtLyXV7yigcd0AT2CimotK5Qtzgt197DLhz/NXAHMswPa1h/gB601hw7wAAAA=[Pipeline] echo +works ok +ha:////4AgWlppQmatIvnCLDCZnR0046dt548KF4Fy1mnNPTMSuAAAAox+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbWJgYK1tC4wmQRYQl/7PAult5Iq/mHSRuYuUkk8yb5r3esM4JTpQs9wZ7h1k7HsNo6+ITpf4WaOKerlwTZgqGSzNJ6sx5QUnFwBLWwErAxqAOlB3aAlvh1UO1QaFtLyXV7yigcd0AT2CimotK5Qtzgt197DLhz/NXAHMswPa1h/gBJFH5RLwAAAA=[Pipeline] echo +lightgreen and combined reset sgr  +ha:////4HoFmWLKIkI9Tuz2n4mKzKuCIPjtwb+WA7Faafh6YRZXAAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTb2BhjZUtoPAGyiLDkfxZYdytP5NW8g8RNrJxkknnTvNcb1jnBiZLl3mDvMGvHYxhtXXyi1N8CTdzTlWvCTMFwaSZJnTkvKKkYWMIaWAnYGNSBskNbYCu8eqg2KLTtpaT6HQU0rhvgCUxUc1GpfGFOsLuPXSb8ef4KYI4F2L72ED9lYOJdvAAAAA==[Pipeline] echo +works ok +ha:[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal +ha:////4Ln4Ti1usH0TUFY6h4qFT8ZreUeRo7lDbzMqBX3EjTZfAAAAox+LCAAAAAAAAP9tjbEOgjAURS8YB1dHP6KExM04uTYsfkGFWgvNe9g+hMkv8tf8B4kkTt7pnrOc1xvrFHHk6FRrqfOUaq/6MLj5qZFjdw08qpYvqmZKHKyq7FhxY08LViwWy7IcK42NpTpw8uQEW92ahymCIVecJc7uoJH75o4nMj2XxUT5whSxuw1NYvp1/gYw9YJsL8jLsv8AFGlGSr0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal +ha:[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal  bold lightgreen +ha:////4KP1GBTrTdTRwr542XvhrlixmKr1h18H7yH7mpjCqRDSAAAApB+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIREg1CVLRWGl5gEmOcWHfBvpBUvIiv8QciIlGx1c4083pjmSKOHJ1qLLWeUuVVF3o3PTVwbK+BB9XwRVVMiYNVpR1Kru1pxpLFYl6WY6GxslQFTp6cYK0b8zBFMOSKs8TJHTRyX9/xRKanspgoXxgjNre+Tky/zt8Axk6Q7QT5dt99ABzjn4K9AAAA[Pipeline] echo +49uyAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYQDiA5R0VppeIFxjHFi3QX7glPxIr7GH4iIRMVWO9PM6w2LFOFI0cnWYucxGS/7MLjpyUyxuwbKsqWLNISJgpW1zTU19jRjTWxhniigVLC0aAIlj45hpVr90FXQ6Kozx8kdFBS+ucMThJrKrCN/YYywvg1NIvx1/gZg7HuGcrfdMIj9B9mBdbK+AAAA[Pipeline] } +ha:[Pipeline] } +ha:////4KQeVjBBTsWoImddPCU9r94N4M9pBeonazxlGiHtR3/wAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGlSQL08SUpMRE3cTUZENdE2MLI90kMxMTXTMz42QjI4vUFAsTEwBd9k+WrwAAAA== +ha:////4MI76wcMq2dN05ewLG1CD7KwCRZtEJik/cVtVY3EpE2HAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYSjQImoaK00vMDExjix7oJ9wal4EV/jD0REomKrnWnm9YZVinCk6GRnsfeYWi+HMLr5yUyxvwbKsqOLbAkTBSsbmxsy9rRgQ2xhmSigVLC22AZKHh3DRnX6oaug0VVnjrM7KCi8ucMThJrLrCN/YYqwvY0mEf46fwMwDQNDuatrBrH/AMo9cRG+AAAA[Pipeline] // ansiColor +ha:////4H7BbEWee3fTq4xXyQx78tPkFuqShNpo39bJTP4b4nc7AAAAox+LCAAAAAAAAP9tjTESgjAQRT8yFraWHiKMtI6VbYbGE0SIMZDZxWQRKk/k1byDjMxY+av/XvNeb6xTxJGjU62lzlOqverD4OanRo7dNfCoWr6omilxsKqyY8WNPS1YsVgsy1bINTaW6sDJkxNsdWsepgiGXHGWOLuDxso3dzyR6bksJsoXpojdbWgS06/zN4Cp7wV5uS8FWfkBjHfWIb4AAAA=[Pipeline] End of Pipeline +Finished: SUCCESS diff --git a/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-crlf.log b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-crlf.log index 92c45d6..200992c 100644 --- a/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-crlf.log +++ b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-crlf.log @@ -18,12 +18,13 @@ works ok  lightgreen bold lightgreen normal lightgreen bold lightgreen normal ha:////4Ln4Ti1usH0TUFY6h4qFT8ZreUeRo7lDbzMqBX3EjTZfAAAAox+LCAAAAAAAAP9tjbEOgjAURS8YB1dHP6KExM04uTYsfkGFWgvNe9g+hMkv8tf8B4kkTt7pnrOc1xvrFHHk6FRrqfOUaq/6MLj5qZFjdw08qpYvqmZKHKyq7FhxY08LViwWy7IcK42NpTpw8uQEW92ahymCIVecJc7uoJH75o4nMj2XxUT5whSxuw1NYvp1/gYw9YJsL8jLsv8AFGlGSr0AAAA=[Pipeline] echo  lightgreen bold lightgreen normal lightgreen bold lightgreen normal -ha:[Pipeline] echo - lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold +ha:[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal ligh ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo - lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal  bold  + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal  bold lightgreen ha:////4KP1GBTrTdTRwr542XvhrlixmKr1h18H7yH7mpjCqRDSAAAApB+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIREg1CVLRWGl5gEmOcWHfBvpBUvIiv8QciIlGx1c4083pjmSKOHJ1qLLWeUuVVF3o3PTVwbK+BB9XwRVVMiYNVpR1Kru1pxpLFYl6WY6GxslQFTp6cYK0b8zBFMOSKs8TJHTRyX9/xRKanspgoXxgjNre+Tky/zt8Axk6Q7QT5dt99ABzjn4K9AAAA[Pipeline] echo -ha:////4D0d8D/qHeobVG5vhdYwbTDvY17TvoYDq8B0eZ7C09uyAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYQDiA5R0VppeIFxjHFi3QX7glPxIr7GH4iIRMVWO9PM6w2LFOFI0cnWYucxGS/7MLjpyUyxuwbKsqWLNISJgpW1zTU19jRjTWxhniigVLC0aAIlj45hpVr90FXQ6Kozx8kdFBS+ucMThJrKrCN/YYywvg1NIvx1/gZg7HuGcrfdMIj9B9mBdbK+AAAA[Pipeline] } +49uyAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYQDiA5R0VppeIFxjHFi3QX7glPxIr7GH4iIRMVWO9PM6w2LFOFI0cnWYucxGS/7MLjpyUyxuwbKsqWLNISJgpW1zTU19jRjTWxhniigVLC0aAIlj45hpVr90FXQ6Kozx8kdFBS+ucMThJrKrCN/YYywvg1NIvx1/gZg7HuGcrfdMIj9B9mBdbK+AAAA[Pipeline] } +ha:[Pipeline] } ha:////4KQeVjBBTsWoImddPCU9r94N4M9pBeonazxlGiHtR3/wAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGlSQL08SUpMRE3cTUZENdE2MLI90kMxMTXTMz42QjI4vUFAsTEwBd9k+WrwAAAA== ha:////4MI76wcMq2dN05ewLG1CD7KwCRZtEJik/cVtVY3EpE2HAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYSjQImoaK00vMDExjix7oJ9wal4EV/jD0REomKrnWnm9YZVinCk6GRnsfeYWi+HMLr5yUyxvwbKsqOLbAkTBSsbmxsy9rRgQ2xhmSigVLC22AZKHh3DRnX6oaug0VVnjrM7KCi8ucMThJrLrCN/YYqwvY0mEf46fwMwDQNDuatrBrH/AMo9cRG+AAAA[Pipeline] // ansiColor ha:////4H7BbEWee3fTq4xXyQx78tPkFuqShNpo39bJTP4b4nc7AAAAox+LCAAAAAAAAP9tjTESgjAQRT8yFraWHiKMtI6VbYbGE0SIMZDZxWQRKk/k1byDjMxY+av/XvNeb6xTxJGjU62lzlOqverD4OanRo7dNfCoWr6omilxsKqyY8WNPS1YsVgsy1bINTaW6sDJkxNsdWsepgiGXHGWOLuDxso3dzyR6bksJsoXpojdbWgS06/zN4Cp7wV5uS8FWfkBjHfWIb4AAAA=[Pipeline] End of Pipeline diff --git a/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-no-notes.log b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-no-notes.log new file mode 100644 index 0000000..5a1fcd4 --- /dev/null +++ b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-no-notes.log @@ -0,0 +1,30 @@ +Started by user unknown or anonymous +Running in Durability level: MAX_SURVIVABILITY +ha:////4HPR6yAvrg8Qw/J1EkAW+oRkK9sifK+1Yg1owCCMcljNAAAAoh+LCAAAAAAAAP9tjTEOwjAQBM8BClpKHuFItIiK1krDC0x8GCfWnbEdkooX8TX+gCESFVvtrLSa5wtWKcKBo5UdUu8otU4GP9jS5Mixv3geZcdn2TIl9igbHBs2eJyx4YwwR1SwULBGaj0nRzbDRnX6rmuvydanHMu2V1A5c4MHCFXMWcf8hSnC9jqYxPTz/BXAFEIGsfuclm8zQVqFvQAAAA==[Pipeline] Start of Pipeline +ha:////4EQtDedZSq5v6ZloVQtQLa+CDepKC0Xq9VIk43niZUUNAAAApR+LCAAAAAAAAP9tjTEOwjAUQ3+KOrAycohUghExsUZZOEFIQkgb/d8mKe3EibgadyBQiQlLlmxL1nu+oE4RjhQdby12HpP2vA+jK4lPFLtroIm3dOGaMFGwXNpJkrGnpUrKFhaxClYC1hZ1oOTRZdiIVt1VExS65pxj2Q4CKm8GeAAThZxVzN8yR9jeRpMIf5y/AJj7DGxXvP/86jduZBmjwAAAAA==[Pipeline] ansiColor +ha:////4LmN+Pw6n1NaJQ7ErOnt3MfVLo5kUJToDDJ6IHS1hLlyAAAApR+LCAAAAAAAAP9tjTEOwjAUQ3+KOrAycoh0gA0xsUZZOEFIQkgb/d8mKe3EibgadyBQiQlLlmxL1nu+oE4RjhQdby12HpP2vA+jK4lPFLtroIm3dOGaMFGwXNpJkrGnpUrKFhaxClYC1hZ1oOTRZdiIVt1VExS65pxj2Q4CKm8GeAAThZxVzN8yR9jeRpMIf5y/AJj7DGxXvP/86jfoP95RwAAAAA==[Pipeline] { +ha:////4LzXftQJX0F1z2gdncUl1iLgtfRtkFgsitFOoiuDpFpPAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGFTMj82QDi+REXUtzQ2Ndk+QUS91EY1MTXWNjyzRDE4NEYwNDEwBbu/A1rwAAAA== +ha:////4Hz6LRLFlcp0y5BZCTN29FoqS4u8Xl/n7YeG3BsPgopsAAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbaGWsbAmNJ0AWEZb8zwLrbuWJvJp3kLiJlZNMMm+a93rDOic4UbLcG+wdZu14DKOti0+U+lugiXu6ck2YKRguzSSpM+cFJRUDS1gDKwEbgzpQdmgLbIVXD9UGhba9lFS/o4DGdQM8gYlqLiqVL8wJdvexy4Q/z18BzLEA29ce4gdpL1fxvAAAAA==[Pipeline] echo +breaks +ha:////4HnB2r4VgzJqgqmrD4g18qXZX2NNIEYvTh5Gkci1D/q5AAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbGBtjZUtoPAGyiLDkfxZYdytP5NW8g8RNrJxkknnTvNcb1jnBiZLl3mDvMGvHYxhtXXyi1N8CTdzTlWvCTMFwaSZJnTkvKKkYWMIaWAnYGNSBskNbYCu8eqg2KLTtpaT6HQU0rhvgCUxUc1GpfGFOsLuPXSb8ef4KYI4F2L72ED+qfHravAAAAA==[Pipeline] echo + lightgreen and separate reset sgr  +ha:////4OYH6MKu52x3PonC1Ud5u7wMz6Wji2CZn9UwqAp4UR5CAAAAox+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbmFgYK1tC4wmQRYQl/7PAult5Iq/mHSRuYuUkk8yb5r3esM4JTpQs9wZ7h1k7HsNo6+ITpf4WaOKerlwTZgqGSzNJ6sx5QUnFwBLWwErAxqAOlB3aAlvh1UO1QaFtLyXV7yigcd0AT2CimotK5Qtzgt197DLhz/NXAHMswPa1h/gB601hw7wAAAA=[Pipeline] echo +works ok +ha:////4AgWlppQmatIvnCLDCZnR0046dt548KF4Fy1mnNPTMSuAAAAox+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbWJgYK1tC4wmQRYQl/7PAult5Iq/mHSRuYuUkk8yb5r3esM4JTpQs9wZ7h1k7HsNo6+ITpf4WaOKerlwTZgqGSzNJ6sx5QUnFwBLWwErAxqAOlB3aAlvh1UO1QaFtLyXV7yigcd0AT2CimotK5Qtzgt197DLhz/NXAHMswPa1h/gBJFH5RLwAAAA=[Pipeline] echo +lightgreen and combined reset sgr  +ha:////4HoFmWLKIkI9Tuz2n4mKzKuCIPjtwb+WA7Faafh6YRZXAAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTb2BhjZUtoPAGyiLDkfxZYdytP5NW8g8RNrJxkknnTvNcb1jnBiZLl3mDvMGvHYxhtXXyi1N8CTdzTlWvCTMFwaSZJnTkvKKkYWMIaWAnYGNSBskNbYCu8eqg2KLTtpaT6HQU0rhvgCUxUc1GpfGFOsLuPXSb8ef4KYI4F2L72ED9lYOJdvAAAAA==[Pipeline] echo +works ok +ha:////4D7Zgg25Qjm2MWdX0qwWsDuYIDqNO9EUW/ZSVggXMIZxAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIBiQ5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+S7bfcBVVhdU70AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal +ha:////4Ln4Ti1usH0TUFY6h4qFT8ZreUeRo7lDbzMqBX3EjTZfAAAAox+LCAAAAAAAAP9tjbEOgjAURS8YB1dHP6KExM04uTYsfkGFWgvNe9g+hMkv8tf8B4kkTt7pnrOc1xvrFHHk6FRrqfOUaq/6MLj5qZFjdw08qpYvqmZKHKyq7FhxY08LViwWy7IcK42NpTpw8uQEW92ahymCIVecJc7uoJH75o4nMj2XxUT5whSxuw1NYvp1/gYw9YJsL8jLsv8AFGlGSr0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal +ha:////3Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM6d1lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal  bold lightgreen +ha:////4KP1GBTrTdTRwr542XvhrlixmKr1h18H7yH7mpjCqRDSAAAApB+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIREg1CVLRWGl5gEmOcWHfBvpBUvIiv8QciIlGx1c4083pjmSKOHJ1qLLWeUuVVF3o3PTVwbK+BB9XwRVVMiYNVpR1Kru1pxpLFYl6WY6GxslQFTp6cYK0b8zBFMOSKs8TJHTRyX9/xRKanspgoXxgjNre+Tky/zt8Axk6Q7QT5dt99ABzjn4K9AAAA[Pipeline] echo +ha:////4D0d8D/qHeobVG5vhdYwbTDvY17TvoYDq8B0eZ7C09uyAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYQDiA5R0VppeIFxjHFi3QX7glPxIr7GH4iIRMVWO9PM6w2LFOFI0cnWYucxGS/7MLjpyUyxuwbKsqWLNISJgpW1zTU19jRjTWxhniigVLC0aAIlj45hpVr90FXQ6Kozx8kdFBS+ucMThJrKrCN/YYywvg1NIvx1/gZg7HuGcrfdMIj9B9mBdbK+AAAA[Pipeline] } +ha:////4KQeVjBBTsWoImddPCU9r94N4M9pBeonazxlGiHtR3/wAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGlSQL08SUpMRE3cTUZENdE2MLI90kMxMTXTMz42QjI4vUFAsTEwBd9k+WrwAAAA== +ha:////4MI76wcMq2dN05ewLG1CD7KwCRZtEJik/cVtVY3EpE2HAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYSjQImoaK00vMDExjix7oJ9wal4EV/jD0REomKrnWnm9YZVinCk6GRnsfeYWi+HMLr5yUyxvwbKsqOLbAkTBSsbmxsy9rRgQ2xhmSigVLC22AZKHh3DRnX6oaug0VVnjrM7KCi8ucMThJrLrCN/YYqwvY0mEf46fwMwDQNDuatrBrH/AMo9cRG+AAAA[Pipeline] // ansiColor +ha:////4H7BbEWee3fTq4xXyQx78tPkFuqShNpo39bJTP4b4nc7AAAAox+LCAAAAAAAAP9tjTESgjAQRT8yFraWHiKMtI6VbYbGE0SIMZDZxWQRKk/k1byDjMxY+av/XvNeb6xTxJGjU62lzlOqverD4OanRo7dNfCoWr6omilxsKqyY8WNPS1YsVgsy1bINTaW6sDJkxNsdWsepgiGXHGWOLuDxso3dzyR6bksJsoXpojdbWgS06/zN4Cp7wV5uS8FWfkBjHfWIb4AAAA=[Pipeline] End of Pipeline +Finished: SUCCESS diff --git a/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog.log b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog.log index 0168447..4d5736b 100644 --- a/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog.log +++ b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog.log @@ -18,12 +18,13 @@ works ok  lightgreen bold lightgreen normal lightgreen bold lightgreen normal ha:////4Ln4Ti1usH0TUFY6h4qFT8ZreUeRo7lDbzMqBX3EjTZfAAAAox+LCAAAAAAAAP9tjbEOgjAURS8YB1dHP6KExM04uTYsfkGFWgvNe9g+hMkv8tf8B4kkTt7pnrOc1xvrFHHk6FRrqfOUaq/6MLj5qZFjdw08qpYvqmZKHKyq7FhxY08LViwWy7IcK42NpTpw8uQEW92ahymCIVecJc7uoJH75o4nMj2XxUT5whSxuw1NYvp1/gYw9YJsL8jLsv8AFGlGSr0AAAA=[Pipeline] echo  lightgreen bold lightgreen normal lightgreen bold lightgreen normal -ha:[Pipeline] echo +ha:[Pipeline] echo  lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo  lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal  bold lightgreen ha:////4KP1GBTrTdTRwr542XvhrlixmKr1h18H7yH7mpjCqRDSAAAApB+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIREg1CVLRWGl5gEmOcWHfBvpBUvIiv8QciIlGx1c4083pjmSKOHJ1qLLWeUuVVF3o3PTVwbK+BB9XwRVVMiYNVpR1Kru1pxpLFYl6WY6GxslQFTp6cYK0b8zBFMOSKs8TJHTRyX9/xRKanspgoXxgjNre+Tky/zt8Axk6Q7QT5dt99ABzjn4K9AAAA[Pipeline] echo -ha:////4D0d8D/qHeobVG5vhdYwbTDvY17TvoYDq8B0eZ7C09uyAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYQDiA5R0VppeIFxjHFi3QX7glPxIr7GH4iIRMVWO9PM6w2LFOFI0cnWYucxGS/7MLjpyUyxuwbKsqWLNISJgpW1zTU19jRjTWxhniigVLC0aAIlj45hpVr90FXQ6Kozx8kdFBS+ucMThJrKrCN/YYywvg1NIvx1/gZg7HuGcrfdMIj9B9mBdbK+AAAA[Pipeline] } +49uyAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYQDiA5R0VppeIFxjHFi3QX7glPxIr7GH4iIRMVWO9PM6w2LFOFI0cnWYucxGS/7MLjpyUyxuwbKsqWLNISJgpW1zTU19jRjTWxhniigVLC0aAIlj45hpVr90FXQ6Kozx8kdFBS+ucMThJrKrCN/YYywvg1NIvx1/gZg7HuGcrfdMIj9B9mBdbK+AAAA[Pipeline] } +ha:[Pipeline] } ha:////4KQeVjBBTsWoImddPCU9r94N4M9pBeonazxlGiHtR3/wAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGlSQL08SUpMRE3cTUZENdE2MLI90kMxMTXTMz42QjI4vUFAsTEwBd9k+WrwAAAA== ha:////4MI76wcMq2dN05ewLG1CD7KwCRZtEJik/cVtVY3EpE2HAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYSjQImoaK00vMDExjix7oJ9wal4EV/jD0REomKrnWnm9YZVinCk6GRnsfeYWi+HMLr5yUyxvwbKsqOLbAkTBSsbmxsy9rRgQ2xhmSigVLC22AZKHh3DRnX6oaug0VVnjrM7KCi8ucMThJrLrCN/YYqwvY0mEf46fwMwDQNDuatrBrH/AMo9cRG+AAAA[Pipeline] // ansiColor ha:////4H7BbEWee3fTq4xXyQx78tPkFuqShNpo39bJTP4b4nc7AAAAox+LCAAAAAAAAP9tjTESgjAQRT8yFraWHiKMtI6VbYbGE0SIMZDZxWQRKk/k1byDjMxY+av/XvNeb6xTxJGjU62lzlOqverD4OanRo7dNfCoWr6omilxsKqyY8WNPS1YsVgsy1bINTaW6sDJkxNsdWsepgiGXHGWOLuDxso3dzyR6bksJsoXpojdbWgS06/zN4Cp7wV5uS8FWfkBjHfWIb4AAAA=[Pipeline] End of Pipeline From 31b6b09cec227d66d50f921a158c2f696b2cb4d9 Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Thu, 30 Jul 2020 21:16:20 +0200 Subject: [PATCH 11/14] #185 Support shortlog lines that stretch into next buffer. Add test --- .../action/ShortlogActionCreator.java | 2 +- .../action/ShortlogActionCreatorTest.java | 15 +++-- .../testlog-long.log | 57 +++++++++++++++++++ 3 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-long.log diff --git a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java index 478e4dc..57fcc23 100644 --- a/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java +++ b/src/main/java/hudson/plugins/ansicolor/action/ShortlogActionCreator.java @@ -64,7 +64,7 @@ private ActionContext findLastActionBefore(File logFile, Collection seri return new ActionContext(lastAction, partialLine + new String(buf, startInBuff, eolPos - startInBuff + eol.length, UTF_8)); } else { // line extends to the next buffer - partialLine = new String(Arrays.copyOfRange(buf, startInBuff, buf.length - 1), UTF_8); + partialLine = new String(Arrays.copyOfRange(buf, startInBuff, buf.length), UTF_8); } } totalRead += read; diff --git a/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java b/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java index 14e4773..8b419ec 100644 --- a/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java +++ b/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest.java @@ -28,21 +28,20 @@ public void setUp() throws Exception { @Test public void canCreateActionForShortlog() { - final String eol = "\n"; - canCreateActionForShortlog(shortlogActionCreator, eol, "testlog.log"); + canCreateActionForShortlog(shortlogActionCreator, "[Pipeline] echo\n", "testlog.log"); } @Test public void canCreateActionForShortlogForWindows() { final String eol = "\r\n"; - canCreateActionForShortlog(new ShortlogActionCreator(lineIdentifier, eol), eol, "testlog-crlf.log"); + canCreateActionForShortlog(new ShortlogActionCreator(lineIdentifier, eol), "[Pipeline] echo" + eol, "testlog-crlf.log"); } - private void canCreateActionForShortlog(ShortlogActionCreator shortlogActionCreator, String eol, String logFile) { + private void canCreateActionForShortlog(ShortlogActionCreator shortlogActionCreator, String shortlogLine, String logFile) { final String lineHash = "mock-line-hash"; final ColorizedAction colorizedAction = new ColorizedAction("xterm", ColorizedAction.Command.START); final String serializedNote = ""; - when(lineIdentifier.hash(eq("[Pipeline] echo" + eol), eq(1L))).thenReturn(lineHash); + when(lineIdentifier.hash(eq(shortlogLine), eq(1L))).thenReturn(lineHash); final File file = new File(getClass().getResource(String.join("/", "", getClass().getName().replace('.', '/'), logFile)).getFile()); final HashMap startActions = new HashMap<>(); @@ -106,4 +105,10 @@ public void wontCreateActionIfActionIsNotActiveAtShortlogLimit() { assertNull(shortlogActionCreator.createActionForShortlog(file, startActions, 3)); verify(lineIdentifier, never()).hash(anyString(), anyLong()); } + + @Test + public void canCreateActionForShortlogOnLogLineExceedingBufferSize() { + final String s = "[Pipeline] echo a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line"; + canCreateActionForShortlog(shortlogActionCreator, s + "\n", "testlog-long.log"); + } } diff --git a/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-long.log b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-long.log new file mode 100644 index 0000000..cdefd70 --- /dev/null +++ b/src/test/resources/hudson/plugins/ansicolor/action/ShortlogActionCreatorTest/testlog-long.log @@ -0,0 +1,57 @@ +Started by user unknown or anonymous +Running in Durability level: MAX_SURVIVABILITY +ha:////4HPR6yAvrg8Qw/J1EkAW+oRkK9sifK+1Yg1owCCMcljNAAAAoh+LCAAAAAAAAP9tjTEOwjAQBM8BClpKHuFItIiK1krDC0x8GCfWnbEdkooX8TX+gCESFVvtrLSa5wtWKcKBo5UdUu8otU4GP9jS5Mixv3geZcdn2TIl9igbHBs2eJyx4YwwR1SwULBGaj0nRzbDRnX6rmuvydanHMu2V1A5c4MHCFXMWcf8hSnC9jqYxPTz/BXAFEIGsfuclm8zQVqFvQAAAA==[Pipeline] Start of Pipeline +ha:////4EQtDedZSq5v6ZloVQtQLa+CDepKC0Xq9VIk43niZUUNAAAApR+LCAAAAAAAAP9tjTEOwjAUQ3+KOrAycohUghExsUZZOEFIQkgb/d8mKe3EibgadyBQiQlLlmxL1nu+oE4RjhQdby12HpP2vA+jK4lPFLtroIm3dOGaMFGwXNpJkrGnpUrKFhaxClYC1hZ1oOTRZdiIVt1VExS65pxj2Q4CKm8GeAAThZxVzN8yR9jeRpMIf5y/AJj7DGxXvP/86jduZBmjwAAAAA==[Pipeline] ansiColor +ha:////4LmN+Pw6n1NaJQ7ErOnt3MfVLo5kUJToDDJ6IHS1hLlyAAAApR+LCAAAAAAAAP9tjTEOwjAUQ3+KOrAycoh0gA0xsUZZOEFIQkgb/d8mKe3EibgadyBQiQlLlmxL1nu+oE4RjhQdby12HpP2vA+jK4lPFLtroIm3dOGaMFGwXNpJkrGnpUrKFhaxClYC1hZ1oOTRZdiIVt1VExS65pxj2Q4CKm8GeAAThZxVzN8yR9jeRpMIf5y/AJj7DGxXvP/86jfoP95RwAAAAA==[Pipeline] { +ha:////4LzXftQJX0F1z2gdncUl1iLgtfRtkFgsitFOoiuDpFpPAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGFTMj82QDi+REXUtzQ2Ndk+QUS91EY1MTXWNjyzRDE4NEYwNDEwBbu/A1rwAAAA== +ha:////4Hz6LRLFlcp0y5BZCTN29FoqS4u8Xl/n7YeG3BsPgopsAAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbaGWsbAmNJ0AWEZb8zwLrbuWJvJp3kLiJlZNMMm+a93rDOic4UbLcG+wdZu14DKOti0+U+lugiXu6ck2YKRguzSSpM+cFJRUDS1gDKwEbgzpQdmgLbIVXD9UGhba9lFS/o4DGdQM8gYlqLiqVL8wJdvexy4Q/z18BzLEA29ce4gdpL1fxvAAAAA==[Pipeline] echo +breaks +ha:////4HnB2r4VgzJqgqmrD4g18qXZX2NNIEYvTh5Gkci1D/q5AAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbGBtjZUtoPAGyiLDkfxZYdytP5NW8g8RNrJxkknnTvNcb1jnBiZLl3mDvMGvHYxhtXXyi1N8CTdzTlWvCTMFwaSZJnTkvKKkYWMIaWAnYGNSBskNbYCu8eqg2KLTtpaT6HQU0rhvgCUxUc1GpfGFOsLuPXSb8ef4KYI4F2L72ED+qfHravAAAAA==[Pipeline] echo + lightgreen and separate reset sgr  +ha:////4OYH6MKu52x3PonC1Ud5u7wMz6Wji2CZn9UwqAp4UR5CAAAAox+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbmFgYK1tC4wmQRYQl/7PAult5Iq/mHSRuYuUkk8yb5r3esM4JTpQs9wZ7h1k7HsNo6+ITpf4WaOKerlwTZgqGSzNJ6sx5QUnFwBLWwErAxqAOlB3aAlvh1UO1QaFtLyXV7yigcd0AT2CimotK5Qtzgt197DLhz/NXAHMswPa1h/gB601hw7wAAAA=[Pipeline] echo +works ok +ha:////4AgWlppQmatIvnCLDCZnR0046dt548KF4Fy1mnNPTMSuAAAAox+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTbWJgYK1tC4wmQRYQl/7PAult5Iq/mHSRuYuUkk8yb5r3esM4JTpQs9wZ7h1k7HsNo6+ITpf4WaOKerlwTZgqGSzNJ6sx5QUnFwBLWwErAxqAOlB3aAlvh1UO1QaFtLyXV7yigcd0AT2CimotK5Qtzgt197DLhz/NXAHMswPa1h/gBJFH5RLwAAAA=[Pipeline] echo +lightgreen and combined reset sgr  +ha:////4HoFmWLKIkI9Tuz2n4mKzKuCIPjtwb+WA7Faafh6YRZXAAAAoh+LCAAAAAAAAP9tjTEOAiEURD9rLGwtPQTb2BhjZUtoPAGyiLDkfxZYdytP5NW8g8RNrJxkknnTvNcb1jnBiZLl3mDvMGvHYxhtXXyi1N8CTdzTlWvCTMFwaSZJnTkvKKkYWMIaWAnYGNSBskNbYCu8eqg2KLTtpaT6HQU0rhvgCUxUc1GpfGFOsLuPXSb8ef4KYI4F2L72ED9lYOJdvAAAAA==[Pipeline] echo +works ok +ha:////4D7Zgg25Qjm2MWdX0qwWsDuYIDqNO9EUW/ZSVggXMIZxAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIBiQ5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+S7bfcBVVhdU70AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal +ha:////4Ln4Ti1usH0TUFY6h4qFT8ZreUeRo7lDbzMqBX3EjTZfAAAAox+LCAAAAAAAAP9tjbEOgjAURS8YB1dHP6KExM04uTYsfkGFWgvNe9g+hMkv8tf8B4kkTt7pnrOc1xvrFHHk6FRrqfOUaq/6MLj5qZFjdw08qpYvqmZKHKyq7FhxY08LViwWy7IcK42NpTpw8uQEW92ahymCIVecJc7uoJH75o4nMj2XxUT5whSxuw1NYvp1/gYw9YJsL8jLsv8AFGlGSr0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal +ha: +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal bold lightgreenal lightgreen bold lightgreen normal  bold lightgreen +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlg +[0m[Pipeline] echo a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line + lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold lightgreen normal lightgreen bold +ha:////4Lthu4nrVB0oXH42Kok0A8vmY1nNwUCdB0zo3aX6R1WiAAAAox+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIRgg5R0VppeIFJjHFi3QX7QlLxIr7GH4iIRMVWO9PM641lijhydKqx1HpKlVdd6N301MCxvQYeVMMXVTElDlaVdii5tqcZSxaLeVmOhcbKUhU4eXKCtW7MwxTBkCvOEid30Mh9fccTmZ7KYqJ8YYzY3Po6Mf06fwMYO0G2F+TbXfcBlgtweL0AAAA=[Pipeline] echo +,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line,a very very very long line +ha:////4KP1GBTrTdTRwr542XvhrlixmKr1h18H7yH7mpjCqRDSAAAApB+LCAAAAAAAAP9tjTEOwjAQBDdBFLSUPMIREg1CVLRWGl5gEmOcWHfBvpBUvIiv8QciIlGx1c4083pjmSKOHJ1qLLWeUuVVF3o3PTVwbK+BB9XwRVVMiYNVpR1Kru1pxpLFYl6WY6GxslQFTp6cYK0b8zBFMOSKs8TJHTRyX9/xRKanspgoXxgjNre+Tky/zt8Axk6Q7QT5dt99ABzjn4K9AAAA[Pipeline] echo +49uyAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYQDiA5R0VppeIFxjHFi3QX7glPxIr7GH4iIRMVWO9PM6w2LFOFI0cnWYucxGS/7MLjpyUyxuwbKsqWLNISJgpW1zTU19jRjTWxhniigVLC0aAIlj45hpVr90FXQ6Kozx8kdFBS+ucMThJrKrCN/YYywvg1NIvx1/gZg7HuGcrfdMIj9B9mBdbK+AAAA[Pipeline] } +ha:[Pipeline] } +ha:////4KQeVjBBTsWoImddPCU9r94N4M9pBeonazxlGiHtR3/wAAAAqx+LCAAAAAAAAP9b85aBtbiIQSujNKU4P0+vIKc0PTOvWC8xrzgzOT8nv0gvMbkkEyjhCKb88ktSdwVsu2efuqidiYHRh4EDIu2ZUsIg5JOVWJaon5OYl64fXFKUmZduXVHEIAU1ODk/rzg/J1XPGUKDDGKAAEYmBoaKghIGlSQL08SUpMRE3cTUZENdE2MLI90kMxMTXTMz42QjI4vUFAsTEwBd9k+WrwAAAA== +ha:////4MI76wcMq2dN05ewLG1CD7KwCRZtEJik/cVtVY3EpE2HAAAApB+LCAAAAAAAAP9tjTEOwjAQBM+JKGgpeYSjQImoaK00vMDExjix7oJ9wal4EV/jD0REomKrnWnm9YZVinCk6GRnsfeYWi+HMLr5yUyxvwbKsqOLbAkTBSsbmxsy9rRgQ2xhmSigVLC22AZKHh3DRnX6oaug0VVnjrM7KCi8ucMThJrLrCN/YYqwvY0mEf46fwMwDQNDuatrBrH/AMo9cRG+AAAA[Pipeline] // ansiColor +ha:////4H7BbEWee3fTq4xXyQx78tPkFuqShNpo39bJTP4b4nc7AAAAox+LCAAAAAAAAP9tjTESgjAQRT8yFraWHiKMtI6VbYbGE0SIMZDZxWQRKk/k1byDjMxY+av/XvNeb6xTxJGjU62lzlOqverD4OanRo7dNfCoWr6omilxsKqyY8WNPS1YsVgsy1bINTaW6sDJkxNsdWsepgiGXHGWOLuDxso3dzyR6bksJsoXpojdbWgS06/zN4Cp7wV5uS8FWfkBjHfWIb4AAAA=[Pipeline] End of Pipeline +Finished: SUCCESS From d04809e31dfbd0df5afab68d01404304fe681e0c Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Thu, 30 Jul 2020 22:43:50 +0200 Subject: [PATCH 12/14] #185 Add integration test for annotating long log file while in shortlog --- .../plugins/ansicolor/JenkinsTestSupport.java | 41 +++++++++++++ .../ShortlogActionCreatorIntegrationTest.java | 57 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java create mode 100644 src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorIntegrationTest.java diff --git a/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java b/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java new file mode 100644 index 0000000..78df181 --- /dev/null +++ b/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java @@ -0,0 +1,41 @@ +package hudson.plugins.ansicolor; + +import hudson.console.AnnotatedLargeText; +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.junit.Rule; +import org.junit.runners.model.Statement; +import org.jvnet.hudson.test.RestartableJenkinsRule; + +import java.io.File; +import java.io.StringWriter; +import java.util.Collection; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertTrue; + +public class JenkinsTestSupport { + @Rule + public RestartableJenkinsRule jenkinsRule = new RestartableJenkinsRule(); + private static final int CONSOLE_TAIL_DEFAULT = 150; + + protected void assertOutputOnRunningPipeline(Collection expectedOutput, Collection notExpectedOutput, String pipelineScript, boolean useShortLog) { + jenkinsRule.addStep(new Statement() { + + @Override + public void evaluate() throws Throwable { + final WorkflowJob project = jenkinsRule.j.jenkins.createProject(WorkflowJob.class, "p"); + project.setDefinition(new CpsFlowDefinition(pipelineScript, true)); + jenkinsRule.j.assertBuildStatusSuccess(project.scheduleBuild2(0)); + StringWriter writer = new StringWriter(); + final WorkflowRun lastBuild = project.getLastBuild(); + final long start = useShortLog ? new File(lastBuild.getRootDir(), "log").length() - CONSOLE_TAIL_DEFAULT * 1024 : 0; + assertTrue(lastBuild.getLogText().writeHtmlTo(start, writer) > 0); + final String html = writer.toString().replaceAll("", ""); + assertThat(html).contains(expectedOutput); + assertThat(html).doesNotContain(notExpectedOutput); + } + }); + } +} diff --git a/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorIntegrationTest.java b/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorIntegrationTest.java new file mode 100644 index 0000000..135279a --- /dev/null +++ b/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorIntegrationTest.java @@ -0,0 +1,57 @@ +package hudson.plugins.ansicolor.action; + +import hudson.plugins.ansicolor.JenkinsTestSupport; +import org.junit.Test; + +import java.util.Arrays; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class ShortlogActionCreatorIntegrationTest extends JenkinsTestSupport { + + @Test + public void canAnotateLongLogOutputInShortlog() { + final String eol = System.lineSeparator(); + final String as1k = IntStream.range(0, 1024).mapToObj(i -> "a").collect(Collectors.joining()); + final String as10k = IntStream.range(0, 10 * 1024).mapToObj(i -> as1k + eol).collect(Collectors.joining()); + final String bs1k = IntStream.range(0, 1024).mapToObj(i -> "b").collect(Collectors.joining()); + final String bs30k = IntStream.range(0, 30).mapToObj(i -> bs1k + eol).collect(Collectors.joining()); + final String cs1k = IntStream.range(0, 1024).mapToObj(i -> "c").collect(Collectors.joining()); + final String cs50k = IntStream.range(0, 50).mapToObj(i -> cs1k + eol).collect(Collectors.joining()); + final String ds1k = IntStream.range(0, 1024).mapToObj(i -> "d").collect(Collectors.joining()); + final String ds50k = IntStream.range(0, 50).mapToObj(i -> ds1k + eol).collect(Collectors.joining()); + +// final String as10k = IntStream.range(0, 10 * 1024).mapToObj(i -> "a").collect(Collectors.joining()); + final String script = "echo '\033[32mBeginning\033[0m'\n" + + "ansiColor('vga') {\n" + + " echo '\033[32m" + as10k + "\033[0m'\n" + + "}\n" + + "ansiColor('xterm') {\n" + + " echo '\033[32m" + bs30k + "\033[0m'\n" + + "}\n" + + "ansiColor('css') {\n" + + " echo '\033[32m" + cs50k + "\033[0m'\n" + + " echo '\033[32m" + ds50k + "\033[0m'\n" + + "}\n" + + "echo 'End'"; + + assertOutputOnRunningPipeline( + Arrays.asList( +// "" + bs30k + "", + "" + cs50k + "", + "" + ds50k + "", + "End" + ), + Arrays.asList( + "Beginning", + "a", + "\033[32m" + as10k + "\033[0m", + "\033[32m" + bs30k + "\033[0m", + "\033[32m" + cs50k + "\033[0m", + "\033[32m" + ds50k + "\033[0m" + ), + script, + true + ); + } +} From fac4722045b526d99bf2f1adbee56640fcf4f032 Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Sat, 1 Aug 2020 13:09:19 +0200 Subject: [PATCH 13/14] #185 Test annotating long log file in shortlog --- .../plugins/ansicolor/JenkinsTestSupport.java | 12 ++++- .../ShortlogActionCreatorIntegrationTest.java | 45 +++++++++---------- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java b/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java index 78df181..a67b683 100644 --- a/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java +++ b/src/test/java/hudson/plugins/ansicolor/JenkinsTestSupport.java @@ -1,6 +1,5 @@ package hudson.plugins.ansicolor; -import hudson.console.AnnotatedLargeText; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; @@ -11,6 +10,9 @@ import java.io.File; import java.io.StringWriter; import java.util.Collection; +import java.util.Collections; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertTrue; @@ -20,6 +22,10 @@ public class JenkinsTestSupport { public RestartableJenkinsRule jenkinsRule = new RestartableJenkinsRule(); private static final int CONSOLE_TAIL_DEFAULT = 150; + protected void assertOutputOnRunningPipeline(String expectedOutput, String notExpectedOutput, String pipelineScript, boolean useShortLog) { + assertOutputOnRunningPipeline(Collections.singletonList(expectedOutput), Collections.singletonList(notExpectedOutput), pipelineScript, useShortLog); + } + protected void assertOutputOnRunningPipeline(Collection expectedOutput, Collection notExpectedOutput, String pipelineScript, boolean useShortLog) { jenkinsRule.addStep(new Statement() { @@ -38,4 +44,8 @@ public void evaluate() throws Throwable { } }); } + + protected static String repeat(String s, int times) { + return IntStream.range(0, times).mapToObj(i -> s).collect(Collectors.joining()); + } } diff --git a/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorIntegrationTest.java b/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorIntegrationTest.java index 135279a..0eb419f 100644 --- a/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorIntegrationTest.java +++ b/src/test/java/hudson/plugins/ansicolor/action/ShortlogActionCreatorIntegrationTest.java @@ -4,51 +4,50 @@ import org.junit.Test; import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.IntStream; public class ShortlogActionCreatorIntegrationTest extends JenkinsTestSupport { + private static final String AS_1K = repeat("a", 1024); + private static final String BS_1K = repeat("b", 1024); + private static final String CS_1K = repeat("c", 1024); + private static final String DS_1K = repeat("d", 1024); @Test public void canAnotateLongLogOutputInShortlog() { - final String eol = System.lineSeparator(); - final String as1k = IntStream.range(0, 1024).mapToObj(i -> "a").collect(Collectors.joining()); - final String as10k = IntStream.range(0, 10 * 1024).mapToObj(i -> as1k + eol).collect(Collectors.joining()); - final String bs1k = IntStream.range(0, 1024).mapToObj(i -> "b").collect(Collectors.joining()); - final String bs30k = IntStream.range(0, 30).mapToObj(i -> bs1k + eol).collect(Collectors.joining()); - final String cs1k = IntStream.range(0, 1024).mapToObj(i -> "c").collect(Collectors.joining()); - final String cs50k = IntStream.range(0, 50).mapToObj(i -> cs1k + eol).collect(Collectors.joining()); - final String ds1k = IntStream.range(0, 1024).mapToObj(i -> "d").collect(Collectors.joining()); - final String ds50k = IntStream.range(0, 50).mapToObj(i -> ds1k + eol).collect(Collectors.joining()); + final String script = "ansiColor('xterm') {\n" + + repeat("echo '\033[32m" + AS_1K + "\033[0m'\n", 150) + + "}"; + assertOutputOnRunningPipeline("" + AS_1K + "", "\033", script, true); + } -// final String as10k = IntStream.range(0, 10 * 1024).mapToObj(i -> "a").collect(Collectors.joining()); + @Test + public void canAnotateLongLogOutputInShortlogMultipleSteps() { final String script = "echo '\033[32mBeginning\033[0m'\n" + "ansiColor('vga') {\n" + - " echo '\033[32m" + as10k + "\033[0m'\n" + + repeat(" echo '\033[32m" + AS_1K + "\033[0m'\n", 10) + "}\n" + "ansiColor('xterm') {\n" + - " echo '\033[32m" + bs30k + "\033[0m'\n" + + repeat(" echo '\033[32m" + BS_1K + "\033[0m'\n", 30) + "}\n" + "ansiColor('css') {\n" + - " echo '\033[32m" + cs50k + "\033[0m'\n" + - " echo '\033[32m" + ds50k + "\033[0m'\n" + + repeat(" echo '\033[32m" + CS_1K + "\033[0m'\n", 50) + + repeat(" echo '\033[32m" + DS_1K + "\033[0m'\n", 50) + "}\n" + "echo 'End'"; assertOutputOnRunningPipeline( Arrays.asList( -// "" + bs30k + "", - "" + cs50k + "", - "" + ds50k + "", + "" + BS_1K + "", + "" + CS_1K + "", + "" + DS_1K + "", "End" ), Arrays.asList( "Beginning", "a", - "\033[32m" + as10k + "\033[0m", - "\033[32m" + bs30k + "\033[0m", - "\033[32m" + cs50k + "\033[0m", - "\033[32m" + ds50k + "\033[0m" + "\033[32m" + AS_1K + "\033[0m", + "\033[32m" + BS_1K + "\033[0m", + "\033[32m" + CS_1K + "\033[0m", + "\033[32m" + DS_1K + "\033[0m" ), script, true From 754b5ac4b81f2c2e1fdf0add407f785d02e7d64e Mon Sep 17 00:00:00 2001 From: Tomek Szmytka Date: Sat, 1 Aug 2020 13:56:16 +0200 Subject: [PATCH 14/14] Amend documentation, update parent pom and maintainers list --- CHANGELOG.md | 2 ++ pom.xml | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af00888..c72fc0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ 0.7.2 (Next) ============ +* [#197](https://github.com/jenkinsci/ansicolor-plugin/pull/197): Recognize timestamper's GlobalDecorator from extension list - [@tszmytka](https://github.com/tszmytka). +* [#185](https://github.com/jenkinsci/ansicolor-plugin/pull/185): Render escape codes correctly in shortlog - [@tszmytka](https://github.com/tszmytka). * Your contribution here. diff --git a/pom.xml b/pom.xml index 16c8197..534cb64 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.jenkins-ci.plugins plugin - 4.3 + 4.4 @@ -30,6 +30,11 @@ Daniel Doubrovkine dblock@dblock.org + + tszmytka + Tomek Szmytka + tszmytka@gmail.com +