From de2457b4eb87f18c8444c41e8b58fa70f1dc3826 Mon Sep 17 00:00:00 2001 From: Ryan Styrczula Date: Mon, 28 May 2018 16:36:28 -0400 Subject: [PATCH 1/3] JENKINS-50636 Use @DataBoundSetter to support optional pipeline params This helps the Snippet Generator provide a smaller example where many defaults are used, and prevents the Jenkins pipeline linter from complaining about missing "required" parameters. --- .../java/hudson/plugins/plot/PlotBuilder.java | 144 ++++++++++++------ 1 file changed, 101 insertions(+), 43 deletions(-) diff --git a/src/main/java/hudson/plugins/plot/PlotBuilder.java b/src/main/java/hudson/plugins/plot/PlotBuilder.java index 68adc93e..d3e1c0de 100644 --- a/src/main/java/hudson/plugins/plot/PlotBuilder.java +++ b/src/main/java/hudson/plugins/plot/PlotBuilder.java @@ -3,6 +3,7 @@ import hudson.Extension; import hudson.FilePath; import hudson.Launcher; +import hudson.Util; import hudson.model.AbstractProject; import hudson.model.Run; import hudson.model.TaskListener; @@ -13,11 +14,13 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import javax.annotation.CheckForNull; import javax.servlet.ServletException; import jenkins.tasks.SimpleBuildStep; import net.sf.json.JSONObject; import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -34,25 +37,33 @@ */ public class PlotBuilder extends Builder implements SimpleBuildStep { + // Required fields private final String group; - private final String title; - private final String numBuilds; - private final String yaxis; private final String style; - private final boolean useDescr; - private final boolean exclZero; - private final boolean logarithmic; - private final boolean keepRecords; - private final String yaxisMinimum; - private final String yaxisMaximum; + + // Optional fields + @CheckForNull + private String title; + @CheckForNull + private String numBuilds; + @CheckForNull + private String yaxis; + @CheckForNull + private String yaxisMinimum; + @CheckForNull + private String yaxisMaximum; + private boolean useDescr; + private boolean exclZero; + private boolean logarithmic; + private boolean keepRecords; + + // Generated? @SuppressWarnings("visibilitymodifier") public String csvFileName; /** * List of data series. */ @SuppressWarnings("visibilitymodifier") - public List series; - @SuppressWarnings("visibilitymodifier") public List csvSeries; @SuppressWarnings("visibilitymodifier") public List propertiesSeries; @@ -60,100 +71,135 @@ public class PlotBuilder extends Builder implements SimpleBuildStep { public List xmlSeries; // Fields in config.jelly must match the parameter names in the "DataBoundConstructor" - @SuppressWarnings("parameternumber") + // Similarly, any optional @DataBoundSetter properties must match @DataBoundConstructor - public PlotBuilder(String group, String title, String numBuilds, String yaxis, String style, - boolean useDescr, boolean exclZero, boolean logarithmic, boolean keepRecords, - String yaxisMinimum, String yaxisMaximum, String csvFileName, - List csvSeries, List propertiesSeries, - List xmlSeries) { + public PlotBuilder(String group, String style, String csvFileName) { this.group = group; - this.title = title; - this.numBuilds = numBuilds; - this.yaxis = yaxis; this.style = style; - this.useDescr = useDescr; - this.exclZero = exclZero; - this.logarithmic = logarithmic; - this.keepRecords = keepRecords; - this.yaxisMinimum = yaxisMinimum; - this.yaxisMaximum = yaxisMaximum; this.csvFileName = csvFileName; - this.csvSeries = csvSeries; - this.propertiesSeries = propertiesSeries; - this.xmlSeries = xmlSeries; - this.series = new ArrayList<>(); - if (csvSeries != null) { - this.series.addAll(csvSeries); - } - if (xmlSeries != null) { - this.series.addAll(xmlSeries); - } - if (propertiesSeries != null) { - this.series.addAll(propertiesSeries); - } } public String getGroup() { return group; } + public String getStyle() { + return style; + } + + @CheckForNull public String getTitle() { return title; } + @DataBoundSetter + public final void setTitle(@CheckForNull String title) { + this.title = Util.fixEmptyAndTrim(title); + } + + @CheckForNull public String getNumBuilds() { return numBuilds; } + @DataBoundSetter + public final void setNumBuilds(@CheckForNull String numBuilds) { + this.numBuilds = Util.fixEmptyAndTrim(numBuilds); + } + + @CheckForNull public String getYaxis() { return yaxis; } - public String getStyle() { - return style; + @DataBoundSetter + public final void setYaxis(@CheckForNull String yaxis) { + this.yaxis = Util.fixEmptyAndTrim(yaxis); } public boolean getUseDescr() { return useDescr; } + @DataBoundSetter + public void setUseDescr(boolean useDescr) { + this.useDescr = useDescr; + } + public boolean getExclZero() { return exclZero; } + @DataBoundSetter + public void setExclZero(boolean exclZero) { + this.exclZero = exclZero; + } + public boolean getLogarithmic() { return logarithmic; } + @DataBoundSetter + public void setLogarithmic(boolean logarithmic) { + this.logarithmic = logarithmic; + } + public boolean getKeepRecords() { return keepRecords; } + @DataBoundSetter + public void setKeepRecords(boolean keepRecords) { + this.keepRecords = keepRecords; + } + + @CheckForNull public String getYaxisMinimum() { return yaxisMinimum; } + @DataBoundSetter + public final void setYaxisMinimum(@CheckForNull String yaxisMinimum) { + this.yaxisMinimum = Util.fixEmptyAndTrim(yaxisMinimum); + } + + @CheckForNull public String getYaxisMaximum() { return yaxisMaximum; } - public List getSeries() { - return series; + @DataBoundSetter + public final void setYaxisMaximum(@CheckForNull String yaxisMaximum) { + this.yaxisMaximum = Util.fixEmptyAndTrim(yaxisMaximum); } public List getCsvSeries() { return csvSeries; } + @DataBoundSetter + public void setCsvSeries(List csvSeries) { + this.csvSeries = csvSeries; + } + public List getPropertiesSeries() { return propertiesSeries; } + @DataBoundSetter + public void setPropertiesSeries(List propertiesSeries) { + this.propertiesSeries = propertiesSeries; + } + public List getXmlSeries() { return xmlSeries; } + @DataBoundSetter + public void setXmlSeries(List xmlSeries) { + this.xmlSeries = xmlSeries; + } + @Override public void perform(Run build, FilePath workspace, Launcher launcher, TaskListener listener) { @@ -161,6 +207,18 @@ public void perform(Run build, FilePath workspace, Launcher launcher, Plot plot = new Plot(title, yaxis, group, numBuilds, csvFileName, style, useDescr, keepRecords, exclZero, logarithmic, yaxisMinimum, yaxisMaximum); + + List series = new ArrayList<>(); + if (csvSeries != null) { + series.addAll(csvSeries); + } + if (xmlSeries != null) { + series.addAll(xmlSeries); + } + if (propertiesSeries != null) { + series.addAll(propertiesSeries); + } + plot.series = series; plot.addBuild(build, listener.getLogger(), workspace); plots.add(plot); From 00da15d7f42b7e7904a1f897448c2d1fae9f9206 Mon Sep 17 00:00:00 2001 From: Ryan Styrczula Date: Sun, 3 Jun 2018 21:39:04 -0400 Subject: [PATCH 2/3] JENKINS-50636 Update required Jenkins version Pipeline support requires a minimum of 2.0 for newer Jenkins API functionality for subsequent unit tests. --- pom.xml | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ac78b093..ddac765e 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ - 1.625.3 + 2.0 7 @@ -51,13 +51,65 @@ org.jenkins-ci.plugins.workflow workflow-step-api - 2.1 + 2.12 net.sf.opencsv opencsv 1.7 + + + org.jenkins-ci.plugins.workflow + workflow-job + 2.10 + test + + + org.jenkins-ci.plugins.workflow + workflow-cps + 2.40 + test + + + org.jenkins-ci.plugins.workflow + workflow-basic-steps + 2.6 + test + + + org.jenkins-ci.plugins.workflow + workflow-durable-task-step + 2.8 + test + + + + + org.jenkins-ci.plugins + structs + 1.10 + + + org.jenkins-ci.plugins.workflow + workflow-support + 2.14 + test + + + org.jenkins-ci.plugins.workflow + workflow-api + 2.20 + test + + + + org.jenkins-ci.plugins.workflow + workflow-step-api + 2.12 + tests + test + From 6b0d3d299ad0daa2d6b165b2431220acceeafca9 Mon Sep 17 00:00:00 2001 From: Ryan Styrczula Date: Sun, 3 Jun 2018 21:41:36 -0400 Subject: [PATCH 3/3] JENKINS-50636 Cover PlotBuilder with tests --- .../hudson/plugins/plot/PlotBuilderTest.java | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 src/test/java/hudson/plugins/plot/PlotBuilderTest.java diff --git a/src/test/java/hudson/plugins/plot/PlotBuilderTest.java b/src/test/java/hudson/plugins/plot/PlotBuilderTest.java new file mode 100644 index 00000000..b2e7043a --- /dev/null +++ b/src/test/java/hudson/plugins/plot/PlotBuilderTest.java @@ -0,0 +1,95 @@ +package hudson.plugins.plot; + +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + + +public class PlotBuilderTest { + + @Rule + public JenkinsRule r = new JenkinsRule(); + + @Test + public void testWithMinimalPipelineArgs() throws Exception { + WorkflowJob p = r.createProject(WorkflowJob.class, "projectUnderTest"); + p.setDefinition(new CpsFlowDefinition( + "node { \n" + + " plot csvFileName: 'plot-minimal.csv',\n" + + " group: 'My Data',\n" + + " style: 'line'\n" + + "}", + true)); + r.buildAndAssertSuccess(p); + } + + @Test + public void testWithXML() throws Exception { + WorkflowJob p = r.createProject(WorkflowJob.class, "projectUnderTest"); + p.setDefinition(new CpsFlowDefinition( + "node { \n" + + " sh '''\n" + + " echo '' > data.xml\n" + + " echo ' ' >> data.xml\n" + + " echo ' ' >> data.xml\n" + + " echo '' >> data.xml\n" + + " '''\n" + + " plot csvFileName: 'plot-xml.csv',\n" + + " group: 'My Data',\n" + + " title: 'Useful Title',\n" + + " style: 'line',\n" + + " yaxis: 'arbitrary',\n" + + " xmlSeries: [\n" + + " [file: 'data.xml',\n" + + " nodeType: 'NODESET',\n" + + " xpath: 'my_data/test/@*']\n" + + " ]\n" + + "}", + true)); + r.buildAndAssertSuccess(p); + } + + @Test + public void testWithCSV() throws Exception { + WorkflowJob p = r.createProject(WorkflowJob.class, "projectUnderTest"); + p.setDefinition(new CpsFlowDefinition( + "node { \n" + + " sh '''\n" + + " echo 'Avg,Median,90,min,max,samples,errors,error %' > data.csv\n" + + " echo '515.33,196,1117,2,16550,97560,360,0.37' >> data.csv\n" + + " '''\n" + + " plot csvFileName: 'plot-csv.csv',\n" + + " group: 'My Data',\n" + + " title: 'Useful Title',\n" + + " style: 'line',\n" + + " yaxis: 'arbitrary',\n" + + " csvSeries: [[file: 'data.csv']]\n" + + "}", + true)); + r.buildAndAssertSuccess(p); + } + + @Test + public void testWithProperties() throws Exception { + WorkflowJob p = r.createProject(WorkflowJob.class, "projectUnderTest"); + p.setDefinition(new CpsFlowDefinition( + "node { \n" + + " sh '''\n" + + " echo 'YVALUE=1' > data.properties\n" + + " '''\n" + + " plot csvFileName: 'plot-properties.csv',\n" + + " group: 'My Data',\n" + + " title: 'Useful Title',\n" + + " style: 'line',\n" + + " yaxis: 'arbitrary',\n" + + " propertiesSeries: [\n" + + " [file: 'data.properties',\n" + + " label: 'My Label']\n" + + " ]\n" + + "}", + true)); + r.buildAndAssertSuccess(p); + } +}