// SgfReaderTest.java
package net.sf.gogui.sgf;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import net.sf.gogui.game.ConstGameInfo;
import net.sf.gogui.game.ConstGameTree;
import net.sf.gogui.game.ConstNode;
import net.sf.gogui.game.GameTree;
import net.sf.gogui.game.GameInfo;
import net.sf.gogui.game.MarkType;
import net.sf.gogui.game.Node;
import net.sf.gogui.game.NodeUtil;
import net.sf.gogui.game.ConstSgfProperties;
import net.sf.gogui.game.TimeSettings;
import static net.sf.gogui.go.GoColor.BLACK;
import static net.sf.gogui.go.GoColor.WHITE;
import static net.sf.gogui.go.GoColor.EMPTY;
import net.sf.gogui.go.GoPoint;
import net.sf.gogui.go.InvalidPointException;
import net.sf.gogui.go.Move;
import net.sf.gogui.version.Version;
public final class SgfReaderTest
extends junit.framework.TestCase
{
public static void main(String args[])
{
junit.textui.TestRunner.run(suite());
}
public static junit.framework.Test suite()
{
return new junit.framework.TestSuite(SgfReaderTest.class);
}
public void testDuplicateProperty() throws Exception
{
readSgfFileString("(;C[foo]C[bar])", false, true);
}
public void testFF4Example() throws Exception
{
SgfReader reader = getReader("ff4_ex.1.sgf");
checkFF4Example(reader);
}
/** Test parsing of human-readable move encoding as used by SmartGo. */
public void testHumanReadable() throws Exception
{
SgfReader reader = getReader("human-readable.sgf");
GameTree tree = reader.getTree();
Node node = tree.getRoot();
assertNull(node.getMove());
node = node.getChild();
assertEquals(Move.get(BLACK, 16, 15), node.getMove());
node = node.getChild();
assertEquals(Move.get(WHITE, 3, 3), node.getMove());
node = node.getChild();
assertEquals(Move.get(BLACK, 2, 15), node.getMove());
node = node.getChild();
assertEquals(Move.get(WHITE, 15, 3), node.getMove());
node = node.getChild();
assertEquals(Move.get(BLACK, 16, 5), node.getMove());
node = node.getChild();
assertEquals(Move.get(WHITE, null), node.getMove());
node = node.getChild();
assertNull(node);
}
public void testInvalidMove() throws Exception
{
readSgfFile("invalidmove.sgf", true, false);
}
/** Test that linebreaks in a text value is handled correctly. */
public void testLinebreaks() throws Exception
{
// Test soft and hard linebreaks (see SGF spec)
ConstGameTree tree = readSgfFileString("(;C[foo\nbar \\\nfoo])");
assertEquals("foo\nbar foo", tree.getRootConst().getComment());
// Test that the linebreaks LF, CR, LFCR, CRLF are handled correctly
tree = readSgfFileString("(;C[foo\nbar])");
assertEquals("foo\nbar", tree.getRootConst().getComment());
tree = readSgfFileString("(;C[foo\rbar])");
assertEquals("foo\nbar", tree.getRootConst().getComment());
tree = readSgfFileString("(;C[foo\n\rbar])");
assertEquals("foo\nbar", tree.getRootConst().getComment());
tree = readSgfFileString("(;C[foo\r\nbar])");
assertEquals("foo\nbar", tree.getRootConst().getComment());
tree = readSgfFileString("(;C[foo\r\n\rbar])");
assertEquals("foo\n\nbar", tree.getRootConst().getComment());
tree = readSgfFileString("(;C[foo\r\n\n\rbar])");
assertEquals("foo\n\nbar", tree.getRootConst().getComment());
// Test escaped line breaks immediately before end of value
tree = readSgfFileString("(;XY[foo\\\n])");
assertEquals("foo", getSgfPropertyValue(tree.getRootConst(), "XY"));
tree = readSgfFileString("(;XY[foo\\\r])");
assertEquals("foo", getSgfPropertyValue(tree.getRootConst(), "XY"));
tree = readSgfFileString("(;XY[foo\\\n\r])");
assertEquals("foo", getSgfPropertyValue(tree.getRootConst(), "XY"));
tree = readSgfFileString("(;XY[foo\\\r\n])");
assertEquals("foo", getSgfPropertyValue(tree.getRootConst(), "XY"));
}
public void testRead() throws Exception
{
readSgfFile("verbose-property-names.sgf", false, false);
}
/** Test that spaces in size property value are ignored.
I don't think they are allowed by the SGF standard, but there is
no reason to create an error in this case. */
public void testSizeWithSpaces() throws Exception
{
ConstGameTree tree = readSgfFileString("(;FF[4]SZ[ 13 ])");
assertEquals(13, tree.getBoardSize());
}
public void testSizeAfterPoints() throws Exception
{
readSgfFile("size-after-valid-points.sgf", false, false);
readSgfFile("size-after-invalid-points.sgf", true, false);
}
/** Test that OT property in unknown format is preserved if not changed. */
public void testTimeSettingsPreserveOvertime() throws Exception
{
// Unknown OT context should not generate a warning, since there is
// no format requirement defined in SGF
ConstGameTree tree =
readSgfFile("time-settings-unknown-ot.sgf", false, false);
ConstNode root = tree.getRootConst();
ConstGameInfo info = root.getGameInfoConst();
TimeSettings settings = info.getTimeSettings();
assertNotNull(settings);
assertFalse(settings.getUseByoyomi());
assertEquals(1800000L, settings.getPreByoyomi());
ConstSgfProperties sgf = root.getSgfPropertiesConst();
assertNotNull(sgf);
assertEquals("8 xyz 16", sgf.getValue("OT", 0));
}
/** Test that OT property in unknown format is replaced if changed. */
public void testTimeSettingsReplaceOvertime() throws Exception
{
SgfReader reader = getReader("time-settings-unknown-ot.sgf");
GameTree tree = reader.getTree();
Node root = tree.getRoot();
TimeSettings settings = new TimeSettings(1800000, 30000, 5);
root.getGameInfo().setTimeSettings(settings);
File file = File.createTempFile("gogui", null);
OutputStream out = new FileOutputStream(file);
new SgfWriter(out, tree, "GoGui", Version.get());
out.close();
reader = new SgfReader(new FileInputStream(file), file, null, 0);
tree = reader.getTree();
root = tree.getRoot();
file.delete();
settings = root.getGameInfo().getTimeSettings();
assertNotNull(settings);
assertTrue(settings.getUseByoyomi());
assertEquals(1800000L, settings.getPreByoyomi());
assertEquals(30000L, settings.getByoyomi());
assertEquals(5, settings.getByoyomiMoves());
ConstSgfProperties sgf = root.getSgfPropertiesConst();
if (sgf != null)
assertFalse(sgf.hasKey("OT"));
}
/** Test FF4 example after writing and reading again.
This is actually a test for SgfWriter. */
public void testWriter() throws Exception
{
SgfReader reader = getReader("ff4_ex.1.sgf");
File file = File.createTempFile("gogui", null);
OutputStream out = new FileOutputStream(file);
new SgfWriter(out, reader.getTree(), "GoGui", Version.get());
out.close();
reader = new SgfReader(new FileInputStream(file), file, null, 0);
checkFF4Example(reader);
file.delete();
}
public void checkFF4Example(SgfReader reader) throws Exception
{
GameTree tree = reader.getTree();
assertEquals(tree.getBoardSize(), 19);
ConstNode root = tree.getRoot();
assertEquals(NodeUtil.subtreeSize(root), 54);
assertEquals(root.getNumberChildren(), 5);
ConstNode node;
node = root.getChildConst(1);
assertEquals(node.getNumberChildren(), 1);
checkSetup(node, 16, 16, 0);
node = node.getChildConst();
assertEquals(node.getNumberChildren(), 1);
checkSetup(node, 0, 0, 9);
node = node.getChildConst();
assertEquals(node.getNumberChildren(), 1);
checkSetup(node, 1, 1, 0);
node = node.getChildConst();
assertEquals(node.getNumberChildren(), 0);
assertEquals(node.getPlayer(), WHITE);
node = root.getChildConst(2);
assertEquals(node.getNumberChildren(), 1);
checkSetup(node, 35, 37, 0);
node = node.getChildConst();
assertEquals(node.getNumberChildren(), 1);
assertEquals(node.getMarkedConst(MarkType.MARK).size(), 9);
assertEquals(node.getMarkedConst(MarkType.CIRCLE).size(), 9);
assertEquals(node.getMarkedConst(MarkType.SQUARE).size(), 9);
assertEquals(node.getMarkedConst(MarkType.TRIANGLE).size(), 9);
assertEquals(node.getMarkedConst(MarkType.SELECT).size(), 9);
assertEquals(node.getMarkedConst(MarkType.TERRITORY_BLACK).size(), 18);
assertEquals(node.getMarkedConst(MarkType.TERRITORY_WHITE).size(), 19);
node = node.getChildConst();
assertEquals(node.getNumberChildren(), 1);
assertEquals(node.getLabelsUnmodifiable().size(), 22);
checkLabel(node, "D17", "1");
checkLabel(node, "F17", "2");
checkLabel(node, "O17", "3");
checkLabel(node, "Q17", "4");
checkLabel(node, "D10", "a");
checkLabel(node, "F10", "b");
checkLabel(node, "O10", "c");
checkLabel(node, "Q10", "d");
checkLabel(node, "G7", "AB");
checkLabel(node, "G6", "ABC");
checkLabel(node, "G5", "ABCD");
checkLabel(node, "G4", "ABCDE");
checkLabel(node, "G3", "ABCDEF");
checkLabel(node, "G2", "ABCDEFG");
checkLabel(node, "G1", "ABCDEFGH");
checkLabel(node, "N7", "12");
checkLabel(node, "N6", "123");
checkLabel(node, "N5", "1234");
checkLabel(node, "N4", "12345");
checkLabel(node, "N3", "123456");
checkLabel(node, "N2", "1234567");
checkLabel(node, "N1", "12345678");
node = node.getChildConst();
assertEquals(node.getNumberChildren(), 0);
checkSgfProperty(node, "AR", "aa:sc", "sa:ac", "aa:sa", "aa:ac",
"cd:cj", "gd:md", "fh:ij", "kj:nh");
checkSgfProperty(node, "DD", "kq:os", "dq:hs");
checkSgfProperty(node, "LN", "pj:pd", "nf:ff", "ih:fj", "kh:nj");
node = root.getChildConst(3);
assertEquals(node.getNumberChildren(), 6);
// Note: These test rely on the fact that the line endings in the FF4
// file were not auto-replaced by some versioning system
assertEquals(node.getComment(),
"There are hard linebreaks & soft linebreaks.\n" +
"Soft linebreaks are linebreaks preceeded by '\\' like"
+ " this one >ok<. Hard line breaks are all other"
+ " linebreaks.\n" +
"Soft linebreaks are converted to >nothing<, i.e."
+ " removed.\n" +
"\n" +
"Note that linebreaks are coded differently on different"
+ " systems.\n" +
"\n" +
"Examples (>ok< shouldn't be split):\n" +
"\n" +
"linebreak 1 \"\\n\": >ok<\n" +
"linebreak 2 \"\\n\\r\": >ok<\n" +
"linebreak 3 \"\\r\\n\": >ok<\n" +
"linebreak 4 \"\\r\": >ok<");
node = node.getChildConst();
assertEquals(node.getNumberChildren(), 4);
}
private void checkLabel(ConstNode node, String pointString, String label)
throws InvalidPointException
{
GoPoint point = GoPoint.parsePoint(pointString, 19);
assertEquals(node.getLabel(point), label);
}
private void checkSgfProperty(ConstNode node, String property,
String... value)
throws InvalidPointException
{
ConstSgfProperties sgf = node.getSgfPropertiesConst();
for (int i = 0; i < value.length; ++i)
assertEquals(value[i], sgf.getValue(property, i));
}
private void checkSetup(ConstNode node, int black, int white, int empty)
throws InvalidPointException
{
assertEquals(node.getSetup(BLACK).size(), black);
assertEquals(node.getSetup(WHITE).size(), white);
assertEquals(node.getSetup(EMPTY).size(), empty);
assertNull(node.getMove());
}
private void checkTimeSettings(String name, long preByoyomi, long byoyomi,
int byoyomiMoves) throws Exception
{
SgfReader reader = getReader(name);
GameTree tree = reader.getTree();
GameInfo info = tree.getGameInfo(tree.getRoot());
TimeSettings timeSettings = info.getTimeSettings();
assertNotNull(timeSettings);
assertEquals(timeSettings.getPreByoyomi(), preByoyomi);
assertEquals(timeSettings.getByoyomi(), byoyomi);
assertEquals(timeSettings.getByoyomiMoves(), byoyomiMoves);
}
private SgfReader getReader(String name) throws SgfError, Exception
{
InputStream in = getClass().getResourceAsStream(name);
if (in == null)
throw new Exception("Resource " + name + " not found");
return new SgfReader(in, null, null, 0);
}
private SgfReader getReaderString(String text) throws SgfError, Exception
{
InputStream in = new ByteArrayInputStream(text.getBytes());
return new SgfReader(in, null, null, 0);
}
private static String getSgfPropertyValue(ConstNode node, String key)
{
return node.getSgfPropertiesConst().getValue(key, 0);
}
private ConstGameTree readSgfFile(String name, boolean expectFailure,
boolean expectWarnings) throws Exception
{
SgfReader reader;
try
{
reader = getReader(name);
readSgfFile(reader, expectFailure, expectWarnings);
}
catch (SgfError error)
{
if (! expectFailure)
fail(error.getMessage());
return null;
}
if (expectFailure)
fail("Reading should result in a failure");
return reader.getTree();
}
private void readSgfFile(SgfReader reader, boolean expectFailure,
boolean expectWarnings) throws Exception
{
if (expectWarnings && reader.getWarnings() == null)
fail("Reading should result in warnings");
if (! expectWarnings && reader.getWarnings() != null)
{
fail("Reading should result in no warnings:\n"
+ reader.getWarnings());
}
}
private ConstGameTree readSgfFileString(String name, boolean expectFailure,
boolean expectWarnings)
throws Exception
{
SgfReader reader;
try
{
reader = getReaderString(name);
readSgfFile(reader, expectFailure, expectWarnings);
}
catch (SgfError error)
{
if (! expectFailure)
fail(error.getMessage());
return null;
}
if (expectFailure)
fail("Reading should result in a failure");
return reader.getTree();
}
private ConstGameTree readSgfFileString(String name) throws Exception
{
return readSgfFileString(name, false, false);
}
}