-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Closed
Labels
defectSomething that is supposed to work, but doesn't. Less severe than a "bug"Something that is supposed to work, but doesn't. Less severe than a "bug"
Milestone
Description
I've been struggling a bit with the XMLExporter recently in my game and this prompted me to write some unit tests for the JmeExporter/JmeImporter implementations. You can see the tests on my fork here:
https://github.com/JosiahGoeman/jmonkeyengine/blob/master/jme3-plugins/src/test/java/com/jme3/export/InputOutputCapsuleTest.java.
All write* and read* methods in OutputCapsule and InputCapsule respectively are tested here. I put in all the edge cases I could think of. BinaryExporter/BinaryImporter pass all tests no problemo, but XMLExporter/XMLImporter do not.
Here's the problems I've discovered so far:
- ☑
Write an empty string -> Reads defVal as if attribute was not present - ☑
Write a string containing an apostrophe/single quote -> throws IOException when reading - ☑
Write a string containing tab, newline, or carriage return -> Reads string with these characters replaced with spaces - ☑
Write any BitSet object -> Reads BitSet containing a single zero - ☑
Write String[] containing a null string -> Reads array with null string having been replaced with an empty string - ☑
Write a 2d array of any type except int[][] containing a null element -> Throws NullPointerException when writing - ☑
Write an ArrayList containing a null element -> Throws IOException when reading - ☑
Write an ArrayList<ByteBuffer> -> Reads ArrayList with same number of entries, but all are null
XMLExporterMREs.java
package com.mygame;
import com.jme3.asset.AssetInfo;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.export.binary.BinaryExporter;
import com.jme3.export.binary.BinaryImporter;
import com.jme3.export.xml.XMLExporter;
import com.jme3.export.xml.XMLImporter;
import com.jme3.math.Vector3f;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Objects;
import org.lwjgl.BufferUtils;
public class XMLExporterMREs
{
static class TestString implements Savable
{
static String testString;
@Override
public void write(JmeExporter je) throws IOException
{
OutputCapsule capsule = je.getCapsule(this);
capsule.write(testString, "test_string", null);
}
@Override
public void read(JmeImporter ji) throws IOException
{
InputCapsule capsule = ji.getCapsule(this);
String readString = capsule.readString("test_string", null);
if(!Objects.equals(testString, readString))
throw new IOException("Expected " + makeWhitespaceExplicit(testString) + " but got " + makeWhitespaceExplicit(readString));
}
}
static class TestBitSet implements Savable
{
private static final BitSet testBitSet = BitSet.valueOf("BitSet".getBytes());
@Override
public void write(JmeExporter je) throws IOException
{
OutputCapsule capsule = je.getCapsule(this);
capsule.write(testBitSet, "test_bit_set", null);
}
@Override
public void read(JmeImporter ji) throws IOException
{
InputCapsule capsule = ji.getCapsule(this);
BitSet bs = capsule.readBitSet("test_bit_set", null);
if(!testBitSet.equals(bs))
throw new IOException("Expected " + testBitSet + " but got " + bs);
}
}
static class TestStringArrayWithNull implements Savable
{
private static final String[] testStringArray = new String[]
{
"hello",
null,
"world"
};
@Override
public void write(JmeExporter je) throws IOException
{
OutputCapsule capsule = je.getCapsule(this);
try
{
capsule.write(testStringArray, "test_string_array", null);
} catch(Exception e)
{
System.out.println(e);
}
}
@Override
public void read(JmeImporter ji) throws IOException
{
InputCapsule capsule = ji.getCapsule(this);
String[] readArray = capsule.readStringArray("test_string_array", null);
if(!Arrays.equals(testStringArray, readArray))
throw new IOException("Expected " + Arrays.toString(testStringArray) + " but got " + Arrays.toString(readArray));
}
}
static class Test2DArrayWithNull implements Savable
{
private static final byte[][] testByteArray2d = new byte[][]
{
new byte[]
{
0, 1, 2
},
null,
new byte[]
{
0, 1, 2
}
};
@Override
public void write(JmeExporter je) throws IOException
{
OutputCapsule capsule = je.getCapsule(this);
capsule.write(testByteArray2d, "test_array_2d", null);
}
@Override
public void read(JmeImporter ji) throws IOException
{
InputCapsule capsule = ji.getCapsule(this);
byte[][] readArray = capsule.readByteArray2D("test_array_2d", null);
if(!Arrays.deepEquals(testByteArray2d, readArray))
throw new IOException("Expected " + Arrays.toString(testByteArray2d) + " but got " + Arrays.toString(readArray));
}
}
static class TestListWithNull implements Savable
{
static final ArrayList<Savable> testArrayList = new ArrayList();
static
{
testArrayList.add(new Vector3f());
testArrayList.add(null);
testArrayList.add(new Vector3f());
}
@Override
public void write(JmeExporter je) throws IOException
{
OutputCapsule capsule = je.getCapsule(this);
capsule.writeSavableArrayList(testArrayList, "test_array_list", null);
}
@Override
public void read(JmeImporter ji) throws IOException
{
InputCapsule capsule = ji.getCapsule(this);
ArrayList<Savable> readArrayList = capsule.readSavableArrayList("test_array_list", null);
if(!testArrayList.equals(readArrayList))
throw new IOException("Expected " + testArrayList + " but got " + readArrayList);
}
}
static class TestByteBufferList implements Savable
{
static final ArrayList<ByteBuffer> testArrayList = new ArrayList();
static
{
testArrayList.add(BufferUtils.createByteBuffer(3).put(new byte[]
{
Byte.MIN_VALUE, Byte.MAX_VALUE
}).rewind());
testArrayList.add(BufferUtils.createByteBuffer(3).put(new byte[]
{
Byte.MIN_VALUE, Byte.MAX_VALUE
}).rewind());
}
@Override
public void write(JmeExporter je) throws IOException
{
OutputCapsule capsule = je.getCapsule(this);
capsule.writeByteBufferArrayList(testArrayList, "test_array_list", null);
}
@Override
public void read(JmeImporter ji) throws IOException
{
InputCapsule capsule = ji.getCapsule(this);
ArrayList<ByteBuffer> readArrayList = capsule.readByteBufferArrayList("test_array_list", null);
if(!testArrayList.equals(readArrayList))
throw new IOException("Expected " + testArrayList + " but got " + readArrayList);
}
}
public static void main(String[] args)
{
String[] problemStrings = new String[]
{
"",
"\'",
"\t",
"\n",
"\r"
};
for(String s : problemStrings)
{
TestString.testString = s;
System.out.println("Testing string: " + makeWhitespaceExplicit(TestString.testString) + "");
saveAndLoad(new BinaryExporter(), new BinaryImporter(), new TestString());
saveAndLoad(new XMLExporter(), new XMLImporter(), new TestString());
System.out.println();
}
System.out.println("Testing BitSet");
saveAndLoad(new BinaryExporter(), new BinaryImporter(), new TestBitSet());
saveAndLoad(new XMLExporter(), new XMLImporter(), new TestBitSet());
System.out.println();
System.out.println("Testing String[] with null element");
saveAndLoad(new BinaryExporter(), new BinaryImporter(), new TestStringArrayWithNull());
saveAndLoad(new XMLExporter(), new XMLImporter(), new TestStringArrayWithNull());
System.out.println();
//for the sake of brevity, I'm only showing this on write(byte[][]),
//but it happens on all the other write() overloads that accept a 2d array, except int[][] curiously.
System.out.println("Testing byte[][] with null element");
saveAndLoad(new BinaryExporter(), new BinaryImporter(), new Test2DArrayWithNull());
try
{
saveAndLoad(new XMLExporter(), new XMLImporter(), new Test2DArrayWithNull());
} catch(NullPointerException e)
{
System.out.println("\n\t" + e);
}
System.out.println();
System.out.println("Testing ArrayList<Savable> with null element");
saveAndLoad(new BinaryExporter(), new BinaryImporter(), new TestListWithNull());
try
{
saveAndLoad(new XMLExporter(), new XMLImporter(), new TestListWithNull());
} catch(NullPointerException e)
{
System.out.println("\n\t" + e);
}
System.out.println();
System.out.println("Testing ArrayList<ByteBuffer>");
saveAndLoad(new BinaryExporter(), new BinaryImporter(), new TestByteBufferList());
saveAndLoad(new XMLExporter(), new XMLImporter(), new TestByteBufferList());
System.out.println();
}
private static String makeWhitespaceExplicit(String s)
{
if(s == null)
return "null";
return "\"" + s.replaceAll("\t", "\\\\t").replaceAll("\n", "\\\\n").replaceAll("\r", "\\\\r").replaceAll("\s", "\\\\s") + "\"";
}
private static void saveAndLoad(JmeExporter exporter, JmeImporter importer, Savable savable)
{
System.out.print("Testing implementation: " + exporter.getClass().getSimpleName() + "...");
// export
byte[] exportedBytes = null;
try(ByteArrayOutputStream outStream = new ByteArrayOutputStream())
{
exporter.save(savable, outStream);
exportedBytes = outStream.toByteArray();
} catch(IOException e)
{
System.out.println("\n\t" + e);
return;
}
//if(exporter instanceof XMLExporter)
// System.out.println(new String(exportedBytes));
// import
try(ByteArrayInputStream inStream = new ByteArrayInputStream(exportedBytes))
{
AssetInfo info = new AssetInfo(null, null)
{
@Override
public InputStream openStream()
{
return inStream;
}
};
importer.load(info); // this is where assertions will fail if loaded data does not match saved data.
} catch(IOException e)
{
System.out.println("\n\t" + e);
return;
}
System.out.println("ok.");
}
}I intend to continue investigating and hopefully resolve these problems.
Metadata
Metadata
Assignees
Labels
defectSomething that is supposed to work, but doesn't. Less severe than a "bug"Something that is supposed to work, but doesn't. Less severe than a "bug"