package hudson.plugins.performance.constraints;
import hudson.AbortException;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Describable;
import hudson.model.Run;
import hudson.plugins.performance.actions.PerformanceBuildAction;
import hudson.plugins.performance.constraints.blocks.TestCaseBlock;
import hudson.plugins.performance.data.ConstraintSettings;
import hudson.plugins.performance.descriptors.ConstraintDescriptor;
import hudson.plugins.performance.reports.PerformanceReport;
import hudson.plugins.performance.reports.UriReport;
import jenkins.model.Jenkins;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
/**
* Parent class for AbsoluteConstraint and RelativeConstraint
*
* @author Rene Kugel
*/
public abstract class AbstractConstraint implements Describable<AbstractConstraint>, ExtensionPoint {
/**
* Holds the information whether constraint is fulfilled(true) or violated(false)
*/
private boolean success = false;
/**
* True if constraint refers to a test case False if constraint refers to a whole report
*/
private boolean isSpecifiedTestCase = false;
/**
* Metric which should be evaluated
*/
private Metric meteredValue;
/**
* Operator which is used to compare values
*/
private Operator operator;
/**
* Determines the build result if constraint is violated
*/
private Escalation escalationLevel;
/**
* Holds relevant information about the evaluation
*/
private String resultMessage = "";
/**
* The report file the constraint refers to
*/
private String relatedPerfReport;
/**
* null if isSpecifiedTestCase == false Holds the test case if isSpecifiedTestCase == true
*/
private TestCaseBlock testCaseBlock;
/**
* Reference for global constraint settings
*/
private ConstraintSettings settings;
public ConstraintDescriptor getDescriptor() {
return (ConstraintDescriptor) Jenkins.getInstance().getDescriptorOrDie(getClass());
}
public static ExtensionList<AbstractConstraint> all() {
return Jenkins.getInstance().getExtensionList(AbstractConstraint.class);
}
protected AbstractConstraint(Metric meteredValue, Operator operator, String relatedPerfReport, Escalation escalationLevel, boolean success, TestCaseBlock testCaseBlock) {
this.relatedPerfReport = relatedPerfReport;
this.success = success;
this.meteredValue = meteredValue;
this.operator = operator;
this.escalationLevel = escalationLevel;
if (testCaseBlock != null) {
this.setSpecifiedTestCase(true);
this.testCaseBlock = testCaseBlock;
} else {
this.setSpecifiedTestCase(false);
}
}
/**
* Cloning of a constraint Note that this is not from the Interface Clonable {@inheritDoc}
*/
public abstract AbstractConstraint clone();
/**
* Evaluates whether the constraint is fulfilled or violated
*
* @param builds all builds that are saved in Jenkins
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws AbortException
* @throws ParseException
*/
public abstract ConstraintEvaluation evaluate(List<? extends Run<?, ?>> builds) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, AbortException,
ParseException;
/**
* Grabs a specified Metric in a specified UriReport
*
* @param meteredValue the metric that should be evaluated
* @param ur the UriReport where the metric should be measured
* @return the value of the specified metric in the specified UriReport
*/
protected double checkMetredValueforUriReport(Metric meteredValue, UriReport ur) {
switch (meteredValue) {
case ERRORPRC:
return ur.errorPercent();
case AVERAGE:
return (double) ur.getAverage();
case LINE90:
return (double) ur.get90Line();
case MEDIAN:
return (double) ur.getMedian();
case MINIMUM:
return (double) ur.getMin();
case MAXIMUM:
return (double) ur.getMax();
default:
return (double) ur.getAverage();
}
}
/**
* Grabs a specified Metric in a specified PerformanceReport
*
* @param meteredValue the metric that should be evaluated
* @param pr the PerformanceReport where the metric should be measured
* @return the value of the specified metric in the specified PerformanceReport
*/
protected double checkMetredValueforPerfReport(Metric meteredValue, PerformanceReport pr) {
switch (meteredValue) {
case ERRORPRC:
return pr.errorPercent();
case AVERAGE:
return (double) pr.getAverage();
case LINE90:
return (double) pr.get90Line();
case MEDIAN:
return (double) pr.getMedian();
case MINIMUM:
return (double) pr.getMin();
case MAXIMUM:
return (double) pr.getMax();
default:
return (double) pr.getAverage();
}
}
/**
* Checks whether all parameters given in the UI are processable.
*
* @param builds all stored jenkins builds
* @throws AbortException if a parameter in the UI is not processable
* @throws ParseException if a timeframe string in the UI is not processable
*/
protected void checkForDefectiveParams(List<? extends Run<?, ?>> builds) throws AbortException {
boolean found = false;
if (builds.get(0).getAction(PerformanceBuildAction.class).getPerformanceReportMap().getPerformanceReport(getRelatedPerfReport()) == null) {
throw new AbortException("Performance Plugin: Could't find a report specified in the performance constraints! Report: \"" + getRelatedPerfReport() + "\"");
} else {
PerformanceReport pr = builds.get(0).getAction(PerformanceBuildAction.class).getPerformanceReportMap().getPerformanceReport(getRelatedPerfReport());
if (isSpecifiedTestCase()) {
for (UriReport ur : pr.getUriListOrdered()) {
if (ur.getUri().equals(getTestCaseBlock().getTestCase())) {
found = true;
}
}
if (!found) {
throw new AbortException("Performance Plugin: Could't find a test case specified in the performance constraints! TestCase: \"" + getTestCaseBlock().getTestCase() + "\" Report: \""
+ getRelatedPerfReport() + "\"");
}
}
}
if (this instanceof AbsoluteConstraint) {
AbsoluteConstraint ac = (AbsoluteConstraint) this;
if (ac.getValue() < 0) {
throw new AbortException("Performance Plugin: The value of a Absolute Constraint can't be negative!");
}
}
if (this instanceof RelativeConstraint) {
RelativeConstraint rc = (RelativeConstraint) this;
if (rc.getTolerance() < 0) {
throw new AbortException("Performance Plugin: The tolerance of a Relative Constraint can't be negative!");
}
if (rc.getTimeframeStart().after(rc.getTimeframeEnd())) {
throw new AbortException("Performance Plugin: The start date of a Relative Constraint can't be after the end date");
}
if (!rc.getPreviousResultsBlock().isChoicePreviousResults()) {
final SimpleDateFormat dfLong = new SimpleDateFormat("yyyy-MM-dd HH:mm");
try {
rc.setTimeframeStart(dfLong.parse(rc.getTimeframeStartString()));
if (!rc.getTimeframeEndString().equals("now")) {
rc.setTimeframeEnd(dfLong.parse(rc.getTimeframeEndString()));
}
} catch (ParseException e) {
throw new AbortException("Performance Plugin: Couldn't parse date in Relative Constraint! Please check the configuration of your constraints");
}
}
}
}
public enum Metric {
AVERAGE("Average", false), MEDIAN("Median", false), LINE90("90% Line", false), MAXIMUM("Maximum", false), MINIMUM("Minimum", false), ERRORPRC("Error %", false);
private final String text;
private boolean isSelected;
private Metric(final String text, boolean isSelected) {
this.text = text;
this.setSelected(isSelected);
}
@Override
public String toString() {
return text;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean isSelected) {
this.isSelected = isSelected;
}
}
public enum Escalation {
INFORMATION("Information", false), WARNING("Warning", false), ERROR("Error", false);
private final String text;
private boolean isSelected;
private Escalation(final String text, boolean isSelected) {
this.text = text;
this.setSelected(isSelected);
}
@Override
public String toString() {
return text;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean isSelected) {
this.isSelected = isSelected;
}
}
public enum Operator {
NOT_GREATER("not be greater than", false), NOT_LESS("not be less than", false), NOT_EQUAL("not be equal to", false);
public final String text;
private boolean isSelected;
private Operator(final String text, boolean isSelected) {
this.text = text;
this.setSelected(isSelected);
}
@Override
public String toString() {
return text;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean isSelected) {
this.isSelected = isSelected;
}
}
public void setSuccess(boolean success) {
this.success = success;
}
public boolean getSuccess() {
return this.success;
}
public String getResultMessage() {
return resultMessage;
}
public void setResultMessage(String resultMessage) {
this.resultMessage = resultMessage;
}
public String getRelatedPerfReport() {
return relatedPerfReport;
}
public void setRelatedPerfReport(String relatedPerfReport) {
this.relatedPerfReport = relatedPerfReport;
}
public Metric getMeteredValue() {
return meteredValue;
}
public void setMeteredValue(Metric meteredValue) {
this.meteredValue = meteredValue;
}
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
public Escalation getEscalationLevel() {
return escalationLevel;
}
public void setEscalationLevel(Escalation escalationLevel) {
this.escalationLevel = escalationLevel;
}
public TestCaseBlock getTestCaseBlock() {
return testCaseBlock;
}
public void setTestCaseBlock(TestCaseBlock testCaseBlock) {
this.testCaseBlock = testCaseBlock;
}
public boolean isSpecifiedTestCase() {
return isSpecifiedTestCase;
}
public void setSpecifiedTestCase(boolean isSpecifiedTestCase) {
this.isSpecifiedTestCase = isSpecifiedTestCase;
}
public ConstraintSettings getSettings() {
return settings;
}
public void setSettings(ConstraintSettings settings) {
this.settings = settings;
}
public String getTestCase() {
if (getTestCaseBlock() != null) {
return getTestCaseBlock().getTestCase();
} else {
return null;
}
}
public void setTestCase(String testCase) {
this.getTestCaseBlock().setTestCase(testCase);
}
}