/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.netbeans.modules.maven.junit; import java.io.File; import java.io.IOException; import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.event.ChangeListener; import org.apache.maven.project.MavenProject; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.versioning.ComparableVersion; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.StringUtils; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.annotations.common.NullAllowed; import org.netbeans.api.project.FileOwnerQuery; import org.netbeans.api.project.Project; import org.netbeans.modules.gsf.testrunner.api.RerunHandler; import org.netbeans.modules.gsf.testrunner.api.RerunType; import org.netbeans.modules.gsf.testrunner.api.Status; import org.netbeans.modules.gsf.testrunner.api.TestSession; import org.netbeans.modules.gsf.testrunner.api.TestSuite; import org.netbeans.modules.gsf.testrunner.api.Testcase; import org.netbeans.modules.gsf.testrunner.api.Trouble; import org.netbeans.modules.gsf.testrunner.api.UnitTestsUsage; import org.netbeans.modules.gsf.testrunner.api.CommonUtils; import org.netbeans.modules.gsf.testrunner.api.CoreManager; import org.netbeans.modules.junit.api.JUnitTestSuite; import org.netbeans.modules.junit.api.JUnitTestcase; import org.netbeans.modules.maven.api.Constants; import org.netbeans.modules.maven.api.NbMavenProject; import org.netbeans.modules.maven.api.PluginPropertyUtils; import org.netbeans.modules.maven.api.execute.RunConfig; import org.netbeans.modules.maven.api.execute.RunUtils; import org.netbeans.modules.maven.api.output.OutputProcessor; import org.netbeans.modules.maven.api.output.OutputVisitor; import org.openide.filesystems.FileUtil; import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.Utilities; /** * * @author mkleint */ public class JUnitOutputListenerProvider implements OutputProcessor { private static final String TESTTYPE_UNIT = "UNIT"; //NOI81N private static final String TESTTYPE_INTEGRATION = "INTEGRATION"; //NOI81N private TestSession session; private String testType; private String reportNameSuffix; private final Pattern runningPattern; private final Pattern outDirPattern2; private final Pattern outDirPattern; private File outputDir; private String runningTestClass; private final Set<String> usedNames; private final long startTimeStamp; private static final Logger LOG = Logger.getLogger(JUnitOutputListenerProvider.class.getName()); private final RunConfig config; private boolean surefireRunningInParallel = false; private ArrayList<String> runningTestClasses; private ArrayList<String> runningTestClassesInParallel; private static final String GROUP_FILE_NAME = "dir"; public JUnitOutputListenerProvider(RunConfig config) { runningPattern = Pattern.compile("(?:\\[surefire\\] )?Running (.*)", Pattern.DOTALL); //NOI18N outDirPattern = Pattern.compile ("(?:\\[INFO\\] )?Surefire report directory\\: (?<" + GROUP_FILE_NAME + ">.*)", Pattern.DOTALL); //NOI18N outDirPattern2 = Pattern.compile("(?:\\[INFO\\] )?Setting reports dir\\: (?<" + GROUP_FILE_NAME + ">.*)", Pattern.DOTALL); //NOI18N this.config = config; usedNames = new HashSet<String>(); startTimeStamp = System.currentTimeMillis(); runningTestClasses = new ArrayList<String>(); runningTestClassesInParallel = new ArrayList<String>(); surefireRunningInParallel = isSurefireRunningInParallel(); } private boolean isSurefireRunningInParallel() { // http://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html // http://maven.apache.org/surefire/maven-surefire-plugin/examples/fork-options-and-parallel-execution.html String parallel = PluginPropertyUtils.getPluginProperty(config.getMavenProject(), Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_SUREFIRE, "parallel", "test", "parallel"); //NOI18N if (parallel != null) { return true; } String forkMode = PluginPropertyUtils.getPluginProperty(config.getMavenProject(), Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_SUREFIRE, "forkMode", "test", "forkMode"); //NOI18N if ("perthread".equals(forkMode)) { String threadCount = PluginPropertyUtils.getPluginProperty(config.getMavenProject(), Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_SUREFIRE, "threadCount", "test", "threadCount"); if (threadCount != null) { if (Integer.parseInt(threadCount) > 1) { return true; } } } String forkCount = PluginPropertyUtils.getPluginProperty(config.getMavenProject(), Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_SUREFIRE, "forkCount", "test", "forkCount"); if (forkCount != null) { int index = forkCount.indexOf("C"); int cpuCores = 1; if (index != -1) { forkCount = forkCount.substring(0, index); cpuCores = Runtime.getRuntime().availableProcessors(); } float forks; try { // example values: "1.5C", "4". http://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#forkCount forks = NumberFormat.getNumberInstance(Locale.ENGLISH).parse(forkCount).floatValue(); } catch (ParseException ex) { LOG.log(Level.FINE, null, ex); forks = 1; } if (forks * cpuCores > 1) { return true; } } return false; } public @Override String[] getRegisteredOutputSequences() { return new String[] { "mojo-execute#surefire:test", //NOI18N "mojo-execute#failsafe:integration-test" //NOI18N }; } public @Override void processLine(String line, OutputVisitor visitor) { Matcher match = outDirPattern.matcher(line); if (match.matches()) { outputDir = new File(match.group(GROUP_FILE_NAME)); if (session == null) { createSession(outputDir); } return; } match = outDirPattern2.matcher(line); if (match.matches()) { outputDir = new File(match.group(GROUP_FILE_NAME)); if (session == null) { createSession(outputDir); } return; } if (session == null) { return; } match = runningPattern.matcher(line); if (match.matches()) { if (surefireRunningInParallel) { // make sure results are displayed in case of a failure runningTestClassesInParallel.add(match.group(1)); } else { if (runningTestClass != null && outputDir != null) { // match.group(1) should be the FQN of a running test class but let's check to be on the safe side // If the matcher matches it means that we have a new test class running, // if not it probably means that this is user's text, e.g. "Running my cool test", so we can safely ignore it if (!isFullJavaId(match.group(1))) { return; } // tests are running sequentially, so update Test Results Window generateTest(); } runningTestClass = match.group(1); } } match = testSuiteStatsPattern.matcher(line); if (match.matches() && surefireRunningInParallel) { runningTestClass = match.group(6); if (runningTestClass != null && outputDir != null && !runningTestClasses.contains(runningTestClass)) { // When using reuseForks=true and a forkCount value larger than one, // the same output is produced many times, so show it only once in Test Results window runningTestClasses.add(runningTestClass); // runningTestClass should be the FQN of a running test class but let's check to be on the safe side // If the matcher matches it means that we have a new test class running, // if not it probably means that this is user's text, e.g. "Running my cool test", so we can safely ignore it if (!isFullJavaId(runningTestClass)) { return; } generateTest(); // runningTestClass did not fail so remove it from the list runningTestClassesInParallel.remove(runningTestClass); // runningTestClass might be the last one so make it null to avoid appearing twice when sequenceEnd() is called runningTestClass = null; } } } private static final String SECONDS_REGEX = "s(?:ec(?:ond)?(?:s|\\(s\\))?)?"; //NOI18N private static final String TESTSUITE_STATS_REGEX = "Tests run: +([0-9]+), +Failures: +([0-9]+), +Errors: +([0-9]+), +Skipped: +([0-9]+), +Time elapsed: +(.+)" + SECONDS_REGEX + " - in (.*)"; private static final Pattern testSuiteStatsPattern = Pattern.compile(TESTSUITE_STATS_REGEX); static boolean isTestSuiteStats(String line) { return testSuiteStatsPattern.matcher(line).matches(); } static String getTestSuiteFromStats(String line) { Matcher matcher = testSuiteStatsPattern.matcher(line); return matcher.matches() ? matcher.group(6) : null; } private static final String JAVA_ID_START_REGEX = "\\p{javaJavaIdentifierStart}"; //NOI18N private static final String JAVA_ID_PART_REGEX = "\\p{javaJavaIdentifierPart}"; //NOI18N private static final String JAVA_ID_REGEX = "(?:" + JAVA_ID_START_REGEX + ')' + "(?:" + JAVA_ID_PART_REGEX + ")*"; //NOI18N private static final String JAVA_ID_REGEX_FULL = JAVA_ID_REGEX + "(?:\\." + JAVA_ID_REGEX + ")*"; //NOI18N private static final Pattern fullJavaIdPattern = Pattern.compile(JAVA_ID_REGEX_FULL); static boolean isFullJavaId(String possibleNewRunningTestClass) { return fullJavaIdPattern.matcher(possibleNewRunningTestClass).matches(); } public @Override void sequenceStart(String sequenceId, OutputVisitor visitor) { session = null; reportNameSuffix = null; testType = null; String reportsDirectory = null; if (("mojo-execute#surefire:test".equals(sequenceId))) { // NOI18N // Fix for #257563 / SUREFIRE-1158, where surefire 2.19.x // removed the printing of the output directory. Get the directory from // the configuration directly or fallback to the plugin standard if (usingSurefire219(this.config.getMavenProject())) { // http://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#reportsDirectory reportsDirectory = getReportsDirectory( Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_SUREFIRE, "test", "${project.build.directory}/surefire-reports"); //NOI81N } reportNameSuffix = PluginPropertyUtils.getPluginProperty( config.getMavenProject(), Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_SUREFIRE, "reportNameSuffix", "test", //NOI81N "surefire.reportNameSuffix"); //NOI81N testType = TESTTYPE_UNIT; } else if ("mojo-execute#failsafe:integration-test".equals(sequenceId)) { //NOI81N reportsDirectory = getReportsDirectory( Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_FAILSAFE, "integration-test", "${project.build.directory}/failsafe-reports"); //NOI81N reportNameSuffix = PluginPropertyUtils.getPluginProperty( config.getMavenProject(), Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_FAILSAFE, "reportNameSuffix", "integration-test", //NOI81N "surefire.reportNameSuffix"); //NOI81N testType = TESTTYPE_INTEGRATION; //NOI81N } if (null != reportsDirectory) { File absoluteFile = new File(reportsDirectory); // configuration might be "target/directory", which is relative // to the maven project or an absolute path File relativeFile = new File(this.config.getMavenProject().getBasedir(), reportsDirectory); if (absoluteFile.exists() && absoluteFile.isDirectory()) { outputDir = absoluteFile; } else { if (relativeFile.exists() && relativeFile.isDirectory()) { outputDir = relativeFile; } } if (null != outputDir) { createSession(outputDir); } } } private String getReportsDirectory(String groupId, String artifactId, String goal, String fallbackExpression) { String reportsDirectory = PluginPropertyUtils.getPluginProperty(config.getMavenProject(), groupId, artifactId, "reportsDirectory", goal, null); // NOI18N if (null == reportsDirectory) { // fallback to default value try { Object defaultValue = PluginPropertyUtils .createEvaluator(config.getMavenProject()) .evaluate(fallbackExpression); if (defaultValue instanceof String) { reportsDirectory = (String) defaultValue; } } catch (ExpressionEvaluationException ex) { // NOP could not resolved default value } } return reportsDirectory; } //#179703 allow multiple sessions per project, in case there are multiple executions of surefire plugin. @NbBundle.Messages({ "# {0} - projectId", "LBL_TESTTYPE_UNIT={0} (Unit)", "# {0} - projectId", "LBL_TESTTYPE_INTEGRATION={0} (Integration)", "# {0} - projectId", "# {1} - index (1 based) of created session", "LBL_TESTTYPE_UNIT_INDEXED={0} (Unit) #{1}", "# {0} - projectId", "# {1} - index (1 based) of created session", "LBL_TESTTYPE_INTEGRATION_INDEXED={0} (Integration) #{1}" }) private String createSessionName(String projectId) { String name; if(testType != null && testType.equals(TESTTYPE_INTEGRATION)) { name = Bundle.LBL_TESTTYPE_INTEGRATION(projectId); } else { name = Bundle.LBL_TESTTYPE_UNIT(projectId); } int index = 2; while (usedNames.contains(name)) { if (testType != null && testType.equals(TESTTYPE_INTEGRATION)) { name = Bundle.LBL_TESTTYPE_INTEGRATION_INDEXED(projectId, index); } else { name = Bundle.LBL_TESTTYPE_UNIT_INDEXED(projectId, index); } index++; } usedNames.add(name); return name; } private CoreManager getManagerProvider() { Collection<? extends Lookup.Item<CoreManager>> providers = Lookup.getDefault().lookupResult(CoreManager.class).allItems(); for (Lookup.Item<CoreManager> provider : providers) { if(provider.getDisplayName().equals(CommonUtils.MAVEN_PROJECT_TYPE.concat("_").concat(CommonUtils.JUNIT_TF))) { return provider.getInstance(); } } return null; } private void createSession(File nonNormalizedFile) { if (session == null) { File fil = FileUtil.normalizeFile(nonNormalizedFile); Project prj = FileOwnerQuery.getOwner(Utilities.toURI(fil)); if (prj != null) { NbMavenProject mvnprj = prj.getLookup().lookup(NbMavenProject.class); if (mvnprj != null) { File projectFile = FileUtil.toFile(prj.getProjectDirectory()); if (projectFile != null) { UnitTestsUsage.getInstance().logUnitTestUsage(Utilities.toURI(projectFile), getJUnitVersion(config.getMavenProject())); } TestSession.SessionType type = TestSession.SessionType.TEST; String action = config.getActionName(); if (action != null) { //custom if (action.contains("debug")) { //NOI81N type = TestSession.SessionType.DEBUG; } } final TestSession.SessionType fType = type; CoreManager junitManager = getManagerProvider(); if (junitManager != null) { junitManager.registerNodeFactory(); } session = new TestSession(createSessionName(mvnprj.getMavenProject().getId()), prj, TestSession.SessionType.TEST); session.setRerunHandler(new RerunHandler() { public @Override void rerun() { RunUtils.executeMaven(config); } public @Override void rerun(Set<Testcase> tests) { RunConfig brc = RunUtils.cloneRunConfig(config); StringBuilder tst = new StringBuilder(); Map<String, Collection<String>> methods = new HashMap<String, Collection<String>>(); //#222776 calculate the approximate space the failed tests will occupy on the cmd line. //important on windows which places a limit on the length. int windowslimitcount = 0; for (Testcase tc : tests) { //TODO just when is the classname null?? if (tc.getClassName() != null) { Collection<String> lst = methods.get(tc.getClassName()); if (lst == null) { lst = new ArrayList<String>(); methods.put(tc.getClassName(), lst); windowslimitcount = windowslimitcount + tc.getClassName().length() + 1; // + 1 for , } lst.add(tc.getName()); windowslimitcount = windowslimitcount + tc.getName().length() + 1; // + 1 for # or + } } boolean exceedsWindowsLimit = Utilities.isWindows() && windowslimitcount > 6000; //just be conservative here, the limit is more (8000+) for (Map.Entry<String, Collection<String>> ent : methods.entrySet()) { tst.append(","); if (exceedsWindowsLimit) { String clazzName = ent.getKey(); int lastDot = ent.getKey().lastIndexOf("."); if (lastDot > -1) { clazzName = clazzName.substring(lastDot + 1); } tst.append(clazzName); } else { tst.append(ent.getKey()); } //#name only in surefire > 2.7.2 and junit > 4.0 or testng // bug works with the setting also for junit 3.x tst.append("#"); boolean first = true; for (String meth : ent.getValue()) { if (!first) { tst.append("+"); } first = false; tst.append(meth); } } if (tst.length() > 0) { brc.setProperty("test", tst.substring(1)); } RunUtils.executeMaven(brc); } public @Override boolean enabled(RerunType type) { //debug should now properly update debug port in runconfig... if (fType.equals(TestSession.SessionType.TEST) || fType.equals(TestSession.SessionType.DEBUG)) { if (RerunType.ALL.equals(type)) { return true; } if (RerunType.CUSTOM.equals(type)) { if (usingTestNG(config.getMavenProject())) { //#214334 test for testng has to come first, as itself depends on junit return usingSurefire28(config.getMavenProject()); } else if (usingJUnit4(config.getMavenProject())) { //#214334 return usingSurefire2121(config.getMavenProject()); } } } return false; } public @Override void addChangeListener(ChangeListener listener) { } public @Override void removeChangeListener(ChangeListener listener) { } }); if (junitManager != null) { junitManager.testStarted(session); } } } } } private boolean usingSurefire219(MavenProject prj) { String v = PluginPropertyUtils.getPluginVersion(prj, Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_SUREFIRE); return v != null && new ComparableVersion(v).compareTo(new ComparableVersion("2.19")) >= 0; } private boolean usingSurefire2121(MavenProject prj) { String v = PluginPropertyUtils.getPluginVersion(prj, Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_SUREFIRE); return v != null && new ComparableVersion(v).compareTo(new ComparableVersion("2.12.1")) >= 0; } private boolean usingSurefire28(MavenProject prj) { String v = PluginPropertyUtils.getPluginVersion(prj, Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_SUREFIRE); return v != null && new ComparableVersion(v).compareTo(new ComparableVersion("2.8")) >= 0; } private boolean usingTestNG(MavenProject prj) { for (Artifact a : prj.getArtifacts()) { if ("org.testng".equals(a.getGroupId()) && "testng".equals(a.getArtifactId())) { return true; } } return false; } private String getJUnitVersion(MavenProject prj) { String juVersion = ""; for (Artifact a : prj.getArtifacts()) { if ("junit".equals(a.getGroupId()) && ("junit".equals(a.getArtifactId()) || "junit-dep".equals(a.getArtifactId()))) { //junit-dep see #214238 String version = a.getVersion(); if (version != null && new ComparableVersion(version).compareTo(new ComparableVersion("4.8")) >= 0) { return "JUNIT4"; //NOI18N } if (version != null && new ComparableVersion(version).compareTo(new ComparableVersion("3.8")) >= 0) { return "JUNIT3"; //NOI18N } } } return juVersion; } private boolean usingJUnit4(MavenProject prj) { // SUREFIRE-724 for (Artifact a : prj.getArtifacts()) { if ("junit".equals(a.getGroupId()) && ("junit".equals(a.getArtifactId()) || "junit-dep".equals(a.getArtifactId()))) { //junit-dep see #214238 String version = a.getVersion(); if (version != null && new ComparableVersion(version).compareTo(new ComparableVersion("4.8")) >= 0) { return true; } } } return false; } public @Override void sequenceEnd(String sequenceId, OutputVisitor visitor) { if (session == null) { return; } if (runningTestClass != null && outputDir != null) { generateTest(); } CoreManager junitManager = getManagerProvider(); if (junitManager != null) { junitManager.sessionFinished(session); } runningTestClass = null; outputDir = null; session = null; surefireRunningInParallel = false; runningTestClasses = null; runningTestClassesInParallel = null; } private static final Pattern COMPARISON_PATTERN = Pattern.compile(".*expected:<(.*)> but was:<(.*)>$"); //NOI18N private static final Pattern COMPARISON_PATTERN_AFTER_65 = Pattern.compile(".*expected \\[(.*)\\] but found \\[(.*)\\]$"); //NOI18N static Trouble constructTrouble(@NonNull String type, @NullAllowed String message, @NullAllowed String text, boolean error) { Trouble t = new Trouble(error); if (message != null) { Matcher match = COMPARISON_PATTERN.matcher(message); if (match.matches()) { t.setComparisonFailure(new Trouble.ComparisonFailure(match.group(1), match.group(2))); } else { match = COMPARISON_PATTERN_AFTER_65.matcher(message); if (match.matches()) { t.setComparisonFailure(new Trouble.ComparisonFailure(match.group(1), match.group(2))); } } } if (text != null) { String[] strs = StringUtils.split(text, "\n"); List<String> lines = new ArrayList<String>(); if (message != null) { lines.add(message); } lines.add(type); for (int i = 1; i < strs.length; i++) { lines.add(strs[i]); } t.setStackTrace(lines.toArray(new String[0])); } return t; } public @Override void sequenceFail(String sequenceId, OutputVisitor visitor) { // try to get the failed test class. How can this be solved if it is not the first one in the list? if(surefireRunningInParallel) { if(runningTestClassesInParallel.isEmpty()) { // no test case is currently running, so do nothing (is this a more serious failure?) return; } runningTestClass = runningTestClassesInParallel.get(0); } sequenceEnd(sequenceId, visitor); } private void generateTest() { String suffix = reportNameSuffix; if (suffix == null) { suffix = ""; } else { //#204480 suffix = "-" + suffix; } File report = new File(outputDir, "TEST-" + runningTestClass + suffix + ".xml"); if (!report.isFile() || report.lastModified() < startTimeStamp) { //#219097 ignore results from previous invokation. if(surefireRunningInParallel) { // try waiting a bit to give time for the result file to be created try { Thread.sleep(500); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); } } if (!report.isFile() || report.lastModified() < startTimeStamp) { // and now try again return; } } if (report.length() > 50 * 1024 * 1024) { LOG.log(Level.INFO, "Skipping report file as size is too big (> 50MB): {0}", report.getPath()); return; } try { SAXBuilder builder = new SAXBuilder(); Document document = null; try { document = builder.build(report); } catch (Exception x) { try { // maybe the report file was not created yet, try waiting a bit and then try again Thread.sleep(500); document = builder.build(report); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); } catch (JDOMException ex) { LOG.log(Level.WARNING, "parsing " + report, x); } } if(document == null) { return; } Element testSuite = document.getRootElement(); assert "testsuite".equals(testSuite.getName()) : "Root name " + testSuite.getName(); //NOI18N TestSuite suite = new JUnitTestSuite(testSuite.getAttributeValue("name"), session); session.addSuite(suite); CoreManager junitManager = getManagerProvider(); if (junitManager != null) { junitManager.displaySuiteRunning(session, suite.getName()); } File output = new File(outputDir, runningTestClass + suffix + "-output.txt"); @SuppressWarnings("unchecked") List<Element> testcases = testSuite.getChildren("testcase"); //NOI18N String nameSuffix = reportNameSuffix != null ? "(" + reportNameSuffix + ")" : ""; for (Element testcase : testcases) { //#204480 String name = testcase.getAttributeValue("name"); if (name.endsWith(nameSuffix)) { name = name.substring(0, name.length() - nameSuffix.length()); } String displayName = name; String methodName = name; //In some cases (e.g. parameterized tests) Surefire appends some extra text to the test method name. //Remove anything past the first non-dot non-Java identifier character (i.e. anything that would not be part of a legal method name) int dotCodePoint = ".".codePointAt(0); for (int i = 0; i < name.length(); i++) { int codePoint = name.codePointAt(i); if (!Character.isJavaIdentifierPart(codePoint) && codePoint != dotCodePoint) { methodName = methodName.substring(0, i); break; } } Testcase test = new JUnitTestcase(methodName, displayName, testType, session); Element stdout = testcase.getChild("system-out"); //NOI18N // If *-output.txt file exists do not log standard output here to avoid logging it twice. // By default surefire only reports standard output for failing testcases. if (!output.isFile() && stdout != null) { logText(stdout.getText(), test, false); } Element failure = testcase.getChild("failure"); //NOI18N Status status = Status.PASSED; Trouble trouble = null; if (failure != null) { status = Status.FAILED; trouble = constructTrouble(failure.getAttributeValue("type"), failure.getAttributeValue("message"), failure.getText(), false); } Element error = testcase.getChild("error"); //NOI18N if (error != null) { status = Status.ERROR; trouble = constructTrouble(error.getAttributeValue("type"), error.getAttributeValue("message"), error.getText(), true); } Element skipped = testcase.getChild("skipped"); //NOI18N if (skipped != null) { status = Status.SKIPPED; } test.setStatus(status); if (trouble != null) { test.setTrouble(trouble); } String time = testcase.getAttributeValue("time"); if (time != null) { // the surefire plugin does not print out localised numbers, so use the english format float fl = NumberFormat.getNumberInstance(Locale.ENGLISH).parse(time).floatValue(); test.setTimeMillis((long)(fl * 1000)); } String classname = testcase.getAttributeValue("classname"); if (classname != null) { //#204480 if (classname.endsWith(nameSuffix)) { classname = classname.substring(0, classname.length() - nameSuffix.length()); } test.setClassName(classname); test.setLocation(test.getClassName().replace('.', '/') + ".java"); } session.addTestCase(test); } String time = testSuite.getAttributeValue("time"); // the surefire plugin does not print out localised numbers, so use the english format float fl = NumberFormat.getNumberInstance(Locale.ENGLISH).parse(time).floatValue(); long timeinmilis = (long) (fl * 1000); if (junitManager != null) { junitManager.displayReport(session, session.getReport(timeinmilis)); } else { // update report status as a minimum session.getReport(timeinmilis).setCompleted(true); } if (output.isFile()) { if (junitManager != null) { junitManager.displayOutput(session, FileUtils.fileRead(output), false); } } } catch (IOException x) { LOG.log(Level.WARNING, "parsing " + report, x); } catch (ParseException x) { LOG.log(Level.WARNING, "parsing " + report, x); } } private void logText(String text, Testcase test, boolean failure) { StringTokenizer tokens = new StringTokenizer(text, "\n"); //NOI18N List<String> lines = new ArrayList<String>(); while (tokens.hasMoreTokens()) { lines.add(tokens.nextToken()); } CoreManager junitManager = getManagerProvider(); if (junitManager != null) { junitManager.displayOutput(session, text, failure); } test.addOutputLines(lines); } }