diff --git a/src/main/java/hudson/plugins/testlink/TestLinkJunitWrapper.java b/src/main/java/hudson/plugins/testlink/TestLinkJunitWrapper.java new file mode 100644 index 0000000..8450464 --- /dev/null +++ b/src/main/java/hudson/plugins/testlink/TestLinkJunitWrapper.java @@ -0,0 +1,155 @@ +/* + * The MIT License + * + * Copyright (c) <2011> + * + * 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> 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>)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 cfs = (Map)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>> { + private final String testResults; + private final PrintStream logger; + + private ParseResultCallable(String testResults, PrintStream logger) { + this.testResults = testResults; + this.logger = logger; + } + + public Map> invoke(File ws, VirtualChannel channel) throws IOException { + FileSet fs = Util.createFileSet(ws, this.testResults); + DirectoryScanner ds = fs.getDirectoryScanner(); + Map> customFields = new HashMap>(); + 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> 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 cfs = new HashMap(); + // 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> getCustomFields (){ return customFields;} +} diff --git a/src/main/java/hudson/plugins/testlink/TestLinkSite.java b/src/main/java/hudson/plugins/testlink/TestLinkSite.java index 342f251..eb38b35 100644 --- a/src/main/java/hudson/plugins/testlink/TestLinkSite.java +++ b/src/main/java/hudson/plugins/testlink/TestLinkSite.java @@ -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()) { diff --git a/src/main/java/hudson/plugins/testlink/result/JUnitCaseClassNameResultSeeker.java b/src/main/java/hudson/plugins/testlink/result/JUnitCaseClassNameResultSeeker.java index d5ece19..b21d963 100644 --- a/src/main/java/hudson/plugins/testlink/result/JUnitCaseClassNameResultSeeker.java +++ b/src/main/java/hudson/plugins/testlink/result/JUnitCaseClassNameResultSeeker.java @@ -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; @@ -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) { @@ -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> customfields = parser.getCustomFields(); + for(final SuiteResult suiteResult : testResult.getSuites()) { final List caseResults = this.filter(suiteResult.getCases()); @@ -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 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); @@ -221,3 +228,4 @@ private String getJUnitNotes( CaseResult testCase , int buildNumber) } } + diff --git a/src/main/java/hudson/plugins/testlink/result/TestCaseWrapper.java b/src/main/java/hudson/plugins/testlink/result/TestCaseWrapper.java index 70ef2a2..9856e3a 100644 --- a/src/main/java/hudson/plugins/testlink/result/TestCaseWrapper.java +++ b/src/main/java/hudson/plugins/testlink/result/TestCaseWrapper.java @@ -74,6 +74,11 @@ public class TestCaseWrapper implements Serializable { */ private TestCase testCase; + /** + * Custom Field execution values + */ + private Map customFieldsExecutionValues = null; + public TestCaseWrapper() { this(new TestCase()); } @@ -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) { @@ -308,4 +313,7 @@ public void setFullExternalId(String fullExternalId) { this.testCase.setFullExternalId(fullExternalId); } + public void setCustomFieldExecutionValue(Map cfs) {this.customFieldsExecutionValues = cfs;} + + public Map getCustomFieldsExecutionValues(){return this.customFieldsExecutionValues;} } diff --git a/src/test/java/hudson/plugins/testlink/TestTestLinkSite.java b/src/test/java/hudson/plugins/testlink/TestTestLinkSite.java index 3705ffb..f7a8b61 100644 --- a/src/test/java/hudson/plugins/testlink/TestTestLinkSite.java +++ b/src/test/java/hudson/plugins/testlink/TestTestLinkSite.java @@ -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; @@ -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 { @@ -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(), null); Report report = testLinkSite.getReport(); assertThat(report.getPassed(), is(1)); assertThat(report.getFailed(), is(0)); @@ -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(), null); Report report = testLinkSite.getReport(); assertThat(report.getPassed(), is(0)); assertThat(report.getFailed(), is(1)); @@ -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(), null); Report report = testLinkSite.getReport(); assertThat(report.getPassed(), is(0)); assertThat(report.getFailed(), is(0));