Skip to content

Commit

Permalink
Formatters can HTTP PUT to an URL
Browse files Browse the repository at this point in the history
  • Loading branch information
aslakhellesoy committed Nov 30, 2012
1 parent e80a5dc commit 97b099c
Show file tree
Hide file tree
Showing 20 changed files with 368 additions and 97 deletions.
1 change: 1 addition & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## [Git master](https://github.com/cucumber/cucumber-jvm/compare/v1.1.1...master)

* [Core] Formatters and `--dotcucumber` can now write to a file or an URL (via HTTP PUT). This allows easier distribution of reports. (Aslak Hellesøy)
* [JUnit] Added `@Cucumber.Options.dotcucumber`, allowing metadata to be written from JUnit. Useful for code completion. ([#418](https://github.com/cucumber/cucumber-jvm/issues/418 Aslak Hellesøy)
* [Core] Embedded data fails to display in HTML reports due to invalid string passed from HTMLFormatter ([#412](https://github.com/cucumber/cucumber-jvm/issues/412) Aslak Hellesøy)
* [Scala] Downgrade to scala 2.9.2 - we'll only use stable versions from now on. (Aslak Hellesøy)
Expand Down
12 changes: 12 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@
<version>2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.webbitserver</groupId>
<artifactId>webbit</artifactId>
<version>0.4.14</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.webbitserver</groupId>
<artifactId>webbit-rest</artifactId>
<version>0.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/cucumber/runtime/Glue.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.List;


Expand All @@ -24,5 +25,5 @@ public interface Glue {

StepDefinitionMatch stepDefinitionMatch(String uri, Step step, I18n i18n);

void writeStepdefsJson(List<String> featurePaths, File dotCucumber) throws IOException;
void writeStepdefsJson(List<String> featurePaths, URL dotCucumber) throws IOException;
}
11 changes: 5 additions & 6 deletions core/src/main/java/cucumber/runtime/RuntimeGlue.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import cucumber.runtime.autocomplete.MetaStepdef;
import cucumber.runtime.autocomplete.StepdefGenerator;
import cucumber.runtime.io.FileResourceLoader;
import cucumber.runtime.io.UTF8FileWriter;
import cucumber.runtime.io.URLOutputStream;
import cucumber.runtime.io.UTF8OutputStreamWriter;
import cucumber.runtime.model.CucumberFeature;
import cucumber.runtime.xstream.LocalizedXStreams;
import gherkin.I18n;
Expand All @@ -12,9 +13,9 @@
import gherkin.formatter.Argument;
import gherkin.formatter.model.Step;

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -100,16 +101,14 @@ private List<StepDefinitionMatch> stepDefinitionMatches(String uri, Step step) {
}

@Override
public void writeStepdefsJson(List<String> featurePaths, File dotCucumber) throws IOException {
public void writeStepdefsJson(List<String> featurePaths, URL dotCucumber) throws IOException {
if (dotCucumber != null) {
List<CucumberFeature> features = load(new FileResourceLoader(), featurePaths, NO_FILTERS);
List<MetaStepdef> metaStepdefs = new StepdefGenerator().generate(stepDefinitionsByPattern.values(), features);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(metaStepdefs);

File file = new File(dotCucumber, "stepdefs.json");
Utils.ensureParentDirExists(file);
Writer stepdefsJson = new UTF8FileWriter(file);
Writer stepdefsJson = new UTF8OutputStreamWriter(new URLOutputStream(new URL(dotCucumber, "stepdefs.json")));
stepdefsJson.append(json);
stepdefsJson.close();
}
Expand Down
8 changes: 5 additions & 3 deletions core/src/main/java/cucumber/runtime/RuntimeOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import gherkin.formatter.Reporter;
import gherkin.util.FixJava;

import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
Expand All @@ -32,7 +33,7 @@ public class RuntimeOptions {
public final List<Formatter> formatters = new ArrayList<Formatter>();
public final List<String> featurePaths = new ArrayList<String>();
private final FormatterFactory formatterFactory = new FormatterFactory();
public File dotCucumber;
public URL dotCucumber;
public boolean dryRun;
public boolean strict = false;
public boolean monochrome = false;
Expand Down Expand Up @@ -88,7 +89,8 @@ private void parse(List<String> args) {
} else if (arg.equals("--format") || arg.equals("-f")) {
formatters.add(formatterFactory.create(args.remove(0)));
} else if (arg.equals("--dotcucumber")) {
dotCucumber = new File(args.remove(0));
String urlOrPath = args.remove(0);
dotCucumber = Utils.toURL(urlOrPath);
} else if (arg.equals("--no-dry-run") || arg.equals("--dry-run") || arg.equals("-d")) {
dryRun = !arg.startsWith("--no-");
} else if (arg.equals("--no-strict") || arg.equals("--strict") || arg.equals("-s")) {
Expand Down
21 changes: 19 additions & 2 deletions core/src/main/java/cucumber/runtime/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -78,11 +80,26 @@ private static Type typeArg(Type type, Class<?> wantedRawType, int index) {
}

public static void ensureParentDirExists(File file) throws IOException {
if(file.getParentFile() != null && !file.getParentFile().isDirectory()) {
if (file.getParentFile() != null && !file.getParentFile().isDirectory()) {
boolean ok = file.getParentFile().mkdirs();
if(!ok) {
if (!ok) {
throw new IOException("Failed to create directory " + file.getParentFile().getAbsolutePath());
}
}
}

public static URL toURL(String pathOrUrl) {
try {
if (!pathOrUrl.endsWith("/")) {
pathOrUrl = pathOrUrl + "/";
}
if (pathOrUrl.matches("^(file|http|https):.*")) {
return new URL(pathOrUrl);
} else {
return new URL("file:" + pathOrUrl);
}
} catch (MalformedURLException e) {
throw new CucumberException("Bad URL:" + pathOrUrl, e);
}
}
}
36 changes: 20 additions & 16 deletions core/src/main/java/cucumber/runtime/formatter/FormatterFactory.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package cucumber.runtime.formatter;

import cucumber.runtime.CucumberException;
import cucumber.runtime.Utils;
import cucumber.runtime.io.UTF8FileWriter;
import cucumber.runtime.io.URLOutputStream;
import cucumber.runtime.io.UTF8OutputStreamWriter;
import gherkin.formatter.Formatter;
import gherkin.formatter.JSONFormatter;
import gherkin.formatter.JSONPrettyFormatter;
Expand All @@ -12,11 +12,14 @@
import java.io.OutputStreamWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static cucumber.runtime.Utils.toURL;

/**
* This class creates {@link Formatter} instances (that may also implement {@link gherkin.formatter.Reporter} from
* a String.
Expand All @@ -27,7 +30,7 @@
* The formatter class must have a constructor that is either empty or takes a single {@link Appendable} or a {@link File}.
*/
public class FormatterFactory {
private final Class[] CTOR_ARGS = new Class[]{null, Appendable.class, File.class};
private final Class[] CTOR_ARGS = new Class[]{null, Appendable.class, URL.class, File.class};

private static final Map<String, Class<? extends Formatter>> FORMATTER_CLASSES = new HashMap<String, Class<? extends Formatter>>() {{
put("null", NullFormatter.class);
Expand Down Expand Up @@ -65,11 +68,11 @@ public Formatter create(String formatterString) {
}
}

private Formatter instantiate(String formatterString, Class<? extends Formatter> formatterClass, String path) throws IOException {
private Formatter instantiate(String formatterString, Class<? extends Formatter> formatterClass, String pathOrUrl) throws IOException {
for (Class ctorArgClass : CTOR_ARGS) {
Constructor<? extends Formatter> constructor = findConstructor(formatterClass, ctorArgClass);
if (constructor != null) {
Object ctorArg = convertOrNull(path, ctorArgClass);
Object ctorArg = convertOrNull(pathOrUrl, ctorArgClass);
try {
if (ctorArgClass == null) {
return constructor.newInstance();
Expand All @@ -88,25 +91,26 @@ private Formatter instantiate(String formatterString, Class<? extends Formatter>
}
}
}
throw new CucumberException(String.format("%s must have a constructor that is either empty or takes a %s or %s", formatterClass, Appendable.class.getName(), File.class.getName()));
throw new CucumberException(String.format("%s must have a constructor that is either empty or takes a %s or %s", formatterClass, Appendable.class.getName(), URL.class.getName()));
}

private Object convertOrNull(String path, Class ctorArgClass) throws IOException {
private Object convertOrNull(String pathOrUrl, Class ctorArgClass) throws IOException {
if (ctorArgClass == null) {
return null;
}
if (ctorArgClass.equals(URL.class)) {
if (pathOrUrl != null) {
return toURL(pathOrUrl);
}
}
if (ctorArgClass.equals(File.class)) {
if (path != null) {
File file = new File(path);
Utils.ensureParentDirExists(file);
return file;
if (pathOrUrl != null) {
return new File(pathOrUrl);
}
}
if (ctorArgClass.equals(Appendable.class)) {
if (path != null) {
File file = new File(path);
Utils.ensureParentDirExists(file);
return new UTF8FileWriter(file);
if (pathOrUrl != null) {
return new UTF8OutputStreamWriter(new URLOutputStream(toURL(pathOrUrl)));
} else {
return defaultOutOrFailIfAlreadyUsed();
}
Expand Down Expand Up @@ -147,7 +151,7 @@ private Appendable defaultOutOrFailIfAlreadyUsed() {
if (defaultOut != null) {
return defaultOut;
} else {
throw new CucumberException("Only one formatter can use STDOUT. If you use more than one formatter you must specify output path with FORMAT:PATH");
throw new CucumberException("Only one formatter can use STDOUT. If you use more than one formatter you must specify output path with FORMAT:PATH_OR_URL");
}
} finally {
defaultOut = null;
Expand Down
19 changes: 8 additions & 11 deletions core/src/main/java/cucumber/runtime/formatter/HTMLFormatter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cucumber.runtime.formatter;

import cucumber.runtime.CucumberException;
import cucumber.runtime.io.URLOutputStream;
import gherkin.deps.com.google.gson.Gson;
import gherkin.deps.com.google.gson.GsonBuilder;
import gherkin.formatter.Formatter;
Expand All @@ -17,13 +18,11 @@
import gherkin.formatter.model.Step;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -43,13 +42,13 @@ class HTMLFormatter implements Formatter, Reporter {
}
};

private final File htmlReportDir;
private final URL htmlReportDir;
private NiceAppendable jsOut;

private boolean firstFeature = true;
private int embeddedIndex;

public HTMLFormatter(File htmlReportDir) {
public HTMLFormatter(URL htmlReportDir) {
this.htmlReportDir = htmlReportDir;
}

Expand Down Expand Up @@ -202,20 +201,18 @@ private NiceAppendable jsOut() {
if (jsOut == null) {
try {
jsOut = new NiceAppendable(new OutputStreamWriter(reportFileOutputStream(JS_REPORT_FILENAME), "UTF-8"));
} catch (UnsupportedEncodingException e) {
} catch (IOException e) {
throw new CucumberException(e);
}
}
return jsOut;
}

private OutputStream reportFileOutputStream(String fileName) {
htmlReportDir.mkdirs();
File file = new File(htmlReportDir, fileName);
try {
return new FileOutputStream(file);
} catch (FileNotFoundException e) {
throw new CucumberException("Error creating file: " + file.getAbsolutePath(), e);
return new URLOutputStream(new URL(htmlReportDir, fileName));
} catch (IOException e) {
throw new CucumberException(e);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cucumber.runtime.formatter;

import cucumber.runtime.CucumberException;
import cucumber.runtime.io.URLOutputStream;
import cucumber.runtime.io.UTF8OutputStreamWriter;
import gherkin.formatter.Formatter;
import gherkin.formatter.Reporter;
import gherkin.formatter.model.Background;
Expand All @@ -22,24 +24,26 @@
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

class JUnitFormatter implements Formatter, Reporter {
private final File out;
private final Writer out;
private final Document doc;
private final Element rootElement;

private TestCase testCase;

public JUnitFormatter(File out) {
this.out = out;
public JUnitFormatter(URL out) throws IOException {
this.out = new UTF8OutputStreamWriter(new URLOutputStream(out));
try {
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
rootElement = doc.createElement("testsuite");
Expand Down
Loading

0 comments on commit 97b099c

Please sign in to comment.