package com.sonyericsson.jenkins.plugins.bfa;
/*
* The MIT License
*
* Copyright 2013 Sony Mobile Communications Inc. 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.
*/
import com.sonyericsson.jenkins.plugins.bfa.model.FailureCause;
import com.sonyericsson.jenkins.plugins.bfa.model.FailureCauseBuildAction;
import com.sonyericsson.jenkins.plugins.bfa.model.FailureCauseDisplayData;
import com.sonyericsson.jenkins.plugins.bfa.model.FailureCauseMatrixBuildAction;
import com.sonyericsson.jenkins.plugins.bfa.model.FoundFailureCause;
import com.sonyericsson.jenkins.plugins.bfa.model.indication.BuildLogIndication;
import com.sonyericsson.jenkins.plugins.bfa.model.indication.Indication;
import hudson.matrix.Axis;
import hudson.matrix.AxisList;
import hudson.matrix.MatrixBuild;
import hudson.matrix.MatrixProject;
import hudson.matrix.MatrixRun;
import hudson.model.Cause;
import hudson.model.FreeStyleProject;
import hudson.model.Result;
import hudson.plugins.parameterizedtrigger.AbstractBuildParameters;
import hudson.plugins.parameterizedtrigger.BlockableBuildTriggerConfig;
import hudson.plugins.parameterizedtrigger.BlockingBehaviour;
import hudson.plugins.parameterizedtrigger.CurrentBuildParameters;
import hudson.plugins.parameterizedtrigger.TriggerBuilder;
import hudson.tasks.Shell;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.CaptureEnvironmentBuilder;
import org.jvnet.hudson.test.JenkinsRule;
import java.util.ArrayList;
import java.util.List;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
//CS IGNORE MagicNumber FOR NEXT 260 LINES. REASON: TestData
/**
* Test fetching display data object for build failure analysis of downstream
* objects.
*
* @author Jan-Olof Sivtoft
*/
public class DisplayDownstreamTest {
/**
* The Jenkins Rule.
*/
@Rule
//CS IGNORE VisibilityModifier FOR NEXT 1 LINES. REASON: Jenkins Rule
public JenkinsRule jenkins = new JenkinsRule();
private MatrixProject matrixProject;
private static final String MATRIX_PROJECT_NEWS = "NEWS";
private static final String PROJECT_NE = "NORTH-EAST";
private static final String PROJECT_EW = "NORTH-WEST";
private static final String PROJECT_SE = "SOUTH-EAST";
private static final String PROJECT_SW = "SOUTH-WEST";
private static final String DEFAULT = "echo I am ${PROJECT_NAME}";
private static final String FAILED = "rapakalja";
/**
* Test the FailureCauseDisplayData object.
*
* @throws Exception if failure build can't be executed
*/
@Test
public void testFailureCauseDisplayData() throws Exception {
FailureCauseDisplayData failureCauseDisplayData =
getDisplayData(executeBuild());
assertNotNull(failureCauseDisplayData.getDownstreamFailureCauses());
assertNotNull(failureCauseDisplayData.getFoundFailureCauses());
FailureCauseDisplayData.Links links =
failureCauseDisplayData.getLinks();
assertNotNull(links);
assertEquals("WEST,SOUTH", links.getProjectDisplayName());
assertNotNull(links.getProjectUrl());
assertEquals("#1", links.getBuildDisplayName());
assertNotNull(links.getBuildUrl());
}
/**
* Test FailureCauseDisplayData object population when no indication is
* specified.
*
* @throws Exception if failure build can't be executed
*/
@Test
public void testMatrixNoIdentifiedCause() throws Exception {
FailureCauseDisplayData failureCauseDisplayData =
getDisplayData(executeBuild());
// It is not the matrix run that fails
assertTrue(failureCauseDisplayData.getFoundFailureCauses().size() == 0);
assertTrue(failureCauseDisplayData.
getDownstreamFailureCauses().size() == 1);
FailureCauseDisplayData downstreamFailureCauseDisplayData =
failureCauseDisplayData.getDownstreamFailureCauses().
get(0);
List<FoundFailureCause> causeListFromAction =
downstreamFailureCauseDisplayData.getFoundFailureCauses();
// No indication added so this is expected
assertTrue(causeListFromAction.size() == 0);
assertEquals(PROJECT_SW, downstreamFailureCauseDisplayData.
getLinks().getProjectDisplayName());
}
/**
* Test FailureCauseDisplayData object population when an indication is
* specified.
*
* @throws Exception if failure cause cant be configured or build can't
* be executed
*/
@Test
public void testMatrixIdentifiedCause() throws Exception {
Indication indication = new BuildLogIndication(".*" + FAILED + ".*");
FailureCause failureCause = BuildFailureScannerHudsonTest.
configureCauseAndIndication("Other cause", "Other description", "Other comment",
"Category", indication);
FailureCauseDisplayData failureCauseDisplayData =
getDisplayData(executeBuild());
// It is not the matrix run that fails
assertTrue(failureCauseDisplayData.getFoundFailureCauses().size() == 0);
assertTrue(failureCauseDisplayData.
getDownstreamFailureCauses().size() == 1);
FailureCauseDisplayData downstreamFailureCauseDisplayData =
failureCauseDisplayData.getDownstreamFailureCauses().
get(0);
List<FoundFailureCause> causeListFromAction =
downstreamFailureCauseDisplayData.getFoundFailureCauses();
assertTrue(causeListFromAction.size() == 1);
// This is the expected indication
assertTrue(BuildFailureScannerHudsonTest.findCauseInList(
causeListFromAction, failureCause));
assertEquals(PROJECT_SW, downstreamFailureCauseDisplayData.getLinks().
getProjectDisplayName());
}
/**
* Test FailureCauseDisplayData object population when an indication is
* specified.
*
* @throws Exception if failure cause cant be configured or build can't
* be executed
*/
@Test
public void testIdentifiedTwoCauses() throws Exception {
final FreeStyleProject child1 = createFreestyleProjectWithShell("child1", FAILED);
final FreeStyleProject child2 = createFreestyleProjectWithShell("child2", FAILED);
final FreeStyleProject parent = jenkins.createFreeStyleProject("parent");
parent.getBuildersList().add(new TriggerBuilder(
new BlockableBuildTriggerConfig(child1.getName() + ", " + child2.getName(),
new BlockingBehaviour(Result.FAILURE, Result.FAILURE, Result.FAILURE),
new ArrayList<AbstractBuildParameters>())));
final Indication indication = new BuildLogIndication(".*" + FAILED + ".*");
BuildFailureScannerHudsonTest.configureCauseAndIndication("Other cause",
"Other description", "Other comment", "Category", indication);
parent.scheduleBuild2(0).get();
final FailureCauseBuildAction buildAction = parent.getFirstBuild().getAction(FailureCauseBuildAction.class);
final FailureCauseDisplayData failureCauseDisplayData = buildAction.getFailureCauseDisplayData();
final List<FailureCauseDisplayData> downstreamCauses = failureCauseDisplayData.getDownstreamFailureCauses();
assertEquals(0, failureCauseDisplayData.getFoundFailureCauses().size());
assertEquals(2, downstreamCauses.size());
for (FailureCauseDisplayData causeDisplayData : downstreamCauses) {
final List<FoundFailureCause> causeListFromAction = causeDisplayData.getFoundFailureCauses();
assertEquals(1, causeListFromAction.size());
assertEquals(causeListFromAction.get(0).getName(), "Other cause");
}
}
/**
* Creates and executes a matrix build.
*
* @return a Matrix build object
* @throws Exception if build couldn't be executed
*/
private MatrixBuild executeBuild() throws Exception {
createMatrixProjectNews();
matrixProject.setQuietPeriod(0);
jenkins.getInstance().rebuildDependencyGraph();
matrixProject.scheduleBuild2(0, new Cause.UserCause()).get();
return matrixProject.getLastBuild();
}
/**
* Returns FailureCauseDisplayData form the first MatrixBuild run
*
* @param build the executed build
* @return a FailureCauseDisplayData object
*/
private FailureCauseDisplayData getDisplayData(MatrixBuild build) {
FailureCauseMatrixBuildAction action =
build.getAction(FailureCauseMatrixBuildAction.class);
// This should be the PROJECT_SW
MatrixRun runProjectSW = action.getRunsWithAction().get(0);
return FailureCauseMatrixBuildAction.getFailureCauseDisplayData(runProjectSW);
}
/**
* Creates a Matrix project with two axis. Four Downstream jobs are also
* created. One of these will fail.
*
* @throws Exception if project(s) can't be created
*/
private void createMatrixProjectNews() throws Exception {
createFreestyleProjectWithDefaultShell(
PROJECT_NE, PROJECT_EW, PROJECT_SE);
createFreestyleProjectWithShell(PROJECT_SW, "rapakalja");
jenkins.getInstance().setNumExecutors(50);
//TODO https://github.com/jenkinsci/jenkins/pull/1596 renders this workaround unnecessary
jenkins.getInstance().setNodes(jenkins.getInstance().getNodes()); // update nodes configuration
matrixProject = jenkins.createMatrixProject(MATRIX_PROJECT_NEWS);
AxisList axes = new AxisList();
axes.add(new Axis("X", "EAST", "WEST"));
axes.add(new Axis("Y", "NORTH", "SOUTH"));
matrixProject.setAxes(axes);
List<AbstractBuildParameters> buildParameters =
new ArrayList<AbstractBuildParameters>();
buildParameters.add(new CurrentBuildParameters());
BlockingBehaviour neverFail =
new BlockingBehaviour("FAILURE", "FAILURE", "UNSTABLE");
BlockableBuildTriggerConfig config = new BlockableBuildTriggerConfig(
"${Y}-${X}", neverFail, buildParameters);
matrixProject.getBuildersList().add(new TriggerBuilder(config));
CaptureEnvironmentBuilder builder = new CaptureEnvironmentBuilder();
matrixProject.getBuildersList().add(builder);
}
/**
* Creates several FreeStyleProjects with a basic shell step. Each shell is
* loaded with a default command: "echo I am ${PROJECT_NAME}"
*
* @param names an array of project names
* @throws Exception if project(s) can't be created
*/
private void createFreestyleProjectWithDefaultShell(String... names)
throws Exception {
for (String name : names) {
createFreestyleProjectWithShell(name, DEFAULT);
}
}
/**
* Creates a FreeStyleProject with a basic shell step. The shell is loaded
* with the supplied command.
*
* @param name the name of the project
* @param command the shell command
* @return created project
* @throws Exception if project(s) can't be created
*/
private FreeStyleProject createFreestyleProjectWithShell(String name, String command)
throws Exception {
final FreeStyleProject project = jenkins.createFreeStyleProject(name);
project.getBuildersList().add(new Shell(command));
return project;
}
}