| /* |
| * 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.junit.ant; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.nio.charset.StandardCharsets; |
| import java.text.NumberFormat; |
| import java.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.GregorianCalendar; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import org.apache.tools.ant.module.spi.AntEvent; |
| import org.apache.tools.ant.module.spi.AntSession; |
| import org.apache.tools.ant.module.spi.TaskStructure; |
| import org.netbeans.api.java.classpath.ClassPath; |
| import org.netbeans.api.java.platform.JavaPlatform; |
| import org.netbeans.api.java.platform.JavaPlatformManager; |
| import org.netbeans.api.project.FileOwnerQuery; |
| import org.netbeans.api.project.Project; |
| import org.netbeans.modules.gsf.testrunner.api.CommonUtils; |
| import org.netbeans.modules.gsf.testrunner.api.CoreManager; |
| import org.netbeans.modules.gsf.testrunner.api.OutputLine; |
| import org.netbeans.modules.gsf.testrunner.api.Report; |
| import org.netbeans.modules.gsf.testrunner.api.Status; |
| import org.netbeans.modules.gsf.testrunner.api.TestSession; |
| import org.netbeans.modules.gsf.testrunner.api.TestSession.SessionType; |
| 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.java.testrunner.JavaRegexpUtils; |
| import org.netbeans.modules.java.testrunner.ant.utils.AntProject; |
| import org.netbeans.modules.junit.api.JUnitTestSuite; |
| import org.netbeans.modules.junit.api.JUnitTestcase; |
| import org.netbeans.modules.junit.api.JUnitUtils; |
| import org.openide.ErrorManager; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.util.Lookup; |
| import org.openide.util.NbBundle; |
| import org.openide.util.Utilities; |
| import org.xml.sax.SAXException; |
| |
| import static java.util.logging.Level.FINER; |
| import static org.netbeans.modules.java.testrunner.JavaRegexpUtils.ADD_ERROR_PREFIX; |
| import static org.netbeans.modules.java.testrunner.JavaRegexpUtils.ADD_FAILURE_PREFIX; |
| import static org.netbeans.modules.java.testrunner.JavaRegexpUtils.END_OF_TEST_PREFIX; |
| import static org.netbeans.modules.java.testrunner.JavaRegexpUtils.START_OF_TEST_PREFIX; |
| import static org.netbeans.modules.java.testrunner.JavaRegexpUtils.TESTCASE_PREFIX; |
| import static org.netbeans.modules.java.testrunner.JavaRegexpUtils.TESTSUITE_PREFIX; |
| import static org.netbeans.modules.java.testrunner.JavaRegexpUtils.TESTSUITE_STATS_PREFIX; |
| import static org.netbeans.modules.java.testrunner.JavaRegexpUtils.TESTS_COUNT_PREFIX; |
| import static org.netbeans.modules.java.testrunner.JavaRegexpUtils.TEST_LISTENER_PREFIX; |
| |
| /** |
| * Obtains events from a single session of an Ant <code>junit</code> task |
| * and builds a {@link Report}. |
| * The events are delivered by the {@link JUnitAntLogger}. |
| * |
| * @see JUnitAntLogger |
| * @see Report |
| * @author Marian Petras |
| */ |
| final class JUnitOutputReader { |
| |
| private static final int MAX_REPORT_FILE_SIZE = 1 << 22; //2 MiB |
| /** */ |
| private static final String XML_FORMATTER_CLASS_NAME |
| = "org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter";//NOI18N |
| |
| /** */ |
| private final NumberFormat numberFormat = NumberFormat.getInstance(); |
| |
| /** */ |
| private final SessionType sessionType; |
| |
| /** whether XML report is expected */ |
| private boolean expectXmlReport; |
| /** */ |
| private final long timeOfSessionStart; |
| |
| /** */ |
| private JavaRegexpUtils regexp = JavaRegexpUtils.getInstance(); |
| |
| /** */ |
| private boolean lastHeaderBrief; |
| /** */ |
| private ClassPath platformSources; |
| |
| private TestSession testSession; |
| |
| private File resultsDir; |
| |
| private JUnitTestcase testcase; |
| |
| private Report report; |
| |
| enum State {DEFAULT, SUITE_STARTED, TESTCASE_STARTED, SUITE_FINISHED, TESTCASE_ISSUE}; |
| |
| private State state = State.DEFAULT; |
| private String testSuite; |
| |
| private final String OUTPUT = "------------- Standard Output ---------------";//NOI18N |
| private final String ERROR = "------------- Standard Error -----------------";//NOI18N |
| private final String END = "------------- ---------------- ---------------";//NOI18N |
| private boolean outputStarted = false; |
| private boolean errorStarted = false; |
| |
| /** Creates a new instance of JUnitOutputReader */ |
| JUnitOutputReader(final AntSession session, |
| final AntSessionInfo sessionInfo, |
| Project project, |
| final Properties props) { |
| CommonUtils.getInstance().setTestingFramework(CommonUtils.JUNIT_TF); |
| this.sessionType = sessionInfo.getSessionType(); |
| File antScript = FileUtil.normalizeFile(session.getOriginatingScript()); |
| this.timeOfSessionStart = sessionInfo.getTimeOfTestTaskStart(); |
| if (project == null){ |
| FileObject fileObj = FileUtil.toFileObject(antScript); |
| project = FileOwnerQuery.getOwner(fileObj); |
| if (project == null) { |
| project = new Project() { |
| public @Override FileObject getProjectDirectory() { |
| return FileUtil.createMemoryFileSystem().getRoot(); |
| } |
| public @Override Lookup getLookup() { |
| return Lookup.EMPTY; |
| } |
| }; |
| } |
| } |
| String className = props.getProperty("classname", ""); //NOI18N |
| String methodName = props.getProperty("methodname"); //NOI18N |
| String sName = JUnitExecutionManager.JUNIT_CUSTOM_FILENAME.equals(FileUtil.toFileObject(antScript).getName()) ? |
| NbBundle.getMessage(JUnitOutputReader.class, "LBL_RerunFailedTests") : //NOI18N |
| methodName != null ? |
| className + "." + methodName : className; |
| |
| CoreManager junitManager = getManagerProvider(); |
| if(junitManager != null) { |
| junitManager.registerNodeFactory(); |
| } |
| this.testSession = new JUnitTestSession(sName, project, sessionType); //NOI18N |
| testSession.setRerunHandler(new JUnitExecutionManager(session, testSession, props)); |
| File projectFile = FileUtil.toFile(project.getProjectDirectory()); |
| if(projectFile != null) { |
| JUnitUtils.logJUnitUsage(Utilities.toURI(projectFile)); |
| } |
| } |
| |
| TestSession getTestSession() { |
| return testSession; |
| } |
| |
| void verboseMessageLogged(final AntEvent event) { |
| final String msg = event.getMessage(); |
| if (msg == null) { |
| return; |
| } |
| verboseMessageLogged(msg); |
| } |
| |
| synchronized void verboseMessageLogged(String msg) { |
| switch(state){ |
| case SUITE_STARTED: { |
| if (msg.startsWith(TEST_LISTENER_PREFIX)) { |
| String testListenerMsg = msg.substring(TEST_LISTENER_PREFIX.length()); |
| if (testListenerMsg.startsWith(TESTS_COUNT_PREFIX)) { |
| // String countStr = testListenerMsg.substring(TESTS_COUNT_PREFIX.length()); |
| return; |
| } |
| |
| int leftBracketIndex = testListenerMsg.indexOf('('); |
| if (leftBracketIndex == -1) { |
| return; |
| } |
| |
| final String shortMsg = testListenerMsg.substring(0, leftBracketIndex); |
| if (shortMsg.equals(START_OF_TEST_PREFIX)) { |
| String restOfMsg = testListenerMsg.substring(START_OF_TEST_PREFIX.length()); |
| if (restOfMsg.length() != 0) { |
| char firstChar = restOfMsg.charAt(0); |
| char lastChar = restOfMsg.charAt(restOfMsg.length() - 1); |
| if ((firstChar == '(') && (lastChar == ')')) { |
| testCaseStarted(restOfMsg.substring(1, restOfMsg.length() - 1)); |
| } |
| } |
| return; |
| } |
| } |
| break; |
| } |
| case TESTCASE_STARTED: { |
| if (msg.startsWith(TEST_LISTENER_PREFIX)) { |
| String testListenerMsg = msg.substring(TEST_LISTENER_PREFIX.length()); |
| int leftBracketIndex = testListenerMsg.indexOf('('); |
| if (leftBracketIndex == -1) { |
| return; |
| } |
| final String shortMsg = testListenerMsg.substring(0, leftBracketIndex); |
| |
| if (shortMsg.equals(END_OF_TEST_PREFIX)) { |
| String restOfMsg = testListenerMsg.substring(END_OF_TEST_PREFIX.length()); |
| if (restOfMsg.length() != 0) { |
| char firstChar = restOfMsg.charAt(0); |
| char lastChar = restOfMsg.charAt(restOfMsg.length() - 1); |
| if ((firstChar == '(') && (lastChar == ')')) { |
| String name = restOfMsg.substring(1, restOfMsg.length() - 1); |
| if (name.equals(testSession.getCurrentTestCase().getName())) { |
| testCaseFinished(); |
| } |
| } |
| } |
| return; |
| } else if (shortMsg.equals(ADD_FAILURE_PREFIX) |
| || shortMsg.equals(ADD_ERROR_PREFIX)) { |
| int lastCharIndex = testListenerMsg.length() - 1; |
| |
| String insideBrackets = testListenerMsg.substring( |
| shortMsg.length() + 1, |
| lastCharIndex); |
| int commaIndex = insideBrackets.indexOf(','); |
| String testName = (commaIndex == -1) |
| ? insideBrackets |
| : insideBrackets.substring(0, commaIndex); |
| if (!testName.equals(testSession.getCurrentTestCase().getName())) { |
| return; |
| } |
| testSession.getCurrentTestCase().setTrouble(new Trouble(shortMsg.equals(ADD_ERROR_PREFIX))); |
| boolean hasErrMsg = (commaIndex != -1) && |
| ((commaIndex + 2) <= insideBrackets.length()); // #166912 |
| if (hasErrMsg) { |
| int errMsgStart; |
| if (Character.isSpaceChar(insideBrackets.charAt(commaIndex + 1))) { |
| errMsgStart = commaIndex + 2; |
| } else { |
| errMsgStart = commaIndex + 1; |
| } |
| String troubleMsg = insideBrackets.substring(errMsgStart); |
| if (!troubleMsg.equals("null")) { //NOI18N |
| addStackTraceLine(testSession.getCurrentTestCase(), troubleMsg, false); |
| } |
| } |
| return; |
| } |
| } |
| break; |
| } |
| case DEFAULT: |
| case SUITE_FINISHED: |
| case TESTCASE_ISSUE: |
| { |
| Matcher matcher = JavaRegexpUtils.JAVA_EXECUTABLE.matcher(msg); |
| if (matcher.find()) { |
| String executable = matcher.group(1); |
| ClassPath platformSrcs = findPlatformSources(executable); |
| if (platformSrcs != null) { |
| this.platformSources = platformSrcs; |
| } |
| } |
| break; |
| } |
| } |
| } |
| |
| @SuppressWarnings("fallthrough") |
| synchronized void messageLogged(final AntEvent event) { |
| final String msg = event.getMessage(); |
| if (msg == null) { |
| return; |
| } |
| if(msg.startsWith(TESTSUITE_PREFIX) && state == State.DEFAULT) { |
| testSuite = msg; |
| } |
| if(msg.startsWith(TESTSUITE_PREFIX) && !msg.equals(testSuite) && state != State.SUITE_FINISHED) { |
| // previous testsuite finished abnormally and state was not set correctly |
| state = State.SUITE_FINISHED; |
| } |
| |
| handleMessage(msg, event.getLogLevel()); |
| |
| switch (state){ |
| case TESTCASE_ISSUE: |
| case SUITE_FINISHED:{ |
| if (msg.startsWith(TESTCASE_PREFIX)) { |
| String header = msg.substring(TESTCASE_PREFIX.length()); |
| boolean success = |
| lastHeaderBrief |
| ? tryParseBriefHeader(header) |
| || !(lastHeaderBrief = !tryParsePlainHeader(header)) |
| : tryParsePlainHeader(header) |
| || (lastHeaderBrief = tryParseBriefHeader(header)); |
| if (success) { |
| state = State.TESTCASE_ISSUE; |
| } |
| break; |
| } |
| } |
| case DEFAULT: { |
| if (msg.startsWith(TESTSUITE_PREFIX)) { |
| String suiteName = msg.substring(TESTSUITE_PREFIX.length()); |
| if (regexp.getFullJavaIdPattern().matcher(suiteName).matches()){ |
| suiteStarted(suiteName); |
| resultsDir = determineResultsDir(event); |
| } |
| } |
| |
| if (state.equals(State.TESTCASE_ISSUE) && !msg.equals("")){ |
| addStackTraceLine(testcase, msg, true); |
| } |
| break; |
| } |
| case SUITE_STARTED: { |
| if (msg.startsWith(TESTSUITE_STATS_PREFIX)) { |
| Matcher matcher = regexp.getSuiteStatsPattern().matcher(msg); |
| if (matcher.matches()) { |
| try { |
| suiteFinished(Integer.parseInt(matcher.group(1)), |
| Integer.parseInt(matcher.group(2)), |
| Integer.parseInt(matcher.group(3)), |
| parseTime(matcher.group(4))); |
| } catch (NumberFormatException ex) { |
| assert false : ex; // #175298 |
| } |
| } else { |
| matcher = regexp.getSuiteStats190Pattern().matcher(msg); |
| if (matcher.matches()) { |
| try { |
| suiteFinished(Integer.parseInt(matcher.group(1)), |
| Integer.parseInt(matcher.group(2)), |
| Integer.parseInt(matcher.group(3)), |
| parseTime(matcher.group(6))); |
| } catch (NumberFormatException ex) { |
| assert false : ex; // #175298 |
| } |
| } else { |
| assert false : |
| "See bug #185544 \n" |
| + "Please, provide details about your test run: \n" |
| + "JUnit version, a way how tests are launched, \n" |
| + "kind of the project and so on. \n" |
| + "Description of your test environment, a sample \n" |
| + "project and steps to reproduce this bug will be \n" |
| + "highly appreciated.\n" |
| + "Cause of this error is the JUnit message about \n" |
| + "execution of the tests doesn't match for usual " |
| + "regexp pattern: \n" |
| + "message: \"" + msg + "\"\n" |
| + "pattern: \"" + regexp.getSuiteStats190Pattern(); |
| } |
| } |
| break; |
| } |
| } |
| case TESTCASE_STARTED: { |
| int posTestListener = msg.indexOf(TEST_LISTENER_PREFIX); |
| if (posTestListener != -1) { |
| displayOutput(msg.substring(0, posTestListener), event.getLogLevel() == AntEvent.LOG_WARN); |
| verboseMessageLogged(msg.substring(posTestListener)); |
| } else { |
| displayOutput(msg, event.getLogLevel() == AntEvent.LOG_WARN); |
| } |
| break; |
| } |
| } |
| } |
| |
| /** |
| */ |
| private int parseTime(String timeString) { |
| int timeMillis; |
| try { |
| double seconds = numberFormat.parse(timeString).doubleValue(); |
| timeMillis = Math.round((float) (seconds * 1000.0)); |
| } catch (ParseException ex) { |
| timeMillis = -1; |
| } |
| return timeMillis; |
| } |
| |
| /** |
| * Tries to determine test results directory. |
| * |
| * @param event Ant event serving as a source of information |
| * @return <code>File<code> object representing the results directory, |
| * or <code>null</code> if the results directory could not be |
| * determined |
| */ |
| private static File determineResultsDir(final AntEvent event) { |
| File resultsDir = null; |
| |
| final String taskName = event.getTaskName(); |
| if (taskName != null) { |
| if (taskName.equals("junit")) { //NOI18N |
| resultsDir = determineJunitTaskResultsDir(event); |
| } else if (taskName.equals("java")) { //NOI18N |
| resultsDir = determineJavaTaskResultsDir(event); |
| } |
| } |
| |
| if ((resultsDir != null) && resultsDir.exists() && resultsDir.isDirectory()) { |
| return resultsDir; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| */ |
| private static File determineJunitTaskResultsDir(final AntEvent event) { |
| final TaskStructure taskStruct = event.getTaskStructure(); |
| if (taskStruct == null) { |
| return null; |
| } |
| |
| String todirAttr = null; |
| boolean hasXmlFileOutput = false; |
| |
| for (TaskStructure taskChild : taskStruct.getChildren()) { |
| String taskChildName = taskChild.getName(); |
| if (taskChildName.equals("batchtest") //NOI18N |
| || taskChildName.equals("test")) { //NOI18N |
| todirAttr = taskChild.getAttribute("todir"); //NOI18N |
| |
| } else if (taskChildName.equals("formatter")) { //NOI18N |
| if (hasXmlFileOutput) { |
| continue; |
| } |
| String typeAttr = taskChild.getAttribute("type"); //NOI18N |
| if ((typeAttr != null) |
| && "xml".equals(event.evaluate(typeAttr))) { //NOI18N |
| String useFileAttr |
| = taskChild.getAttribute("usefile"); //NOI18N |
| if ((useFileAttr == null) |
| || AntProject.toBoolean(event.evaluate(useFileAttr))) { |
| hasXmlFileOutput = true; |
| } |
| } |
| } |
| } |
| |
| if (!hasXmlFileOutput) { |
| return null; |
| } |
| |
| File resultsDir = (todirAttr != null) ? getFile(todirAttr, event) |
| : getBaseDir(event); |
| return findAbsolutePath(resultsDir, taskStruct, event); |
| } |
| |
| /** |
| */ |
| private static File determineJavaTaskResultsDir(final AntEvent event) { |
| final TaskStructure taskStruct = event.getTaskStructure(); |
| if (taskStruct == null) { |
| return null; |
| } |
| |
| String todirPath = null; |
| |
| for (TaskStructure taskChild : taskStruct.getChildren()) { |
| String taskChildName = taskChild.getName(); |
| if (taskChildName.equals("arg")) { //NOI18N |
| String valueAttr = taskChild.getAttribute("value"); //NOI18N |
| if (valueAttr == null) { |
| valueAttr = taskChild.getAttribute("line"); //NOI18N |
| } |
| if (valueAttr != null) { |
| valueAttr = event.evaluate(valueAttr); |
| if (valueAttr.startsWith("formatter=")) { //NOI18N |
| String formatter = valueAttr.substring("formatter=".length());//NOI18N |
| int commaIndex = formatter.indexOf(','); |
| if ((commaIndex != -1) |
| && formatter.substring(0, commaIndex).equals(XML_FORMATTER_CLASS_NAME)) { |
| String fullReportFileName = formatter.substring(commaIndex + 1); |
| todirPath = new File(fullReportFileName).getParent(); |
| if (todirPath == null) { |
| todirPath = "."; //NOI18N |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (todirPath == null) { |
| return null; |
| } |
| |
| File resultsDir = (!todirPath.equals(".")) ? new File(todirPath) //NOI18N |
| : null; |
| return findAbsolutePath(resultsDir, taskStruct, event); |
| } |
| |
| private static File findAbsolutePath(File path, TaskStructure taskStruct, AntEvent event) { |
| if (isAbsolute(path)) { |
| return path; |
| } |
| |
| String forkAttr = taskStruct.getAttribute("fork"); //NOI18N |
| if ((forkAttr != null) && AntProject.toBoolean(event.evaluate(forkAttr))) { |
| String dirAttr = taskStruct.getAttribute("dir"); //NOI18N |
| if (dirAttr != null) { |
| path = combine(getFile(dirAttr, event), path); |
| if (isAbsolute(path)) { |
| return path; |
| } |
| } |
| } |
| |
| return combine(getBaseDir(event), path); |
| } |
| |
| private static File combine(File parentPath, File path) { |
| return (path != null) ? new File(parentPath, path.getPath()) |
| : parentPath; |
| } |
| |
| private static boolean isAbsolute(File path) { |
| return (path != null) && path.isAbsolute(); |
| } |
| |
| private static File getFile(String attrValue, AntEvent event) { |
| return new File(event.evaluate(attrValue)); |
| } |
| |
| private static File getBaseDir(AntEvent event) { |
| return new File(event.getProperty("basedir")); //NOI18N |
| } |
| |
| /** |
| */ |
| private ClassPath findPlatformSources(final String javaExecutable) { |
| |
| /* Copied from JavaAntLogger */ |
| |
| final JavaPlatform[] platforms = JavaPlatformManager.getDefault() |
| .getInstalledPlatforms(); |
| for (int i = 0; i < platforms.length; i++) { |
| FileObject fo = platforms[i].findTool("java"); //NOI18N |
| if (fo != null) { |
| File f = FileUtil.toFile(fo); |
| if (f.getAbsolutePath().startsWith(javaExecutable)) { |
| return platforms[i].getSourceFolders(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| 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(org.netbeans.modules.gsf.testrunner.api.CommonUtils.ANT_PROJECT_TYPE.concat("_").concat(org.netbeans.modules.gsf.testrunner.api.CommonUtils.JUNIT_TF))) { |
| return provider.getInstance(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Notifies that a test (Ant) task was just started. |
| * |
| * @param expectedSuitesCount expected number of test suites going to be |
| * executed by this task |
| */ |
| void testTaskStarted(int expectedSuitesCount, boolean expectXmlOutput) { |
| this.expectXmlReport = expectXmlOutput; |
| CoreManager junitManager = getManagerProvider(); |
| if(junitManager != null) { |
| junitManager.testStarted(testSession); |
| } |
| } |
| |
| /** |
| */ |
| void testTaskFinished() { |
| closePereviousReport(); // #171050 |
| } |
| |
| private void closePereviousReport(){ |
| TestSuite currentSuite = testSession.getCurrentSuite(); |
| if (currentSuite != null){ |
| //try to get results from report xml file |
| if (resultsDir != null) { |
| File reportFile = findReportFile(); |
| if ((reportFile != null) && isValidReportFile(reportFile)) { |
| JUnitTestSuite reportSuite = parseReportFile(reportFile); |
| if ((reportSuite != null) && (reportSuite.getName().equals(currentSuite.getName()))) { |
| lastSuiteTime = reportSuite.getElapsedTime(); |
| for(Testcase tc: currentSuite.getTestcases()){ |
| if (!tc.getOutput().isEmpty()){ |
| List<String> output = new ArrayList<>(); |
| for(OutputLine l: tc.getOutput()){ |
| output.add(l.getLine()); |
| } |
| Testcase rtc = findTest(reportSuite, tc.getName()); |
| |
| if (rtc != null) |
| rtc.addOutputLines(output); |
| } |
| } |
| if (!reportSuite.getTestcases().isEmpty()){ |
| currentSuite.getTestcases().clear(); |
| currentSuite.getTestcases().addAll(reportSuite.getTestcases()); |
| } |
| } |
| } |
| } |
| if (report == null){ |
| report = testSession.getReport(lastSuiteTime); |
| }else{ |
| report.update(testSession.getReport(lastSuiteTime)); |
| } |
| switch(state){ |
| case SUITE_STARTED: |
| case TESTCASE_STARTED: |
| if (report.getTotalTests() == report.getPassed() + report.getErrors() + report.getFailures()) { |
| Logger.getLogger(JUnitOutputReader.class.getName()).log(Level.WARNING, "Ensure that the output-stream is not closed."); |
| } else { |
| report.setAborted(true); |
| } |
| default: |
| CoreManager junitManager = getManagerProvider(); |
| if (junitManager != null) { |
| junitManager.displayReport(testSession, report, true); |
| } else { // update report status as a minimum |
| report.setCompleted(true); |
| } |
| } |
| report = null; |
| lastSuiteTime = 0; |
| } |
| |
| } |
| |
| /** |
| */ |
| void buildFinished(final AntEvent event) { |
| CoreManager junitManager = getManagerProvider(); |
| if(junitManager != null) { |
| junitManager.sessionFinished(testSession); |
| } |
| } |
| |
| private long lastSuiteTime = 0; |
| |
| /** |
| * Notifies that a test suite was just started. |
| * |
| * @param suiteName name of the suite; or {@code null} |
| * if the suite name is unknown |
| */ |
| private void suiteStarted(final String suiteName) { |
| closePereviousReport(); |
| TestSuite suite = new JUnitTestSuite(suiteName, testSession); |
| testSession.addSuite(suite); |
| CoreManager junitManager = getManagerProvider(); |
| if(junitManager != null) { |
| junitManager.displaySuiteRunning(testSession, suite); |
| } |
| state = State.SUITE_STARTED; |
| platformSources = null; |
| } |
| |
| private void suiteFinished(int total, int failures, int errors ,long time) { |
| int addFail = failures; |
| int addError = errors; |
| int addPass = total - failures - errors; |
| TestSuite suite = testSession.getCurrentSuite(); |
| for(Testcase tc: suite.getTestcases()){ |
| switch(tc.getStatus()){ |
| case ERROR: addError--;break; |
| case FAILED: addFail--;break; |
| default: addPass--; |
| } |
| } |
| for(int i=0; i<addPass; i++){ |
| JUnitTestcase tc = new JUnitTestcase("Unknown", "Unknown", testSession); //NOI18N |
| tc.setStatus(Status.PASSED); |
| testSession.addTestCase(tc); |
| } |
| for(int i=0; i<addFail; i++){ |
| JUnitTestcase tc = new JUnitTestcase("Unknown", "Unknown", testSession); //NOI18N |
| tc.setStatus(Status.FAILED); |
| testSession.addTestCase(tc); |
| } |
| for(int i=0; i<addError; i++){ |
| JUnitTestcase tc = new JUnitTestcase("Unknown", "Unknown", testSession); //NOI18N |
| tc.setStatus(Status.ERROR); |
| testSession.addTestCase(tc); |
| } |
| |
| lastSuiteTime = time; |
| state = State.SUITE_FINISHED; |
| testSession.finishSuite(suite); |
| } |
| |
| private void testCaseStarted(String name){ |
| JUnitTestcase tc = new JUnitTestcase(name, "JUnit Test", testSession); |
| testSession.addTestCase(tc); //NOI18N |
| state = State.TESTCASE_STARTED; |
| } |
| |
| private void testCaseFinished(){ |
| if (report == null){ |
| report = testSession.getReport(0); |
| }else{ |
| report.update(testSession.getReport(0)); |
| } |
| CoreManager junitManager = getManagerProvider(); |
| if(junitManager != null) { |
| junitManager.displayReport(testSession, report, false); |
| } else { // update report status as a minimum |
| report.setCompleted(false); |
| } |
| state = State.SUITE_STARTED; |
| } |
| |
| //------------------ UPDATE OF DISPLAY ------------------- |
| |
| private void handleMessage(String msg, int logLevel) { |
| if (msg.equals(END)) { |
| if (outputStarted) { |
| outputStarted = false; |
| } else if (errorStarted) { |
| errorStarted = false; |
| } |
| } |
| if (outputStarted || errorStarted) { |
| if (logLevel == AntEvent.LOG_INFO) { |
| displayOutput(msg, outputStarted ? false : true); |
| } |
| } |
| if (msg.equals(OUTPUT)) { |
| outputStarted = true; |
| } |
| if (msg.equals(ERROR)) { |
| errorStarted = true; |
| } |
| } |
| |
| /** |
| */ |
| private void displayOutput(final String text, final boolean error) { |
| if (outputStarted || errorStarted) { |
| CoreManager junitManager = getManagerProvider(); |
| if(junitManager != null) { |
| junitManager.displayOutput(testSession, text, error); |
| } |
| } else { |
| if (!error) { |
| List<String> addedLines = new ArrayList<String>(); |
| addedLines.add(text); |
| Testcase tc = testSession.getCurrentTestCase(); |
| if (tc != null) { |
| tc.addOutputLines(addedLines); |
| } |
| } |
| } |
| } |
| |
| //-------------------------------------------------------- |
| |
| /** |
| * Parses given plain header and sets the time of the test case if possible. |
| * @param testcaseHeader the test case header. |
| * @return {@code true} is success, otherwise {@code false}. |
| */ |
| private boolean tryParsePlainHeader(String testcaseHeader) { |
| final Matcher matcher = regexp.getTestcaseHeaderPlainPattern() |
| .matcher(testcaseHeader); |
| if (matcher.matches()) { |
| String methodName = matcher.group(1); |
| String timeString = matcher.group(2); |
| testcase = findTest(testSession.getCurrentSuite(), methodName); |
| if(testcase != null) { // #187035 |
| testcase.setTimeMillis(parseTime(timeString)); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private JUnitTestcase findTest(TestSuite suite, String methodName){ |
| JUnitTestcase ret = null; |
| for(Testcase tcase: suite.getTestcases()){ |
| if (tcase.getName().equals(methodName)){ |
| ret = (JUnitTestcase)tcase; |
| break; |
| } |
| } |
| return ret; |
| } |
| |
| /** |
| */ |
| private boolean tryParseBriefHeader(String testcaseHeader) { |
| final Matcher matcher = regexp.getTestcaseHeaderBriefPattern() |
| .matcher(testcaseHeader); |
| if (matcher.matches()) { |
| String methodName = matcher.group(1); |
| String clsName = matcher.group(2); |
| boolean error = (matcher.group(3) == null); |
| |
| testcase = findTest(testSession.getCurrentSuite(), methodName); |
| if (testcase == null){ // probably TestListener interface not reported test progress for some reason (for ex. debug mode) |
| testcase = new JUnitTestcase(methodName, "JUnit test", testSession); |
| testSession.addTestCase(testcase); |
| } |
| testcase.setClassName(clsName); |
| Trouble trouble = testcase.getTrouble(); |
| if (trouble == null){ |
| trouble = new Trouble(error); |
| testcase.setTrouble(trouble); |
| }else{ |
| trouble.setError(error); |
| trouble.setStackTrace(null); |
| } |
| |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| private File findReportFile() { |
| File file = new File(resultsDir, |
| "TEST-" + testSession.getCurrentSuite().getName() + ".xml"); //NOI18N |
| return (file.isFile() ? file : null); |
| } |
| |
| /** |
| */ |
| private boolean isValidReportFile(File reportFile) { |
| if (!reportFile.canRead()) { |
| return false; |
| } |
| |
| long lastModified = reportFile.lastModified(); |
| long timeDelta = lastModified - timeOfSessionStart; |
| |
| final Logger logger = Logger.getLogger("org.netbeans.modules.junit.outputreader.timestamps");//NOI18N |
| final Level logLevel = FINER; |
| if (logger.isLoggable(logLevel)) { |
| logger.log(logLevel, "Report file: " + reportFile.getPath());//NOI18N |
| |
| final GregorianCalendar timeStamp = new GregorianCalendar(); |
| |
| timeStamp.setTimeInMillis(timeOfSessionStart); |
| logger.log(logLevel, "Session start: " + String.format("%1$tT.%2$03d", timeStamp, timeStamp.get(GregorianCalendar.MILLISECOND)));//NOI18N |
| |
| timeStamp.setTimeInMillis(lastModified); |
| logger.log(logLevel, "Report timestamp: " + String.format("%1$tT.%2$03d", timeStamp, timeStamp.get(GregorianCalendar.MILLISECOND)));//NOI18N |
| } |
| |
| if (timeDelta >= 0) { |
| return true; |
| } |
| |
| return -timeDelta <= timeOfSessionStart % 1000; |
| |
| } |
| |
| private JUnitTestSuite parseReportFile(File reportFile) { |
| final long fileSize = reportFile.length(); |
| if ((fileSize < 0l) || (fileSize > MAX_REPORT_FILE_SIZE)) { |
| return null; |
| } |
| |
| JUnitTestSuite suite = null; |
| try { |
| suite = XmlOutputParser.parseXmlOutput( |
| new InputStreamReader(new FileInputStream(reportFile), StandardCharsets.UTF_8), testSession); |
| } catch (SAXException ex) { |
| /* This exception has already been handled. */ |
| } catch (IOException ex) { |
| /* |
| * Failed to read the report file - but we still have |
| * the report built from the Ant output. |
| */ |
| int severity = ErrorManager.INFORMATIONAL; |
| ErrorManager errMgr = ErrorManager.getDefault(); |
| if (errMgr.isLoggable(severity)) { |
| errMgr.notify( |
| severity, |
| errMgr.annotate( |
| ex, |
| "I/O exception while reading JUnit XML report file from JUnit: "));//NOI18N |
| } |
| } |
| return suite; |
| } |
| |
| /** |
| */ |
| private static int parseNonNegativeInteger(String str) |
| throws NumberFormatException { |
| final int len = str.length(); |
| if ((len == 0) || (len > 8)) { |
| throw new NumberFormatException(); |
| } |
| |
| char c = str.charAt(0); |
| if ((c < '0') || (c > '9')) { |
| throw new NumberFormatException(); |
| } |
| int result = c - '0'; |
| |
| if (len > 1) { |
| for (char d : str.substring(1).toCharArray()) { |
| if ((d < '0') || (d > '9')) { |
| throw new NumberFormatException(); |
| } |
| result = 10 * result + (d - '0'); |
| } |
| } |
| |
| return result; |
| } |
| |
| private void addStackTraceLine(Testcase testcase, String line, boolean validateST){ |
| Trouble trouble = testcase.getTrouble(); |
| if ((trouble == null) || (line == null) || (line.length() == 0) || (line.equals("null"))){ //NOI18N |
| return; |
| } |
| |
| if (validateST){ |
| boolean valid = false; |
| Pattern[] patterns = new Pattern[]{regexp.getCallstackLinePattern(), |
| regexp.getComparisonHiddenPattern(), |
| regexp.getFullJavaIdPattern()}; |
| for(Pattern pattern: patterns){ |
| Matcher matcher = pattern.matcher(line); |
| if (matcher.matches()){ |
| valid = true; |
| break; |
| } |
| } |
| if (!valid){ |
| return; |
| } |
| } |
| |
| String[] stArray = trouble.getStackTrace(); |
| if (stArray == null){ |
| trouble.setStackTrace(new String[]{line}); |
| Matcher matcher = regexp.getComparisonPattern().matcher(line); |
| if (matcher.matches()){ |
| trouble.setComparisonFailure( |
| new Trouble.ComparisonFailure( |
| matcher.group(1)+matcher.group(2)+matcher.group(3), |
| matcher.group(4)+matcher.group(5)+matcher.group(6)) |
| ); |
| return; |
| } |
| matcher = regexp.getComparisonHiddenPattern().matcher(line); |
| if (matcher.matches()){ |
| trouble.setComparisonFailure( |
| new Trouble.ComparisonFailure( |
| matcher.group(1), |
| matcher.group(2)) |
| ); |
| return; |
| } |
| |
| } else { |
| List<String> stList = new ArrayList<>(Arrays.asList(testcase.getTrouble().getStackTrace())); |
| if (!line.startsWith(stList.get(stList.size()-1))){ |
| stList.add(line); |
| trouble.setStackTrace(stList.toArray(new String[stList.size()])); |
| } |
| } |
| } |
| } |