mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
test for ANSI coloring performance + fixes
This commit is contained in:
@@ -24,7 +24,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* See <a href="http://en.wikipedia.org/wiki/ANSI_escape_code">ANSI escape code</a>.
|
||||
@@ -34,10 +33,11 @@ import java.util.regex.Pattern;
|
||||
public class AnsiEscapeDecoder {
|
||||
private static final char ESC_CHAR = '\u001B'; // Escape sequence start character
|
||||
private static final String CSI = ESC_CHAR + "["; // "Control Sequence Initiator"
|
||||
private static final Pattern INNER_PATTERN = Pattern.compile(Pattern.quote("m" + CSI));
|
||||
private static final String M_CSI = "m" + CSI;
|
||||
private static final char BACKSPACE = '\b';
|
||||
|
||||
private Key myCurrentTextAttributes;
|
||||
private final ColoredOutputTypeRegistry myColoredOutputTypeRegistry = ColoredOutputTypeRegistry.getInstance();
|
||||
|
||||
/**
|
||||
* Parses ansi-color codes from text and sends text fragments with color attributes to textAcceptor
|
||||
@@ -48,9 +48,9 @@ public class AnsiEscapeDecoder {
|
||||
* It can implement ColoredChunksAcceptor to receive list of pairs (text, attribute).
|
||||
*/
|
||||
public void escapeText(@NotNull String text, @NotNull Key outputType, @NotNull ColoredTextAcceptor textAcceptor) {
|
||||
List<Pair<String, Key>> chunks = null;
|
||||
int pos = 0;
|
||||
text = normalizeAsciiControlCharacters(text);
|
||||
int pos = 0;
|
||||
List<Pair<String, Key>> chunks = null;
|
||||
while (true) {
|
||||
int escSeqBeginInd = text.indexOf(CSI, pos);
|
||||
if (escSeqBeginInd < 0) {
|
||||
@@ -67,8 +67,8 @@ public class AnsiEscapeDecoder {
|
||||
String escSeq = text.substring(escSeqBeginInd, escSeqEndInd);
|
||||
// this is a simple fix for RUBY-8996:
|
||||
// we replace several consecutive escape sequences with one which contains all these sequences
|
||||
String colorAttribute = INNER_PATTERN.matcher(escSeq).replaceAll(";");
|
||||
myCurrentTextAttributes = ColoredOutputTypeRegistry.getInstance().getOutputKey(colorAttribute);
|
||||
String colorAttribute = StringUtil.replace(escSeq, M_CSI, ";");
|
||||
myCurrentTextAttributes = myColoredOutputTypeRegistry.getOutputKey(colorAttribute);
|
||||
}
|
||||
pos = escSeqEndInd;
|
||||
}
|
||||
@@ -189,17 +189,12 @@ public class AnsiEscapeDecoder {
|
||||
return myCurrentTextAttributes != null ? myCurrentTextAttributes : outputType;
|
||||
}
|
||||
|
||||
public void coloredTextAvailable(@NotNull List<Pair<String, Key>> textChunks, ColoredTextAcceptor textAcceptor) {
|
||||
for (Pair<String, Key> textChunk : textChunks) {
|
||||
textAcceptor.coloredTextAvailable(textChunk.getFirst(), textChunk.getSecond());
|
||||
}
|
||||
}
|
||||
|
||||
public interface ColoredChunksAcceptor extends ColoredTextAcceptor {
|
||||
void coloredChunksAvailable(List<Pair<String, Key>> chunks);
|
||||
void coloredChunksAvailable(@NotNull List<Pair<String, Key>> chunks);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ColoredTextAcceptor {
|
||||
void coloredTextAvailable(String text, Key attributes);
|
||||
void coloredTextAvailable(@NotNull String text, @NotNull Key attributes);
|
||||
}
|
||||
}
|
||||
|
||||
+75
-2
@@ -2,14 +2,22 @@ package com.intellij.execution.process;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayInputStream;
|
||||
import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream;
|
||||
import com.intellij.testFramework.PlatformTestCase;
|
||||
import com.intellij.testFramework.PlatformTestUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class AnsiEscapeDecoderTest extends PlatformTestCase {
|
||||
|
||||
@@ -79,16 +87,81 @@ public class AnsiEscapeDecoderTest extends PlatformTestCase {
|
||||
private static AnsiEscapeDecoder.ColoredChunksAcceptor createExpectedAcceptor(@NotNull final Pair<String, Key>... expected) {
|
||||
return new AnsiEscapeDecoder.ColoredChunksAcceptor() {
|
||||
@Override
|
||||
public void coloredChunksAvailable(List<Pair<String, Key>> chunks) {
|
||||
public void coloredChunksAvailable(@NotNull List<Pair<String, Key>> chunks) {
|
||||
List<Pair<String, String>> expectedWithKeyName = toListWithKeyName(Arrays.asList(expected));
|
||||
List<Pair<String, String>> actualWithKeyName = toListWithKeyName(chunks);
|
||||
Assert.assertEquals(expectedWithKeyName, actualWithKeyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void coloredTextAvailable(String text, Key attributes) {
|
||||
public void coloredTextAvailable(@NotNull String text, @NotNull Key attributes) {
|
||||
throw new RuntimeException(); // shouldn't be called
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void testPerformance() throws IOException {
|
||||
byte[] buffer = new byte[100000];
|
||||
BufferExposingByteArrayOutputStream outputStream = new BufferExposingByteArrayOutputStream(buffer);
|
||||
BufferExposingByteArrayInputStream inputStream = new BufferExposingByteArrayInputStream(buffer);
|
||||
AtomicBoolean finished = new AtomicBoolean();
|
||||
Process testProcess = new Process() {
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
return outputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getErrorStream() {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int waitFor() {
|
||||
while (!finished.get());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int exitValue() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
finished.set(true);
|
||||
}
|
||||
};
|
||||
|
||||
withProcessHandlerFrom(testProcess, handler -> {
|
||||
PlatformTestUtil.startPerformanceTest("ansi color", 15000, ()->{
|
||||
for (int i=0; i<2_000_000;i++) {
|
||||
handler.notifyTextAvailable(i+"Chrome 35.0.1916 (Linux): Executed 0 of 1\u001B[32m SUCCESS\u001B[39m (0 secs / 0 secs)\n", ProcessOutputTypes.STDOUT);
|
||||
handler.notifyTextAvailable(i+"Plain\u001B[32mGreen\u001B[39mNormal\u001B[1A\u001B[2K\u001B[31mRed\u001B[39m\n", ProcessOutputTypes.SYSTEM);
|
||||
}
|
||||
}).assertTiming();
|
||||
});
|
||||
}
|
||||
|
||||
public static void withProcessHandlerFrom(@NotNull Process testProcess, @NotNull Consumer<ProcessHandler> consumer) {
|
||||
KillableColoredProcessHandler handler = new KillableColoredProcessHandler(testProcess, "testProcess");
|
||||
handler.setShouldDestroyProcessRecursively(false);
|
||||
handler.startNotify();
|
||||
handler.notifyTextAvailable("Running stuff...\n", ProcessOutputTypes.STDOUT);
|
||||
|
||||
try {
|
||||
consumer.consume(handler);
|
||||
}
|
||||
finally {
|
||||
handler.doDestroyProcess();
|
||||
handler.notifyProcessTerminated(0);
|
||||
handler.waitFor();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,7 +601,7 @@ public class PlatformTestUtil {
|
||||
String logMessage = message;
|
||||
if (duration > expectedOnMyMachine) {
|
||||
int percentage = (int)(100.0 * (duration - expectedOnMyMachine) / expectedOnMyMachine);
|
||||
logMessage += ": " + percentage + "% longer";
|
||||
logMessage += ": " + "\u001B[31;1m " + percentage + "% longer" + "\u001B[0m";
|
||||
}
|
||||
logMessage +=
|
||||
"\n Expected: " + formatTime(expectedOnMyMachine) + "\n Actual: " + formatTime(duration) + "\n " + Timings.getStatistics();
|
||||
@@ -634,7 +634,9 @@ public class PlatformTestUtil {
|
||||
System.gc();
|
||||
String s = "Another epic fail (remaining attempts: " + attempts + "): " + logMessage;
|
||||
TeamCityLogger.warning(s, null);
|
||||
System.err.println(s);
|
||||
if (UsefulTestCase.IS_UNDER_TEAMCITY) {
|
||||
System.err.println(s);
|
||||
}
|
||||
//if (attempts == 1) {
|
||||
// try {
|
||||
// Class.forName("com.intellij.util.ProfilingUtil").getMethod("startCPUProfiling").invoke(null);
|
||||
|
||||
Reference in New Issue
Block a user