2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.debugger.impl;
18 import com.intellij.execution.process.ProcessOutputTypes;
19 import com.intellij.idea.IdeaLogger;
20 import com.intellij.openapi.application.Application;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.application.PathManager;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.projectRoots.Sdk;
25 import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
26 import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
27 import com.intellij.openapi.util.Key;
28 import com.intellij.openapi.util.SystemInfo;
29 import com.intellij.openapi.util.ThrowableComputable;
30 import com.intellij.openapi.util.io.FileUtilRt;
31 import com.intellij.openapi.util.text.StringUtil;
32 import com.intellij.openapi.util.text.StringUtilRt;
33 import com.intellij.openapi.vfs.CharsetToolkit;
34 import com.intellij.util.containers.HashMap;
35 import org.jetbrains.annotations.NotNull;
36 import org.junit.Assert;
39 import java.io.FileOutputStream;
40 import java.net.InetAddress;
41 import java.net.UnknownHostException;
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.List;
46 import java.util.regex.Matcher;
47 import java.util.regex.Pattern;
49 public class OutputChecker {
50 private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.OutputChecker");
51 private static final String JDK_HOME_STR = "!JDK_HOME!";
52 private static final String TEST_JDK_HOME_STR = "!TEST_JDK!";
54 protected final String myAppPath;
56 public static final Key[] OUTPUT_ORDER = new Key[] {
57 ProcessOutputTypes.SYSTEM, ProcessOutputTypes.STDOUT, ProcessOutputTypes.STDERR
59 private Map<Key, StringBuffer> myBuffers;
60 protected String myTestName;
63 //ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2
64 private static final Pattern JDI_BUG_OUTPUT_PATTERN_1 =
65 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");
66 //JDWP exit error AGENT_ERROR_NO_JNI_ENV(183): [../../../src/share/back/util.c:820]
67 private static final Pattern JDI_BUG_OUTPUT_PATTERN_2 =
68 Pattern.compile("JDWP\\s+exit\\s+error\\s+AGENT_ERROR_NO_JNI_ENV.*\\]\n");
70 public OutputChecker(String appPath) {
74 public void init(String testName) {
75 IdeaLogger.ourErrorsOccurred = null;
77 testName = Character.toLowerCase(testName.charAt(0)) + testName.substring(1);
78 myTestName = testName;
80 myBuffers = new HashMap<Key, StringBuffer>();
84 public void print(String s, Key outputType) {
86 if (myBuffers != null) {
87 StringBuffer buffer = myBuffers.get(outputType);
89 myBuffers.put(outputType, buffer = new StringBuffer());
96 public void println(String s, Key outputType) {
97 print(s + "\n", outputType);
100 public void checkValid(Sdk jdk) throws Exception {
101 checkValid(jdk, false);
104 public void checkValid(Sdk jdk, boolean sortClassPath) throws Exception {
105 if (IdeaLogger.ourErrorsOccurred != null) {
106 throw IdeaLogger.ourErrorsOccurred;
109 String actual = preprocessBuffer(jdk, buildOutputString(), sortClassPath);
111 File outs = new File(myAppPath + File.separator + "outs");
112 assert outs.exists() || outs.mkdirs() : outs;
114 File outFile = new File(outs, myTestName + ".out");
115 if (!outFile.exists()) {
116 if (SystemInfo.isWindows) {
117 final File winOut = new File(outs, myTestName + ".win.out");
118 if (winOut.exists()) {
122 else if (SystemInfo.isUnix) {
123 final File unixOut = new File(outs, myTestName + ".unx.out");
124 if (unixOut.exists()) {
130 if (!outFile.exists()) {
131 FileOutputStream fos = new FileOutputStream(outFile, false);
133 fos.write(actual.getBytes());
138 LOG.error("Test file created " + outFile.getPath() + "\n" + "**************** Don't forget to put it into VCS! *******************");
141 String originalText = FileUtilRt.loadFile(outFile, CharsetToolkit.UTF8);
142 String expected = StringUtilRt.convertLineSeparators(originalText);
143 if (!expected.equals(actual)) {
144 System.out.println("expected:");
145 System.out.println(originalText);
146 System.out.println("actual:");
147 System.out.println(actual);
149 final int len = Math.min(expected.length(), actual.length());
150 if (expected.length() != actual.length()) {
151 System.out.println("Text sizes differ: expected " + expected.length() + " but actual: " + actual.length());
153 if (expected.length() > len) {
154 System.out.println("Rest from expected text is: \"" + expected.substring(len) + "\"");
156 else if (actual.length() > len) {
157 System.out.println("Rest from actual text is: \"" + actual.substring(len) + "\"");
160 Assert.assertEquals(originalText, actual);
165 private synchronized String buildOutputString() {
166 final StringBuilder result = new StringBuilder();
167 for (Key key : OUTPUT_ORDER) {
168 final StringBuffer buffer = myBuffers.get(key);
169 if (buffer != null) {
170 result.append(buffer.toString());
173 return result.toString();
176 private String preprocessBuffer(final Sdk testJdk, final String buffer, final boolean sortClassPath) throws Exception {
177 Application application = ApplicationManager.getApplication();
179 if (application == null) return buffer;
181 return application.runReadAction(new ThrowableComputable<String, Exception>() {
183 public String compute() throws UnknownHostException {
184 String internalJdkHome = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk().getHomeDirectory().getPath();
185 //System.out.println("internalJdkHome = " + internalJdkHome);
187 String result = buffer;
188 //System.out.println("Original Output = " + result);
189 final boolean shouldIgnoreCase = !SystemInfo.isFileSystemCaseSensitive;
191 result = StringUtil.replace(result, "\r\n", "\n");
192 result = StringUtil.replace(result, "\r", "\n");
193 result = replaceAdditionalInOutput(result);
194 final String testJdkHome = testJdk.getHomePath();
195 result = StringUtil.replace(result, testJdkHome.replace('/', File.separatorChar), TEST_JDK_HOME_STR, shouldIgnoreCase);
196 result = StringUtil.replace(result, testJdkHome, TEST_JDK_HOME_STR, shouldIgnoreCase);
197 result = StringUtil.replace(result, myAppPath, "!APP_PATH!", shouldIgnoreCase);
198 result = StringUtil.replace(result, myAppPath.replace(File.separatorChar, '/'), "!APP_PATH!", shouldIgnoreCase);
199 result = StringUtil.replace(result, JavaSdkUtil.getIdeaRtJarPath(), "!RT_JAR!", shouldIgnoreCase);
200 result = StringUtil.replace(result, JavaSdkUtil.getJunit4JarPath(), "!JUNIT4_JAR!", shouldIgnoreCase);
201 result = StringUtil.replace(result, InetAddress.getLocalHost().getCanonicalHostName(), "!HOST_NAME!", shouldIgnoreCase);
202 result = StringUtil.replace(result, InetAddress.getLocalHost().getHostName(), "!HOST_NAME!", shouldIgnoreCase);
203 result = StringUtil.replace(result, "127.0.0.1", "!HOST_NAME!", shouldIgnoreCase);
204 result = StringUtil.replace(result, JavaSdkUtil.getIdeaRtJarPath().replace('/', File.separatorChar), "!RT_JAR!", shouldIgnoreCase);
205 result = StringUtil.replace(result, internalJdkHome.replace('/', File.separatorChar), JDK_HOME_STR, shouldIgnoreCase);
206 result = StringUtil.replace(result, internalJdkHome, JDK_HOME_STR, shouldIgnoreCase);
207 result = StringUtil.replace(result, PathManager.getHomePath(), "!IDEA_HOME!", shouldIgnoreCase);
208 result = StringUtil.replace(result, "Process finished with exit code 255", "Process finished with exit code -1");
210 // result = result.replaceAll(" +\n", "\n");
211 result = result.replaceAll("!HOST_NAME!:\\d*", "!HOST_NAME!:!HOST_PORT!");
212 result = result.replaceAll("at \\'.*?\\'", "at '!HOST_NAME!:PORT_NAME!'");
213 result = result.replaceAll("address: \\'.*?\\'", "address: '!HOST_NAME!:PORT_NAME!'");
214 result = result.replaceAll("file.*AppletPage.*\\.html", "file:/!APPLET_HTML!");
215 result = result.replaceAll("\"(!JDK_HOME!.*?)\"", "$1");
216 result = result.replaceAll("\"(!APP_PATH!.*?)\"", "$1");
217 result = result.replaceAll("\"(" + TEST_JDK_HOME_STR + ".*?)\"", "$1");
219 // unquote extra params
220 result = result.replaceAll("\"(-D.*)\"", "$1");
222 result = result.replaceAll("-Didea.launcher.port=\\d*", "-Didea.launcher.port=!IDEA_LAUNCHER_PORT!");
223 result = result.replaceAll("-Dfile.encoding=[\\w\\d-]*", "-Dfile.encoding=!FILE_ENCODING!");
224 result = result.replaceAll("\\((.*)\\:\\d+\\)", "($1:!LINE_NUMBER!)");
226 result = fixSlashes(result, TEST_JDK_HOME_STR);
227 result = fixSlashes(result, JDK_HOME_STR);
229 result = stripQuotesAroundClasspath(result);
231 final Matcher matcher = Pattern.compile("-classpath\\s+(\\S+)\\s+").matcher(result);
232 while (matcher.find()) {
233 final String classPath = matcher.group(1);
234 final String[] classPathElements = classPath.split(File.pathSeparator);
236 // Combine all JDK jars into one marker
237 List<String> classpathRes = new ArrayList<String>();
238 boolean hasJdkJars = false;
239 for (String element : classPathElements) {
240 if (!element.startsWith(JDK_HOME_STR)) {
241 classpathRes.add(element);
248 classpathRes.add("!JDK_JARS!");
252 Collections.sort(classpathRes);
255 final String sortedPath = StringUtil.join(classpathRes, ";");
256 result = StringUtil.replace(result, classPath, sortedPath);
259 result = JDI_BUG_OUTPUT_PATTERN_1.matcher(result).replaceAll("");
260 result = JDI_BUG_OUTPUT_PATTERN_2.matcher(result).replaceAll("");
268 private static String fixSlashes(String text, final String jdkHomeMarker) {
269 int commandLineStart = text.indexOf(jdkHomeMarker);
270 while (commandLineStart != -1) {
271 final StringBuilder builder = new StringBuilder(text);
272 int i = commandLineStart + 1;
273 while (i < builder.length()) {
274 char c = builder.charAt(i);
275 if (c == '\n') break;
276 else if (c == File.separatorChar) builder.setCharAt(i, '\\');
279 text = builder.toString();
280 commandLineStart = text.indexOf(jdkHomeMarker, commandLineStart + 1);
285 protected String replaceAdditionalInOutput(String str) {
289 //do not depend on spaces in jdk path
290 private static String stripQuotesAroundClasspath(String result) {
291 final String clsp = "-classpath ";
294 clspIdx = result.indexOf(clsp, clspIdx);
299 final int spaceIdx = result.indexOf(" ", clspIdx + clsp.length());
301 result = result.substring(0, clspIdx) +
303 StringUtil.stripQuotesAroundValue(result.substring(clspIdx + clsp.length(), spaceIdx)) +
304 result.substring(spaceIdx);
305 clspIdx += clsp.length();