Skip to content

Commit

Permalink
Feature to parser custom fields from Junit.xml and update in TestLink
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohammad Azim Khan committed Aug 1, 2016
1 parent 377fb58 commit 98c3295
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 10 deletions.
155 changes: 155 additions & 0 deletions src/main/java/hudson/plugins/testlink/TestLinkJunitWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* The MIT License
*
* Copyright (c) <2011> <Bruno P. Kinoshita>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.plugins.testlink;

import hudson.tasks.junit.JUnitParser;
import hudson.tasks.junit.TestResult;
import hudson.model.Run;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.TaskListener;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Map;
import java.util.HashMap;

import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.DirectoryScanner;
import hudson.Util;
import jenkins.MasterToSlaveFileCallable;
import hudson.remoting.VirtualChannel;
import org.dom4j.io.SAXReader;
import hudson.util.io.ParserConfigurator;
import org.dom4j.Document;
import org.dom4j.Element;
import java.util.List;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Created by azikha01 on 29/07/2016.
*/
public class TestLinkJunitWrapper extends JUnitParser {
private Map<String, Map<String, String>> customFields = null;
private PrintStream logger = null;
private static final Logger LOGGER = Logger.getLogger("hudson.plugins.testlink");

public TestLinkJunitWrapper(boolean keepLongStdio, boolean allowEmptyResults) {
super(keepLongStdio, allowEmptyResults);
}

public TestResult parseResult(String testResultLocations, Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener) throws InterruptedException, IOException {

logger = listener.getLogger();
TestResult r = super.parseResult(testResultLocations, build, workspace, launcher, listener);

/* Second parse of files to find Test Case custom field values */
this.customFields = (Map<String, Map<String, String>>)workspace.act(new TestLinkJunitWrapper.ParseResultCallable(testResultLocations, logger));
Iterator it = customFields.entrySet().iterator();
while (it.hasNext()){
Map.Entry pair = (Map.Entry)it.next();
logger.println("Test Case " + pair.getKey());
Map<String, String> cfs = (Map<String, String>)pair.getValue();
Iterator itt = cfs.entrySet().iterator();
while (itt.hasNext()) {
Map.Entry pairr = (Map.Entry)itt.next();
logger.println("\tCustom field = " + pairr.getKey() + " value = " + pairr.getValue());
}
}
return r;
}

private static final class ParseResultCallable extends MasterToSlaveFileCallable<Map<String, Map<String, String>>> {
private final String testResults;
private final PrintStream logger;

private ParseResultCallable(String testResults, PrintStream logger) {
this.testResults = testResults;
this.logger = logger;
}

public Map<String, Map<String, String>> invoke(File ws, VirtualChannel channel) throws IOException {
FileSet fs = Util.createFileSet(ws, this.testResults);
DirectoryScanner ds = fs.getDirectoryScanner();
Map<String, Map<String, String>> customFields = new HashMap<String, Map<String, String>>();
String[] files = ds.getIncludedFiles();
if(files.length > 0) {
String[] reportFiles = ds.getIncludedFiles();
File baseDir = ds.getBasedir();

int len$ = reportFiles.length;

for(int f = 0; f < len$; ++f) {
String value = reportFiles[f];
File reportFile = new File(baseDir, value);

try {
this.parseCustomFields(reportFile, customFields);
} catch (org.dom4j.DocumentException e) {
throw new IOException(e);
}
}

}

return customFields;
}

private void parseCustomFields (File reportFile, Map<String, Map<String, String>> customFields) throws org.dom4j.DocumentException {
String xmlReport = reportFile.getName();
SAXReader saxReader = new SAXReader();
Document result = saxReader.read(reportFile);
Element root = result.getRootElement();
List testCases = root.elements("testcase");

for(Iterator stdout = testCases.iterator(); stdout.hasNext();) {
Element tc = (Element)stdout.next();
String m = tc.attributeValue("classname");
Map<String, String> cfs = new HashMap<String, String>();
// Get other attributes and extract custom fields
List children = tc.elements();
for (Iterator child = children.iterator(); child.hasNext();){
// get tag and text
Element childe = (Element)child.next();
// exclude Junit defined names like error, failure, stdin, stdout
if (childe.getName().equals("skipped") ||
childe.getName().equals("error") ||
childe.getName().equals("failure") ||
childe.getName().equals("system-out") ||
childe.getName().equals("system-err")
) {
continue;
}
cfs.put(childe.getName(), childe.getText());
// what should we do with these custom fields
customFields.put(m, cfs);
}
}
}
}

public Map<String, Map<String, String>> getCustomFields (){ return customFields;}
}
2 changes: 1 addition & 1 deletion src/main/java/hudson/plugins/testlink/TestLinkSite.java
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ public int updateTestCase(TestCaseWrapper testCase) {
null, // bug id
platformId, // platform id
platformName, // platform name
null, // custom fields
testCase.getCustomFieldsExecutionValues(), // custom fields
null);

switch(testCase.getExecutionStatus()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import hudson.Launcher;
import hudson.model.BuildListener;
import hudson.model.AbstractBuild;
import hudson.plugins.testlink.TestLinkJunitWrapper;
import hudson.plugins.testlink.TestLinkSite;
import hudson.plugins.testlink.util.Messages;
import hudson.tasks.junit.JUnitParser;
Expand Down Expand Up @@ -63,7 +64,7 @@ public class JUnitCaseClassNameResultSeeker extends AbstractJUnitResultSeeker {
/**
* @param includePattern Include pattern used when looking for results
* @param keyCustomField Key custom field to match against the results
* @param attachJunitXML Bit that enables attaching result file to TestLink
* @param attachJUnitXML Bit that enables attaching result file to TestLink
*/
@DataBoundConstructor
public JUnitCaseClassNameResultSeeker(String includePattern, String keyCustomField, boolean attachJUnitXML, boolean includeNotes) {
Expand All @@ -90,9 +91,11 @@ public String getDisplayName() {
public void seek(TestCaseWrapper[] automatedTestCases, AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener, TestLinkSite testlink) throws ResultSeekerException {
listener.getLogger().println( Messages.Results_JUnit_LookingForTestClasses() ); // i18n
try {
final JUnitParser parser = new JUnitParser(false);
final TestResult testResult = parser.parse(this.includePattern, build, launcher, listener);

listener.getLogger().println("invoking TestLinkJunitWrapper");
final TestLinkJunitWrapper parser = new TestLinkJunitWrapper(false, false);
final TestResult testResult = parser.parseResult(this.includePattern, build, build.getWorkspace(), launcher, listener);
final Map<String, Map<String, String>> customfields = parser.getCustomFields();

for(final SuiteResult suiteResult : testResult.getSuites()) {

final List<CaseResult> caseResults = this.filter(suiteResult.getCases());
Expand All @@ -109,7 +112,11 @@ public void seek(TestCaseWrapper[] automatedTestCases, AbstractBuild<?, ?> build
//final ExecutionStatus previousStatus = automatedTestCase.getCustomFieldAndStatus().get(value);
final ExecutionStatus status = this.getExecutionStatus(caseResult);
automatedTestCase.addCustomFieldAndStatus(value, status);

Map<String, String> cfs = customfields.get(caseResult.getClassName());
if (cfs != null && cfs.size() > 0){
automatedTestCase.setCustomFieldExecutionValue(cfs);
}

if(this.isIncludeNotes()) {
final String notes = this.getJUnitNotes(caseResult, build.number);
automatedTestCase.appendNotes(notes);
Expand Down Expand Up @@ -221,3 +228,4 @@ private String getJUnitNotes( CaseResult testCase , int buildNumber)
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public class TestCaseWrapper implements Serializable {
*/
private TestCase testCase;

/**
* Custom Field execution values
*/
private Map<String, String> customFieldsExecutionValues = null;

public TestCaseWrapper() {
this(new TestCase());
}
Expand Down Expand Up @@ -147,7 +152,7 @@ public ExecutionStatus getExecutionStatus() {
/**
* Calculates the new value of this wrapped test case execution status,
* given a number of custom fields.
* @param numberOfCustomFields
* @param keyCustomFieldName
* @return new value of this wrapped test case execution status
*/
public ExecutionStatus getExecutionStatus(String keyCustomFieldName) {
Expand Down Expand Up @@ -308,4 +313,7 @@ public void setFullExternalId(String fullExternalId) {
this.testCase.setFullExternalId(fullExternalId);
}

public void setCustomFieldExecutionValue(Map<String, String> cfs) {this.customFieldsExecutionValues = cfs;}

public Map<String, String> getCustomFieldsExecutionValues(){return this.customFieldsExecutionValues;}
}
9 changes: 6 additions & 3 deletions src/test/java/hudson/plugins/testlink/TestTestLinkSite.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import static org.mockito.Mockito.when;
import hudson.plugins.testlink.result.TestCaseWrapper;

import org.hamcrest.core.StringContains;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
Expand All @@ -33,6 +34,8 @@
import br.eti.kinoshita.testlinkjavaapi.model.TestPlan;
import br.eti.kinoshita.testlinkjavaapi.model.TestProject;

import java.util.HashMap;

@RunWith(MockitoJUnitRunner.class)
public class TestTestLinkSite {

Expand Down Expand Up @@ -86,7 +89,7 @@ public void testUpdateTestCaseWithPassedStatus() {
verify(api)
.reportTCResult(3, 4, 2, status, 1,
"build-name", "notes", null, null, null, "platform",
null, null);
new HashMap<String, String>(), null);
Report report = testLinkSite.getReport();
assertThat(report.getPassed(), is(1));
assertThat(report.getFailed(), is(0));
Expand All @@ -106,7 +109,7 @@ public void testUpdateTestCaseWithFailedStatus() {
verify(api)
.reportTCResult(3, 4, 2, status, 1,
"build-name", "notes", null, null, null, "platform",
null, null);
new HashMap<String, String>(), null);
Report report = testLinkSite.getReport();
assertThat(report.getPassed(), is(0));
assertThat(report.getFailed(), is(1));
Expand All @@ -126,7 +129,7 @@ public void testUpdateTestCaseWithBlockedStatus() {
verify(api)
.reportTCResult(3, 4, 2, status, 1,
"build-name", "notes", null, null, null, "platform",
null, null);
new HashMap<String, String>(), null);
Report report = testLinkSite.getReport();
assertThat(report.getPassed(), is(0));
assertThat(report.getFailed(), is(0));
Expand Down

0 comments on commit 98c3295

Please sign in to comment.