InTestLinkXmlRunListener.java
/**
* Copyright 1&1 Internet AG, https://github.com/1and1/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.oneandone.testlinkjunit.tljunit;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.apache.maven.shared.utils.xml.Xpp3Dom;
import org.junit.Ignore;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
/**
* {@link org.junit.runner.notification.RunListener} which logs everything to Testlink compatible XML file.
*
* @author Mirko Friedenhagen
*/
class InTestLinkXmlRunListener extends AbstractInTestLinkRunListener {
/** Dom-creator for the total results. */
private final Xpp3Dom results;
/** Name of the tester, default to the System property user.name. */
private final String testerName;
/** Dom-creator for the results of the current testcase. */
private final ThreadLocal<Xpp3Dom> currentTestCase = new ThreadLocal<Xpp3Dom>();
/**
* @param testerName Name of the tester, default to the System property user.name.
*/
public InTestLinkXmlRunListener(final String testerName) {
results = new Xpp3Dom("results");
this.testerName = testerName;
}
/**
* {@inheritDoc}
*
* {@link TestLink} annotation must have either {@link TestLink#internalId()} or {@link TestLink#externalId()} set.
*/
@Override
public void testStarted(Description description) {
resetCurrentFailure();
final Xpp3Dom testCase = new Xpp3Dom("testcase");
setCurrentTestCase(testCase);
testCase.addChild(createTester(testerName));
testCase.addChild(createTimeStamp(new Date()));
// This is the only place where we access results, so we should be fine to just synchronize here.
synchronized (results) {
results.addChild(testCase);
}
final TestLinkId<?> id = TestLinkId.fromDescription(description);
testCase.setAttribute(id.getType(), String.valueOf(id.getId()));
}
/** {@inheritDoc} */
@Override
public void testIgnored(Description description) {
testStarted(description);
final Xpp3Dom testCase = getCurrentTestCase();
testCase.addChild(createResult(TestState.blocked));
final String message = description.getAnnotation(Ignore.class).value();
testCase.addChild(createNotes(String.format("'%s' BLOCKED because '%s'.", description.getDisplayName(), message)));
}
/** {@inheritDoc} */
@Override
public void testAssumptionFailure(Failure failure) {
setFailedOrIgnoredForFailureOrAssumptionFailure(failure, TestState.blocked);
}
/** {@inheritDoc} */
@Override
public void testFailure(Failure failure) {
setFailedOrIgnoredForFailureOrAssumptionFailure(failure, TestState.failed);
}
/**
* Attaches the notes of the Failure to the result set depending on the TestState.
*
* Ignored Testcases (or those where an Assumption failed) are marked as BLOCKED,
* otherwise report as FAILED.
*
* @param failure either a real Failure or a blocked testcase.
* @param testState FAILED or BLOCKED.
*/
private void setFailedOrIgnoredForFailureOrAssumptionFailure(Failure failure, TestState testState) {
setCurrentFailure(failure);
final Xpp3Dom testCase = getCurrentTestCase();
testCase.addChild(createResult(testState));
final String message = failure.getMessage();
final Xpp3Dom notes;
if (message != null) {
notes = createNotes(String.format("'%s' " + testState.getDescription() + " because '%s'.",
failure.getTestHeader(), message));
} else {
notes = createNotes(String.format("'%s' " + testState.getDescription() + " because '%s'.",
failure.getTestHeader(), failure.getTrace()));
}
testCase.addChild(notes);
}
/**
* {@inheritDoc}
*
* This will set the test to PASSED only when we have no {@link InTestLinkXmlRunListener#currentFailure}.
*/
@Override
public void testFinished(Description description) {
if (hasPassed()) {
final Xpp3Dom testCase = getCurrentTestCase();
testCase.addChild(createResult(TestState.passed));
testCase.addChild(createNotes(String.format("'%s' PASSED.", description.getDisplayName())));
}
}
/**
* Creates a new element.
*
* @param elementName elementName of XML
* @param text value of XML
* @return dom
*/
private Xpp3Dom createElementWithText(String elementName, String text) {
final Xpp3Dom element = new Xpp3Dom(elementName);
element.setValue(text);
return element;
}
/**
* Creates a new tester element filled with the tester.
*
* @param userName
* name of the user got from system property {@code testlink.userName} or {@code user.name}.
* @return <tester> element.
*/
Xpp3Dom createTester(final String userName) {
return createElementWithText("tester", userName);
}
/**
* Creates a new timestamp element.
*
* @param date
* of the test run.
* @return <timestamp> element.
*/
Xpp3Dom createTimeStamp(final Date date) {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
return createElementWithText("timestamp", dateFormat.format(date));
}
/**
* Creates a new notes element.
*
* @param notesValue
* additional notes.
* @return <notes> element.
*/
Xpp3Dom createNotes(final String notesValue) {
return createElementWithText("notes", notesValue);
}
/**
* Creates a new result element.
*
* @param testState
* to report
* @return <result> element.
*/
Xpp3Dom createResult(final TestState testState) {
return createElementWithText("result", testState.getState());
}
/**
* @return the results
*/
Xpp3Dom getResults() {
return results;
}
/**
* @return the currentTestCase
*/
private Xpp3Dom getCurrentTestCase() {
return currentTestCase.get();
}
/**
* @param currentTestCase
* the currentTestCase to set
*/
private void setCurrentTestCase(Xpp3Dom currentTestCase) {
this.currentTestCase.set(currentTestCase);
}
}