From 3c102076876d26ee387045214c03f70d2ee6d5fa Mon Sep 17 00:00:00 2001 From: Allen Benz Date: Fri, 29 Jan 2021 01:29:31 -0800 Subject: [PATCH 1/8] Add feature to optionally include causes in the JCasC export --- .../jenkins/plugins/bfa/PluginImpl.java | 57 +++++++++++ .../plugins/bfa/model/FailureCause.java | 38 +++++++- .../bfa/model/FailureCauseModification.java | 17 ++++ .../model/indication/BuildLogIndication.java | 2 + .../MultilineBuildLogIndication.java | 2 + .../plugins/bfa/PluginImpl/config.jelly | 3 + .../plugins/bfa/PluginImpl/config.properties | 1 + .../jcasc/ConfigurationAsCodeLocalTest.java | 94 ++++++++++++++++++- .../bfa/jcasc/jcasc-local-expected.yml | 14 +++ .../plugins/bfa/jcasc/jcasc-local-less.yml | 13 +++ .../jcasc/jcasc-local-no-export-expected.yml | 19 ++++ .../bfa/jcasc/jcasc-local-no-export.yml | 31 ++++++ .../jenkins/plugins/bfa/jcasc/jcasc-local.yml | 12 +++ 13 files changed, 298 insertions(+), 5 deletions(-) create mode 100644 src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-less.yml create mode 100644 src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export-expected.yml create mode 100644 src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export.yml diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java index 19aff29e..51e90291 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java @@ -58,6 +58,7 @@ import javax.annotation.Nonnull; import java.io.File; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.logging.Level; @@ -155,6 +156,7 @@ public class PluginImpl extends GlobalConfiguration { private String fallbackCategoriesAsString; private transient List fallbackCategories; + private Boolean exportCausesToJCasCEnabled; private transient CopyOnWriteList causes; private KnowledgeBase knowledgeBase; @@ -629,6 +631,61 @@ public void setSlackFailureCategories(String slackFailureCategories) { this.slackFailureCategories = slackFailureCategories; } + /** + * Get if causes from the current knowledge base should be included in the JCasC export is enabled. + * @return exportCausesToJCasCEnabled - on or off. null == off. + */ + public Boolean getExportCausesToJCasCEnabled() { + return exportCausesToJCasCEnabled; + } + + /** + * Set if this feature is enabled or not. When on, causes will in the JCasC export. + * @param exportCausesToJCasCEnabled + */ + @DataBoundSetter + public void setExportCausesToJCasCEnabled(Boolean exportCausesToJCasCEnabled) { + this.exportCausesToJCasCEnabled = exportCausesToJCasCEnabled; + } + + /** + * Set the causes. + * + * @param causes value + */ + @DataBoundSetter + public void setCauses(List causes) { + if (knowledgeBase != null) { + for (FailureCause cause:causes) { + try { + knowledgeBase.saveCause(cause); + } catch (Exception e) { + logger.log(Level.WARNING, null, e); + } + } + } + } + + /** + * Get the causes, used by JCasC to include it in the configuration export. + * + * @return the causes + */ + public Collection getCauses() { + // The Causes export can be very large so we only include it if enabled. + if (exportCausesToJCasCEnabled == null || !exportCausesToJCasCEnabled) { + return null; + } + if (knowledgeBase != null) { + try { + return knowledgeBase.getCauses(); + } catch (Exception e) { + return null; + } + } + return null; + } + /** * The number of threads to have in the pool for each build. Used by the {@link BuildFailureScanner}. * Will return nothing less than {@link #MINIMUM_NR_OF_SCAN_THREADS}. diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java index a1b84cee..dc8fb682 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java @@ -46,6 +46,7 @@ import jenkins.model.Jenkins; import net.sf.json.JSONObject; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerRequest; @@ -86,13 +87,26 @@ public class FailureCause implements Serializable, Action, Describable indications) { + this(id, name, description, null, null, (List)null, + indications, null); + } + + /** + * Standard constructor. + * + * @param id the id. + * @param name the name of this FailureCause. + * @param description the description of this FailureCause. * @param comment the comment of this FailureCause. * @param lastOccurred the time at which this FailureCause last occurred. * @param categories the categories of this FailureCause. * @param indications the list of indications * @param modifications the modification history of this FailureCause. */ - @DataBoundConstructor public FailureCause(String id, String name, String description, String comment, Date lastOccurred, String categories, List indications, List modifications) { @@ -401,6 +415,7 @@ public Date getAndInitiateLastOccurred() { * * @param lastOccurred the occurrence to set. */ + @DataBoundSetter public void setLastOccurred(Date lastOccurred) { if (lastOccurred == null) { this.lastOccurred = null; @@ -441,6 +456,26 @@ public List getCategories() { return categories; } + /** + * Setter for the comment. + * + * @param comment + */ + @DataBoundSetter + public void setComment(String comment) { + this.comment = comment; + } + + /** + * Setter for the FailureCauseModifications done to this FailureCause. + * + * @param modifications + */ + @DataBoundSetter + public void setModifications(List modifications) { + this.modifications = modifications; + } + /** * Returns the categories as a String, used for the view. * @@ -541,6 +576,7 @@ private void loadLastOccurred() { * * @param categories the categories. */ + @DataBoundSetter public void setCategories(List categories) { this.categories = categories; } diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java index 39156322..eb50452d 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCauseModification.java @@ -25,6 +25,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.kohsuke.stapler.DataBoundConstructor; import java.io.Serializable; import java.util.Date; @@ -54,6 +55,22 @@ public FailureCauseModification(@JsonProperty("user") String user, @JsonProperty } } + /** + * Constructor for FailureCauseModification. + * + * @param user The user who made the modification. + * @param time The time at which the modification was done. + */ + @DataBoundConstructor + public FailureCauseModification(String user, String time) { + this.user = user; + if (time == null) { + this.time = null; + } else { + this.time = new Date(time); + } + } + /** * Getter for the time. * diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/BuildLogIndication.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/BuildLogIndication.java index 6a17a0bd..b99bf211 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/BuildLogIndication.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/BuildLogIndication.java @@ -43,6 +43,7 @@ import hudson.model.Run; import hudson.util.FormValidation; import jenkins.model.Jenkins; +import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.interceptor.RequirePOST; @@ -59,6 +60,7 @@ */ @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class", visible = true) @JsonIgnoreProperties(ignoreUnknown = true) +@Symbol("buildLog") public class BuildLogIndication extends Indication { private static final long serialVersionUID = -2889792693081908532L; diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/MultilineBuildLogIndication.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/MultilineBuildLogIndication.java index 84b858c8..c68ff74c 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/MultilineBuildLogIndication.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/indication/MultilineBuildLogIndication.java @@ -31,6 +31,7 @@ import com.sonyericsson.jenkins.plugins.bfa.model.MultilineBuildLogFailureReader; import hudson.Extension; import hudson.model.Hudson; +import org.jenkinsci.Symbol; import org.kohsuke.stapler.DataBoundConstructor; import java.util.regex.Pattern; @@ -42,6 +43,7 @@ */ @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class", visible = true) @JsonIgnoreProperties(ignoreUnknown = true) +@Symbol("multilineBuildLog") public class MultilineBuildLogIndication extends BuildLogIndication { private static final long serialVersionUID = 8436383594898812087L; diff --git a/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.jelly b/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.jelly index 200791a6..72c2e433 100644 --- a/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.jelly +++ b/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.jelly @@ -92,5 +92,8 @@ + + + diff --git a/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.properties b/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.properties index 9e842444..d293f8c1 100644 --- a/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.properties +++ b/src/main/resources/com/sonyericsson/jenkins/plugins/bfa/PluginImpl/config.properties @@ -1,4 +1,5 @@ enableGerritTriggerDescription=This option allows BFA to forward the description of the found causes to the Gerrit-Trigger-plugin, ultimately allowing users to see their build issues directly inside Gerrit. +exportCausesToJCasCEnabled=If the knowledge base is large this will make the JCasC export large. nrOfScanThreadsDescription=Number of threads per build to use when scanning the failed builds. testResultParsingEnabledDescription=Treat failed test cases (as indicated by JUnit/xUnit/... publishers) as failure causes. testResultCategoriesDescription=A space-separated list of categories to use for failure causes representing failed test cases. diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeLocalTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeLocalTest.java index 8bca828b..f028bca7 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeLocalTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeLocalTest.java @@ -2,19 +2,29 @@ import com.sonyericsson.jenkins.plugins.bfa.PluginImpl; import com.sonyericsson.jenkins.plugins.bfa.db.LocalFileKnowledgeBase; +import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; +import com.sonyericsson.jenkins.plugins.bfa.model.FailureCauseModification; +import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication; +import com.sonyericsson.jenkins.plugins.bfa.model.indication.Indication; +import com.sonyericsson.jenkins.plugins.bfa.model.indication.MultilineBuildLogIndication; import com.sonyericsson.jenkins.plugins.bfa.sod.ScanOnDemandVariables; import io.jenkins.plugins.casc.ConfigurationContext; import io.jenkins.plugins.casc.ConfiguratorRegistry; import io.jenkins.plugins.casc.misc.ConfiguredWithCode; import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule; import io.jenkins.plugins.casc.model.CNode; -import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import java.util.ArrayList; +import java.util.List; + import static io.jenkins.plugins.casc.misc.Util.getUnclassifiedRoot; import static io.jenkins.plugins.casc.misc.Util.toStringFromYamlFile; import static io.jenkins.plugins.casc.misc.Util.toYamlString; import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.hamcrest.core.IsNull.nullValue; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.assertThat; @@ -31,18 +41,28 @@ public class ConfigurationAsCodeLocalTest { static final int EXPECTED_SOD_THREAD_KEEP_ALIVE_TIME = 17; static final int EXPECTED_SOD_JOB_SHUTDOWN_TIMEOUT = 32; + static final String EXPECTED_ID = "035e67d4-6692-4b1f-adf9-95c43948b349"; + static final String EXPECTED_DESCRIPTION = "A problem was found"; + static final String EXPECTED_COMMENT = "To show what a cause looks like"; + static final String EXPECTED_CATEGORIES = "example second"; + static final String EXPECTED_NAME = "Found problems"; + static final String EXPECTED_BUILD_LOG_EXPRESSION = ".*problem.*"; + static final String EXPECTED_MULTILINE_BUILD_LOG_EXPRESSION = "many.*problems"; + static final String EXPECTED_USER = "Somebody"; + static final long EXPECTED_MODIFICATION_TIME = 1611951507000L; + /** * Jenkins rule. */ - @ClassRule - @ConfiguredWithCode("jcasc-local.yml") + @Rule //CS IGNORE VisibilityModifier FOR NEXT 1 LINES. REASON: Jenkins Rule - public static JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule(); + public JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule(); /** * Support config as code import. */ @Test + @ConfiguredWithCode("jcasc-local.yml") public void shouldSupportConfigurationAsCode() { PluginImpl plugin = PluginImpl.getInstance(); @@ -65,6 +85,71 @@ public void shouldSupportConfigurationAsCode() { assertThat(plugin.getTestResultCategories(), is("hgjghhlllllaa")); assertThat(plugin.isTestResultParsingEnabled(), is(true)); + + List initialCauses = new ArrayList(plugin.getCauses()); + assertThat(initialCauses.size(), is(1)); + FailureCause cause = initialCauses.get(0); + assertThat(cause.getId(), is(EXPECTED_ID)); + assertThat(cause.getDescription(), is(EXPECTED_DESCRIPTION)); + assertThat(cause.getComment(), is(EXPECTED_COMMENT)); + assertThat(cause.getCategoriesAsString(), is(EXPECTED_CATEGORIES)); + assertThat(cause.getName(), is(EXPECTED_NAME)); + + List indications = cause.getIndications(); + assertThat(indications.size(), is(2)); + assertThat(indications.get(0), instanceOf(BuildLogIndication.class)); + assertThat(indications.get(1), instanceOf(MultilineBuildLogIndication.class)); + BuildLogIndication buildLog = (BuildLogIndication)indications.get(0); + assertThat(buildLog.getUserProvidedExpression(), is(EXPECTED_BUILD_LOG_EXPRESSION)); + MultilineBuildLogIndication multilineBuildLog = (MultilineBuildLogIndication)indications.get(1); + assertThat(multilineBuildLog.getUserProvidedExpression(), is(EXPECTED_MULTILINE_BUILD_LOG_EXPRESSION)); + } + + /** + * Support config as code import with a minimal definition. + */ + @Test + @ConfiguredWithCode("jcasc-local-less.yml") + public void shouldSupportConfigurationAsCodeWithLessCauseParameters() { + PluginImpl plugin = PluginImpl.getInstance(); + + List initialCauses = new ArrayList<>(plugin.getCauses()); + assertThat(initialCauses.size(), is(1)); + + FailureCause cause = initialCauses.get(0); + assertThat(cause.getId(), is(EXPECTED_ID)); + assertThat(cause.getDescription(), is(EXPECTED_DESCRIPTION)); + assertThat(cause.getComment(), is(nullValue())); + assertThat(cause.getCategoriesAsString(), is(nullValue())); + assertThat(cause.getName(), is(EXPECTED_NAME)); + + assertThat(cause.getIndications().size(), is(1)); + assertThat(cause.getIndications().get(0), instanceOf(BuildLogIndication.class)); + BuildLogIndication buildLog = (BuildLogIndication)cause.getIndications().get(0); + assertThat(buildLog.getUserProvidedExpression(), is(EXPECTED_BUILD_LOG_EXPRESSION)); + + assertThat(cause.getModifications().size(), is(1)); + FailureCauseModification modification = cause.getModifications().get(0); + assertThat(modification.getTime(), is(notNullValue())); + assertThat(modification.getTime().getTime(), is(EXPECTED_MODIFICATION_TIME)); + assertThat(modification.getUser(), is(EXPECTED_USER)); + } + + /** + * Not enabling causes export prevents causes from being exported. + */ + @Test + @ConfiguredWithCode("jcasc-local-no-export.yml") + public void shouldNotExportCausesIfFeatureIsNotEnabled() throws Exception { + ConfiguratorRegistry registry = ConfiguratorRegistry.get(); + ConfigurationContext context = new ConfigurationContext(registry); + CNode yourAttribute = getUnclassifiedRoot(context).get("buildFailureAnalyzer"); + + String exported = toYamlString(yourAttribute); + + String expected = toStringFromYamlFile(this, "jcasc-local-no-export-expected.yml"); + + assertThat(exported, is(expected)); } /** @@ -73,6 +158,7 @@ public void shouldSupportConfigurationAsCode() { * @throws Exception if so. */ @Test + @ConfiguredWithCode("jcasc-local.yml") public void shouldSupportConfigurationExport() throws Exception { ConfiguratorRegistry registry = ConfiguratorRegistry.get(); ConfigurationContext context = new ConfigurationContext(registry); diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-expected.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-expected.yml index 5990751a..d5377fc2 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-expected.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-expected.yml @@ -1,4 +1,18 @@ +causes: +- categories: + - "example" + - "second" + comment: "To show what a cause looks like" + description: "A problem was found" + id: "035e67d4-6692-4b1f-adf9-95c43948b349" + indications: + - buildLog: + pattern: ".*problem.*" + - multilineBuildLog: + pattern: "(?m)(?s)^[^\\r\\n]*?many.*problems[^\\r\\n]*?$" + name: "Found problems" doNotAnalyzeAbortedJob: true +exportCausesToJCasCEnabled: true gerritTriggerEnabled: true globalEnabled: true graphsEnabled: false diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-less.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-less.yml new file mode 100644 index 00000000..20cc6078 --- /dev/null +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-less.yml @@ -0,0 +1,13 @@ +unclassified: + buildFailureAnalyzer: + causes: + - description: "A problem was found" + id: "035e67d4-6692-4b1f-adf9-95c43948b349" + indications: + - buildLog: + pattern: ".*problem.*" + name: "Found problems" + modifications: + - time: "Fri Jan 29 20:18:27 GMT 2021" + user: "Somebody" + exportCausesToJCasCEnabled: true diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export-expected.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export-expected.yml new file mode 100644 index 00000000..5990751a --- /dev/null +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export-expected.yml @@ -0,0 +1,19 @@ +doNotAnalyzeAbortedJob: true +gerritTriggerEnabled: true +globalEnabled: true +graphsEnabled: false +knowledgeBase: "localFile" +maxLogSize: 10 +noCausesEnabled: true +noCausesMessage: "No problems were identified. Please contribute causes to help others" +nrOfScanThreads: 6 +slackFailureCategories: "ALL" +slackNotifEnabled: false +sodVariables: + maximumSodWorkerThreads: 4 + minimumSodWorkerThreads: 2 + sodCorePoolNumberOfThreads: 6 + sodThreadKeepAliveTime: 17 + sodWaitForJobShutdownTimeout: 32 +testResultCategories: "hgjghhlllllaa" +testResultParsingEnabled: true diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export.yml new file mode 100644 index 00000000..fc04c29c --- /dev/null +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export.yml @@ -0,0 +1,31 @@ +unclassified: + buildFailureAnalyzer: + causes: + - categories: [ "example", "second" ] + comment: "To show what a cause looks like" + description: "A problem was found" + id: "035e67d4-6692-4b1f-adf9-95c43948b349" + indications: + - buildLog: + pattern: ".*problem.*" + - multilineBuildLog: + pattern: "many.*problems" + name: "Found problems" + doNotAnalyzeAbortedJob: true + gerritTriggerEnabled: true + globalEnabled: true + graphsEnabled: false + knowledgeBase: "localFile" + maxLogSize: 10 + noCausesEnabled: true + noCausesMessage: "No problems were identified. Please contribute causes to help others" + nrOfScanThreads: 6 + slackNotifEnabled: false + sodVariables: + maximumSodWorkerThreads: 4 + minimumSodWorkerThreads: 2 + sodCorePoolNumberOfThreads: 6 + sodThreadKeepAliveTime: 17 + sodWaitForJobShutdownTimeout: 32 + testResultCategories: "hgjghhlllllaa" + testResultParsingEnabled: true \ No newline at end of file diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local.yml index 632ab023..83c3b051 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local.yml @@ -1,6 +1,18 @@ unclassified: buildFailureAnalyzer: + causes: + - categories: [ "example", "second" ] + comment: "To show what a cause looks like" + description: "A problem was found" + id: "035e67d4-6692-4b1f-adf9-95c43948b349" + indications: + - buildLog: + pattern: ".*problem.*" + - multilineBuildLog: + pattern: "many.*problems" + name: "Found problems" doNotAnalyzeAbortedJob: true + exportCausesToJCasCEnabled: true gerritTriggerEnabled: true globalEnabled: true graphsEnabled: false From 856aee4870f83aa0af09a68fd4f2e1ef256968eb Mon Sep 17 00:00:00 2001 From: Allen Benz Date: Fri, 29 Jan 2021 17:43:41 -0800 Subject: [PATCH 2/8] satisfy javadoc lints. --- .../java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java | 3 ++- .../sonyericsson/jenkins/plugins/bfa/model/FailureCause.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java index 51e90291..4bf25301 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java @@ -641,7 +641,8 @@ public Boolean getExportCausesToJCasCEnabled() { /** * Set if this feature is enabled or not. When on, causes will in the JCasC export. - * @param exportCausesToJCasCEnabled + * + * @param exportCausesToJCasCEnabled on or off. null == off. */ @DataBoundSetter public void setExportCausesToJCasCEnabled(Boolean exportCausesToJCasCEnabled) { diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java index dc8fb682..21cd1727 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/model/FailureCause.java @@ -459,7 +459,7 @@ public List getCategories() { /** * Setter for the comment. * - * @param comment + * @param comment the comment */ @DataBoundSetter public void setComment(String comment) { @@ -469,7 +469,7 @@ public void setComment(String comment) { /** * Setter for the FailureCauseModifications done to this FailureCause. * - * @param modifications + * @param modifications the modifications */ @DataBoundSetter public void setModifications(List modifications) { From 6be3f41fd5d6bb265381dbd4ff17e3f46ff3e0f7 Mon Sep 17 00:00:00 2001 From: Allen Benz Date: Sat, 10 Apr 2021 12:30:33 -0700 Subject: [PATCH 3/8] Use boolean instead of Boolean for exportCausesToJCasCEnabled --- .../com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java | 8 ++++---- .../plugins/bfa/jcasc/jcasc-local-no-export-expected.yml | 1 + .../jenkins/plugins/bfa/jcasc/jcasc-local-no-export.yml | 2 +- .../jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java index 4bf25301..77f469eb 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java @@ -156,7 +156,7 @@ public class PluginImpl extends GlobalConfiguration { private String fallbackCategoriesAsString; private transient List fallbackCategories; - private Boolean exportCausesToJCasCEnabled; + private boolean exportCausesToJCasCEnabled; private transient CopyOnWriteList causes; private KnowledgeBase knowledgeBase; @@ -635,7 +635,7 @@ public void setSlackFailureCategories(String slackFailureCategories) { * Get if causes from the current knowledge base should be included in the JCasC export is enabled. * @return exportCausesToJCasCEnabled - on or off. null == off. */ - public Boolean getExportCausesToJCasCEnabled() { + public boolean getExportCausesToJCasCEnabled() { return exportCausesToJCasCEnabled; } @@ -645,7 +645,7 @@ public Boolean getExportCausesToJCasCEnabled() { * @param exportCausesToJCasCEnabled on or off. null == off. */ @DataBoundSetter - public void setExportCausesToJCasCEnabled(Boolean exportCausesToJCasCEnabled) { + public void setExportCausesToJCasCEnabled(boolean exportCausesToJCasCEnabled) { this.exportCausesToJCasCEnabled = exportCausesToJCasCEnabled; } @@ -674,7 +674,7 @@ public void setCauses(List causes) { */ public Collection getCauses() { // The Causes export can be very large so we only include it if enabled. - if (exportCausesToJCasCEnabled == null || !exportCausesToJCasCEnabled) { + if (!getExportCausesToJCasCEnabled()) { return null; } if (knowledgeBase != null) { diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export-expected.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export-expected.yml index 5990751a..c8b7da3e 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export-expected.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export-expected.yml @@ -1,4 +1,5 @@ doNotAnalyzeAbortedJob: true +exportCausesToJCasCEnabled: false gerritTriggerEnabled: true globalEnabled: true graphsEnabled: false diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export.yml index fc04c29c..fa066d4f 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-local-no-export.yml @@ -28,4 +28,4 @@ unclassified: sodThreadKeepAliveTime: 17 sodWaitForJobShutdownTimeout: 32 testResultCategories: "hgjghhlllllaa" - testResultParsingEnabled: true \ No newline at end of file + testResultParsingEnabled: true diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml index 4f30360b..33e06482 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml @@ -1,4 +1,5 @@ doNotAnalyzeAbortedJob: true +exportCausesToJCasCEnabled: false gerritTriggerEnabled: true globalEnabled: true graphsEnabled: false From 4a864a12c141e7d6eb8cc93391caf75e4ba7b29b Mon Sep 17 00:00:00 2001 From: Allen Benz Date: Sat, 10 Apr 2021 12:30:46 -0700 Subject: [PATCH 4/8] Better logging for get/setCauses exception --- .../java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java index 77f469eb..348f05c7 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java @@ -661,7 +661,7 @@ public void setCauses(List causes) { try { knowledgeBase.saveCause(cause); } catch (Exception e) { - logger.log(Level.WARNING, null, e); + logger.log(Level.WARNING, "Failed to save cause to the knowledge base", e); } } } @@ -681,6 +681,7 @@ public Collection getCauses() { try { return knowledgeBase.getCauses(); } catch (Exception e) { + logger.log(Level.WARNING, "Failed to get causes from the knowledge base", e); return null; } } From f544c1c81ffde03b084cd526d64b0cc696303b0f Mon Sep 17 00:00:00 2001 From: Allen Benz Date: Thu, 30 Sep 2021 16:57:47 -0700 Subject: [PATCH 5/8] get -> is, works with JCasC and is to the java bean standard --- .../java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java index 348f05c7..2be69152 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java @@ -635,7 +635,7 @@ public void setSlackFailureCategories(String slackFailureCategories) { * Get if causes from the current knowledge base should be included in the JCasC export is enabled. * @return exportCausesToJCasCEnabled - on or off. null == off. */ - public boolean getExportCausesToJCasCEnabled() { + public boolean isExportCausesToJCasCEnabled() { return exportCausesToJCasCEnabled; } @@ -674,7 +674,7 @@ public void setCauses(List causes) { */ public Collection getCauses() { // The Causes export can be very large so we only include it if enabled. - if (!getExportCausesToJCasCEnabled()) { + if (!isExportCausesToJCasCEnabled()) { return null; } if (knowledgeBase != null) { From 2757966f68e56eafde1b2314495287e7f4a8f512 Mon Sep 17 00:00:00 2001 From: Allen Benz Date: Thu, 30 Sep 2021 16:58:32 -0700 Subject: [PATCH 6/8] Be clear that setCauses duplicates causes if ids are absent. --- .../java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java index 2be69152..cd8f99f6 100644 --- a/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java +++ b/src/main/java/com/sonyericsson/jenkins/plugins/bfa/PluginImpl.java @@ -650,7 +650,8 @@ public void setExportCausesToJCasCEnabled(boolean exportCausesToJCasCEnabled) { } /** - * Set the causes. + * Save causes to the db. Assumes that the ids are kept from when the causes were fetched. + * Failing to do so may result in duplicate causes. * * @param causes value */ From b35c395c0751d3aee0fb2b4535fed8192e298169 Mon Sep 17 00:00:00 2001 From: Allen Benz Date: Thu, 30 Sep 2021 17:09:28 -0700 Subject: [PATCH 7/8] Add JCasC export causes tests for mongodb --- .../jcasc/ConfigurationAsCodeLocalTest.java | 2 +- .../jcasc/ConfigurationAsCodeMongoTest.java | 70 +++++++++++++++++-- .../plugins/bfa/jcasc/jcasc-mongo-less.yml | 22 ++++++ .../jcasc/jcasc-mongo-no-export-expected.yml | 28 ++++++++ .../bfa/jcasc/jcasc-mongo-no-export.yml | 39 +++++++++++ 5 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-less.yml create mode 100644 src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export-expected.yml create mode 100644 src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export.yml diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeLocalTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeLocalTest.java index f028bca7..679a880e 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeLocalTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeLocalTest.java @@ -26,7 +26,7 @@ import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; import static org.hamcrest.core.IsInstanceOf.instanceOf; -import static org.junit.Assert.assertThat; +import static org.hamcrest.MatcherAssert.assertThat; /** * Checks configuration as code integration for local DB. diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeMongoTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeMongoTest.java index 49ead71d..60f0765c 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeMongoTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeMongoTest.java @@ -2,20 +2,29 @@ import com.sonyericsson.jenkins.plugins.bfa.PluginImpl; import com.sonyericsson.jenkins.plugins.bfa.db.MongoDBKnowledgeBase; +import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication; +import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; +import com.sonyericsson.jenkins.plugins.bfa.model.FailureCauseModification; import com.sonyericsson.jenkins.plugins.bfa.sod.ScanOnDemandVariables; import io.jenkins.plugins.casc.ConfigurationContext; import io.jenkins.plugins.casc.ConfiguratorRegistry; import io.jenkins.plugins.casc.misc.ConfiguredWithCode; import io.jenkins.plugins.casc.misc.JenkinsConfiguredWithCodeRule; import io.jenkins.plugins.casc.model.CNode; -import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import java.util.ArrayList; +import java.util.List; + import static io.jenkins.plugins.casc.misc.Util.getUnclassifiedRoot; import static io.jenkins.plugins.casc.misc.Util.toStringFromYamlFile; import static io.jenkins.plugins.casc.misc.Util.toYamlString; import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertThat; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.hamcrest.core.IsNull.notNullValue; +import static org.hamcrest.core.IsNull.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; /** * Checks configuration as code integration for mongo DB. @@ -25,15 +34,15 @@ public class ConfigurationAsCodeMongoTest { /** * Jenkins rule. */ - @ClassRule - @ConfiguredWithCode("jcasc-mongo.yml") + @Rule //CS IGNORE VisibilityModifier FOR NEXT 1 LINES. REASON: Jenkins Rule - public static JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule(); + public JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule(); /** * Support config as code import. */ @Test + @ConfiguredWithCode("jcasc-mongo.yml") public void shouldSupportConfigurationAsCode() { PluginImpl plugin = PluginImpl.getInstance(); @@ -77,6 +86,7 @@ public void shouldSupportConfigurationAsCode() { * @throws Exception if so. */ @Test + @ConfiguredWithCode("jcasc-mongo.yml") public void shouldSupportConfigurationExport() throws Exception { ConfiguratorRegistry registry = ConfiguratorRegistry.get(); ConfigurationContext context = new ConfigurationContext(registry); @@ -89,4 +99,54 @@ public void shouldSupportConfigurationExport() throws Exception { assertThat(exported, is(expected)); } + + /** + * Support config as code import with a minimal definition. + */ + @Test + @ConfiguredWithCode("jcasc-mongo-less.yml") + public void shouldSupportConfigurationAsCodeWithLessCauseParameters() { + PluginImpl plugin = PluginImpl.getInstance(); + + MongoDBKnowledgeBase knowledgeBase = (MongoDBKnowledgeBase)plugin.getKnowledgeBase(); + + List initialCauses = new ArrayList<>(knowledgeBase.getCauses()); + assertThat(initialCauses.size(), is(1)); + + FailureCause cause = initialCauses.get(0); + assertThat(cause.getId(), is(ConfigurationAsCodeLocalTest.EXPECTED_ID)); + assertThat(cause.getDescription(), is(ConfigurationAsCodeLocalTest.EXPECTED_DESCRIPTION)); + assertThat(cause.getComment(), is(nullValue())); + assertThat(cause.getCategoriesAsString(), is(nullValue())); + assertThat(cause.getName(), is(ConfigurationAsCodeLocalTest.EXPECTED_NAME)); + + assertThat(cause.getIndications().size(), is(1)); + assertThat(cause.getIndications().get(0), instanceOf(BuildLogIndication.class)); + BuildLogIndication buildLog = (BuildLogIndication)cause.getIndications().get(0); + assertThat(buildLog.getUserProvidedExpression(), is(ConfigurationAsCodeLocalTest.EXPECTED_BUILD_LOG_EXPRESSION)); + + assertThat(cause.getModifications().size(), is(1)); + FailureCauseModification modification = cause.getModifications().get(0); + assertThat(modification.getTime(), is(notNullValue())); + assertThat(modification.getTime().getTime(), is(ConfigurationAsCodeLocalTest.EXPECTED_MODIFICATION_TIME)); + assertThat(modification.getUser(), is(ConfigurationAsCodeLocalTest.EXPECTED_USER)); + } + + /** + * Not enabling causes export prevents causes from being exported. + */ + @Test + @ConfiguredWithCode("jcasc-mongo-no-export.yml") + public void shouldNotExportCausesIfFeatureIsNotEnabled() throws Exception { + ConfiguratorRegistry registry = ConfiguratorRegistry.get(); + ConfigurationContext context = new ConfigurationContext(registry); + CNode yourAttribute = getUnclassifiedRoot(context).get("buildFailureAnalyzer"); + + String exported = toYamlString(yourAttribute) + .replaceAll(".+password: .+", ""); // ignore dynamic password secret + + String expected = toStringFromYamlFile(this, "jcasc-mongo-no-export-expected.yml"); + + assertThat(exported, is(expected)); + } } diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-less.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-less.yml new file mode 100644 index 00000000..aad9eaf1 --- /dev/null +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-less.yml @@ -0,0 +1,22 @@ +unclassified: + buildFailureAnalyzer: + knowledgeBase: + mongoDB: + dbName: "bfa" + enableStatistics: true + host: "localhost" + userName: bfa + password: changeme + port: 27017 + successfulLogging: false + causes: + - description: "A problem was found" + id: "035e67d4-6692-4b1f-adf9-95c43948b349" + indications: + - buildLog: + pattern: ".*problem.*" + name: "Found problems" + modifications: + - time: "Fri Jan 29 20:18:27 GMT 2021" + user: "Somebody" + exportCausesToJCasCEnabled: true diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export-expected.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export-expected.yml new file mode 100644 index 00000000..33e06482 --- /dev/null +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export-expected.yml @@ -0,0 +1,28 @@ +doNotAnalyzeAbortedJob: true +exportCausesToJCasCEnabled: false +gerritTriggerEnabled: true +globalEnabled: true +graphsEnabled: false +knowledgeBase: + mongoDB: + dbName: "bfa" + enableStatistics: true + host: "localhost" + + port: 27017 + successfulLogging: false + userName: "bfa" +maxLogSize: 10 +noCausesEnabled: true +noCausesMessage: "No problems were identified. Please contribute causes to help others" +nrOfScanThreads: 6 +slackFailureCategories: "ALL" +slackNotifEnabled: false +sodVariables: + maximumSodWorkerThreads: 4 + minimumSodWorkerThreads: 2 + sodCorePoolNumberOfThreads: 6 + sodThreadKeepAliveTime: 17 + sodWaitForJobShutdownTimeout: 32 +testResultCategories: "hgjghhlllllaa" +testResultParsingEnabled: true diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export.yml new file mode 100644 index 00000000..846c004c --- /dev/null +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export.yml @@ -0,0 +1,39 @@ +unclassified: + buildFailureAnalyzer: + causes: + - categories: [ "example", "second" ] + comment: "To show what a cause looks like" + description: "A problem was found" + id: "035e67d4-6692-4b1f-adf9-95c43948b349" + indications: + - buildLog: + pattern: ".*problem.*" + - multilineBuildLog: + pattern: "many.*problems" + name: "Found problems" + doNotAnalyzeAbortedJob: true + gerritTriggerEnabled: true + globalEnabled: true + graphsEnabled: false + knowledgeBase: + mongoDB: + dbName: "bfa" + enableStatistics: true + host: "localhost" + userName: bfa + password: changeme + port: 27017 + successfulLogging: false + maxLogSize: 10 + noCausesEnabled: true + noCausesMessage: "No problems were identified. Please contribute causes to help others" + nrOfScanThreads: 6 + slackNotifEnabled: false + sodVariables: + maximumSodWorkerThreads: 4 + minimumSodWorkerThreads: 2 + sodCorePoolNumberOfThreads: 6 + sodThreadKeepAliveTime: 17 + sodWaitForJobShutdownTimeout: 32 + testResultCategories: "hgjghhlllllaa" + testResultParsingEnabled: true From 39d287ff26431604fd14c93ed8bb19d77471c9c0 Mon Sep 17 00:00:00 2001 From: Allen Benz Date: Thu, 30 Sep 2021 19:33:32 -0700 Subject: [PATCH 8/8] Run tests in ConfigurationAsCodeMongotTest with an embedded mongodb external resource. --- .../plugins/bfa/db/EmbeddedMongoRule.java | 175 ++++++++++++++++++ .../jcasc/ConfigurationAsCodeMongoTest.java | 41 +++- .../bfa/jcasc/jcasc-mongo-expected.yml | 17 +- .../plugins/bfa/jcasc/jcasc-mongo-less.yml | 4 +- .../jcasc/jcasc-mongo-no-export-expected.yml | 2 +- .../bfa/jcasc/jcasc-mongo-no-export.yml | 4 +- .../jenkins/plugins/bfa/jcasc/jcasc-mongo.yml | 14 +- 7 files changed, 244 insertions(+), 13 deletions(-) create mode 100644 src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/EmbeddedMongoRule.java diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/EmbeddedMongoRule.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/EmbeddedMongoRule.java new file mode 100644 index 00000000..ca83bd59 --- /dev/null +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/db/EmbeddedMongoRule.java @@ -0,0 +1,175 @@ +/* + * The MIT License + * + * Copyright 2013 Sony Mobile Communications AB. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.sonyericsson.jenkins.plugins.bfa.db; + +import de.flapdoodle.embed.mongo.Command; +import de.flapdoodle.embed.mongo.MongodExecutable; +import de.flapdoodle.embed.mongo.MongodProcess; +import de.flapdoodle.embed.mongo.MongodStarter; +import de.flapdoodle.embed.mongo.config.ArtifactStoreBuilder; +import de.flapdoodle.embed.mongo.config.DownloadConfigBuilder; +import de.flapdoodle.embed.mongo.config.IMongodConfig; +import de.flapdoodle.embed.mongo.config.MongodConfigBuilder; +import de.flapdoodle.embed.mongo.config.Net; +import de.flapdoodle.embed.mongo.config.RuntimeConfigBuilder; +import de.flapdoodle.embed.mongo.distribution.Version; +import de.flapdoodle.embed.process.config.IRuntimeConfig; +import de.flapdoodle.embed.process.runtime.Network; +import hudson.util.Secret; + +import org.junit.After; +import org.junit.Before; + +import java.io.IOException; +import java.util.Collections; + +import com.mongodb.BasicDBObject; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; + +/** + * A JUnit rule that can be used for running tests with Embedded MongoDB. + * A knowledgeBase is created during setUp, which is backed up by a + * real MongoDB instance. + * + * @author Fredrik Persson <fredrik6.persson@sonymobile.com> + * + */ +public class EmbeddedMongoRule extends org.junit.rules.ExternalResource { + + /** + * @return KnowledgeBase to be used for testing. + */ + public KnowledgeBase getKnowledgeBase() { + return knowledgeBase; + }; + private KnowledgeBase knowledgeBase; + + private static final String LOCALHOST = "127.0.0.1"; + private String dbName; + private String username; + private String password; + + private static String mongoURL = System.getProperty(EmbeddedMongoRule.class.getName() + ".mongoURL"); + + private MongodExecutable mongodExe = null; + private MongodProcess mongodProc = null; + /** + * @return Port used by embedded mongo db + */ + public int getPort() { + return mongoPort; + }; + private int mongoPort = 0; + + /** + * Use an embedded mongo db with a random port and jenkinsbfa as the db name. + */ + public EmbeddedMongoRule() { + this(0, "jenkinsbfa", null, null); + } + + /** + * Use an embedded mongo db with the given settings. + * @param port desired port of the mongodb, use 0 for a random port + * @param dbName name of the database to use when creating the user + * @param username a username to create in the db + * @param password the password to use for the new user + */ + public EmbeddedMongoRule(int port, String dbName, String username, String password) { + mongoPort = port; + this.dbName = dbName; + this.username = username; + this.password = password; + } + + /** + * Sets up an instance of {@link MongoDBKnowledgeBase} backed up by a real MongoDB, to be used for testing. + * @throws IOException if something goes wrong + */ + @Before + public void before() throws IOException { + MongodStarter runtime; + if (mongoURL != null) { + // Use separate URL for fetching mongoDB artifacts. + Command command = Command.MongoD; + de.flapdoodle.embed.process.config.store.DownloadConfigBuilder downloadConf = new DownloadConfigBuilder() + .defaultsForCommand(command).downloadPath(mongoURL); + de.flapdoodle.embed.process.store.ArtifactStoreBuilder artifactStoreBuilder = new ArtifactStoreBuilder() + .defaults(command).download(downloadConf); + + IRuntimeConfig runtimeConfig = new RuntimeConfigBuilder() + .defaults(command) + .artifactStore(artifactStoreBuilder) + .build(); + + runtime = MongodStarter.getInstance(runtimeConfig); + } else { + runtime = MongodStarter.getDefaultInstance(); + } + + MongodConfigBuilder confBuilder = new MongodConfigBuilder() + .version(Version.Main.V3_4); + + if (mongoPort != 0) { + confBuilder = confBuilder.net(new Net(mongoPort, Network.localhostIsIPv6())); + } + + IMongodConfig conf = confBuilder.build(); + + mongodExe = runtime.prepare(conf); + mongodProc = mongodExe.start(); + mongoPort = conf.net().getPort(); + + // even if authentication isn't enabled, connecting with a username requires it to exist + if (username != null && password != null) { + try (MongoClient client = MongoClients.create(String.format("mongodb://localhost:%d", mongoPort))) { + client + .getDatabase(dbName) + .runCommand( + new BasicDBObject("createUser", username) + .append("pwd", password) + .append("roles", + Collections.singletonList(new BasicDBObject("role", "dbOwner").append("db", dbName)))); + } + } + + Secret encryptedPassword = null; + if (password != null) { + encryptedPassword = Secret.fromString(password); + } + knowledgeBase = new MongoDBKnowledgeBase(LOCALHOST, mongoPort, dbName, username, encryptedPassword, true, false); + } + + /** + * Tears down the test environment. + */ + @After + public void after() { + if (this.mongodProc != null) { + this.mongodProc.stop(); + this.mongodExe.stop(); + } + } +} diff --git a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeMongoTest.java b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeMongoTest.java index 60f0765c..887b2d19 100644 --- a/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeMongoTest.java +++ b/src/test/java/com/sonyericsson/jenkins/plugins/bfa/jcasc/ConfigurationAsCodeMongoTest.java @@ -2,7 +2,10 @@ import com.sonyericsson.jenkins.plugins.bfa.PluginImpl; import com.sonyericsson.jenkins.plugins.bfa.db.MongoDBKnowledgeBase; +import com.sonyericsson.jenkins.plugins.bfa.db.EmbeddedMongoRule; import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication; +import com.sonyericsson.jenkins.plugins.bfa.model.indication.Indication; +import com.sonyericsson.jenkins.plugins.bfa.model.indication.MultilineBuildLogIndication; import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause; import com.sonyericsson.jenkins.plugins.bfa.model.FailureCauseModification; import com.sonyericsson.jenkins.plugins.bfa.sod.ScanOnDemandVariables; @@ -13,6 +16,7 @@ import io.jenkins.plugins.casc.model.CNode; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.RuleChain; import java.util.ArrayList; import java.util.List; @@ -31,12 +35,20 @@ */ public class ConfigurationAsCodeMongoTest { + static final String EXPECTED_ID = "61566f2a8f5c6de699e69b46"; + static final String EXPECTED_DB = "bfadb"; + static final String EXPECTED_USERNAME = "bfa"; + static final String EXPECTED_PASSWORD = "changeme"; + static final int EXPECTED_PORT = 27017; + /** * Jenkins rule. */ @Rule //CS IGNORE VisibilityModifier FOR NEXT 1 LINES. REASON: Jenkins Rule - public JenkinsConfiguredWithCodeRule j = new JenkinsConfiguredWithCodeRule(); + public RuleChain chain = RuleChain + .outerRule(new EmbeddedMongoRule(EXPECTED_PORT, EXPECTED_DB, EXPECTED_USERNAME, EXPECTED_PASSWORD)) + .around(new JenkinsConfiguredWithCodeRule()); /** * Support config as code import. @@ -53,10 +65,10 @@ public void shouldSupportConfigurationAsCode() { MongoDBKnowledgeBase knowledgeBase = (MongoDBKnowledgeBase)plugin.getKnowledgeBase(); assertThat(knowledgeBase.getHost(), is("localhost")); - assertThat(knowledgeBase.getDbName(), is("bfa")); + assertThat(knowledgeBase.getDbName(), is(EXPECTED_DB)); assertThat(knowledgeBase.isEnableStatistics(), is(true)); - assertThat(knowledgeBase.getUserName(), is("bfa")); - assertThat(knowledgeBase.getPassword().getPlainText(), is("changeme")); + assertThat(knowledgeBase.getUserName(), is(EXPECTED_USERNAME)); + assertThat(knowledgeBase.getPassword().getPlainText(), is(EXPECTED_PASSWORD)); assertThat(knowledgeBase.isSuccessfulLogging(), is(false)); assertThat(plugin.getNoCausesMessage(), is(ConfigurationAsCodeLocalTest.NO_CAUSES_MESSAGE)); @@ -78,6 +90,25 @@ public void shouldSupportConfigurationAsCode() { assertThat(plugin.getTestResultCategories(), is("hgjghhlllllaa")); assertThat(plugin.isTestResultParsingEnabled(), is(true)); + + List initialCauses = new ArrayList(plugin.getCauses()); + assertThat(initialCauses.size(), is(1)); + FailureCause cause = initialCauses.get(0); + assertThat(cause.getId(), is(EXPECTED_ID)); + assertThat(cause.getDescription(), is(ConfigurationAsCodeLocalTest.EXPECTED_DESCRIPTION)); + assertThat(cause.getComment(), is(ConfigurationAsCodeLocalTest.EXPECTED_COMMENT)); + assertThat(cause.getCategoriesAsString(), is(ConfigurationAsCodeLocalTest.EXPECTED_CATEGORIES)); + assertThat(cause.getName(), is(ConfigurationAsCodeLocalTest.EXPECTED_NAME)); + + List indications = cause.getIndications(); + assertThat(indications.size(), is(2)); + assertThat(indications.get(0), instanceOf(BuildLogIndication.class)); + assertThat(indications.get(1), instanceOf(MultilineBuildLogIndication.class)); + BuildLogIndication buildLog = (BuildLogIndication)indications.get(0); + assertThat(buildLog.getUserProvidedExpression(), is(ConfigurationAsCodeLocalTest.EXPECTED_BUILD_LOG_EXPRESSION)); + MultilineBuildLogIndication multilineBuildLog = (MultilineBuildLogIndication)indications.get(1); + assertThat(multilineBuildLog.getUserProvidedExpression(), + is(ConfigurationAsCodeLocalTest.EXPECTED_MULTILINE_BUILD_LOG_EXPRESSION)); } /** @@ -114,7 +145,7 @@ public void shouldSupportConfigurationAsCodeWithLessCauseParameters() { assertThat(initialCauses.size(), is(1)); FailureCause cause = initialCauses.get(0); - assertThat(cause.getId(), is(ConfigurationAsCodeLocalTest.EXPECTED_ID)); + assertThat(cause.getId(), is(EXPECTED_ID)); assertThat(cause.getDescription(), is(ConfigurationAsCodeLocalTest.EXPECTED_DESCRIPTION)); assertThat(cause.getComment(), is(nullValue())); assertThat(cause.getCategoriesAsString(), is(nullValue())); diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml index 33e06482..1ac275a1 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-expected.yml @@ -1,11 +1,24 @@ +causes: +- categories: + - "example" + - "second" + comment: "To show what a cause looks like" + description: "A problem was found" + id: "61566f2a8f5c6de699e69b46" + indications: + - buildLog: + pattern: ".*problem.*" + - multilineBuildLog: + pattern: "(?m)(?s)^[^\\r\\n]*?many.*problems[^\\r\\n]*?$" + name: "Found problems" doNotAnalyzeAbortedJob: true -exportCausesToJCasCEnabled: false +exportCausesToJCasCEnabled: true gerritTriggerEnabled: true globalEnabled: true graphsEnabled: false knowledgeBase: mongoDB: - dbName: "bfa" + dbName: "bfadb" enableStatistics: true host: "localhost" diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-less.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-less.yml index aad9eaf1..73e49ff3 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-less.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-less.yml @@ -2,7 +2,7 @@ unclassified: buildFailureAnalyzer: knowledgeBase: mongoDB: - dbName: "bfa" + dbName: "bfadb" enableStatistics: true host: "localhost" userName: bfa @@ -11,7 +11,7 @@ unclassified: successfulLogging: false causes: - description: "A problem was found" - id: "035e67d4-6692-4b1f-adf9-95c43948b349" + id: "61566f2a8f5c6de699e69b46" indications: - buildLog: pattern: ".*problem.*" diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export-expected.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export-expected.yml index 33e06482..30cf2023 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export-expected.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export-expected.yml @@ -5,7 +5,7 @@ globalEnabled: true graphsEnabled: false knowledgeBase: mongoDB: - dbName: "bfa" + dbName: "bfadb" enableStatistics: true host: "localhost" diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export.yml index 846c004c..39a5fd67 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo-no-export.yml @@ -4,7 +4,7 @@ unclassified: - categories: [ "example", "second" ] comment: "To show what a cause looks like" description: "A problem was found" - id: "035e67d4-6692-4b1f-adf9-95c43948b349" + id: "61566f2a8f5c6de699e69b46" indications: - buildLog: pattern: ".*problem.*" @@ -17,7 +17,7 @@ unclassified: graphsEnabled: false knowledgeBase: mongoDB: - dbName: "bfa" + dbName: "bfadb" enableStatistics: true host: "localhost" userName: bfa diff --git a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo.yml b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo.yml index 20977277..4d8eada6 100644 --- a/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo.yml +++ b/src/test/resources/com/sonyericsson/jenkins/plugins/bfa/jcasc/jcasc-mongo.yml @@ -1,12 +1,24 @@ unclassified: buildFailureAnalyzer: + causes: + - categories: [ "example", "second" ] + comment: "To show what a cause looks like" + description: "A problem was found" + id: "61566f2a8f5c6de699e69b46" + indications: + - buildLog: + pattern: ".*problem.*" + - multilineBuildLog: + pattern: "many.*problems" + name: "Found problems" doNotAnalyzeAbortedJob: true + exportCausesToJCasCEnabled: true gerritTriggerEnabled: true globalEnabled: true graphsEnabled: false knowledgeBase: mongoDB: - dbName: "bfa" + dbName: "bfadb" enableStatistics: true host: "localhost" userName: bfa