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

Add output handler to configuration #958

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
89 changes: 67 additions & 22 deletions src/main/java/net/masterthought/cucumber/Configuration.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package net.masterthought.cucumber;

import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import net.masterthought.cucumber.json.support.Status;
import net.masterthought.cucumber.outputhandlers.FilesystemOutputHandler;
import net.masterthought.cucumber.outputhandlers.OutputHandler;
import net.masterthought.cucumber.presentation.PresentationMode;
import net.masterthought.cucumber.reducers.ReducingMethod;
import net.masterthought.cucumber.sorting.SortingMethod;
import org.apache.commons.lang.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;

import java.io.File;
import java.util.*;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you revert this change?
prefer to have full name class to import

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class Configuration {

private static final String EMBEDDINGS_DIRECTORY = "embeddings";
Expand All @@ -29,7 +31,7 @@ public class Configuration {
private Collection<Pattern> tagsToExcludeFromChart = new ArrayList<>();
private SortingMethod sortingMethod = SortingMethod.NATURAL;
private List<ReducingMethod> reducingMethods = new ArrayList<>();

private List<OutputHandler> outputHandlers = new ArrayList<>();
private List<PresentationMode> presentationModes = new ArrayList<>();
private List<String> classificationFiles;

Expand All @@ -40,6 +42,7 @@ public class Configuration {
public Configuration(File reportDirectory, String projectName) {
this.reportDirectory = reportDirectory;
this.projectName = projectName;
outputHandlers.add(new FilesystemOutputHandler());
}

/**
Expand Down Expand Up @@ -71,6 +74,7 @@ public boolean isTrendsStatsFile() {

/**
* Calls {@link #setTrends(File, int)} with zero limit.
*
* @param trendsFile file with trends
*/
public void setTrendsStatsFile(File trendsFile) {
Expand All @@ -88,10 +92,11 @@ public int getTrendsLimit() {

/**
* Checks if the trends page should be generated and displayed.
*
* @return <code>true</code> if the page with trends should be displayed
*/
public boolean isTrendsAvailable() {
return getTrendsLimit() > -1 && isTrendsStatsFile();
return getTrendsLimit() > -1 && isTrendsStatsFile();
}

/**
Expand All @@ -100,12 +105,12 @@ public boolean isTrendsAvailable() {
* To disable saving and displaying trends page set to -1.
* Otherwise number of previous builds is equal to provided limit.
*
* @param trendsFile file where information about previous builds is stored
* @param limit number of builds that should be presented (older builds are skipped)
* @param trendsFile file where information about previous builds is stored
* @param limit number of builds that should be presented (older builds are skipped)
*/
public void setTrends(File trendsFile, int limit) {
this.trendsFile = trendsFile;
this.trendsLimit = limit;
trendsLimit = limit;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the formatter works strange :)
this line is updated but above doesn't

}

/**
Expand Down Expand Up @@ -169,7 +174,7 @@ public String getDirectorySuffixWithSeparator() {
*/
public File getEmbeddingDirectory() {
return new File(getReportDirectory().getAbsolutePath(), ReportBuilder.BASE_DIRECTORY +
this.getDirectorySuffixWithSeparator() + File.separatorChar + Configuration.EMBEDDINGS_DIRECTORY);
getDirectorySuffixWithSeparator() + File.separatorChar + Configuration.EMBEDDINGS_DIRECTORY);
}

/**
Expand Down Expand Up @@ -226,7 +231,7 @@ public void setSortingMethod(SortingMethod sortingMethod) {
* Returns the default sorting method.
*/
public SortingMethod getSortingMethod() {
return this.sortingMethod;
return sortingMethod;
}

/**
Expand All @@ -235,7 +240,7 @@ public SortingMethod getSortingMethod() {
* @param reducingMethod type of reduction
*/
public void addReducingMethod(ReducingMethod reducingMethod) {
this.reducingMethods.add(reducingMethod);
reducingMethods.add(reducingMethod);
}

/**
Expand All @@ -249,6 +254,7 @@ public List<ReducingMethod> getReducingMethods() {

/**
* Checks if the configuration has given {@link ReducingMethod} set.
*
* @param reducingMethod method to validate
* @return <code>true</code> if method was set, otherwise <code>false</code>
*/
Expand All @@ -262,7 +268,7 @@ public boolean containsReducingMethod(ReducingMethod reducingMethod) {
* @param presentationMode method used for presentation
*/
public void addPresentationModes(PresentationMode presentationMode) {
this.presentationModes.add(presentationMode);
presentationModes.add(presentationMode);
}

/**
Expand All @@ -288,7 +294,7 @@ public void addClassificationFiles(List<String> classificationFiles) {
* Returns the list of properties files.
*/
public List<String> getClassificationFiles() {
return this.classificationFiles;
return classificationFiles;
}

/**
Expand All @@ -310,36 +316,75 @@ public void setNotFailingStatuses(Set<Status> notFailingStatuses) {

/**
* Sets explicit qualifier to use for the given json file.
* @param jsonFileName JSON file name - without the extension
* @param qualifier Qualifier to use
*
* @param jsonFileName JSON file name - without the extension
* @param qualifier Qualifier to use
*/
public void setQualifier(@NonNull String jsonFileName, @NonNull String qualifier) {
qualifiers.put(jsonFileName, qualifier);
}

/**
* Retrieves explicit qualifier to use for a given json file.
* @param jsonFileName JSON file name - without the extension
* @return Qualifier specified for this file or <code>null</code> if none specified
*
* @param jsonFileName JSON file name - without the extension
* @return Qualifier specified for this file or <code>null</code> if none specified
*/
public String getQualifier(@NonNull String jsonFileName) {
return qualifiers.get(jsonFileName);
}

/**
* Checks whether an explicit qualifier was specified for a given json file.
* @param jsonFileName JSON file name - without the extension
* @return <code>true</code> if the qualifier was specified, <code>false</code> otherwise
*
* @param jsonFileName JSON file name - without the extension
* @return <code>true</code> if the qualifier was specified, <code>false</code> otherwise
*/
public boolean containsQualifier(@NonNull String jsonFileName) {
return qualifiers.containsKey(jsonFileName);
}

/**
* Removes explicit qualifier for a given json file.
* @param jsonFileName JSON file name - without the extension
*
* @param jsonFileName JSON file name - without the extension
*/
public void removeQualifier(@NonNull String jsonFileName) {
qualifiers.remove(jsonFileName);
}

/**
* Adds the given OutputHandler to the List of OutputHandlers
*
* @param outputHandler Handler that will be called with the generated files
*/
public void addOutputHandler(OutputHandler outputHandler) {
outputHandlers.add(outputHandler);
}

/**
* Removes the given OutputHandler from the List of OutputHandlers
*
* @param outputHandlerToRemove OutputHandler to be removed
*/
public void removeOutputHandler(OutputHandler outputHandlerToRemove) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if we need it as long as this is used only by test

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Idea is that it can be used as a library, not every Method a library offers is used by itself.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class is not even designed that way so probably not used that way. Please apply philosophy YAGNI

outputHandlers.remove(outputHandlerToRemove);
}

/**
* Gets the List of all currently configured OutputHandlers
* By default the {@link FilesystemOutputHandler} is configured
*
* @return the List of OutputHandlers
*/
public List<OutputHandler> getOutputHandlers() {
return outputHandlers;
}

/**
* Clears all OutputHandlers from the List of OutputHandlers
*/
public void clearOutputHandlers() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another method used only by tests

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Idea is that it can be used as a library, not every Method a library offers is used by itself.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

outputHandlers.clear();
}
}
43 changes: 11 additions & 32 deletions src/main/java/net/masterthought/cucumber/ReportBuilder.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,19 @@
package net.masterthought.cucumber;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import net.masterthought.cucumber.generators.ErrorPage;
import net.masterthought.cucumber.generators.FailuresOverviewPage;
import net.masterthought.cucumber.generators.FeatureReportPage;
import net.masterthought.cucumber.generators.FeaturesOverviewPage;
import net.masterthought.cucumber.generators.StepsOverviewPage;
import net.masterthought.cucumber.generators.TagReportPage;
import net.masterthought.cucumber.generators.TagsOverviewPage;
import net.masterthought.cucumber.generators.TrendsOverviewPage;
import net.masterthought.cucumber.generators.*;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

import net.masterthought.cucumber.json.Feature;
import net.masterthought.cucumber.json.support.TagObject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.*;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
FlorianKaemmerer marked this conversation as resolved.
Show resolved Hide resolved

public class ReportBuilder {

Expand Down Expand Up @@ -83,9 +69,6 @@ public Reportable generateReports() {
// first copy static resources so ErrorPage is displayed properly
copyStaticResources();

// create directory for embeddings before files are generated
createEmbeddingsDirectory();

// add metadata info sourced from files
reportParser.parseClassificationsFiles(configuration.getClassificationFiles());

Expand Down Expand Up @@ -133,18 +116,14 @@ private void copyStaticResources() {
copyResources("images", "favicon.png");
}

private void createEmbeddingsDirectory() {
configuration.getEmbeddingDirectory().mkdirs();
}

private void copyResources(String resourceLocation, String... resources) {
for (String resource : resources) {
File tempFile = new File(configuration.getReportDirectory().getAbsoluteFile(),
BASE_DIRECTORY + configuration.getDirectorySuffixWithSeparator() + File.separatorChar + resourceLocation + File.separatorChar + resource);
// don't change this implementation unless you verified it works on Jenkins
try {
FileUtils.copyInputStreamToFile(
this.getClass().getResourceAsStream("/" + resourceLocation + "/" + resource), tempFile);
try (InputStream resourceAsStream = getClass().getResourceAsStream("/" + resourceLocation + "/" + resource)) {
final byte[] resourceAsBytes = IOUtils.toByteArray(resourceAsStream);
configuration.getOutputHandlers().forEach(outputHandler -> outputHandler.handle(tempFile, resourceAsBytes));
} catch (IOException e) {
// based on FileUtils implementation, should never happen even is declared
throw new ValidationException(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
package net.masterthought.cucumber.generators;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import net.masterthought.cucumber.Configuration;
import net.masterthought.cucumber.ReportBuilder;
import net.masterthought.cucumber.ReportResult;
Expand All @@ -27,6 +17,12 @@
import org.apache.velocity.app.event.EventCartridge;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import java.io.*;
Copy link
Owner

@damianszczepanik damianszczepanik Oct 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets separate all changes related to formatting to another PR which will be merged first and then we can reduce number of changes only to the important once.
Also be consistent :)

import java.nio.charset.StandardCharsets;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Delivers common methods for page generation.
*
Expand Down Expand Up @@ -57,7 +53,7 @@ protected AbstractPage(ReportResult reportResult, String templateFileName, Confi
this.reportResult = reportResult;
this.configuration = configuration;

this.engine.init(buildProperties());
engine.init(buildProperties());
buildGeneralParameters();
}

Expand All @@ -81,8 +77,12 @@ private void generateReport() {
Template template = engine.getTemplate("templates/generators/" + templateFileName);
File reportFile = new File(configuration.getReportDirectory(),
ReportBuilder.BASE_DIRECTORY + configuration.getDirectorySuffixWithSeparator() + File.separatorChar + getWebPage());
try (Writer writer = new OutputStreamWriter(new FileOutputStream(reportFile), StandardCharsets.UTF_8)) {

try (ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
Writer writer = new OutputStreamWriter(new BufferedOutputStream(out), StandardCharsets.UTF_8)) {
template.merge(context, writer);
writer.flush();
configuration.getOutputHandlers().forEach(outputHandler -> outputHandler.handle(reportFile, out.toByteArray()));
} catch (IOException e) {
throw new ValidationException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@

import com.fasterxml.jackson.databind.JsonNode;
import net.masterthought.cucumber.Configuration;
import net.masterthought.cucumber.ValidationException;
import net.masterthought.cucumber.json.Embedding;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;

Expand Down Expand Up @@ -37,18 +34,18 @@ public Embedding deserialize(JsonNode rootNode, Configuration configuration) {
embedding = new Embedding(mimeType, encodedData);
}

storeEmbedding(embedding, configuration.getEmbeddingDirectory());
storeEmbedding(embedding, configuration);

return embedding;
}

private String getBase64EncodedData(String data) {
try{
try {
// If we can successfully decode the data we consider it to be base64 encoded,
// so we do not need to do anything here
Base64.getDecoder().decode(data);
return data;
}catch (IllegalArgumentException e){
} catch (IllegalArgumentException e) {
// decoding failed, therefore we consider the data not to be encoded,
// so we need to encode it
return new String(Base64.getEncoder().encode(data.getBytes(UTF_8)), UTF_8);
Expand All @@ -65,15 +62,12 @@ private String findMimeType(JsonNode rootNode) {
return rootNode.get("mime_type").asText();
}

private void storeEmbedding(Embedding embedding, File embeddingDirectory) {
Path file = FileSystems.getDefault().getPath(embeddingDirectory.getAbsolutePath(),
private void storeEmbedding(Embedding embedding, Configuration configuration) {
Path path = FileSystems.getDefault().getPath(configuration.getEmbeddingDirectory().getAbsolutePath(),
embedding.getFileId() + "." + embedding.getExtension());
final File file = new File(path.toUri());
byte[] decodedData = Base64.getDecoder().decode(embedding.getData().getBytes(UTF_8));
try {
Files.write(file, decodedData);
} catch (IOException e) {
throw new ValidationException(e);
}
configuration.getOutputHandlers().forEach(outputHandler -> outputHandler.handle(file, decodedData));
}

}
Loading