Skip to content
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

changes to overcome performance issues with huge fitnesse result files #10

Merged
merged 5 commits into from
Dec 12, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ public FitnesseBuildAction(boolean fitnesseStarted, String fitnesseHost, int fit
}

public String getLinkFor(String fitnessePage) {
return getLinkFor(fitnessePage, null);
return getLinkFor(fitnessePage, null, fitnessePage);
}

public String getLinkFor(String fitnessePage, String hudsonHost) {
return getLinkFor(fitnessePage, hudsonHost, fitnessePage);
}

public String getLinkFor(String fitnessePage, String hudsonHost, String display) {
if (fitnesseStarted) return fitnessePage;

String host = fitnesseHost;
Expand All @@ -40,7 +44,7 @@ public String getLinkFor(String fitnessePage, String hudsonHost) {
}
}
return String.format("<a href=\"http://%s:%s/%s\">%s</a>",
host, fitnessePort, fitnessePage, fitnessePage);
host, fitnessePort, fitnessePage, display);
}

}
13 changes: 13 additions & 0 deletions src/main/java/hudson/plugins/fitnesse/FitnesseResults.java
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ public String toHtml(FitnesseResults results) {

/**
* referenced in body.jelly. Link is apparently relative to
* This is the left column, which reads the results from file
*/
public String getDetailsLink() {
if (details == null) {
Expand All @@ -342,6 +343,18 @@ public String getDetailsLink() {
// return String.format("<a href=\"%s/%s\">%s</a>",
// getUrl(), "Details", "Details");
}

/**
* referenced in body.jelly.
* The link points to the history of the fitnesse server. Note the history may not always be available.
*/
public String getDetailRemoteLink() {
FitnesseBuildAction buildAction = getOwner().getAction(FitnesseBuildAction.class);
if (buildAction == null) {
buildAction = FitnesseBuildAction.NULL_ACTION;
}
return buildAction.getLinkFor(getName() + "?pageHistory&resultDate="+getResultsDate(), null, "Details");
}

@Override
public String getErrorDetails() {
Expand Down
166 changes: 125 additions & 41 deletions src/main/java/hudson/plugins/fitnesse/FitnesseResultsRecorder.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@
import hudson.model.Result;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.plugins.fitnesse.NativePageCounts.Counts;
import hudson.tasks.BuildStep;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Publisher;
import hudson.tasks.Recorder;
import hudson.util.FormValidation;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
Expand All @@ -24,6 +28,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.xml.transform.TransformerException;
Expand All @@ -32,7 +37,7 @@
import org.kohsuke.stapler.QueryParameter;

public class FitnesseResultsRecorder extends Recorder {

private final String fitnessePathToXmlResultsIn;

@DataBoundConstructor
Expand All @@ -52,7 +57,8 @@ public String getFitnessePathToXmlResultsIn() {
*/
@Override
public Collection<Action> getProjectActions(AbstractProject<?, ?> project) {
return Collections.<Action>singleton(new FitnesseProjectAction(project));
return Collections
.<Action> singleton(new FitnesseProjectAction(project));
}

/**
Expand All @@ -70,29 +76,37 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
BuildListener listener) throws InterruptedException, IOException {
try {
FilePath[] resultFiles = getResultFiles(build);
FitnesseResults results = getResults(listener.getLogger(), resultFiles);
if (results == null) return true; // no Fitnesse results found at all

FitnesseResultsAction action = new FitnesseResultsAction(build, results);
if (results.getBuildResult() != null) build.setResult(results.getBuildResult());
FitnesseResults results = getResults(listener.getLogger(),
resultFiles, build.getRootDir());
if (results == null)
return true; // no Fitnesse results found at all

FitnesseResultsAction action = new FitnesseResultsAction(build,
results);
if (results.getBuildResult() != null)
build.setResult(results.getBuildResult());
build.addAction(action);
return true;
} catch (Throwable t) {
t.printStackTrace(listener.getLogger());
if (t instanceof InterruptedException) throw (InterruptedException) t;
if (t instanceof InterruptedException)
throw (InterruptedException) t;
build.setResult(Result.FAILURE);
return false;
}
}

private FilePath[] getResultFiles(AbstractBuild<?, ?> build) throws IOException, InterruptedException {
private FilePath[] getResultFiles(AbstractBuild<?, ?> build)
throws IOException, InterruptedException {
FilePath workingDirectory = FitnesseExecutor.getWorkingDirectory(build);
return getResultFiles(workingDirectory);
}

public FilePath[] getResultFiles(FilePath workingDirectory) throws IOException, InterruptedException {
FilePath resultsFile = FitnesseExecutor.getResultsFilePath(workingDirectory, fitnessePathToXmlResultsIn);


public FilePath[] getResultFiles(FilePath workingDirectory)
throws IOException, InterruptedException {
FilePath resultsFile = FitnesseExecutor.getResultsFilePath(
workingDirectory, fitnessePathToXmlResultsIn);

if (resultsFile.exists()) {
// directly configured single file
return new FilePath[] { resultsFile };
Expand All @@ -102,14 +116,16 @@ public FilePath[] getResultFiles(FilePath workingDirectory) throws IOException,
}
}

public FitnesseResults getResults(PrintStream logger, FilePath[] resultsFiles) throws IOException, TransformerException {
public FitnesseResults getResults(PrintStream logger,
FilePath[] resultsFiles, File rootDir)
throws IOException, TransformerException {
List<FitnesseResults> resultsList = new ArrayList<FitnesseResults>();

for (FilePath filePath : resultsFiles) {
FitnesseResults singleResults = getResults(logger, filePath);
FitnesseResults singleResults = getResults(logger, filePath, rootDir);
resultsList.add(singleResults);
}

if (resultsList.isEmpty()) {
return null;
}
Expand All @@ -118,18 +134,25 @@ public FitnesseResults getResults(PrintStream logger, FilePath[] resultsFiles) t
}
return CompoundFitnesseResults.createFor(resultsList);
}

public FitnesseResults getResults(PrintStream logger, FilePath resultsFile) throws IOException, TransformerException {

public FitnesseResults getResults(PrintStream logger, FilePath resultsFile,
File rootDir) throws IOException, TransformerException {
InputStream resultsInputStream = null;
try {
logger.println("Reading results as " + Charset.defaultCharset().displayName()
+ " from " + resultsFile.getRemote());
logger.println("Reading results as "
+ Charset.defaultCharset().displayName() + " from "
+ resultsFile.getRemote());
resultsInputStream = resultsFile.read();

logger.println("Parsing results... ");
NativePageCountsParser pageCountsParser = new NativePageCountsParser();
NativePageCounts pageCounts = pageCountsParser.parse(resultsInputStream);

NativePageCounts pageCounts = pageCountsParser
.parse(resultsInputStream);

logger.println("all-content: " + pageCounts.getAllContents().size());
logger.println("resultsFile: " + getFitnessePathToXmlResultsIn());
writeFitnesseResultFiles(logger, pageCounts, rootDir);

logger.println("Got results: " + pageCounts.getSummary());
return new FitnesseResults(pageCounts);
} finally {
Expand All @@ -152,34 +175,95 @@ public DescriptorImpl getDescriptor() {
}

/**
* See <tt>src/main/resources/hudson/plugins/fitnesse/FitnesseResultsRecorder/config.jelly</tt>
*/
@Extension
public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> {

public FormValidation doCheckFitnessePathToXmlResultsIn(@QueryParameter String value) throws IOException, ServletException {
if (value.length()==0)
return FormValidation.error("Please specify where to read fitnesse results from.");
if (!value.endsWith("xml"))
return FormValidation.warning("File does not end with 'xml': is that correct?");
return FormValidation.ok();
* See
* <tt>src/main/resources/hudson/plugins/fitnesse/FitnesseResultsRecorder/config.jelly</tt>
*/
@Extension
public static final class DescriptorImpl extends
BuildStepDescriptor<Publisher> {

public FormValidation doCheckFitnessePathToXmlResultsIn(
@QueryParameter String value) throws IOException,
ServletException {
if (value.length() == 0)
return FormValidation
.error("Please specify where to read fitnesse results from.");
if (!value.endsWith("xml"))
return FormValidation
.warning("File does not end with 'xml': is that correct?");
return FormValidation.ok();
}

/**
* {@link BuildStepDescriptor}
*/
@Override
* {@link BuildStepDescriptor}
*/
@Override
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
// works with any kind of project
return true;
}

/**
* {@link ModelObject}
*/
* {@link ModelObject}
*/
@Override
public String getDisplayName() {
return "Publish Fitnesse results report";
}
}

/**
* Gets a parsed fitnesse result and writes it to separate file. Putting the
* fitnesse result in a separate file as performance reasons. E.g. for a
* huge Test-Suite the actual fitnesse result can grow up to several MB.
* First implementation of fitnesse plugin has stored the result to the
* build.xml. This was very handy to present the result but slowed down
* jenkins since a request to the fitnesse result leat to putting the entire
* build.xml into the memory. With this function the fitnesse result is only
* load to memory if the user clicks on it.
*
* @param logger
* @param pageCounts
* @param rootDir
*/
private void writeFitnesseResultFiles(PrintStream logger,
NativePageCounts pageCounts, File rootDir) {
String rootDirName = rootDir.getAbsolutePath() + "/";
logger.println("write fitnesse results to: " + rootDirName);
Map<String, String> allContent = pageCounts.getAllContents();
logger.println("allContent:\n" + allContent.keySet());
// iterate over all fitnesse tests in a suite
for (Counts iCount : pageCounts.getAllCounts()) {
String name = iCount.page;
String content = allContent.get(name);
if (null == content) {
logger.println("could not find content for page: " + name);
continue;
}
BufferedWriter out = null;
String fileName = rootDirName + name;
try {
// Create separate file for every test in a suite
FileWriter fstream = new FileWriter(fileName);
out = new BufferedWriter(fstream);
out.write(content);
// Just store the path to the filename.
// Any help welcome how to
// restore the path of the saved file when the user wants to see the details
iCount.contentFile = fileName;
} catch (IOException e) {
logger.println("error while writing to out file" + fileName
+ "\n" + e.toString());
} finally {
if (null != out) {
try {
out.close();
} catch (IOException e) {
logger.println("could not close out stream: " + "\n"
+ fileName + e.toString());
}
}
}
}
}
}
Loading