diff --git a/metamorph/build.gradle b/metamorph/build.gradle
index ed537f1a5..5fd1c5121 100644
--- a/metamorph/build.gradle
+++ b/metamorph/build.gradle
@@ -25,6 +25,9 @@ dependencies {
implementation project(':metafacture-mangling')
implementation project(':metafacture-javaintegration')
implementation 'org.slf4j:slf4j-api:1.7.21'
+ testImplementation project(':metafacture-io')
+ testImplementation project(':metafacture-json')
+ testImplementation project(':metafacture-strings')
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.5.5'
testRuntimeOnly 'org.slf4j:slf4j-simple:1.7.21'
@@ -45,4 +48,6 @@ test {
showStandardStreams = true
exceptionFormat = 'full'
}
+
+ systemProperties System.properties
}
diff --git a/metamorph/src/test/java/org/metafacture/metamorph/Issue366Test.java b/metamorph/src/test/java/org/metafacture/metamorph/Issue366Test.java
new file mode 100644
index 000000000..bed96a453
--- /dev/null
+++ b/metamorph/src/test/java/org/metafacture/metamorph/Issue366Test.java
@@ -0,0 +1,96 @@
+package org.metafacture.metamorph;
+
+import org.metafacture.io.LineReader;
+import org.metafacture.io.ObjectJavaIoWriter;
+import org.metafacture.json.JsonDecoder;
+import org.metafacture.json.JsonEncoder;
+import org.metafacture.strings.StringReader;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.LongAdder;
+import java.util.stream.IntStream;
+
+public final class Issue366Test {
+
+ private static final String INPUT = "{\"10012\":{\"a\":\"V\"}}";
+
+ private static final String[] OUTPUT = new String[]{
+ "{\"r\":{\"k\":\"K\",\"v\":\"V\"}}\n",
+ "{\"r\":{\"v\":\"V\",\"k\":\"K\"}}\n",
+ "{\"r\":{\"k\":\"K\",\"v\":\"V\"}}\n",
+ "{\"r\":{\"k\":\"K\",\"v\":\"V\"}}\n"
+ };
+
+ private static final String MORPH = "" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ "";
+
+ private static final int OPTION = Integer.valueOf(System.getProperty("issue366.option", "0"));
+
+ private static final int ITER = Integer.valueOf(System.getProperty("issue366.iter", "10"));
+
+ private final Map outcome = new HashMap<>();
+
+ private final Object[] args = new String[]{".a", "?2", "??"};
+ // Either one of these replacements avoids the issue:
+ private final Object[] opts = new String[]{"", "??", "?2"};
+
+ @Test
+ public void testTransform() {
+ if (OPTION > args.length) {
+ throw new RuntimeException("Invalid option: " + OPTION);
+ }
+ else if (OPTION > 0) {
+ args[OPTION - 1] = opts[OPTION - 1];
+ }
+
+ final String morph = String.format(MORPH, args);
+ System.out.println(morph);
+
+ IntStream.range(0, ITER).forEach(i -> transform(morph));
+ outcome.forEach((k, v) -> System.err.print(v.sum() + "/" + ITER + ": " + k));
+
+ Assert.assertEquals(1, outcome.size());
+ Assert.assertEquals(OUTPUT[OPTION], outcome.keySet().iterator().next());
+ }
+
+ private void transform(final String morph) {
+ final StringReader reader = new StringReader();
+ final Writer writer = new StringWriter();
+
+ reader
+ .setReceiver(new LineReader())
+ .setReceiver(new JsonDecoder())
+ .setReceiver(new Metamorph(new java.io.StringReader(morph)))
+ .setReceiver(new JsonEncoder())
+ // ObjectJavaIoWriter in org.metafacture.io has been deprecated
+ .setReceiver(new ObjectJavaIoWriter<>(writer));
+
+ try {
+ reader.process(INPUT);
+ reader.closeStream();
+
+ outcome.computeIfAbsent(writer.toString(), k -> new LongAdder()).increment();
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}