blob: a250715fec12f048e87bf487d5896cd2820e6a40 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.debugger.impl;
import com.intellij.execution.process.ProcessOutputTypes;
import com.intellij.idea.IdeaLogger;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.util.containers.HashMap;
import org.junit.Assert;
import java.io.File;
import java.io.FileOutputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class OutputChecker {
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.OutputChecker");
private static final String JDK_HOME_STR = "!JDK_HOME!";
protected final String myAppPath;
public static final Key[] OUTPUT_ORDER = new Key[] {
ProcessOutputTypes.SYSTEM, ProcessOutputTypes.STDOUT, ProcessOutputTypes.STDERR
};
private Map<Key, StringBuffer> myBuffers;
protected String myTestName;
//ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2
private static final Pattern JDI_BUG_OUTPUT_PATTERN_1 =
Pattern.compile("ERROR\\:\\s+JDWP\\s+Unable\\s+to\\s+get\\s+JNI\\s+1\\.2\\s+environment,\\s+jvm-\\>GetEnv\\(\\)\\s+return\\s+code\\s+=\\s+-2\n");
//JDWP exit error AGENT_ERROR_NO_JNI_ENV(183): [../../../src/share/back/util.c:820]
private static final Pattern JDI_BUG_OUTPUT_PATTERN_2 =
Pattern.compile("JDWP\\s+exit\\s+error\\s+AGENT_ERROR_NO_JNI_ENV.*\\]\n");
public OutputChecker(String appPath) {
myAppPath = appPath;
}
public void init(String testName) {
IdeaLogger.ourErrorsOccurred = null;
testName = Character.toLowerCase(testName.charAt(0)) + testName.substring(1);
myTestName = testName;
synchronized (this) {
myBuffers = new HashMap<Key, StringBuffer>();
}
}
public void print(String s, Key outputType) {
synchronized (this) {
if (myBuffers != null) {
StringBuffer buffer = myBuffers.get(outputType);
if (buffer == null) {
myBuffers.put(outputType, buffer = new StringBuffer());
}
buffer.append(s);
}
}
}
public void println(String s, Key outputType) {
print(s + "\n", outputType);
}
public void checkValid(Sdk jdk) throws Exception {
checkValid(jdk, false);
}
public void checkValid(Sdk jdk, boolean sortClassPath) throws Exception {
if (IdeaLogger.ourErrorsOccurred != null) {
throw IdeaLogger.ourErrorsOccurred;
}
String actual = preprocessBuffer(jdk, buildOutputString(), sortClassPath);
File outs = new File(myAppPath + File.separator + "outs");
assert outs.exists() || outs.mkdirs() : outs;
File outFile = new File(outs, myTestName + ".out");
if (!outFile.exists()) {
if (SystemInfo.isWindows) {
final File winOut = new File(outs, myTestName + ".win.out");
if (winOut.exists()) {
outFile = winOut;
}
}
else if (SystemInfo.isUnix) {
final File unixOut = new File(outs, myTestName + ".unx.out");
if (unixOut.exists()) {
outFile = unixOut;
}
}
}
if (!outFile.exists()) {
FileOutputStream fos = new FileOutputStream(outFile, false);
try {
fos.write(actual.getBytes());
}
finally {
fos.close();
}
LOG.error("Test file created " + outFile.getPath() + "\n" + "**************** Don't forget to put it into VCS! *******************");
}
else {
String originalText = FileUtilRt.loadFile(outFile, CharsetToolkit.UTF8);
String expected = StringUtilRt.convertLineSeparators(originalText);
if (!expected.equals(actual)) {
System.out.println("expected:");
System.out.println(originalText);
System.out.println("actual:");
System.out.println(actual);
final int len = Math.min(expected.length(), actual.length());
if (expected.length() != actual.length()) {
System.out.println("Text sizes differ: expected " + expected.length() + " but actual: " + actual.length());
}
if (expected.length() > len) {
System.out.println("Rest from expected text is: \"" + expected.substring(len) + "\"");
}
else if (actual.length() > len) {
System.out.println("Rest from actual text is: \"" + actual.substring(len) + "\"");
}
Assert.assertEquals(originalText, actual);
}
}
}
private synchronized String buildOutputString() {
final StringBuilder result = new StringBuilder();
for (Key key : OUTPUT_ORDER) {
final StringBuffer buffer = myBuffers.get(key);
if (buffer != null) {
result.append(buffer.toString());
}
}
return result.toString();
}
private String preprocessBuffer(final Sdk testJdk, final String buffer, final boolean sortClassPath) throws Exception {
Application application = ApplicationManager.getApplication();
if (application == null) return buffer;
return application.runReadAction(new ThrowableComputable<String, Exception>() {
@Override
public String compute() throws UnknownHostException {
String internalJdkHome = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk().getHomeDirectory().getPath();
//System.out.println("internalJdkHome = " + internalJdkHome);
String result = buffer;
//System.out.println("Original Output = " + result);
final boolean shouldIgnoreCase = !SystemInfo.isFileSystemCaseSensitive;
result = StringUtil.replace(result, "\r\n", "\n");
result = StringUtil.replace(result, "\r", "\n");
result = replaceAdditionalInOutput(result);
result = StringUtil.replace(result, testJdk.getHomePath(), "!TEST_JDK!", shouldIgnoreCase);
result = StringUtil.replace(result, myAppPath, "!APP_PATH!", shouldIgnoreCase);
result = StringUtil.replace(result, myAppPath.replace(File.separatorChar, '/'), "!APP_PATH!", shouldIgnoreCase);
result = StringUtil.replace(result, JavaSdkUtil.getIdeaRtJarPath(), "!RT_JAR!", shouldIgnoreCase);
result = StringUtil.replace(result, JavaSdkUtil.getJunit4JarPath(), "!JUNIT4_JAR!", shouldIgnoreCase);
result = StringUtil.replace(result, InetAddress.getLocalHost().getCanonicalHostName(), "!HOST_NAME!", shouldIgnoreCase);
result = StringUtil.replace(result, InetAddress.getLocalHost().getHostName(), "!HOST_NAME!", shouldIgnoreCase);
result = StringUtil.replace(result, "127.0.0.1", "!HOST_NAME!", shouldIgnoreCase);
result = StringUtil.replace(result, JavaSdkUtil.getIdeaRtJarPath().replace('/', File.separatorChar), "!RT_JAR!", shouldIgnoreCase);
result = StringUtil.replace(result, internalJdkHome.replace('/', File.separatorChar), JDK_HOME_STR, shouldIgnoreCase);
result = StringUtil.replace(result, internalJdkHome, JDK_HOME_STR, shouldIgnoreCase);
result = StringUtil.replace(result, PathManager.getHomePath(), "!IDEA_HOME!", shouldIgnoreCase);
result = StringUtil.replace(result, "Process finished with exit code 255", "Process finished with exit code -1");
// result = result.replaceAll(" +\n", "\n");
result = result.replaceAll("!HOST_NAME!:\\d*", "!HOST_NAME!:!HOST_PORT!");
result = result.replaceAll("at \\'.*?\\'", "at '!HOST_NAME!:PORT_NAME!'");
result = result.replaceAll("address: \\'.*?\\'", "address: '!HOST_NAME!:PORT_NAME!'");
result = result.replaceAll("file.*AppletPage.*\\.html", "file:/!APPLET_HTML!");
result = result.replaceAll("\"(!JDK_HOME!.*?)\"", "$1");
result = result.replaceAll("\"(!APP_PATH!.*?)\"", "$1");
result = result.replaceAll("-Didea.launcher.port=\\d*", "-Didea.launcher.port=!IDEA_LAUNCHER_PORT!");
result = result.replaceAll("-Dfile.encoding=[\\w\\d-]*", "-Dfile.encoding=!FILE_ENCODING!");
result = result.replaceAll("\\((.*)\\:\\d+\\)", "($1:!LINE_NUMBER!)");
int commandLineStart = result.indexOf(JDK_HOME_STR);
while (commandLineStart != -1) {
final StringBuilder builder = new StringBuilder(result);
int i = commandLineStart + 1;
while (i < builder.length()) {
char c = builder.charAt(i);
if (c == '\n') break;
else if (c == File.separatorChar) builder.setCharAt(i, '\\');
i++;
}
result = builder.toString();
commandLineStart = result.indexOf(JDK_HOME_STR, commandLineStart + 1);
}
result = stripQuotesAroundClasspath(result);
final Matcher matcher = Pattern.compile("-classpath\\s+(\\S+)\\s+").matcher(result);
while (matcher.find()) {
final String classPath = matcher.group(1);
final String[] classPathElements = classPath.split(File.pathSeparator);
// Combine all JDK jars into one marker
List<String> classpathRes = new ArrayList<String>();
boolean hasJdkJars = false;
for (String element : classPathElements) {
if (!element.startsWith(JDK_HOME_STR)) {
classpathRes.add(element);
}
else {
hasJdkJars = true;
}
}
if (hasJdkJars) {
classpathRes.add("!JDK_JARS!");
}
if (sortClassPath) {
Collections.sort(classpathRes);
}
final String sortedPath = StringUtil.join(classpathRes, ";");
result = StringUtil.replace(result, classPath, sortedPath);
}
result = JDI_BUG_OUTPUT_PATTERN_1.matcher(result).replaceAll("");
result = JDI_BUG_OUTPUT_PATTERN_2.matcher(result).replaceAll("");
return result;
}
});
}
protected String replaceAdditionalInOutput(String str) {
return str;
}
//do not depend on spaces in jdk path
private static String stripQuotesAroundClasspath(String result) {
final String clsp = "-classpath ";
int clspIdx = 0;
while (true) {
clspIdx = result.indexOf(clsp, clspIdx);
if (clspIdx <= -1) {
break;
}
final int spaceIdx = result.indexOf(" ", clspIdx + clsp.length());
if (spaceIdx > -1) {
result = result.substring(0, clspIdx) +
clsp +
StringUtil.stripQuotesAroundValue(result.substring(clspIdx + clsp.length(), spaceIdx)) +
result.substring(spaceIdx);
clspIdx += clsp.length();
} else {
break;
}
}
return result;
}
}