Skip to content

Commit

Permalink
Merge branch 'xstream-converters' of https://github.com/rankinc/cucum…
Browse files Browse the repository at this point in the history
…ber-jvm into rankinc-xstream-converters

This fixes #1010
  • Loading branch information
mpkorstanje committed Jun 18, 2017
2 parents 280113e + 9ec2d0f commit 314bfc8
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 3 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/cucumber/runtime/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, Collectio
this.resourceLoader = resourceLoader;
this.classLoader = classLoader;
this.runtimeOptions = runtimeOptions;
Glue glue = optionalGlue != null ? optionalGlue : new RuntimeGlue(undefinedStepsTracker, new LocalizedXStreams(classLoader));
Glue glue = optionalGlue != null ? optionalGlue : new RuntimeGlue(undefinedStepsTracker, new LocalizedXStreams(classLoader, runtimeOptions.getConverters()));
this.stats = new Stats(runtimeOptions.isMonochrome());
this.bus = new EventBus(stopWatch);
this.runner = new Runner(glue, bus, backends, runtimeOptions);
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/java/cucumber/runtime/RuntimeOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import cucumber.api.formatter.Formatter;
import cucumber.api.formatter.StrictAware;
import cucumber.runner.EventBus;
import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter;
import cucumber.runtime.formatter.PluginFactory;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.model.CucumberFeature;
Expand Down Expand Up @@ -34,6 +35,7 @@
import static cucumber.util.FixJava.join;
import static cucumber.util.FixJava.map;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableList;

// IMPORTANT! Make sure USAGE.txt is always uptodate if this class changes.
public class RuntimeOptions {
Expand Down Expand Up @@ -66,6 +68,7 @@ public String map(String keyword) {
private final List<String> junitOptions = new ArrayList<String>();
private final PluginFactory pluginFactory;
private final List<Object> plugins = new ArrayList<Object>();
private final List<XStreamConverter> converters = new ArrayList<XStreamConverter>();
private boolean dryRun;
private boolean strict = false;
private boolean monochrome = false;
Expand Down Expand Up @@ -211,6 +214,11 @@ private void parse(List<String> args) {
parsedPluginData.updatePluginSummaryPrinterNames(pluginSummaryPrinterNames);
}

RuntimeOptions withConverters(List<XStreamConverter> converters) {
this.converters.addAll(converters);
return this;
}

private void addLineFilters(Map<String, List<Long>> parsedLineFilters, String key, List<Long> lines) {
if (parsedLineFilters.containsKey(key)) {
parsedLineFilters.get(key).addAll(lines);
Expand Down Expand Up @@ -328,6 +336,10 @@ public List<Object> getPlugins() {
return plugins;
}

List<XStreamConverter> getConverters() {
return unmodifiableList(converters);
}

public Formatter formatter(ClassLoader classLoader) {
return pluginProxy(classLoader, Formatter.class);
}
Expand Down
27 changes: 26 additions & 1 deletion core/src/main/java/cucumber/runtime/RuntimeOptionsFactory.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cucumber.runtime;

import cucumber.api.CucumberOptions;
import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter;
import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverters;
import cucumber.runtime.formatter.PluginFactory;
import cucumber.runtime.io.MultiLoader;

Expand All @@ -22,7 +24,9 @@ public RuntimeOptionsFactory(Class clazz) {

public RuntimeOptions create() {
List<String> args = buildArgsFromOptions();
return new RuntimeOptions(args);
List<XStreamConverter> converters = buildConverters();
return new RuntimeOptions(args)
.withConverters(converters);
}

private List<String> buildArgsFromOptions() {
Expand Down Expand Up @@ -141,6 +145,27 @@ private void addJunitOptions(CucumberOptions options, List<String> args) {
}
}

private List<XStreamConverter> buildConverters() {
List<XStreamConverter> converters = new ArrayList<XStreamConverter>();

for (Class<?> classWithConverters = clazz;
hasSuperClass(classWithConverters);
classWithConverters = classWithConverters.getSuperclass()
) {
XStreamConverters xstreamConverters = classWithConverters.getAnnotation(XStreamConverters.class);
if (xstreamConverters != null) {
Collections.addAll(converters, xstreamConverters.value());
}

XStreamConverter xstreamConverter = classWithConverters.getAnnotation(XStreamConverter.class);
if (xstreamConverter != null) {
converters.add(xstreamConverter);
}
}

return converters;
}

static String packagePath(Class clazz) {
return packagePath(packageName(clazz.getName()));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
package cucumber.runtime.xstream;

import cucumber.deps.com.thoughtworks.xstream.XStream;
import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter;
import cucumber.deps.com.thoughtworks.xstream.converters.Converter;
import cucumber.deps.com.thoughtworks.xstream.converters.ConverterLookup;
import cucumber.deps.com.thoughtworks.xstream.converters.ConverterMatcher;
import cucumber.deps.com.thoughtworks.xstream.converters.ConverterRegistry;
import cucumber.deps.com.thoughtworks.xstream.converters.SingleValueConverter;
import cucumber.deps.com.thoughtworks.xstream.core.DefaultConverterLookup;
import cucumber.runtime.CucumberException;
import cucumber.runtime.ParameterInfo;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public class LocalizedXStreams {
private final Map<Locale, LocalizedXStream> xStreamsByLocale = new HashMap<Locale, LocalizedXStream>();
private final List<XStreamConverter> extraConverters;
private final ClassLoader classLoader;

public LocalizedXStreams(ClassLoader classLoader) {
public LocalizedXStreams(ClassLoader classLoader, List<XStreamConverter> extraConverters) {
this.classLoader = classLoader;
this.extraConverters = extraConverters;
}

public LocalizedXStreams(ClassLoader classLoader) {
this(classLoader, Collections.<XStreamConverter>emptyList());
}

public LocalizedXStream get(Locale locale) {
LocalizedXStream xStream = xStreamsByLocale.get(locale);
if (xStream == null) {
xStream = newXStream(locale);
registerExtraConverters(xStream);
xStreamsByLocale.put(locale, xStream);
}
return xStream;
Expand All @@ -37,6 +48,21 @@ private LocalizedXStream newXStream(Locale locale) {
return new LocalizedXStream(classLoader, lookup, lookup, locale);
}

private void registerExtraConverters(LocalizedXStream xStream) {
for (XStreamConverter converter : extraConverters) {
try {
ConverterMatcher matcher = converter.value().newInstance();
if (matcher instanceof Converter) {
xStream.registerConverter((Converter) matcher, converter.priority());
}
} catch (InstantiationException e) {
throw new CucumberException(e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new CucumberException(e.getMessage(), e);
}
}
}

public static class LocalizedXStream extends XStream {
private final Locale locale;
private final ThreadLocal<List<TimeConverter>> timeConverters = new ThreadLocal<List<TimeConverter>>() {
Expand Down
26 changes: 26 additions & 0 deletions core/src/test/java/cucumber/runtime/DummyConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package cucumber.runtime;

import cucumber.deps.com.thoughtworks.xstream.converters.Converter;
import cucumber.deps.com.thoughtworks.xstream.converters.MarshallingContext;
import cucumber.deps.com.thoughtworks.xstream.converters.UnmarshallingContext;
import cucumber.deps.com.thoughtworks.xstream.io.HierarchicalStreamReader;
import cucumber.deps.com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class DummyConverter implements Converter {

@Override
public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext ctx) {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext ctx) {
throw new UnsupportedOperationException("Not supported yet.");
}

@Override
public boolean canConvert(Class type) {
return false;
}

}
59 changes: 59 additions & 0 deletions core/src/test/java/cucumber/runtime/RuntimeOptionsFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import cucumber.api.CucumberOptions;
import cucumber.api.SnippetType;
import cucumber.runtime.io.ResourceLoader;
import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter;
import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverters;
import cucumber.deps.com.thoughtworks.xstream.converters.basic.LongConverter;
import org.junit.Test;

import java.util.Iterator;
Expand All @@ -11,9 +14,11 @@

import static cucumber.runtime.RuntimeOptionsFactory.packageName;
import static cucumber.runtime.RuntimeOptionsFactory.packagePath;
import cucumber.runtime.xstream.PatternConverter;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;

Expand Down Expand Up @@ -134,6 +139,41 @@ public void create_with_junit_options() {
assertEquals(asList("option1", "option2=value"), runtimeOptions.getJunitOptions());
}

@Test
public void create_with_xstream_converter() {
RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(ClassWithConverter.class);
RuntimeOptions runtimeOptions = runtimeOptionsFactory.create();

List<XStreamConverter> converters = runtimeOptions.getConverters();
assertNotNull(converters);
assertEquals(1, converters.size());
assertEquals(DummyConverter.class, converters.get(0).value());
}

@Test
public void create_with_xstream_converters() {
RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(ClassWithConverters.class);
RuntimeOptions runtimeOptions = runtimeOptionsFactory.create();

List<XStreamConverter> converters = runtimeOptions.getConverters();
assertNotNull(converters);
assertEquals(2, converters.size());
assertEquals(DummyConverter.class, converters.get(0).value());
assertEquals(PatternConverter.class, converters.get(1).value());
}

@Test
public void create_with_xstream_converter_from_baseclass() {
RuntimeOptionsFactory runtimeOptionsFactory = new RuntimeOptionsFactory(SubclassWithConverter.class);
RuntimeOptions runtimeOptions = runtimeOptionsFactory.create();

List<XStreamConverter> converters = runtimeOptions.getConverters();
assertNotNull(converters);
assertEquals(2, converters.size());
assertEquals(LongConverter.class, converters.get(0).value());
assertEquals(DummyConverter.class, converters.get(1).value());
}

private void assertPluginExists(List<Object> plugins, String pluginName) {
boolean found = false;
for (Object plugin : plugins) {
Expand Down Expand Up @@ -211,4 +251,23 @@ static class ClassWithNoSummaryPrinterPlugin {
static class ClassWithJunitOption {
// empty
}

@XStreamConverter(DummyConverter.class)
static class ClassWithConverter {
// empty
}

@XStreamConverters({
@XStreamConverter(DummyConverter.class),
@XStreamConverter(PatternConverter.class)
})
static class ClassWithConverters {
// empty
}

@XStreamConverter(LongConverter.class)
static class SubclassWithConverter extends ClassWithConverter {
// empty
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cucumber.runtime.xstream;

import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter;
import cucumber.deps.com.thoughtworks.xstream.converters.Converter;
import cucumber.deps.com.thoughtworks.xstream.converters.ConverterLookup;
import cucumber.deps.com.thoughtworks.xstream.converters.MarshallingContext;
import cucumber.deps.com.thoughtworks.xstream.converters.UnmarshallingContext;
import cucumber.deps.com.thoughtworks.xstream.io.HierarchicalStreamReader;
import cucumber.deps.com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;

public class ExternalConverterTest {

private List<XStreamConverter> extraConverters;
private ClassLoader classLoader;

@Before
public void setUp() throws Exception {
extraConverters = new ArrayList<XStreamConverter>();
classLoader = Thread.currentThread().getContextClassLoader();
}

@Test
public void shouldUseExtraConverter() {
extraConverters.add(Registration.class.getAnnotation(XStreamConverter.class));
LocalizedXStreams transformers = new LocalizedXStreams(classLoader, extraConverters);

ConverterLookup lookup = transformers.get(Locale.US).getConverterLookup();
Converter c = lookup.lookupConverterForType(MyClass.class);
assertTrue(c instanceof AlwaysConverter);
}

@XStreamConverter(AlwaysConverter.class)
public static class Registration {
}

public static class AlwaysConverter implements Converter {

@Override
public void marshal(Object o, HierarchicalStreamWriter writer, MarshallingContext ctx) {
throw new UnsupportedOperationException("DUMMY MARSHAL");
}

@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext ctx) {
throw new UnsupportedOperationException("DUMMY UNMARSHAL");
}

@Override
public boolean canConvert(Class type) {
return true;
}

}

public static class MyClass {
public final String s;

public MyClass(String s) {
this.s = s;
}
}

}

0 comments on commit 314bfc8

Please sign in to comment.