-
Notifications
You must be signed in to change notification settings - Fork 232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added maximum string length constraint. (#449) #452
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
import java.util.Set; | ||
import java.util.UUID; | ||
|
||
import com.fasterxml.jackson.core.StreamReadConstraints; | ||
import edu.umd.cs.findbugs.annotations.NonNull; | ||
import hudson.AbortException; | ||
import hudson.Extension; | ||
|
@@ -78,487 +79,499 @@ | |
private boolean undefinedAsNotFailingStatus; | ||
|
||
private int trendsLimit; | ||
private int maxStringLengthConstraint = 20_000_000; | ||
private String sortingMethod; | ||
private List<Classification> classifications; | ||
private String customJsFiles; | ||
private String customCssFiles; | ||
|
||
private boolean mergeFeaturesById; | ||
private boolean mergeFeaturesWithRetest; | ||
private boolean hideEmptyHooks; | ||
private boolean skipEmptyJSONFiles; | ||
private boolean expandAllSteps; | ||
|
||
private String classificationsFilePattern = ""; | ||
|
||
@DataBoundConstructor | ||
public CucumberReportPublisher(String fileIncludePattern) { | ||
this.fileIncludePattern = fileIncludePattern; | ||
} | ||
|
||
/** | ||
* This method, invoked after object is resurrected from persistence, | ||
* to keep backward compatibility. | ||
*/ | ||
protected void keepBackwardCompatibility() { | ||
if (classifications == null) { | ||
classifications = new ArrayList<>(); | ||
} | ||
if (sortingMethod == null) { | ||
sortingMethod = SortingMethod.NATURAL.name(); | ||
} | ||
|
||
reportTitle = StringUtils.defaultString(reportTitle); | ||
} | ||
|
||
private static void log(TaskListener listener, String message) { | ||
listener.getLogger().println("[CucumberReport] " + message); | ||
} | ||
|
||
public String getFileIncludePattern() { | ||
return fileIncludePattern; | ||
} | ||
|
||
public List<Classification> getClassifications() { | ||
return classifications; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setClassifications(List<Classification> classifications) { | ||
// don't store the classifications if there was no element provided | ||
if (CollectionUtils.isNotEmpty(classifications)) { | ||
this.classifications = classifications; | ||
} | ||
} | ||
|
||
@DataBoundSetter | ||
public void setCustomJsFiles(String customJsFiles) { | ||
this.customJsFiles = customJsFiles; | ||
} | ||
|
||
public String getCustomJsFiles() { | ||
return customJsFiles; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setCustomCssFiles(String customCssFiles) { | ||
this.customCssFiles = customCssFiles; | ||
} | ||
|
||
public String getCustomCssFiles() { | ||
return customCssFiles; | ||
} | ||
|
||
public int getTrendsLimit() { | ||
return trendsLimit; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setTrendsLimit(int trendsLimit) { | ||
this.trendsLimit = trendsLimit; | ||
} | ||
|
||
public int getMaxStringLengthConstraint() { | ||
return maxStringLengthConstraint; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setMaxStringLengthConstraint(int maxStringLengthConstraint) { | ||
this.maxStringLengthConstraint = maxStringLengthConstraint; | ||
} | ||
|
||
public String getFileExcludePattern() { | ||
return fileExcludePattern; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setFileExcludePattern(String fileExcludePattern) { | ||
this.fileExcludePattern = fileExcludePattern; | ||
} | ||
|
||
public String getJsonReportDirectory() { | ||
return jsonReportDirectory; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setJsonReportDirectory(String jsonReportDirectory) { | ||
this.jsonReportDirectory = jsonReportDirectory; | ||
} | ||
|
||
public String getReportTitle() { | ||
return reportTitle; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setReportTitle(String reportTitle) { | ||
this.reportTitle = StringUtils.isEmpty(reportTitle) ? "" : reportTitle.trim(); | ||
} | ||
|
||
public String getDirectorySuffix() { | ||
return StringUtils.isEmpty(this.reportTitle) | ||
? "" | ||
: UUID.nameUUIDFromBytes(reportTitle.getBytes(StandardCharsets.UTF_8)).toString(); | ||
} | ||
|
||
public String getDirectorySuffixWithSeparator() { | ||
return StringUtils.isEmpty(this.reportTitle) | ||
? "" | ||
: ReportBuilder.SUFFIX_SEPARATOR + getDirectorySuffix(); | ||
} | ||
|
||
public int getFailedStepsNumber() { | ||
return failedStepsNumber; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setFailedStepsNumber(int failedStepsNumber) { | ||
this.failedStepsNumber = failedStepsNumber; | ||
} | ||
|
||
public int getSkippedStepsNumber() { | ||
return skippedStepsNumber; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setSkippedStepsNumber(int skippedStepsNumber) { | ||
this.skippedStepsNumber = skippedStepsNumber; | ||
} | ||
|
||
public int getPendingStepsNumber() { | ||
return pendingStepsNumber; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setPendingStepsNumber(int pendingStepsNumber) { | ||
this.pendingStepsNumber = pendingStepsNumber; | ||
} | ||
|
||
public int getUndefinedStepsNumber() { | ||
return undefinedStepsNumber; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setUndefinedStepsNumber(int undefinedStepsNumber) { | ||
this.undefinedStepsNumber = undefinedStepsNumber; | ||
} | ||
|
||
public int getFailedScenariosNumber() { | ||
return failedScenariosNumber; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setFailedScenariosNumber(int failedScenariosNumber) { | ||
this.failedScenariosNumber = failedScenariosNumber; | ||
} | ||
|
||
public int getFailedFeaturesNumber() { | ||
return failedFeaturesNumber; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setFailedFeaturesNumber(int failedFeaturesNumber) { | ||
this.failedFeaturesNumber = failedFeaturesNumber; | ||
} | ||
|
||
|
||
public double getFailedStepsPercentage() { | ||
return failedStepsPercentage; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setFailedStepsPercentage(double failedStepsPercentage) { | ||
this.failedStepsPercentage = failedStepsPercentage; | ||
} | ||
|
||
public double getSkippedStepsPercentage() { | ||
return skippedStepsPercentage; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setSkippedStepsPercentage(double skippedStepsPercentage) { | ||
this.skippedStepsPercentage = skippedStepsPercentage; | ||
} | ||
|
||
public double getPendingStepsPercentage() { | ||
return pendingStepsPercentage; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setPendingStepsPercentage(double pendingStepsPercentage) { | ||
this.pendingStepsPercentage = pendingStepsPercentage; | ||
} | ||
|
||
public double getUndefinedStepsPercentage() { | ||
return undefinedStepsPercentage; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setUndefinedStepsPercentage(double undefinedStepsPercentage) { | ||
this.undefinedStepsPercentage = undefinedStepsPercentage; | ||
} | ||
|
||
public double getFailedScenariosPercentage() { | ||
return failedScenariosPercentage; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setFailedScenariosPercentage(double failedScenariosPercentage) { | ||
this.failedScenariosPercentage = failedScenariosPercentage; | ||
} | ||
|
||
public double getFailedFeaturesPercentage() { | ||
return failedFeaturesPercentage; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setFailedFeaturesPercentage(double failedFeaturesPercentage) { | ||
this.failedFeaturesPercentage = failedFeaturesPercentage; | ||
} | ||
|
||
|
||
public String getBuildStatus() { | ||
return buildStatus; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setBuildStatus(String buildStatus) { | ||
this.buildStatus = buildStatus; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setStopBuildOnFailedReport(boolean stopBuildOnFailedReport) { | ||
this.stopBuildOnFailedReport = stopBuildOnFailedReport; | ||
} | ||
|
||
public boolean getStopBuildOnFailedReport() { | ||
return stopBuildOnFailedReport; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setFailedAsNotFailingStatus(boolean failedAsNotFailingStatus) { | ||
this.failedAsNotFailingStatus = failedAsNotFailingStatus; | ||
} | ||
|
||
public boolean getFailedAsNotFailingStatus() { | ||
return failedAsNotFailingStatus; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setSkippedAsNotFailingStatus(boolean skippedAsNotFailingStatus) { | ||
this.skippedAsNotFailingStatus = skippedAsNotFailingStatus; | ||
} | ||
|
||
public boolean getSkippedAsNotFailingStatus() { | ||
return skippedAsNotFailingStatus; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setPendingAsNotFailingStatus(boolean pendingAsNotFailingStatus) { | ||
this.pendingAsNotFailingStatus = pendingAsNotFailingStatus; | ||
} | ||
|
||
public boolean getPendingAsNotFailingStatus() { | ||
return pendingAsNotFailingStatus; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setUndefinedAsNotFailingStatus(boolean undefinedAsNotFailingStatus) { | ||
this.undefinedAsNotFailingStatus = undefinedAsNotFailingStatus; | ||
} | ||
|
||
public boolean getUndefinedAsNotFailingStatus() { | ||
return undefinedAsNotFailingStatus; | ||
} | ||
|
||
|
||
@DataBoundSetter | ||
public void setSortingMethod(String sortingMethod) { | ||
this.sortingMethod = sortingMethod; | ||
} | ||
|
||
public String getSortingMethod() { | ||
return sortingMethod; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setClassificationsFilePattern(String classificationsFilePattern) { | ||
this.classificationsFilePattern = classificationsFilePattern; | ||
} | ||
|
||
public String getClassificationsFilePattern() { | ||
return classificationsFilePattern; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setMergeFeaturesById(boolean mergeFeaturesById) { | ||
this.mergeFeaturesById = mergeFeaturesById; | ||
} | ||
|
||
public boolean getMergeFeaturesById() { | ||
return mergeFeaturesById; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setMergeFeaturesWithRetest(boolean mergeFeaturesWithRetest) { | ||
this.mergeFeaturesWithRetest = mergeFeaturesWithRetest; | ||
} | ||
|
||
public boolean getMergeFeaturesWithRetest() { | ||
return mergeFeaturesWithRetest; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setHideEmptyHooks(boolean hideEmptyHooks) { | ||
this.hideEmptyHooks = hideEmptyHooks; | ||
} | ||
|
||
public boolean getHideEmptyHooks() { | ||
return hideEmptyHooks; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setSkipEmptyJSONFiles(boolean skipEmptyJSONFiles) { | ||
this.skipEmptyJSONFiles = skipEmptyJSONFiles; | ||
} | ||
|
||
public boolean getSkipEmptyJSONFiles() { | ||
return skipEmptyJSONFiles; | ||
} | ||
|
||
@DataBoundSetter | ||
public void setExpandAllSteps(boolean expandAllSteps) { | ||
this.expandAllSteps = expandAllSteps; | ||
} | ||
|
||
public boolean getExpandAllSteps() { | ||
return expandAllSteps; | ||
} | ||
|
||
@Override | ||
public void perform(@NonNull Run<?, ?> run, @NonNull FilePath workspace, @NonNull Launcher launcher, @NonNull TaskListener listener) | ||
throws InterruptedException, IOException { | ||
|
||
keepBackwardCompatibility(); | ||
|
||
generateReport(run, workspace, listener); | ||
|
||
SafeArchiveServingRunAction caa = new SafeArchiveServingRunAction( | ||
run, | ||
new File(run.getRootDir(), ReportBuilder.BASE_DIRECTORY + getDirectorySuffixWithSeparator()), | ||
ReportBuilder.BASE_DIRECTORY + getDirectorySuffixWithSeparator(), | ||
ReportBuilder.HOME_PAGE, | ||
CucumberReportBaseAction.ICON_NAME, | ||
getActionName(), | ||
getDirectorySuffixWithSeparator() | ||
); | ||
run.addAction(caa); | ||
} | ||
|
||
private String getActionName() { | ||
return StringUtils.isEmpty(reportTitle) ? Messages.SidePanel_DisplayNameNoTitle() : String.format(Messages.SidePanel_DisplayName(), reportTitle); | ||
} | ||
|
||
private void generateReport(Run<?, ?> build, FilePath workspace, TaskListener listener) throws InterruptedException, IOException { | ||
|
||
log(listener, "Using Cucumber Reports version " + getPomVersion(listener)); | ||
|
||
// create directory where trends will be stored | ||
final File trendsDir = new File(build.getParent().getRootDir(), TRENDS_DIR + getDirectorySuffixWithSeparator()); | ||
if (!trendsDir.exists() && !trendsDir.mkdirs()) { | ||
throw new IllegalStateException("Could not create directory for trends: " + trendsDir); | ||
} | ||
|
||
// source directory (possibly on slave) | ||
String parsedJsonReportDirectory = evaluateMacro(build, workspace, listener, jsonReportDirectory); | ||
log(listener, String.format("JSON report directory is \"%s\"", parsedJsonReportDirectory)); | ||
FilePath inputReportDirectory = new FilePath(workspace, parsedJsonReportDirectory); | ||
|
||
File directoryForReport = build.getRootDir(); | ||
File directoryCache = new File( | ||
directoryForReport, | ||
ReportBuilder.BASE_DIRECTORY + getDirectorySuffixWithSeparator() + File.separatorChar + ".cache" | ||
); | ||
|
||
if (directoryCache.exists()) { | ||
throw new IllegalStateException("Cache directory " + directoryCache + " already exists. Another report with the same title already generated?"); | ||
} else if (!directoryCache.mkdirs()) { | ||
throw new IllegalStateException("Could not create directory for cache: " + directoryCache); | ||
} | ||
|
||
// copies Classifications files to cache... | ||
int copiedFilesProperties = inputReportDirectory.copyRecursiveTo(DEFAULT_FILE_INCLUDE_PATTERN_CLASSIFICATIONS, new FilePath(directoryCache)); | ||
log(listener, String.format("Copied %d properties files from workspace \"%s\" to reports directory \"%s\"", | ||
copiedFilesProperties, inputReportDirectory.getRemote(), directoryCache)); | ||
|
||
// copies custom JS and CSS files to cache... | ||
List<String> cachedCustomJsFiles = new ArrayList<>(); | ||
if (StringUtils.isNotEmpty(customJsFiles)) { | ||
cachedCustomJsFiles.addAll(copyFilesAndGetList(listener, | ||
workspace, | ||
directoryCache, | ||
customJsFiles, | ||
null) | ||
); | ||
} | ||
List<String> cachedCustomCssFiles = new ArrayList<>(); | ||
if (StringUtils.isNotEmpty(customCssFiles)) { | ||
cachedCustomCssFiles.addAll(copyFilesAndGetList(listener, | ||
workspace, | ||
directoryCache, | ||
customCssFiles, | ||
null) | ||
); | ||
} | ||
|
||
// copies JSON files to cache and | ||
// exclude JSONs that should be skipped (as configured by the user) | ||
List<String> jsonFilesToProcess = copyFilesAndGetList(listener, inputReportDirectory, directoryCache, fileIncludePattern, fileExcludePattern); | ||
log(listener, String.format("Processing %d json files:", jsonFilesToProcess.size())); | ||
for (String jsonFile : jsonFilesToProcess) { | ||
log(listener, jsonFile); | ||
} | ||
|
||
String buildNumber = Integer.toString(build.getNumber()); | ||
// this works for normal and multi-config/matrix jobs | ||
// for matrix jobs, this will include the matrix job name and the specific | ||
// configuration/permutation name as well. this also includes the '/' so | ||
// we don't have to modify how the cucumber plugin report generator's links | ||
String projectName = build.getParent().getDisplayName(); | ||
|
||
Configuration configuration = new Configuration(directoryForReport, projectName); | ||
configuration.setBuildNumber(buildNumber); | ||
configuration.setDirectorySuffix(getDirectorySuffix()); | ||
configuration.setTrends(new File(trendsDir, TRENDS_FILE), trendsLimit); | ||
configuration.setSortingMethod(SortingMethod.valueOf(sortingMethod)); | ||
if (mergeFeaturesById) { | ||
configuration.addReducingMethod(ReducingMethod.MERGE_FEATURES_BY_ID); | ||
} | ||
if (mergeFeaturesWithRetest) { | ||
configuration.addReducingMethod(ReducingMethod.MERGE_FEATURES_WITH_RETEST); | ||
} | ||
if (skipEmptyJSONFiles) { | ||
configuration.addReducingMethod(ReducingMethod.SKIP_EMPTY_JSON_FILES); | ||
} | ||
if (hideEmptyHooks) { | ||
configuration.addReducingMethod(ReducingMethod.HIDE_EMPTY_HOOKS); | ||
} | ||
if (expandAllSteps) { | ||
configuration.addPresentationModes(PresentationMode.EXPAND_ALL_STEPS); | ||
} | ||
|
||
configuration.addPresentationModes(PresentationMode.RUN_WITH_JENKINS); | ||
|
||
if (CollectionUtils.isNotEmpty(classifications)) { | ||
log(listener, String.format("Adding %d classification(s)", classifications.size())); | ||
addClassificationsToBuildReport(build, workspace, listener, configuration, classifications); | ||
} | ||
|
||
if (CollectionUtils.isNotEmpty(cachedCustomJsFiles)) { | ||
configuration.addCustomJsFiles(cachedCustomJsFiles); | ||
} | ||
|
||
if (CollectionUtils.isNotEmpty(cachedCustomCssFiles)) { | ||
configuration.addCustomCssFiles(cachedCustomCssFiles); | ||
} | ||
|
||
List<String> classificationFiles = fetchPropertyFiles(directoryCache, listener); | ||
if (CollectionUtils.isNotEmpty(classificationFiles)) { | ||
configuration.addClassificationFiles(classificationFiles); | ||
} | ||
|
||
setFailingStatuses(configuration); | ||
|
||
StreamReadConstraints.overrideDefaultStreamReadConstraints(StreamReadConstraints.builder().maxStringLength(maxStringLengthConstraint).build()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that should be implemented in core component https://github.com/damianszczepanik/cucumber-reporting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On it. |
||
|
||
ReportBuilder reportBuilder = new ReportBuilder(jsonFilesToProcess, configuration); | ||
Reportable result = reportBuilder.generateReports(); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,11 @@ | |
field="trendsLimit"> | ||
<f:textbox default="0"/> | ||
</f:entry> | ||
<f:entry | ||
title="${%maxStringLengthConstraint.title}" | ||
field="maxStringLengthConstraint"> | ||
<f:textbox default="20000000"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we should consider bumping it a little by default. Maybe 25M? This would cover the needs of all the people at #449. What do you think, @damianszczepanik? In such case, maybe making it configurable isn't even required (unless someone makes a case where a higher constraint is needed). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @damianszczepanik , would you consider this simple fix from @felipecrs for #449? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @damianszczepanik, if you agree, I can send a PR for it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO solution should be implemented first in https://github.com/damianszczepanik/cucumber-reporting |
||
</f:entry> | ||
</f:section> | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice,
would be perfect having reference to the code from that value was taken
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @damianszczepanik, here is the refence (the default value used by jackson-core, line 46):
https://github.com/FasterXML/jackson-core/pull/1019/files#diff-41ffdb6337d4e68c7ae85301ca9f13fb7fcfe00a3244e3b70a604bf49fa12efe