diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt new file mode 100644 index 0000000..7d21b9c --- /dev/null +++ b/COPYRIGHT.txt @@ -0,0 +1,13 @@ +Copyright 2008-2010 Nokia Siemens Networks Oyj + +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. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..12a1304 --- /dev/null +++ b/README.txt @@ -0,0 +1,21 @@ +Copyright 2008-2010 Nokia Siemens Networks Oyj + +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. + +================== + +This plugin publishes Robot Framework (http://code.google.com/p/robotframework/) +test reports for Hudson. + +See http://wiki.hudson-ci.org/display/HUDSON/Robot+Framework+Plugin +for more information. diff --git a/pom.xml b/pom.xml new file mode 100755 index 0000000..9545698 --- /dev/null +++ b/pom.xml @@ -0,0 +1,64 @@ + + 4.0.0 + + org.jvnet.hudson.plugins + plugin + 1.343 + ../pom.xml + + + robot + 1.0-SNAPSHOT + Robot Framework plugin + Shows Robot Framework test results in project + hpi + http://wiki.hudson-ci.org/display/HUDSON/Robot+Framework+Plugin + + + + jpiironen + Janne Piironen + janne.piironen@gmail.com + + + + + scm:git:git://github.com/hudson/robot-plugin.git + scm:git:git@github.com:hudson/robot-plugin.git + http://github.com/hudson/robot-plugin + + + + + org.mockito + mockito-core + 1.8.0 + test + + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.0 + + + org.apache.maven.scm + maven-scm-provider-gitexe + 1.3 + + + + + + + + org.jvnet.wagon-svn + wagon-svn + 1.9 + + + + diff --git a/src/main/java/hudson/plugins/robot/AbstractRobotAction.java b/src/main/java/hudson/plugins/robot/AbstractRobotAction.java new file mode 100644 index 0000000..7208b28 --- /dev/null +++ b/src/main/java/hudson/plugins/robot/AbstractRobotAction.java @@ -0,0 +1,42 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot; + +import hudson.model.Action; + +public class AbstractRobotAction implements Action { + + /** + * {@inheritDoc} + */ + public String getIconFileName() { + return "/plugin/robot/robot.png"; + } + + /** + * {@inheritDoc} + */ + public String getDisplayName() { + return Messages.robot_sidebar_link(); + } + + /** + * {@inheritDoc} + */ + public String getUrlName() { + return "robot"; + } +} diff --git a/src/main/java/hudson/plugins/robot/RobotBuildAction.java b/src/main/java/hudson/plugins/robot/RobotBuildAction.java new file mode 100755 index 0000000..28ab5af --- /dev/null +++ b/src/main/java/hudson/plugins/robot/RobotBuildAction.java @@ -0,0 +1,126 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot; + +import hudson.FilePath; +import hudson.model.AbstractBuild; +import hudson.model.DirectoryBrowserSupport; +import hudson.plugins.robot.model.RobotResult; + +import java.io.IOException; + +import javax.servlet.ServletException; + +import org.apache.commons.lang.StringUtils; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; + +public class RobotBuildAction extends AbstractRobotAction { + + private AbstractBuild build; + private RobotResult result; + private String reportFileName; + private String outputPath; + + + /** + * Create new Robot build action + * @param build Build which this action is associated to + * @param result Robot result + * @param outputPath Path where the Robot report is stored relative to build root + * @param reportFileName Name of Robot html report file stored + */ + public RobotBuildAction(AbstractBuild build, RobotResult result, + String outputPath, String reportFileName) { + this.build = build; + this.result = result; + this.outputPath = outputPath; + this.reportFileName = reportFileName; + } + + /** + * Get build associated to action + * @return build object + */ + public AbstractBuild getBuild() { + return build; + } + + /** + * Get Robot result + * @return result object + */ + public RobotResult getResult() { + return result; + } + + /** + * Get file name for Robot html report. Reverts to default if no file name is saved. + * @return file name as string + */ + public String getReportFileName(){ + //Check for empty name for backwards compatibility + return StringUtils.isBlank(reportFileName) ? RobotPublisher.DEFAULT_REPORT_FILE : reportFileName; + } + + /** + * Get ratio of passed tests per total tests. Accounts for all tests run. + * @return percent number + */ + public double getOverallPassPercentage(){ + return result.getPassPercentage(false); + } + + /** + * Get ratio of passed tests per total tests. Accounts for only critical tests run. + * @return percent number + */ + public double getCriticalPassPercentage() { + return result.getPassPercentage(true); + } + + /** + * Serves Robot html report via robot url. Shows not found page if file is missing. + * @param req + * @param rsp + * @throws IOException + * @throws ServletException + * @throws InterruptedException + */ + public void doDynamic(StaplerRequest req, StaplerResponse rsp) + throws IOException, ServletException, InterruptedException { + String indexFile = getReportFileName(); + FilePath robotDir = getRobotDir(); + + if(!new FilePath(robotDir, indexFile).exists()){ + rsp.sendRedirect2("notfound"); + return; + } + + DirectoryBrowserSupport dbs = new DirectoryBrowserSupport(this, + getRobotDir(), getDisplayName(), + "folder.gif", false); + dbs.setIndexFileName(indexFile); + dbs.generateResponse(req, rsp, this); + } + + private FilePath getRobotDir() { + FilePath rootDir = new FilePath(build.getRootDir()); + if (StringUtils.isNotBlank(outputPath)) + return new FilePath(rootDir, outputPath); + return rootDir; + } +} \ No newline at end of file diff --git a/src/main/java/hudson/plugins/robot/RobotProjectAction.java b/src/main/java/hudson/plugins/robot/RobotProjectAction.java new file mode 100755 index 0000000..0b700d1 --- /dev/null +++ b/src/main/java/hudson/plugins/robot/RobotProjectAction.java @@ -0,0 +1,122 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot; + +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.plugins.robot.graph.RobotGraph; +import hudson.plugins.robot.graph.RobotGraphHelper; +import hudson.util.ChartUtil; +import hudson.util.Graph; + +import java.io.IOException; +import java.util.Calendar; + +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; + +public class RobotProjectAction extends AbstractRobotAction { + + private AbstractProject project; + + /** + * Create new Robot project action + * @param project Project which this action will be applied to + */ + public RobotProjectAction(AbstractProject project) { + this.project = project; + } + + /** + * Get associated project. + * @return + */ + public AbstractProject getProject() { + return project; + } + + /** + * Returns true if there are any builds in the associated project. + * @return + */ + public boolean isDisplayGraph() { + if (project.getBuilds().size() > 0) + return true; + + return false; + } + + /** + * Return the action of last build associated with robot + * @return + */ + public RobotBuildAction getLastBuildAction(){ + AbstractBuild lastBuild = getLastBuildWithRobot(); + if(lastBuild != null) + return lastBuild.getAction(RobotBuildAction.class); + else return null; + } + + /** + * Return robot trend graph in the request. + * @param req + * @param rsp + * @throws IOException + */ + public void doGraph(StaplerRequest req, StaplerResponse rsp) + throws IOException { + if (ChartUtil.awtProblemCause != null) { + rsp.sendRedirect2(req.getContextPath() + "/images/headless.png"); + return; + } + + Calendar t = project.getLastCompletedBuild().getTimestamp(); + + if (req.checkIfModified(t, rsp)) + return; + + Graph g = new RobotGraph(project.getLastBuild(), RobotGraphHelper.createDataSetForProject(project), Messages.robot_trendgraph_testcases(), + Messages.robot_trendgraph_builds(), 500, 200); + g.doPng(req, rsp); + } + + /** + * Show Robot html report of the latest build. If no builds are associated with Robot, returns info page. + * @param req + * @param rsp + * @throws IOException + */ + public void doIndex(StaplerRequest req, StaplerResponse rsp) + throws IOException { + AbstractBuild lastBuild = getLastBuildWithRobot(); + if (lastBuild == null) { + rsp.sendRedirect2("nodata"); + } else { + int buildNumber = lastBuild.getNumber(); + rsp.sendRedirect2("../" + buildNumber + "/" + getUrlName()); + } + } + + private AbstractBuild getLastBuildWithRobot() { + AbstractBuild lastBuild = (AbstractBuild) project + .getLastBuild(); + while (lastBuild != null + && lastBuild.getAction(RobotBuildAction.class) == null) { + lastBuild = lastBuild.getPreviousBuild(); + } + return lastBuild; + } +} diff --git a/src/main/java/hudson/plugins/robot/RobotPublisher.java b/src/main/java/hudson/plugins/robot/RobotPublisher.java new file mode 100755 index 0000000..1ce4fed --- /dev/null +++ b/src/main/java/hudson/plugins/robot/RobotPublisher.java @@ -0,0 +1,325 @@ +/* + * Copyright 2008-2010 Nokia Siemens Networks Oyj + * + * 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 hudson.plugins.robot; + +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.Action; +import hudson.model.BuildListener; +import hudson.model.Result; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.plugins.robot.model.RobotResult; +import hudson.plugins.robot.parser.RobotParser; +import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.BuildStepMonitor; +import hudson.tasks.Publisher; +import hudson.tasks.Recorder; +import hudson.util.FormValidation; + +import java.io.IOException; +import java.io.PrintStream; + +import javax.servlet.ServletException; + +import org.apache.commons.lang.StringUtils; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; + +public class RobotPublisher extends Recorder { + + protected static final String DEFAULT_REPORT_FILE = "report.html"; + protected static final String FILE_ARCHIVE_DIR = "robot-plugin"; + + private static final String DEFAULT_OUTPUT_FILE = "output.xml"; + private static final String DEFAULT_LOG_FILE = "log.html"; + + private String outputPath; + private String reportFileName; + private String logFileName; + private String outputFileName; + private double passThreshold; + private double unstableThreshold; + private boolean onlyCritical; + + /** + * Create new publisher for Robot Framework results + * @param outputPath Path to Robot Framework's output files + * @param outputFileName Name of Robot output xml + * @param reportFileName Name of Robot report html + * @param logFileName Name of Robot log html + * @param passThreshold Threshold of test pass percentage for successful builds + * @param unstableThreshold Threhold of test pass percentage for unstable builds + * @param onlyCritical True if only critical tests are included in pass percentage + */ + @DataBoundConstructor + public RobotPublisher(String outputPath, String outputFileName, + String reportFileName, String logFileName, double passThreshold, + double unstableThreshold, boolean onlyCritical) { + this.outputPath = outputPath; + this.outputFileName = outputFileName; + this.reportFileName = reportFileName; + this.passThreshold = passThreshold; + this.unstableThreshold = unstableThreshold; + this.logFileName = logFileName; + this.onlyCritical = onlyCritical; + } + + + /** + * Gets the output path of Robot files + * @return + */ + public String getOutputPath() { + return outputPath; + } + + /** + * Gets the name of output xml file. Reverts to default if empty or whitespace. + * @return + */ + public String getOutputFileName() { + if (StringUtils.isBlank(outputFileName)) + return DEFAULT_OUTPUT_FILE; + return outputFileName; + } + + /** + * Gets the name of report html file. Reverts to default if empty or whitespace. + * @return + */ + public String getReportFileName() { + if (StringUtils.isBlank(reportFileName)) + return DEFAULT_REPORT_FILE; + return reportFileName; + } + + /** + * Gets the name of log html file. Reverts to default if empty or whitespace. + * @return + */ + public String getLogFileName() { + if (StringUtils.isBlank(logFileName)) + return DEFAULT_LOG_FILE; + return logFileName; + } + + /** + * Gets the test pass percentage threshold for successful builds. + * @return + */ + public double getPassThreshold() { + return passThreshold; + } + + /** + * Gets the test pass percentage threshold for unstable builds. + * @return + */ + public double getUnstableThreshold() { + return unstableThreshold; + } + + /** + * Gets if only critical tests should be accounted for the thresholds. + * @return + */ + public boolean getOnlyCritical() { + return onlyCritical; + } + + /** + * {@inheritDoc} + */ + @Override + public Action getProjectAction(AbstractProject project) { + return new RobotProjectAction(project); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean perform(AbstractBuild build, Launcher launcher, + BuildListener listener) { + if (build.getResult() != Result.ABORTED) { + PrintStream logger = listener.getLogger(); + logger.println(Messages.robot_publisher_started()); + logger.println(Messages.robot_publisher_parsing()); + + RobotResult result; + try { + FilePath robotOutputDir = new FilePath(build.getWorkspace(), + outputPath); + FilePath robotOutputFile = new FilePath(robotOutputDir, + getOutputFileName()); + RobotParser parser = new RobotParser(); + result = robotOutputFile.act(parser); + logger.println(Messages.robot_publisher_done()); + logger.println(Messages.robot_publisher_copying()); + copyRobotFilesToBuildDir(build); + logger.println(Messages.robot_publisher_done()); + } catch (Exception e) { + logger.println(Messages.robot_publisher_fail()); + e.getCause().printStackTrace(logger); + build.setResult(Result.FAILURE); + return true; + } + + logger.println(Messages.robot_publisher_assigning()); + + RobotBuildAction action = new RobotBuildAction(build, result, + FILE_ARCHIVE_DIR, getReportFileName()); + build.addAction(action); + + logger.println(Messages.robot_publisher_done()); + logger.println(Messages.robot_publisher_checking()); + + Result buildResult = getBuildResult(build, result); + build.setResult(buildResult); + + logger.println(Messages.robot_publisher_done()); + logger.println(Messages.robot_publisher_finished()); + } + return true; + } + + /** + * Determines the build result based on set thresholds. If build is already failed before the tests it won't be changed to successful. + * @param build Build to be evaluated + * @param result Results associated to build + * @return Result of build + */ + protected Result getBuildResult(AbstractBuild build, + RobotResult result) { + if (build.getResult() != Result.FAILURE) { + double passPercentage = result.getPassPercentage(onlyCritical); + if (passPercentage < getUnstableThreshold()) { + return Result.FAILURE; + } else if (passPercentage < getPassThreshold()) { + return Result.UNSTABLE; + } + return Result.SUCCESS; + } + return Result.FAILURE; + } + + private void copyRobotFilesToBuildDir(AbstractBuild build) + throws IOException, InterruptedException { + FilePath srcDir = new FilePath(build.getWorkspace(), outputPath); + FilePath destDir = new FilePath(new FilePath(build.getRootDir()), + FILE_ARCHIVE_DIR); + copySplittedFiles(srcDir, destDir, getLogFileName()); + copySplittedFiles(srcDir, destDir, getReportFileName()); + copySplittedFiles(srcDir, destDir, getOutputFileName()); + } + + private static void copySplittedFiles(FilePath src, FilePath dest, + String filename) throws IOException, InterruptedException { + src.copyRecursiveTo(trimSuffix(filename) + "*" + getSuffix(filename), + dest); + } + + private static String trimSuffix(String filename) { + int index = filename.lastIndexOf('.'); + if (index > 0) { + filename = filename.substring(0, index); + } + return filename; + } + + private static String getSuffix(String filename) { + int index = filename.lastIndexOf('.'); + if (index > 0) { + return filename.substring(index); + } + return ""; + } + + /** + * Descriptor for the publisher + */ + @Extension + public static final class DescriptorImpl extends + BuildStepDescriptor { + + /** + * {@inheritDoc}} + */ + @Override + public boolean isApplicable(Class aClass) { + return true; + } + + /** + * {@inheritDoc}} + */ + @Override + public String getDisplayName() { + return Messages.robot_description(); + } + + /** + * Validates the unstable threshold input field. + * @param value + * @return + * @throws IOException + * @throws ServletException + */ + public FormValidation doCheckUnstableThreshold( + @QueryParameter String value) throws IOException, + ServletException { + if (isPercentageValue(value)) + return FormValidation.ok(); + else + return FormValidation + .error(Messages.robot_config_percentvalidation()); + } + + /** + * Validates the pass threshold input field. + * @param value + * @return + * @throws IOException + * @throws ServletException + */ + public FormValidation doCheckPassThreshold(@QueryParameter String value) + throws IOException, ServletException { + if (isPercentageValue(value)) + return FormValidation.ok(); + else + return FormValidation + .error(Messages.robot_config_percentvalidation()); + } + + private boolean isPercentageValue(String value) { + try { + double doubleValue = Double.parseDouble(value); + if (doubleValue <= 100 && doubleValue >= 0) + return true; + else + return false; + } catch (NumberFormatException e) { + return false; + } + } + } + + public BuildStepMonitor getRequiredMonitorService() { + return BuildStepMonitor.NONE; + } +} diff --git a/src/main/java/hudson/plugins/robot/graph/RobotGraph.java b/src/main/java/hudson/plugins/robot/graph/RobotGraph.java new file mode 100755 index 0000000..6939fed --- /dev/null +++ b/src/main/java/hudson/plugins/robot/graph/RobotGraph.java @@ -0,0 +1,106 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot.graph; + +import hudson.model.AbstractBuild; +import hudson.util.Graph; +import hudson.util.ShiftedCategoryAxis; + +import java.awt.Color; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.CategoryAxis; +import org.jfree.chart.axis.CategoryLabelPositions; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.CategoryPlot; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.renderer.category.CategoryItemRenderer; +import org.jfree.chart.title.LegendTitle; +import org.jfree.data.category.CategoryDataset; +import org.jfree.ui.RectangleEdge; +import org.jfree.ui.RectangleInsets; + +public class RobotGraph extends Graph { + + private final String yLabel; + private final String xLabel; + + private final CategoryDataset categoryDataset; + + public static final int DEFAULT_CHART_WIDTH = 500; + public static final int DEFAULT_CHART_HEIGHT = 200; + + /** + * Construct a new styled trend graph for given dataset + * @param owner Build which the graph is associated to + * @param categoryDataset Category data for graph + * @param yLabel Y label name + * @param xLabel X label name + * @param chartWidth Chart width in pixels + * @param chartHeight Chart height in pixels + */ + public RobotGraph(AbstractBuild owner, + CategoryDataset categoryDataset, String yLabel, String xLabel, + int chartWidth, int chartHeight) { + super(owner.getTimestamp(), chartWidth, chartHeight); + this.yLabel = yLabel; + this.xLabel = xLabel; + this.categoryDataset = categoryDataset; + } + + /** + * Creates a Robot trend graph + * @return the JFreeChart graph object + */ + protected JFreeChart createGraph() { + + final JFreeChart chart = ChartFactory.createStackedAreaChart(null, + null, yLabel, categoryDataset, PlotOrientation.VERTICAL, true, + true, false); + + final LegendTitle legend = chart.getLegend(); + legend.setPosition(RectangleEdge.RIGHT); + + chart.setBackgroundPaint(Color.white); + + final CategoryPlot plot = (CategoryPlot) chart.getPlot(); + plot.setForegroundAlpha(0.7f); + plot.setBackgroundPaint(Color.white); + plot.setRangeGridlinePaint(Color.darkGray); + + final CategoryAxis domainAxis = new ShiftedCategoryAxis(xLabel); + domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45); + domainAxis.setLowerMargin(0.0); + domainAxis.setUpperMargin(0.0); + domainAxis.setCategoryMargin(0.0); + plot.setDomainAxis(domainAxis); + + final NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); + rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); + rangeAxis.setAutoRange(true); + rangeAxis.setAutoRangeMinimumSize(5); + rangeAxis.setLowerBound(0); + + final CategoryItemRenderer renderer = plot.getRenderer(); + renderer.setSeriesPaint(0, Color.green); + renderer.setSeriesPaint(1, Color.red); + + plot.setInsets(new RectangleInsets(5.0, 0, 0, 5.0)); + + return chart; + } +} diff --git a/src/main/java/hudson/plugins/robot/graph/RobotGraphHelper.java b/src/main/java/hudson/plugins/robot/graph/RobotGraphHelper.java new file mode 100644 index 0000000..62d161b --- /dev/null +++ b/src/main/java/hudson/plugins/robot/graph/RobotGraphHelper.java @@ -0,0 +1,94 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot.graph; + +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.plugins.robot.Messages; +import hudson.plugins.robot.RobotBuildAction; +import hudson.util.ChartUtil; +import hudson.util.ChartUtil.NumberOnlyBuildLabel; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeSet; + +import org.jfree.data.category.CategoryDataset; +import org.jfree.data.category.DefaultCategoryDataset; + +public class RobotGraphHelper { + + /** + * Create a dataset for the trend graph. + * @param project + * @return + */ + public static CategoryDataset createDataSetForProject(AbstractProject project) { + List values = new ArrayList(); + List rows = new ArrayList(); + List columns = new ArrayList(); + + for (AbstractBuild build = project.getLastBuild(); build != null; build = build + .getPreviousBuild()) { + RobotBuildAction action = build.getAction(RobotBuildAction.class); + + Number failed = 0, passed = 0; + if (action != null) { + failed = action.getResult().getOverallFailed(); + passed = action.getResult().getOverallPassed(); + } + + // default 'zero value' must be set over zero to circumvent + // JFreeChart stacked area rendering problem with zero values + if (failed.intValue() < 1) + failed = 0.01f; + if (passed.intValue() < 1) + passed = 0.01f; + + ChartUtil.NumberOnlyBuildLabel label = new ChartUtil.NumberOnlyBuildLabel( + build); + + values.add(passed); + rows.add(Messages.robot_trendgraph_passed()); + columns.add(label); + + values.add(failed); + rows.add(Messages.robot_trendgraph_failed()); + columns.add(label); + } + + // Code from DataSetBuilder, reversed row order for passed tests to go + // first into dataset for nicer order when rendered in chart + DefaultCategoryDataset dataset = new DefaultCategoryDataset(); + + TreeSet rowSet = new TreeSet(rows); + TreeSet colSet = new TreeSet( + columns); + + Comparable[] _rows = rowSet.toArray(new Comparable[rowSet.size()]); + Comparable[] _cols = colSet.toArray(new Comparable[colSet.size()]); + + // insert rows and columns in the right order, reverse rows + for (int i = _rows.length - 1; i >= 0; i--) + dataset.setValue(null, _rows[i], _cols[0]); + for (Comparable c : _cols) + dataset.setValue(null, _rows[0], c); + + for (int i = 0; i < values.size(); i++) + dataset.addValue(values.get(i), rows.get(i), columns.get(i)); + return dataset; + } +} diff --git a/src/main/java/hudson/plugins/robot/model/RobotResult.java b/src/main/java/hudson/plugins/robot/model/RobotResult.java new file mode 100755 index 0000000..a56f824 --- /dev/null +++ b/src/main/java/hudson/plugins/robot/model/RobotResult.java @@ -0,0 +1,142 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot.model; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +/** + * Class representing Robot Framework test results. + * + */ +public class RobotResult implements Serializable { + + private static final long serialVersionUID = 1L; + + private String timeStamp; + private List overallStats; + + //backwards compatibility with old builds + private transient List statsBySuite; + + /* + * The data structure of passed and failed tests is awkward and fragile but + * needs some consideration before migrating so that old builds won't break. + */ + + /** + * Get number of passed critical tests. + * @return + */ + public long getCriticalPassed(){ + if(overallStats.isEmpty()) return 0; + return overallStats.get(0).getPass(); + } + + /** + * Get number of failed critical tests. + * @return + */ + public long getCriticalFail(){ + if(overallStats.isEmpty()) return 0; + return overallStats.get(0).getFail(); + } + + /** + * Get total number of critical tests. + * @return + */ + public long getCriticalTotal(){ + if(overallStats.isEmpty()) return 0; + return overallStats.get(0).getTotal(); + } + + /** + * Get number of all passed tests. + * @return + */ + public long getOverallPassed(){ + if(overallStats.isEmpty()) return 0; + return overallStats.get(1).getPass(); + } + + /** + * Get number of all failed tests. + * @return + */ + public long getOverallFailed(){ + if(overallStats.isEmpty()) return 0; + return overallStats.get(1).getFail(); + } + + /** + * Get number of all tests. + * @return + */ + public long getOverallTotal(){ + if(overallStats.isEmpty()) return 0; + return overallStats.get(1).getTotal(); + } + + /** + * Get pass/fail stats by category. + * @return List containing 'critical tests' and 'all tests' + */ + public List getStatsByCategory() { + return overallStats; + } + + public void setStatsByCategory(List statsByCategory) { + this.overallStats = statsByCategory; + } + + /** + * Get the timestamp of the original test run + * @return + */ + public String getTimeStamp() { + return timeStamp; + } + + public void setTimeStamp(String timeStamp) { + this.timeStamp = timeStamp; + } + + /** + * Returns pass percentage of passed tests per total tests. + * @param onlyCritical true if only critical tests are to be calculated + * @return Percentage value rounded to 1 decimal + */ + public double getPassPercentage(boolean onlyCritical) { + long passed, total; + if(onlyCritical) { + passed = getCriticalPassed(); + total = getCriticalTotal(); + } else { + passed = getOverallPassed(); + total = getOverallTotal(); + } + double percentage = (double) passed / total * 100; + return roundToDecimals(percentage, 1); + } + + private static double roundToDecimals(double value, int decimals){ + BigDecimal bd = new BigDecimal(Double.toString(value)); + bd = bd.setScale(decimals, BigDecimal.ROUND_HALF_UP); + return bd.doubleValue(); + } +} \ No newline at end of file diff --git a/src/main/java/hudson/plugins/robot/model/RobotResultStatistics.java b/src/main/java/hudson/plugins/robot/model/RobotResultStatistics.java new file mode 100755 index 0000000..fadbf16 --- /dev/null +++ b/src/main/java/hudson/plugins/robot/model/RobotResultStatistics.java @@ -0,0 +1,53 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot.model; + +import java.io.Serializable; + +/** + * Class representing a single testsuite/category. + * + */ +public class RobotResultStatistics implements Serializable { + + private static final long serialVersionUID = 1L; + + private long pass; + private long fail; + + //backwards compatibility with old builds + private transient String name; + + public long getPass() { + return pass; + } + + public void setPass(long pass) { + this.pass = pass; + } + + public long getFail() { + return fail; + } + + public void setFail(long fail) { + this.fail = fail; + } + + public long getTotal(){ + return fail + pass; + } +} diff --git a/src/main/java/hudson/plugins/robot/parser/RobotParser.java b/src/main/java/hudson/plugins/robot/parser/RobotParser.java new file mode 100755 index 0000000..2240b92 --- /dev/null +++ b/src/main/java/hudson/plugins/robot/parser/RobotParser.java @@ -0,0 +1,76 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot.parser; + +import hudson.FilePath; +import hudson.plugins.robot.model.RobotResult; +import hudson.plugins.robot.model.RobotResultStatistics; +import hudson.remoting.VirtualChannel; +import hudson.util.Digester2; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +import org.apache.commons.digester.Digester; + +/** + * A parser for reading Robot Framework XML output into RobotResult object. + * Reads pass/fail statistics for critical and all tests from XML part: + * + * ... + * + * Critical tests + * All tests + * + * ... + * + * + */ +public class RobotParser implements FilePath.FileCallable { + + private static final long serialVersionUID = 1L; + + /* + * (non-Javadoc) + * @see hudson.FilePath.FileCallable#invoke(java.io.File, hudson.remoting.VirtualChannel) + */ + public RobotResult invoke(File outputFile, VirtualChannel channel) throws IOException { + RobotResult result = null; + + Digester digester = new Digester2(); + digester.setValidating(false); + digester.setClassLoader(RobotParser.class.getClassLoader()); + + digester.addObjectCreate("robot", RobotResult.class); + digester.addSetProperties("robot", "generated", "timeStamp"); + + digester.addObjectCreate("robot/statistics/total", ArrayList.class); + + digester.addObjectCreate("robot/statistics/total/stat", RobotResultStatistics.class); + digester.addSetProperties("robot/statistics/total/stat", "pass", "pass"); + digester.addSetNext("robot/statistics/total/stat", "add"); + + digester.addSetNext("robot/statistics/total", "setStatsByCategory"); + + try { + result = (RobotResult) digester.parse(outputFile); + } catch (Exception e) { + throw new IOException(e); + } + return result; + } +} \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/Messages.properties b/src/main/resources/hudson/plugins/robot/Messages.properties new file mode 100644 index 0000000..edb4de4 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/Messages.properties @@ -0,0 +1,31 @@ +# Copyright 2008-2010 Nokia Siemens Networks Oyj +# +# 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. +robot.sidebar.link=Robot results +robot.description=Publish Robot Framework test results + +robot.publisher.started=Robot results publisher started... +robot.publisher.parsing=-Parsing output xml: +robot.publisher.copying=-Copying log files to build dir: +robot.publisher.assigning=-Assigning results to build: +robot.publisher.checking=-Checking thresholds: +robot.publisher.finished=Done publishing Robot results. +robot.publisher.done= Done! +robot.publisher.fail= Failed! + +robot.config.percentvalidation=Entry must be percentage value between 0-100 + +robot.trendgraph.passed=Passed +robot.trendgraph.failed=Failed +robot.trendgraph.testcases=Number of testcases +robot.trendgraph.builds=Build number \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotBuildAction/notfound.jelly b/src/main/resources/hudson/plugins/robot/RobotBuildAction/notfound.jelly new file mode 100755 index 0000000..e8d9136 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotBuildAction/notfound.jelly @@ -0,0 +1,24 @@ + + + + + +

${%notfound.header}

+

${%notfound.message(it.reportFileName)}

+
+
+
diff --git a/src/main/resources/hudson/plugins/robot/RobotBuildAction/notfound.properties b/src/main/resources/hudson/plugins/robot/RobotBuildAction/notfound.properties new file mode 100644 index 0000000..16bdd35 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotBuildAction/notfound.properties @@ -0,0 +1,15 @@ +# Copyright 2008-2010 Nokia Siemens Networks Oyj +# +# 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. +notfound.header=No robot report found! +notfound.message=This build was associated with "{0}" which was not found. Please check your configuration to make sure the report will appear in future builds. \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotBuildAction/summary.jelly b/src/main/resources/hudson/plugins/robot/RobotBuildAction/summary.jelly new file mode 100755 index 0000000..12c2fe2 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotBuildAction/summary.jelly @@ -0,0 +1,21 @@ + + + +

${%summary.header}

+ +
+
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotBuildAction/summary.properties b/src/main/resources/hudson/plugins/robot/RobotBuildAction/summary.properties new file mode 100644 index 0000000..6a070d8 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotBuildAction/summary.properties @@ -0,0 +1,14 @@ +# Copyright 2008-2010 Nokia Siemens Networks Oyj +# +# 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. +summary.header=Robot test summary: \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotProjectAction/floatingBox.jelly b/src/main/resources/hudson/plugins/robot/RobotProjectAction/floatingBox.jelly new file mode 100755 index 0000000..6a39b3c --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotProjectAction/floatingBox.jelly @@ -0,0 +1,27 @@ + + + +
+
${%trend.graph.header}
+
+ + + +
+
+
+
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotProjectAction/floatingBox.properties b/src/main/resources/hudson/plugins/robot/RobotProjectAction/floatingBox.properties new file mode 100644 index 0000000..edb914b --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotProjectAction/floatingBox.properties @@ -0,0 +1,14 @@ +# Copyright 2008-2010 Nokia Siemens Networks Oyj +# +# 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. +trend.graph.header=Robot Framework Tests Trend (all tests) \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotProjectAction/jobMain.jelly b/src/main/resources/hudson/plugins/robot/RobotProjectAction/jobMain.jelly new file mode 100755 index 0000000..19c12cc --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotProjectAction/jobMain.jelly @@ -0,0 +1,30 @@ + + + + +

${%summary.header}

+ + + + + +

${%noresults}

+
+
+
+
+
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotProjectAction/jobMain.properties b/src/main/resources/hudson/plugins/robot/RobotProjectAction/jobMain.properties new file mode 100644 index 0000000..7588066 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotProjectAction/jobMain.properties @@ -0,0 +1,15 @@ +# Copyright 2008-2010 Nokia Siemens Networks Oyj +# +# 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. +summary.header=Latest Robot results: +noresults=No results available yet. \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotProjectAction/nodata.jelly b/src/main/resources/hudson/plugins/robot/RobotProjectAction/nodata.jelly new file mode 100755 index 0000000..b9cf5c3 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotProjectAction/nodata.jelly @@ -0,0 +1,27 @@ + + + + + +

${%header}

+

${%description}

+

${%content}

+
+
+
diff --git a/src/main/resources/hudson/plugins/robot/RobotProjectAction/nodata.properties b/src/main/resources/hudson/plugins/robot/RobotProjectAction/nodata.properties new file mode 100644 index 0000000..c495fa2 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotProjectAction/nodata.properties @@ -0,0 +1,16 @@ +# Copyright 2008-2010 Nokia Siemens Networks Oyj +# +# 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. +header=Robot results +description=No robot results available yet! +content=Run a build with robot plugin enabled to see results here \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotPublisher/config.jelly b/src/main/resources/hudson/plugins/robot/RobotPublisher/config.jelly new file mode 100755 index 0000000..d7764b4 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotPublisher/config.jelly @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + ${%thresholds.onlycritical} + +
+
+
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotPublisher/config.properties b/src/main/resources/hudson/plugins/robot/RobotPublisher/config.properties new file mode 100644 index 0000000..0daac82 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotPublisher/config.properties @@ -0,0 +1,11 @@ +output.directory=Directory of Robot output +output.directory.description=Path to directory containing robot xml and html files (relative to build workspace) +advanced.outputxml=Output xml name +advanced.outputxml.description=Name of the xml file containing robot output +advanced.reporthtml=Report html name +advanced.reporthtml.description=Name of the html file containing robot test report +advanced.loghtml=Log html name +advanced.loghtml.description=Name of the html file containing detailed robot test log + +thresholds.label=Thresholds for build result +thresholds.onlycritical=Use thresholds for critical tests only \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotPublisher/help-logFileName.html b/src/main/resources/hudson/plugins/robot/RobotPublisher/help-logFileName.html new file mode 100644 index 0000000..45a28c6 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotPublisher/help-logFileName.html @@ -0,0 +1,18 @@ + +
+File name relative to output path. Splitted files will be copied automatically.If file name is specified log.html then all log-xxx.html will be saved. +
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotPublisher/help-outputFileName.html b/src/main/resources/hudson/plugins/robot/RobotPublisher/help-outputFileName.html new file mode 100644 index 0000000..2b7bcd8 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotPublisher/help-outputFileName.html @@ -0,0 +1,18 @@ + +
+File name relative to output path. Splitted files will be copied automatically. If file name is specified output.xml then all output-xxx.xml will be saved. +
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotPublisher/help-outputFileNamePattern.html b/src/main/resources/hudson/plugins/robot/RobotPublisher/help-outputFileNamePattern.html new file mode 100755 index 0000000..0d31874 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotPublisher/help-outputFileNamePattern.html @@ -0,0 +1,18 @@ + +
+If no file is specified output.xml is assumed as filename. +
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotPublisher/help-reportFileName.html b/src/main/resources/hudson/plugins/robot/RobotPublisher/help-reportFileName.html new file mode 100755 index 0000000..c48c6fa --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotPublisher/help-reportFileName.html @@ -0,0 +1,18 @@ + +
+File name relative to output path. Splitted files will be copied automatically. If file name is specified report.html then all report-xxx.html will be saved. +
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/RobotPublisher/help.html b/src/main/resources/hudson/plugins/robot/RobotPublisher/help.html new file mode 100755 index 0000000..6f7d8d1 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/RobotPublisher/help.html @@ -0,0 +1,21 @@ + +
+

Publishes Robot +Framework test reports into Hudson user interface.

+

Note that you must configure your build to produce these reports +before you can publish them with this plugin.

+
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/util/robotsummary.jelly b/src/main/resources/hudson/plugins/robot/util/robotsummary.jelly new file mode 100644 index 0000000..1e0cd04 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/util/robotsummary.jelly @@ -0,0 +1,24 @@ + + + +
    +
  • ${%critical.ratio(attrs.action.criticalPassPercentage)}
  • +
  • ${%all.ratio(attrs.action.overallPassPercentage)}
  • +
  • ${%critical.amount(attrs.action.result.criticalPassed, attrs.action.result.criticalTotal)}
  • +
  • ${%all.amount(attrs.action.result.overallPassed, attrs.action.result.overallTotal)}
  • +
+
\ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/util/robotsummary.properties b/src/main/resources/hudson/plugins/robot/util/robotsummary.properties new file mode 100644 index 0000000..c53eca5 --- /dev/null +++ b/src/main/resources/hudson/plugins/robot/util/robotsummary.properties @@ -0,0 +1,17 @@ +# Copyright 2008-2010 Nokia Siemens Networks Oyj +# +# 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. +critical.ratio=Pass ratio (Critical tests): {0}% +all.ratio=Pass ratio (All tests): {0}% +critical.amount=Tests passed (Critical tests): {0}/{1} +all.amount=Tests passed (All tests): {0}/{1} \ No newline at end of file diff --git a/src/main/resources/hudson/plugins/robot/util/taglib b/src/main/resources/hudson/plugins/robot/util/taglib new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly new file mode 100755 index 0000000..5c391ca --- /dev/null +++ b/src/main/resources/index.jelly @@ -0,0 +1,18 @@ + +
+ This publisher stores Robot Framework test reports for builds and shows summaries of them in project and build views along with trend graph. +
\ No newline at end of file diff --git a/src/main/webapp/help-thresholds.html b/src/main/webapp/help-thresholds.html new file mode 100755 index 0000000..ac2afbc --- /dev/null +++ b/src/main/webapp/help-thresholds.html @@ -0,0 +1,20 @@ + +
+

Specify thresholds for marking a build unstable or passed. Thresholds are expressed in percentage of tests that passed compared to total amount of tests run. +Builds with pass percentage under the blue threshold wild be marked unstable and pass percentage under yellow threshold will yield a failed build.

+

If "Use thresholds for critical tests only" is selected thresholds will be compared to the pass ratio of critical tests instead of all tests.

+
\ No newline at end of file diff --git a/src/main/webapp/robot-large.png b/src/main/webapp/robot-large.png new file mode 100755 index 0000000..0ec26d2 Binary files /dev/null and b/src/main/webapp/robot-large.png differ diff --git a/src/main/webapp/robot.png b/src/main/webapp/robot.png new file mode 100755 index 0000000..0f17553 Binary files /dev/null and b/src/main/webapp/robot.png differ diff --git a/src/test/java/hudson/plugins/robot/RobotBuildActionTest.java b/src/test/java/hudson/plugins/robot/RobotBuildActionTest.java new file mode 100644 index 0000000..df8e004 --- /dev/null +++ b/src/test/java/hudson/plugins/robot/RobotBuildActionTest.java @@ -0,0 +1,30 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot; + +import junit.framework.TestCase; + +public class RobotBuildActionTest extends TestCase { + + protected void setUp() throws Exception { + super.setUp(); + } + + public void testEmptyReportNameShouldRevertToDefault() { + RobotBuildAction action = new RobotBuildAction(null, null, "", ""); + assertEquals(RobotPublisher.DEFAULT_REPORT_FILE, action.getReportFileName()); + } +} diff --git a/src/test/java/hudson/plugins/robot/RobotProjectActionTest.java b/src/test/java/hudson/plugins/robot/RobotProjectActionTest.java new file mode 100644 index 0000000..4b9e320 --- /dev/null +++ b/src/test/java/hudson/plugins/robot/RobotProjectActionTest.java @@ -0,0 +1,81 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import hudson.model.FreeStyleBuild; +import hudson.model.FreeStyleProject; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + + +public class RobotProjectActionTest extends TestCase { + + protected void setUp() throws Exception { + super.setUp(); + } + + public void testShouldNotDisplayGraph() { + FreeStyleProject p = mock(FreeStyleProject.class); + when(p.getBuilds()).thenReturn(new ArrayList()); + + RobotProjectAction action = new RobotProjectAction(p); + assertFalse(action.isDisplayGraph()); + } + + public void testShouldDisplayGraph() throws Exception { + FreeStyleProject p = mock(FreeStyleProject.class); + List mockList = new ArrayList(); + mockList.add(new FreeStyleBuild(p)); + when(p.getBuilds()).thenReturn(mockList); + + RobotProjectAction action = new RobotProjectAction(p); + assertTrue(action.isDisplayGraph()); + } + + public void testShouldGetLastBuildAction(){ + FreeStyleProject p = mock(FreeStyleProject.class); + + FreeStyleBuild lastBuild = mock(FreeStyleBuild.class); + FreeStyleBuild buildWithAction = mock(FreeStyleBuild.class); + RobotBuildAction buildAction = new RobotBuildAction(null,null,"",""); + + when(buildWithAction.getAction(RobotBuildAction.class)).thenReturn(buildAction); + when(p.getLastBuild()).thenReturn(lastBuild); + when(lastBuild.getPreviousBuild()).thenReturn(buildWithAction); + + RobotProjectAction projectAction = new RobotProjectAction(p); + assertEquals(buildAction, projectAction.getLastBuildAction()); + } + + public void testShouldReturnNullWhenNoActions() { + FreeStyleProject p = mock(FreeStyleProject.class); + + FreeStyleBuild lastBuild = mock(FreeStyleBuild.class); + FreeStyleBuild firstBuild = mock(FreeStyleBuild.class); + + when(p.getLastBuild()).thenReturn(lastBuild); + when(lastBuild.getPreviousBuild()).thenReturn(firstBuild); + + RobotProjectAction projectAction = new RobotProjectAction(p); + assertNull(projectAction.getLastBuildAction()); + } + +} diff --git a/src/test/java/hudson/plugins/robot/RobotPublisherSystemTest.java b/src/test/java/hudson/plugins/robot/RobotPublisherSystemTest.java new file mode 100644 index 0000000..37aad3f --- /dev/null +++ b/src/test/java/hudson/plugins/robot/RobotPublisherSystemTest.java @@ -0,0 +1,173 @@ +/* + * Copyright 2008-2010 Nokia Siemens Networks Oyj + * + * 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 hudson.plugins.robot; + +import hudson.model.FreeStyleBuild; +import hudson.model.Result; +import hudson.model.FreeStyleProject; +import hudson.model.Hudson; +import hudson.model.Project; +import hudson.model.Run; + +import java.io.File; +import java.util.List; +import java.util.concurrent.Future; + +import org.jvnet.hudson.test.HudsonTestCase; +import org.jvnet.hudson.test.recipes.LocalData; + +import com.gargoylesoftware.htmlunit.WebAssert; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +public class RobotPublisherSystemTest extends HudsonTestCase { + + public void testRoundTripConfig() throws Exception{ + FreeStyleProject p = createFreeStyleProject(); + RobotPublisher before = new RobotPublisher("a", "b", "c", "d", 11, 27, true); + p.getPublishersList().add(before); + + submit(createWebClient().getPage(p, "configure") + .getFormByName("config")); + + RobotPublisher after = p.getPublishersList().get(RobotPublisher.class); + + assertEqualBeans(before, after, "outputPath,outputFileName,reportFileName,logFileName,passThreshold,unstableThreshold,onlyCritical"); + } + + public void testConfigView() throws Exception{ + FreeStyleProject p = createFreeStyleProject(); + RobotPublisher before = new RobotPublisher("a", "b", "c", "d", 11, 27, true); + p.getPublishersList().add(before); + HtmlPage page = createWebClient().getPage(p,"configure"); + WebAssert.assertTextPresent(page, "Publish Robot Framework"); + WebAssert.assertInputPresent(page, "_.outputPath"); + WebAssert.assertInputContainsValue(page, "_.outputPath", "a"); + WebAssert.assertInputPresent(page, "_.outputFileName"); + WebAssert.assertInputContainsValue(page, "_.outputFileName", "b"); + WebAssert.assertInputPresent(page, "_.reportFileName"); + WebAssert.assertInputContainsValue(page, "_.reportFileName", "c"); + WebAssert.assertInputPresent(page, "_.logFileName"); + WebAssert.assertInputContainsValue(page, "_.logFileName", "d"); + WebAssert.assertInputPresent(page, "_.unstableThreshold"); + WebAssert.assertInputContainsValue(page, "_.unstableThreshold", "27.0"); + WebAssert.assertInputPresent(page, "_.passThreshold"); + WebAssert.assertInputContainsValue(page, "_.passThreshold", "11.0"); + WebAssert.assertInputPresent(page, "_.onlyCritical"); + WebAssert.assertInputContainsValue(page, "_.onlyCritical", "on"); + } + + @LocalData + public void testPublish() throws Exception{ + Hudson hudson = Hudson.getInstance(); + List projects = hudson.getProjects(); + Project testProject = projects.get(0); + Future run = testProject.scheduleBuild2(0); + + while(!run.isDone()){ + Thread.sleep(5); + } + + Run lastBuild = testProject.getLastBuild(); + assertTrue("Build wasn't a success", lastBuild.getResult() == Result.SUCCESS); + + File storedOutput = new File(lastBuild.getRootDir(), RobotPublisher.FILE_ARCHIVE_DIR + "/output.xml"); + File storedSplitOutput = new File(lastBuild.getRootDir(), RobotPublisher.FILE_ARCHIVE_DIR + "/output-001.xml"); + File storedReport = new File(lastBuild.getRootDir(), RobotPublisher.FILE_ARCHIVE_DIR + "/report.html"); + File storedSplitReport = new File(lastBuild.getRootDir(), RobotPublisher.FILE_ARCHIVE_DIR + "/report.html"); + File storedLog = new File(lastBuild.getRootDir(), RobotPublisher.FILE_ARCHIVE_DIR + "/log.html"); + File storedSplitLog = new File(lastBuild.getRootDir(), RobotPublisher.FILE_ARCHIVE_DIR + "/log.html"); + assertTrue("output.xml was not stored", storedOutput.exists()); + assertTrue("output-001.xml was not stored", storedSplitOutput.exists()); + assertTrue("report.html was not stored", storedReport.exists()); + assertTrue("report-001.html was not stored", storedSplitReport.exists()); + assertTrue("log.html was not stored", storedLog.exists()); + assertTrue("log-001.html was not stored", storedSplitLog.exists()); + } + + @LocalData + public void testActionViewsWithNoData() throws Exception{ + WebClient wc = new WebClient(); + HtmlPage page = wc.goTo("job/robot/"); + + WebAssert.assertElementPresentByXPath(page, "//div[@id='navigation']//a[@href='/job/robot/robot']"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/p[contains(.,'No results available yet.')]"); + WebAssert.assertElementNotPresentByXPath(page, "//td[@id='main-panel']//img[@src='robot/graph']"); + + page = wc.goTo("job/robot/robot/"); + WebAssert.assertTextPresent(page, "No robot results available yet!"); + } + + @LocalData + public void testActionViewsWithData() throws Exception{ + Hudson hudson = Hudson.getInstance(); + List projects = hudson.getProjects(); + Project testProject = projects.get(0); + Future run = testProject.scheduleBuild2(0); + + while(!run.isDone()){ + Thread.sleep(5); + } + + WebClient wc = new WebClient(); + + HtmlPage page = wc.goTo("job/robot/"); + WebAssert.assertElementPresentByXPath(page, "//div[@id='navigation']//a[@href='/job/robot/robot']"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/h3[contains(.,'Latest Robot results:')]"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/ul/li[contains(.,'Pass ratio (Critical tests): 50%')]"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/ul/li[contains(.,'Pass ratio (All tests): 60%')]"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/ul/li[contains(.,'Tests passed (Critical tests): 4/8')]"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/ul/li[contains(.,'Tests passed (All tests): 6/10')]"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//img[@src='robot/graph']"); + + page = wc.goTo("job/robot/robot/"); + WebAssert.assertTitleEquals(page, "Testcases & Othercases Test Report"); + + page = wc.goTo("job/robot/1/"); + WebAssert.assertElementPresentByXPath(page, "//div[@id='navigation']//a[@href='/job/robot/1/robot']"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/h3[contains(.,'Robot test summary:')]"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/ul/li[contains(.,'Pass ratio (Critical tests): 50%')]"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/ul/li[contains(.,'Pass ratio (All tests): 60%')]"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/ul/li[contains(.,'Tests passed (Critical tests): 4/8')]"); + WebAssert.assertElementPresentByXPath(page, "//td[@id='main-panel']//a/ul/li[contains(.,'Tests passed (All tests): 6/10')]"); + + page = wc.goTo("job/robot/1/robot/"); + WebAssert.assertTitleEquals(page, "Testcases & Othercases Test Report"); + } + + @LocalData + public void testMissingReportFile() throws Exception{ + Hudson hudson = Hudson.getInstance(); + List projects = hudson.getProjects(); + Project testProject = projects.get(0); + Future run = testProject.scheduleBuild2(0); + + while(!run.isDone()){ + Thread.sleep(5); + } + + WebClient wc = new WebClient(); + + File buildRoot = testProject.getLastBuild().getRootDir(); + File robotHtmlReport = new File(buildRoot, RobotPublisher.FILE_ARCHIVE_DIR + "/report.html"); + robotHtmlReport.delete(); + + HtmlPage page = wc.goTo("job/robot/robot/"); + WebAssert.assertTextPresent(page, "No robot report found!"); + + page = wc.goTo("job/robot/1/robot/"); + WebAssert.assertTextPresent(page, "No robot report found!"); + } +} diff --git a/src/test/java/hudson/plugins/robot/RobotPublisherTest.java b/src/test/java/hudson/plugins/robot/RobotPublisherTest.java new file mode 100644 index 0000000..af8d253 --- /dev/null +++ b/src/test/java/hudson/plugins/robot/RobotPublisherTest.java @@ -0,0 +1,104 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import hudson.model.FreeStyleBuild; +import hudson.model.Result; +import hudson.model.AbstractBuild; +import hudson.plugins.robot.model.RobotResult; +import junit.framework.TestCase; + +public class RobotPublisherTest extends TestCase { + + protected void setUp() throws Exception { + super.setUp(); + } + + public void testBlankConfigShouldReturnDefaults() { + RobotPublisher testable = new RobotPublisher(" "," ", " ", " ", 0, 0, false); + + assertEquals("output.xml", testable.getOutputFileName()); + assertEquals("report.html", testable.getReportFileName()); + assertEquals("log.html", testable.getLogFileName()); + } + + public void testShouldReturnSuccessWhenThresholdsExceeded() throws Exception{ + boolean onlyCritical = false; + + RobotPublisher publisher = new RobotPublisher("","","","",99.9,99,onlyCritical); + RobotResult mockResult = mock(RobotResult.class); + AbstractBuild mockBuild = mock(FreeStyleBuild.class); + + when(mockBuild.getResult()).thenReturn(Result.SUCCESS); + when(mockResult.getPassPercentage(onlyCritical)).thenReturn(100.0); + + assertEquals(Result.SUCCESS, publisher.getBuildResult(mockBuild,mockResult)); + } + + public void testShouldFailWhenFailedBuild() throws Exception{ + boolean onlyCritical = false; + + RobotPublisher publisher = new RobotPublisher("","","","",0,0,onlyCritical); + RobotResult mockResult = mock(RobotResult.class); + AbstractBuild mockBuild = mock(FreeStyleBuild.class); + + when(mockBuild.getResult()).thenReturn(Result.FAILURE); + when(mockResult.getPassPercentage(onlyCritical)).thenReturn(100.0); + + assertEquals(Result.FAILURE, publisher.getBuildResult(mockBuild,mockResult)); + } + + public void testShouldFailWhenUnstableThresholdNotExceeded(){ + boolean onlyCritical = false; + + RobotPublisher publisher = new RobotPublisher("","","","",90,50,onlyCritical); + RobotResult mockResult = mock(RobotResult.class); + AbstractBuild mockBuild = mock(FreeStyleBuild.class); + + when(mockBuild.getResult()).thenReturn(Result.SUCCESS); + when(mockResult.getPassPercentage(onlyCritical)).thenReturn(49.9); + + assertEquals(Result.FAILURE, publisher.getBuildResult(mockBuild,mockResult)); + } + + public void testShouldBeUnstableWhenPassThresholdNotExceeded(){ + boolean onlyCritical = false; + + RobotPublisher publisher = new RobotPublisher("","","","",90,50,onlyCritical); + RobotResult mockResult = mock(RobotResult.class); + AbstractBuild mockBuild = mock(FreeStyleBuild.class); + + when(mockBuild.getResult()).thenReturn(Result.SUCCESS); + when(mockResult.getPassPercentage(onlyCritical)).thenReturn(89.9); + + assertEquals(Result.UNSTABLE, publisher.getBuildResult(mockBuild,mockResult)); + } + + public void testShouldBeSuccessWithOnlyCritical(){ + boolean onlyCritical = false; + + RobotPublisher publisher = new RobotPublisher("","","","",90,50,onlyCritical); + RobotResult mockResult = mock(RobotResult.class); + AbstractBuild mockBuild = mock(FreeStyleBuild.class); + + when(mockBuild.getResult()).thenReturn(Result.SUCCESS); + when(mockResult.getPassPercentage(onlyCritical)).thenReturn(90.0); + + assertEquals(Result.SUCCESS, publisher.getBuildResult(mockBuild,mockResult)); + } +} diff --git a/src/test/java/hudson/plugins/robot/model/RobotResultTest.java b/src/test/java/hudson/plugins/robot/model/RobotResultTest.java new file mode 100644 index 0000000..3890b3a --- /dev/null +++ b/src/test/java/hudson/plugins/robot/model/RobotResultTest.java @@ -0,0 +1,48 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot.model; + +import java.util.ArrayList; + +import junit.framework.TestCase; + +public class RobotResultTest extends TestCase { + + private RobotResult result; + + protected void setUp() throws Exception { + super.setUp(); + result = new RobotResult(); + RobotResultStatistics criticalStats = new RobotResultStatistics(); + criticalStats.setPass(5); + criticalStats.setFail(4); + RobotResultStatistics overallStats = new RobotResultStatistics(); + overallStats.setPass(1); + overallStats.setFail(2); + ArrayList statsByCategory = new ArrayList(); + statsByCategory.add(criticalStats); + statsByCategory.add(overallStats); + result.setStatsByCategory(statsByCategory); + } + + public void testShouldReturnCriticalPassPercentage(){ + assertEquals(55.6, result.getPassPercentage(true)); + } + public void testShouldReturnOverallPassPercentage(){ + assertEquals(33.3, result.getPassPercentage(false)); + } + +} diff --git a/src/test/java/hudson/plugins/robot/parser/RobotParserTest.java b/src/test/java/hudson/plugins/robot/parser/RobotParserTest.java new file mode 100644 index 0000000..4b2f8ef --- /dev/null +++ b/src/test/java/hudson/plugins/robot/parser/RobotParserTest.java @@ -0,0 +1,51 @@ +/* +* Copyright 2008-2010 Nokia Siemens Networks Oyj +* +* 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 hudson.plugins.robot.parser; +import hudson.plugins.robot.model.RobotResult; + +import java.io.File; + +import junit.framework.TestCase; + + +public class RobotParserTest extends TestCase { + + private RobotResult parserResult; + + + protected void setUp() throws Exception { + super.setUp(); + RobotParser parser = new RobotParser(); + File testOutputFile = new File(getClass().getResource("output.xml").toURI()); + parserResult = parser.invoke(testOutputFile, null); + } + + public void testShouldParseAllPassedTests(){ + assertEquals(7, parserResult.getOverallPassed()); + } + + public void testShouldParseAllFailedTests(){ + assertEquals(6, parserResult.getOverallFailed()); + } + + public void testShouldParseCriticalPassedTests(){ + assertEquals(4, parserResult.getCriticalPassed()); + } + + public void testShouldParseCriticalFailedTests(){ + assertEquals(6, parserResult.getCriticalFail()); + } +} diff --git a/src/test/resources/hudson/plugins/robot/RobotPublisherSystemTest.zip b/src/test/resources/hudson/plugins/robot/RobotPublisherSystemTest.zip new file mode 100644 index 0000000..b962afd Binary files /dev/null and b/src/test/resources/hudson/plugins/robot/RobotPublisherSystemTest.zip differ diff --git a/src/test/resources/hudson/plugins/robot/parser/output.xml b/src/test/resources/hudson/plugins/robot/parser/output.xml new file mode 100755 index 0000000..c4b7869 --- /dev/null +++ b/src/test/resources/hudson/plugins/robot/parser/output.xml @@ -0,0 +1,296 @@ + + + + Exampledocumentation + + + Some documentation for testing purposes + + + + + Fails if the given objects are unequal. + + ${HELLO} + Hello, world! + + + + + + + + + + + + + Fails the test immediately with the given + (optional) message. + + + ${FAILMSG} + + + Test failed miserably! + + + + + + + Test failed miserably! + + + + + + Fails if the given objects are unequal. + + Hello, world! + Good bye, world! + + + Hello, world! != Good bye, world! + + + + + + + Hello, world! != Good bye, world! + + + + + + + Fails if `item1` does not contain `item2` one or + more times. + + + Hello, world! + world + + + + + + + + + + + + + Some documentation for testing purposes + + + + + Fails if the given objects are unequal. + + ${HELLO} + Hello, world! + + + + + + + + + + + + + Fails the test immediately with the given + (optional) message. + + + ${FAILMSG} + + + Test failed miserably! + + + + + + + Test failed miserably! + + + + + + Fails if the given objects are unequal. + + Hello, world! + Good bye, world! + + + Hello, world! != Good bye, world! + + + + + + + Hello, world! != Good bye, world! + + + + + + + Fails if `item1` does not contain `item2` one or + more times. + + + Hello, world! + world + + + + + + + + + + + + + + + + + Fails if the given objects are unequal. + + ${HELLO} + Hello, world! + + + + + + + + + +Some documentation for testing purposes + + + + + +Fails if the given objects are unequal. + +${HELLO} +Hello, world! + + + + + + + + + + +Fails the test immediately with the given (optional) message. + +${FAILMSG} + +Test failed miserably! + + + + +Test failed miserably! + + + + +Fails if the given objects are unequal. + +Hello, world! +Good bye, world! + +Hello, world! != Good bye, world! + + + + +Hello, world! != Good bye, world! + + + + +Fails if `item1` does not contain `item2` one or more times. + +Hello, world! +world + + + + + + + + + + + +Critical Tests +All Tests + + + + +Othercases & Testcases +Othercases & Testcases.Othercases +Othercases & Testcases.Testcases +Somecases + + + +