blob: a97bb9b0e856f300d9b1dc4a857fe3aeb6cc22f0 [file] [log] [blame]
/*
* 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.testng.ant;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.UnsupportedCharsetException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.*;
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.*;
import org.netbeans.modules.gsf.testrunner.api.TestSession.SessionType;
import org.netbeans.modules.testng.api.TestNGTestSuite;
import org.netbeans.modules.testng.api.TestNGTestcase;
import org.netbeans.modules.testng.api.XmlOutputParser;
import org.netbeans.modules.testng.api.XmlResult;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Lookup;
import org.xml.sax.SAXException;
/**
* 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 TestNGAntLogger}.
*
* @see TestNGAntLogger
* @see Report
* @author Marian Petras
* @author Lukas Jungmann
*/
final class TestNGOutputReader {
private static final Logger LOG = Logger.getLogger(TestNGOutputReader.class.getName());
private static final Logger progressLogger = Logger.getLogger(
"org.netbeans.modules.testng.outputreader.progress");
/**
*
*/
private final NumberFormat numberFormat = NumberFormat.getInstance();
/**
*
*/
private final SessionType sessionType;
/**
* whether XML report is expected
*/
private boolean offline;
private boolean noresults = true;
/**
*
*/
private final File antScript;
/**
*
*/
private final long timeOfSessionStart;
private long lastSuiteTime = 0;
private ClassPath platformSources;
private TestNGTestSession testSession;
private Project project;
private File resultsDir;
private Map<String, Report> reports;
private int successPercentage;
private int passedWithErrorsFailure;
private boolean descriptionInPassedWithErrors;
private String suiteName;
private String testCase;
private String parameters;
private String values;
private String duration;
private int msDuration;
private boolean failedInConfigurationMethod;
private boolean failedInAfterClass;
private boolean failedInBeforeClass;
private boolean failedInAfterMethod;
private boolean failedInBeforeMethod;
private boolean canAddToStackTrace;
private long currentTime;
private String currentSuitename;
/**
* Creates a new instance of TestNGOutputReader
*/
TestNGOutputReader(final AntSession session,
final AntSessionInfo sessionInfo,
final Project project,
final Properties props) {
CommonUtils.getInstance().setTestingFramework(CommonUtils.TESTNG_TF);
this.project = project;
this.sessionType = sessionInfo.getSessionType();
this.antScript = FileUtil.normalizeFile(session.getOriginatingScript());
this.timeOfSessionStart = sessionInfo.getTimeOfTestTaskStart();
if (project == null) {
FileObject fileObj = FileUtil.toFileObject(antScript);
this.project = FileOwnerQuery.getOwner(fileObj);
}
CoreManager testngManager = getManagerProvider();
if(testngManager != null) {
testngManager.registerNodeFactory();
}
this.testSession = new TestNGTestSession(sessionInfo.getSessionName(), this.project, sessionType);
testSession.setRerunHandler(new TestNGExecutionManager(session, testSession, props));
reports = new HashMap<String, Report>();
}
/**
* for tests
*/
TestNGOutputReader(TestNGTestSession session) {
testSession = session;
sessionType = session.getSessionType();
antScript = null;
timeOfSessionStart = System.currentTimeMillis();
project = session.getProject();
reports = new HashMap<String, Report>();
}
private CoreManager getManagerProvider() {
Collection<? extends Lookup.Item<CoreManager>> providers = Lookup.getDefault().lookupResult(CoreManager.class).allItems();
for (Lookup.Item<CoreManager> provider : providers) {
if(provider.getDisplayName().endsWith("_".concat(org.netbeans.modules.gsf.testrunner.api.CommonUtils.TESTNG_TF))) {
return provider.getInstance();
}
}
return null;
}
Project getProject() {
return project;
}
TestSession getTestSession() {
return testSession;
}
void verboseMessageLogged(final AntEvent event) {
final String msg = event.getMessage();
if (msg == null) {
return;
}
// msg could contain arbitrary charachters printed by the user,
// so TestNG specific output could exist e.g. in the middle of msg.
if (!msg.contains(RegexpUtils.TEST_LISTENER_PREFIX) || offline) {
//this message is not for us...
return;
}
if (noresults) noresults = false;
verboseMessageLogged(msg);
// displayOutput(msg, event.getLogLevel() == AntEvent.LOG_WARN);
}
private boolean suiteSummary = false;
private long elapsedTime = 0;
// TestNG version > 6.5.2 does not by default add the org.testng.reporters.VerboseReporter listener to the testng task,
// see https://github.com/cbeust/testng/commit/6fc192911c8c58f38583a657c1edac20e6508004
// This brakes the "Test" project action as the TestNGAntLogger does not get notified about any output. This leads to
// waiting to read the results from the build/test/results/testng-results.xml file, which leads to no on-line results
// visualization in the Test Results Window. So, the org.testng.reporters.VerboseReporter listener is added to build-impl.xsl
// file for all project types. This fixes the "Test" project action for TestNG version > 6.5.2 but produces dublicate output
// for version <= 6.5.2 that need to be escaped. This will happen if old project uses "Dedicated Folder for Storing Libraries",
// which means that the testng dependency for that project will not be automatically updated while the build-impl.xml file will.
// These two new variables come to the rescue.
private String lastMessageLogged = "";
private boolean suiteFinished = false;
private class SuiteStats {
private String name = null;
private int testRun = -1;
private int testFail = -1;
private int testSkip = -1;
private int confFail = 0;
private int confSkip = 0;
}
private SuiteStats suiteStat;
private List<String> txt = new ArrayList<String>();
private static int x = 0;
private void handleInvocationCount(int invocationCount) {
double actual = (double) (invocationCount - passedWithErrorsFailure) / (double) invocationCount;
double expected = (double) successPercentage / 100.0;
duration = Integer.toString(msDuration);
if (actual == 100) {
testFinished("PASSED", suiteName, testCase, parameters, values, duration);
} else if (actual < expected) {
testFinished("FAILED", suiteName, testCase, parameters, values, duration);
} else {
testFinished("PASSED with failures", suiteName, testCase, parameters, values, duration);
}
}
/**
*/
synchronized void verboseMessageLogged(String msg) {
String in = getMessage(msg);
if(in.equals(lastMessageLogged)) {
return;
}
lastMessageLogged = in;
if(descriptionInPassedWithErrors) {
Matcher m = Pattern.compile(RegexpUtils.TEST_REGEX_3).matcher(in);
if(m.matches()) {
int currentInvocation = Integer.parseInt(m.group(3));
int invocationCount = Integer.parseInt(m.group(4));
if (currentInvocation == invocationCount) {
handleInvocationCount(invocationCount);
}
} else {
assert false : "Cannot match: '" + in + "'.";
}
descriptionInPassedWithErrors = false;
addDescription(in.substring(0, in.lastIndexOf('(')).trim());
return;
}
//suite starting
if (in.startsWith("RUNNING: ")) {
passedWithErrorsFailure = 0;
successPercentage = -1;
msDuration = 0;
descriptionInPassedWithErrors = false;
failedInAfterClass = false;
failedInBeforeClass = false;
failedInAfterMethod = false;
failedInBeforeMethod = false;
failedInConfigurationMethod = false;
canAddToStackTrace = true;
suiteFinished = false;
Matcher m = Pattern.compile(RegexpUtils.RUNNING_SUITE_REGEX).matcher(in);
if (m.matches()) {
suiteStarted(m.group(1), Integer.valueOf(m.group(2)), m.group(3));
} else {
assert false : "Cannot match: '" + in + "'.";
}
return;
}
//suite finishing
if (in.equals("===============================================")) {
if(suiteFinished) {
return;
}
suiteSummary = !suiteSummary;
if(txt.size() > 0 && failedInAfterClass) {
testStarted(suiteName, testCase, parameters, values);
addStackTrace(txt);
txt.clear();
testFinished("FAILED", suiteName, testCase, parameters, values, duration);
}
if (suiteSummary) {
suiteStat = new SuiteStats();
} else {
suiteFinished(suiteStat);
suiteStat = null;
suiteFinished = true;
lastMessageLogged = "";
}
if(txt.size() > 0) {
addStackTrace(txt);
txt.clear();
}
return;
} else if (suiteSummary) {
if (suiteStat.name != null) {
Matcher m = Pattern.compile(RegexpUtils.STATS_REGEX).matcher(in);
if (suiteStat.testRun < 0) {
//Tests run/fail/skip
if (m.matches()) {
suiteStat.testRun = Integer.valueOf(m.group(1));
suiteStat.testFail = Integer.valueOf(m.group(2));
suiteStat.testSkip = Integer.valueOf(m.group(4));
} else {
assert false : "Cannot match: '" + in + "'.";
}
} else {
//Configuration fail/skip
if (m.matches()) {
suiteStat.confFail = Integer.valueOf(m.group(1));
suiteStat.confSkip = Integer.valueOf(m.group(2));
} else {
assert false : "Cannot match: '" + in + "'.";
}
}
} else {
suiteStat.name = in.trim();
}
return;
}
//test
if (in.startsWith("INVOKING: ")) {
if(txt.size() > 0 && failedInBeforeMethod) {
testStarted(suiteName, testCase, parameters, values);
addStackTrace(txt);
txt.clear();
testFinished("FAILED", suiteName, testCase, parameters, values, duration);
}
failedInAfterMethod = false;
failedInBeforeMethod = false;
if(failedInBeforeClass) {
return;
}
if(failedInConfigurationMethod) {
canAddToStackTrace = false;
}
if (txt.size() > 0 && !failedInConfigurationMethod) {
addStackTrace(txt);
txt.clear();
}
Matcher m = Pattern.compile(RegexpUtils.TEST_REGEX).matcher(in);
if (m.matches()) {
testStarted(m.group(1), m.group(2), m.group(4), m.group(6));
String percent = m.group(12);
if (percent != null) {
percent = percent.substring(percent.indexOf(": ") + 2, percent.length() - 1);
successPercentage = Integer.parseInt(percent);
}
} else {
Matcher m2 = Pattern.compile(RegexpUtils.TEST_REGEX_2).matcher(in);
if (m2.matches()) {
x++;
testStarted(m2.group(1), m2.group(2), m2.group(4), "UNKNOWN#" + x);
} else {
assert false : "Cannot match: '" + in + "'.";
}
}
return;
}
Matcher m = Pattern.compile(RegexpUtils.TEST_REGEX).matcher(in);
if (in.startsWith("PASSED: ")) {
if (m.matches()) {
if(successPercentage == -1) {
testFinished("PASSED", m.group(1), m.group(2), m.group(4), m.group(6), m.group(8));
} else {
int currentInvocation;
int invocationCount;
int dur;
try {
dur = Integer.parseInt(m.group(8));
currentInvocation = Integer.parseInt(m.group(10));
invocationCount = Integer.parseInt(m.group(11));
msDuration += dur;
} catch (NumberFormatException ex) {
return;
}
if (currentInvocation == invocationCount) {
testFinished("PASSED", m.group(1), m.group(2), m.group(4), m.group(6), Integer.toString(msDuration));
successPercentage = -1;
}
}
} else {
Matcher m2 = Pattern.compile(RegexpUtils.TEST_REGEX_2).matcher(in);
if (m2.matches()) {
testFinished("PASSED", m2.group(1), m2.group(2), m2.group(4), "UNKNOWN#" + x, "0");
} else {
assert false : "Cannot match: '" + in + "'.";
}
}
return;
}
if (in.startsWith("PASSED with failures: ")) {
if (m.matches()) {
passedWithErrorsFailure++;
int currentInvocation;
int invocationCount;
int dur;
suiteName = m.group(1);
testCase = m.group(2);
parameters = m.group(4);
values = m.group(6);
duration = m.group(8);
try{
dur = Integer.parseInt(duration);
currentInvocation = Integer.parseInt(m.group(10));
invocationCount = Integer.parseInt(m.group(11));
msDuration += dur;
} catch(NumberFormatException ex) {
descriptionInPassedWithErrors = true;
return;
}
if (currentInvocation == invocationCount) {
handleInvocationCount(invocationCount);
successPercentage = -1;
}
} else {
Matcher m2 = Pattern.compile(RegexpUtils.TEST_REGEX_2).matcher(in);
if (m2.matches()) {
testFinished("PASSED with failures", m2.group(1), m2.group(2), m2.group(4), "UNKNOWN#" + x, "0");
} else {
assert false : "Cannot match: '" + in + "'.";
}
}
return;
}
if (in.startsWith("SKIPPED: ")) {
if (m.matches()) {
if(failedInConfigurationMethod) {
if (txt.size() > 0) {
addStackTrace(txt);
}
}
testFinished("SKIPPED", m.group(1), m.group(2), m.group(4), m.group(6), m.group(8));
} else {
Matcher m2 = Pattern.compile(RegexpUtils.TEST_REGEX_2).matcher(in);
if (m2.matches()) {
testFinished("SKIPPED", m2.group(1), m2.group(2), m2.group(4), "UNKNOWN#" + x, "0");
} else {
assert false : "Cannot match: '" + in + "'.";
}
}
return;
}
if (in.startsWith("FAILED: ")) {
if (m.matches()) {
if(successPercentage == -1) {
testFinished("FAILED", m.group(1), m.group(2), m.group(4), m.group(6), m.group(8));
} else {
int currentInvocation;
int invocationCount;
int dur;
try {
dur = Integer.parseInt(m.group(8));
currentInvocation = Integer.parseInt(m.group(10));
invocationCount = Integer.parseInt(m.group(11));
msDuration += dur;
} catch (NumberFormatException ex) {
return;
}
if (currentInvocation == invocationCount) {
testFinished("FAILED", m.group(1), m.group(2), m.group(4), m.group(6), Integer.toString(msDuration));
successPercentage = -1;
}
}
} else {
Matcher m2 = Pattern.compile(RegexpUtils.TEST_REGEX_2).matcher(in);
if (m2.matches()) {
testFinished("FAILED", m2.group(1), m2.group(2), m2.group(4), "UNKNOWN#" + x, "0");
} else {
assert false : "Cannot match: '" + in + "'.";
}
}
return;
}
//configuration methods
if (in.startsWith("FAILED CONFIGURATION: ")) {
if (m.matches()) {
suiteName = m.group(1);
testCase = m.group(2);
parameters = m.group(4);
values = m.group(6);
duration = m.group(8);
}
if (in.contains(" - @AfterClass")) {
failedInAfterClass = true;
if (txt.size() > 0) {
addStackTrace(txt);
}
}
if (in.contains(" - @BeforeClass")) {
failedInBeforeClass = true;
}
if (in.contains(" - @AfterMethod")) {
failedInAfterMethod = true;
}
if (in.contains(" - @BeforeMethod")) {
failedInBeforeMethod = true;
}
failedInConfigurationMethod = true;
// clear previous stacktrace of FAILED test method as we have a more serious problem in setUp/tearDown methods
txt.clear();
return;
}
if (in.startsWith("SKIPPED CONFIGURATION: ")) {
if (failedInAfterMethod && txt.size() > 0) {
testStarted(suiteName, testCase, parameters, values);
addStackTrace(txt);
txt.clear();
testFinished("FAILED", suiteName, testCase, parameters, values, duration);
}
if (!failedInBeforeClass && txt.size() > 0) {
addStackTrace(txt);
txt.clear();
}
if (failedInBeforeClass && txt.size() > 0) {
testStarted(suiteName, testCase, parameters, values);
addStackTrace(txt);
txt.clear();
testFinished("FAILED", suiteName, testCase, parameters, values, duration);
}
return;
}
if (in.startsWith("PASSED CONFIGURATION: ")) {
if (txt.size() > 0) {
addStackTrace(txt);
txt.clear();
}
return;
}
if (in.contains(" CONFIGURATION: ")) {
if(txt.size() > 0 && failedInAfterClass) {
testStarted(suiteName, testCase, parameters, values);
addStackTrace(txt);
txt.clear();
testFinished("FAILED", suiteName, testCase, parameters, values, duration);
}
if (txt.size() > 0) {
addStackTrace(txt);
txt.clear();
}
return;
}
Matcher m1 = Pattern.compile(RegexpUtils.RUNNING_SUITE_REGEX).matcher(in);
if (!(m.matches() || m1.matches())) {
if (txt.isEmpty() && in.startsWith(" ")) {
//we received test description
addDescription(in.trim());
} else if (in.trim().length() > 0) {
//we have a stacktrace
if(canAddToStackTrace || failedInConfigurationMethod) {
txt.add(in);
}
}
}
}
synchronized void messageLogged(final AntEvent event) {
final String msg = event.getMessage();
if (msg == null) {
return;
}
Testcase tc = testSession.getCurrentTestCase();
if (tc != null) {
tc.getOutput().add(new OutputLine(msg, false));
}
if (!offline) {
// msg could contain arbitrary charachters printed by the user,
// so display them in TRW before parsing TestNG specific output.
int index = msg.indexOf(RegexpUtils.TEST_LISTENER_PREFIX);
String message = msg;
if(index != -1) {
message = message.substring(0, index);
}
//log/verbose level = 0 so don't show output
if (!message.isEmpty() && !message.startsWith(RegexpUtils.TEST_LISTENER_PREFIX)) {
displayOutput(message, event.getLogLevel() == AntEvent.LOG_WARN);
}
if (index == -1) {
//this message is not for us...
return;
}
verboseMessageLogged(event);
}
}
/**
*/
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("testng")) { //NOI18N
resultsDir = determineTestNGTaskResultsDir(event);
} else if (taskName.equals("java")) { //NOI18N
resultsDir = determineJavaTaskResultsDir(event);
} else {
assert false : "Unexpected task: " + taskName;
}
}
if ((resultsDir != null) && resultsDir.exists() && resultsDir.isDirectory()) {
return resultsDir;
} else {
return null;
}
}
/**
*/
private static File determineTestNGTaskResultsDir(final AntEvent event) {
final TaskStructure taskStruct = event.getTaskStructure();
if (taskStruct == null) {
return null;
}
String todirAttr = (taskStruct.getAttribute("outputdir") != null) //NOI18N
? taskStruct.getAttribute("outputdir") //NOI18N
: (taskStruct.getAttribute("workingDir") != null) //NOI18N
? taskStruct.getAttribute("workingDir") + "test-output" //NOI18N
: "test-output"; //NOI18N
File resultsDir = new File(event.evaluate(todirAttr));
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);
int index = valueAttr.indexOf("-d "); //NOI18N
if (-1 < index) {
todirPath = valueAttr.substring(index + 3);
if (todirPath.contains(" ")) {
index = todirPath.startsWith("\"") //NOI18N
? todirPath.indexOf("\"", 1) + 1 //NOI18N
: todirPath.indexOf(" "); //NOI18N
todirPath = todirPath.substring(0, index);
//found, let's finish
break;
}
}
}
}
}
if (todirPath == null) {
//-d not set, what about parent java/exec's 'dir'?
String dir = taskStruct.getAttribute("dir");
if (dir != null) {
todirPath = event.evaluate(dir) + "/test-output";
} else {
todirPath = "test-output";
}
}
File resultsDir = new File(event.evaluate(todirPath));
return findAbsolutePath(resultsDir, taskStruct, event);
}
private static File findAbsolutePath(File path, TaskStructure taskStruct, AntEvent event) {
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);
//XXX - look for a "subpath" in case of forked JRE; is there a better way?
String path = f.getAbsolutePath();
if (path.startsWith(javaExecutable)
|| javaExecutable.startsWith(path.substring(0, path.length() - 8))) {
return platforms[i].getSourceFolders();
}
}
}
return null;
}
/**
* Notifies that a test (Ant) task was just started.
*/
void testTaskStarted(boolean expectXmlOutput, AntEvent event) {
this.offline = expectXmlOutput;
if (!offline) {
CoreManager testngManager = getManagerProvider();
if (testngManager != null) {
testngManager.testStarted(testSession);
}
}
resultsDir = determineResultsDir(event);
}
/**
*/
void testTaskFinished() {
CoreManager testngManager = getManagerProvider();
if (offline) {
if (testngManager != null) {
testngManager.testStarted(testSession);
}
}
if (offline || noresults) {
//get results from report xml file
if (resultsDir != null) {
File reportFile = findReportFile();
if ((reportFile != null) && isValidReportFile(reportFile)) {
XmlResult reportSuite = parseReportFile(reportFile, testSession);
for (TestNGTestSuite ts : reportSuite.getTestSuites()) {
if (testngManager != null) {
testngManager.displaySuiteRunning(testSession, ts);
}
testSession.setCurrentSuite(ts.getName());
testSession.addSuite(ts);
Report report = testSession.getReport(ts.getElapsedTime());
if (testngManager != null) {
testngManager.displayReport(testSession, report, true);
} else { // update report status as a minimum
report.setCompleted(true);
}
}
}
}
}
x = 0;
}
/**
*/
void buildFinished(final AntEvent event) {
CoreManager testngManager = getManagerProvider();
if (testngManager != null) {
testngManager.sessionFinished(testSession);
}
}
//------------------ UPDATE OF DISPLAY -------------------
/**
*/
private void displayOutput(final String text, final boolean error) {
CoreManager testngManager = getManagerProvider();
if (testngManager != null) {
testngManager.displayOutput(testSession, text, error);
}
// if (state == State.TESTCASE_STARTED) {
// List<String> addedLines = new ArrayList<String>();
// addedLines.add(text);
// Testcase tc = testSession.getCurrentTestCase();
// if (tc != null) {
// tc.addOutputLines(addedLines);
// }
// }
}
//--------------------------------------------------------
private File findReportFile() {
File file = new File(resultsDir, "testng-results.xml"); //NOI18N
return (file.isFile() ? file : null);
}
/**
*/
private boolean isValidReportFile(File reportFile) {
if (!reportFile.canRead()) {
return false;
}
if (reportFile.canRead()) {
return true;
}
long lastModified = reportFile.lastModified();
long timeDelta = lastModified - timeOfSessionStart;
final Logger logger = Logger.getLogger("org.netbeans.modules.testng.outputreader.timestamps");//NOI18N
final Level logLevel = Level.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(Calendar.MILLISECOND)));//NOI18N
timeStamp.setTimeInMillis(lastModified);
logger.log(logLevel, "Report timestamp: " + String.format("%1$tT.%2$03d", timeStamp, timeStamp.get(Calendar.MILLISECOND)));//NOI18N
}
if (timeDelta >= 0) {
return true;
}
/*
* Normally we would return 'false' here, but:
*
* We must take into account that modification timestamps of files
* usually do not hold milliseconds, just seconds. The worst case we
* must accept is that the session started on YYYY.MM.DD hh:mm:ss.999
* and the file was saved exactly in the same millisecond but its time
* stamp is just YYYY.MM.DD hh:mm:ss, i.e 999 milliseconds earlier.
*/
return -timeDelta <= timeOfSessionStart % 1000;
// if (timeDelta < -999) {
// return false;
// }
//
// final GregorianCalendar sessStartCal = new GregorianCalendar();
// sessStartCal.setTimeInMillis(timeOfSessionStart);
// int sessStartMillis = sessStartCal.get(Calendar.MILLISECOND);
// if (timeDelta < -sessStartMillis) {
// return false;
// }
//
// final GregorianCalendar fileModCal = new GregorianCalendar();
// fileModCal.setTimeInMillis(lastModified);
// if (fileModCal.get(Calendar.MILLISECOND) != 0) {
// /* So the file's timestamp does hold milliseconds! */
// return false;
// }
//
// /*
// * Now we know that milliseconds are not part of file's timestamp.
// * Let's substract the milliseconds part and check whether the delta is
// * non-negative, now that we only check seconds:
// */
// return lastModified >= (timeOfSessionStart - sessStartMillis);
}
private static XmlResult parseReportFile(File reportFile, TestSession session) {
XmlResult reports = null;
try {
reports = XmlOutputParser.parseXmlOutput(
new InputStreamReader(
new FileInputStream(reportFile),
"UTF-8"), session); //NOI18N
} catch (UnsupportedCharsetException ex) {
assert false;
} 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.
*/
Logger.getLogger(TestNGOutputReader.class.getName()).log(Level.INFO, "I/O exception while reading TestNG XML report file from TestNG: ", ex);//NOI18N
}
return reports;
}
private void suiteStarted(String name, int expectedTCases, String config) {
name = testSession.getSuiteName(name);
TestSuite suite = new TestNGTestSuite(name, testSession, expectedTCases, config);
testSession.addSuite(suite);
testSession.setCurrentSuite(name);
CoreManager testngManager = getManagerProvider();
if (testngManager != null) {
testngManager.displaySuiteRunning(testSession, suite);
}
platformSources = null;
reports.put(name, new Report(name, project));
}
private void suiteFinished(SuiteStats stats) {
stats.name = testSession.getCurrentSuite().getName();
testSession.setCurrentSuite(stats.name);
TestNGTestSuite s = (TestNGTestSuite) testSession.getCurrentSuite();
s.setElapsedTime(elapsedTime);
s.finish(stats.testRun, stats.testFail, stats.testSkip, stats.confFail, stats.confSkip);
Report r = reports.get(stats.name);
r.setElapsedTimeMillis(elapsedTime);
CoreManager testngManager = getManagerProvider();
if (testngManager != null) {
testngManager.displayReport(testSession, r, true);
} else { // update report status as a minimum
r.setCompleted(true);
}
elapsedTime = 0;
}
private void testStarted(String suiteName, String testCase, String parameters, String values) {
suiteName = testSession.getCurrentSuite().getName();
testSession.setCurrentSuite(suiteName);
TestNGTestcase tc = ((TestNGTestSuite)testSession.getCurrentSuite()).getTestCase(testCase, values);
if (tc == null) {
tc = new TestNGTestcase(testCase, parameters, values, testSession);
testSession.addTestCase(tc);
CoreManager testngManager = getManagerProvider();
if (testngManager != null) {
testngManager.testStarted(testSession);
}
} else {
tc.addValues(values);
//TODO: increment test case time
}
}
private void testFinished(String st, String suiteName, String testCase, String parameters, String values, String duration) {
suiteName = testSession.getCurrentSuite().getName();
testSession.setCurrentSuite(suiteName);
TestNGTestcase tc = ((TestNGTestSuite)testSession.getCurrentSuite()).getTestCase(testCase, values);
CoreManager testngManager = getManagerProvider();
if (tc == null) {
//TestNG does not log invoke message for junit tests...
tc = new TestNGTestcase(testCase, parameters, values, testSession);
testSession.addTestCase(tc);
if (testngManager != null) {
testngManager.testStarted(testSession);
}
}
assert tc != null;
if ("PASSED".equals(st)) {
tc.setStatus(Status.PASSED);
} else if ("PASSED with failures".equals(st)) {
tc.setStatus(Status.PASSEDWITHERRORS);
} else if ("FAILED".equals(st)) {
tc.setStatus(Status.FAILED);
} else if ("SKIPPED".equals(st)) {
tc.setStatus(Status.SKIPPED);
}
long dur = 0;
if (duration != null) {
dur = Long.valueOf(duration);
}
tc.setTimeMillis(dur);
elapsedTime += dur;
if("FAILED".equals(st)) {
currentTime = dur;
currentSuitename = suiteName;
} else {
currentTime = -1;
currentSuitename = null;
Report r = reports.get(suiteName);
r.update(testSession.getReport(dur));
if (testngManager != null) {
testngManager.displayReport(testSession, r, false);
} else { // update report status as a minimum
r.setCompleted(false);
}
}
}
private String getMessage(String msg) {
int prefixLength = RegexpUtils.TEST_LISTENER_PREFIX.length();
int index = msg.indexOf(RegexpUtils.TEST_LISTENER_PREFIX);
// msg could contain arbitrary charachters printed by the user,
// so remove them before parsing TestNG specific output.
return msg.substring(index + prefixLength).replace("\n", "");
}
private void addDescription(String in) {
Testcase tc = testSession.getCurrentTestCase();
//FIXME!!! tc should never be null
//looks like some bug :-(
if (tc != null) {
((TestNGTestcase) tc).setDescription(in);
}
}
private void addStackTrace(List<String> txt) {
Trouble t = new Trouble(false);
Matcher matcher = RegexpUtils.getInstance().getComparisonPattern().matcher(txt.get(0));
if (matcher.matches()) {
t.setComparisonFailure(
new Trouble.ComparisonFailure(
matcher.group(1) + matcher.group(2) + matcher.group(3),
matcher.group(4) + matcher.group(5) + matcher.group(6)));
} else {
matcher = RegexpUtils.getInstance().getComparisonHiddenPattern().matcher(txt.get(0));
if (matcher.matches()) {
t.setComparisonFailure(
new Trouble.ComparisonFailure(
matcher.group(1),
matcher.group(2)));
}
}
matcher = RegexpUtils.getInstance().getComparisonAfter65Pattern().matcher(txt.get(0));
if (matcher.matches()) {
t.setComparisonFailure(
new Trouble.ComparisonFailure(
matcher.group(1) + matcher.group(2) + matcher.group(3),
matcher.group(4) + matcher.group(5) + matcher.group(6)));
} else {
matcher = RegexpUtils.getInstance().getComparisonAfter65HiddenPattern().matcher(txt.get(0));
if (matcher.matches()) {
t.setComparisonFailure(
new Trouble.ComparisonFailure(
matcher.group(1),
matcher.group(2)));
}
}
t.setStackTrace(txt.toArray(new String[txt.size()]));
testSession.getCurrentTestCase().setTrouble(t);
if (currentTime != -1 && currentSuitename != null) {
Report r = reports.get(currentSuitename);
r.update(testSession.getReport(currentTime));
CoreManager testngManager = getManagerProvider();
if (testngManager != null) {
testngManager.displayReport(testSession, r, false);
} else { // update report status as a minimum
r.setCompleted(false);
}
}
}
}