787 lines
29 KiB
Python
787 lines
29 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
"""Tests for sgf.py."""
|
||
|
|
||
|
from __future__ import with_statement
|
||
|
|
||
|
from textwrap import dedent
|
||
|
|
||
|
from gomill_tests import gomill_test_support
|
||
|
|
||
|
from gomill import sgf
|
||
|
|
||
|
def make_tests(suite):
|
||
|
suite.addTests(gomill_test_support.make_simple_tests(globals()))
|
||
|
|
||
|
|
||
|
def test_new_sgf_game(tc):
|
||
|
g1 = sgf.Sgf_game(9)
|
||
|
tc.assertEqual(g1.get_size(), 9)
|
||
|
root = g1.get_root()
|
||
|
tc.assertEqual(root.get_raw('FF'), '4')
|
||
|
tc.assertEqual(root.get_raw('GM'), '1')
|
||
|
tc.assertEqual(root.get_raw('SZ'), '9')
|
||
|
tc.assertEqual(root.get_raw_property_map(), {
|
||
|
'FF': ['4'],
|
||
|
'GM': ['1'],
|
||
|
'SZ': ['9'],
|
||
|
'CA': ['UTF-8'],
|
||
|
});
|
||
|
tc.assertEqual(list(root), [])
|
||
|
tc.assertEqual(root.parent, None)
|
||
|
tc.assertIs(root.owner, g1)
|
||
|
|
||
|
def test_sgf_game_from_coarse_game_tree(tc):
|
||
|
class Namespace(object):
|
||
|
pass
|
||
|
coarse_game = Namespace()
|
||
|
coarse_game.sequence = [{'SZ' : ["9"]}, {'B' : ["aa"]}]
|
||
|
coarse_game.children = []
|
||
|
g1 = sgf.Sgf_game.from_coarse_game_tree(coarse_game)
|
||
|
tc.assertEqual(g1.get_size(), 9)
|
||
|
root = g1.get_root()
|
||
|
tc.assertIs(root.get_raw_property_map(), coarse_game.sequence[0])
|
||
|
tc.assertEqual(root.parent, None)
|
||
|
tc.assertIs(root.owner, g1)
|
||
|
tc.assertEqual(len(root), 1)
|
||
|
|
||
|
coarse_game2 = Namespace()
|
||
|
coarse_game2.sequence = [{'SZ' : ["0"]}, {'B' : ["aa"]}]
|
||
|
coarse_game2.children = []
|
||
|
tc.assertRaisesRegexp(ValueError, "size out of range: 0",
|
||
|
sgf.Sgf_game.from_coarse_game_tree, coarse_game2)
|
||
|
|
||
|
def test_sgf_game_from_string(tc):
|
||
|
g1 = sgf.Sgf_game.from_string("(;)")
|
||
|
tc.assertEqual(g1.get_size(), 19)
|
||
|
tc.assertRaisesRegexp(ValueError, "unexpected end of SGF data",
|
||
|
sgf.Sgf_game.from_string, "(;SZ[9]")
|
||
|
g2 = sgf.Sgf_game.from_string("(;SZ[9])")
|
||
|
tc.assertEqual(g2.get_size(), 9)
|
||
|
tc.assertRaisesRegexp(ValueError, "bad SZ property: a",
|
||
|
sgf.Sgf_game.from_string, "(;SZ[a])")
|
||
|
tc.assertRaisesRegexp(ValueError, "size out of range: 27",
|
||
|
sgf.Sgf_game.from_string, "(;SZ[27])")
|
||
|
tc.assertRaisesRegexp(ValueError, "unknown encoding: $",
|
||
|
sgf.Sgf_game.from_string, "(;CA[])")
|
||
|
|
||
|
def test_node(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(
|
||
|
r"(;KM[6.5]C[sample\: comment]AB[ai][bh][ee]AE[];B[dg])")
|
||
|
node0 = sgf_game.get_root()
|
||
|
node1 = list(sgf_game.main_sequence_iter())[1]
|
||
|
tc.assertEqual(node0.get_size(), 19)
|
||
|
tc.assertEqual(node0.get_encoding(), "ISO-8859-1")
|
||
|
tc.assertIs(node0.has_property('KM'), True)
|
||
|
tc.assertIs(node0.has_property('XX'), False)
|
||
|
tc.assertIs(node1.has_property('KM'), False)
|
||
|
tc.assertEqual(set(node0.properties()), set(["KM", "C", "AB", "AE"]))
|
||
|
tc.assertEqual(set(node1.properties()), set(["B"]))
|
||
|
tc.assertEqual(node0.get_raw('C'), r"sample\: comment")
|
||
|
tc.assertEqual(node0.get_raw('AB'), "ai")
|
||
|
tc.assertEqual(node0.get_raw('AE'), "")
|
||
|
tc.assertRaises(KeyError, node0.get_raw, 'XX')
|
||
|
tc.assertEqual(node0.get_raw_list('KM'), ['6.5'])
|
||
|
tc.assertEqual(node0.get_raw_list('AB'), ['ai', 'bh', 'ee'])
|
||
|
tc.assertEqual(node0.get_raw_list('AE'), [''])
|
||
|
tc.assertRaises(KeyError, node0.get_raw_list, 'XX')
|
||
|
tc.assertRaises(KeyError, node0.get_raw, 'XX')
|
||
|
|
||
|
def test_property_combination(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string("(;XX[1]YY[2]XX[3]YY[4])")
|
||
|
node0 = sgf_game.get_root()
|
||
|
tc.assertEqual(node0.get_raw_list("XX"), ["1", "3"])
|
||
|
tc.assertEqual(node0.get_raw_list("YY"), ["2", "4"])
|
||
|
|
||
|
def test_node_get(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(dedent(r"""
|
||
|
(;AP[testsuite:0]CA[utf-8]DT[2009-06-06]FF[4]GM[1]KM[7.5]PB[Black engine]
|
||
|
PL[B]PW[White engine][xs]RE[W+R]SZ[9]AB[ai][bh][ee]AW[fd][gc]AE[]BM[2]VW[]
|
||
|
EV[Test
|
||
|
event]
|
||
|
C[123:\)
|
||
|
abc]
|
||
|
YY[none
|
||
|
sense]
|
||
|
;B[dg]KO[]AR[ab:cd][de:fg]FG[515:first move]
|
||
|
LB[ac:lbl][bc:lbl2])
|
||
|
"""))
|
||
|
root = sgf_game.get_root()
|
||
|
node1 = list(sgf_game.main_sequence_iter())[1]
|
||
|
tc.assertRaises(KeyError, root.get, 'XX')
|
||
|
tc.assertEqual(root.get('C'), "123:)\nabc") # Text
|
||
|
tc.assertEqual(root.get('EV'), "Test event") # Simpletext
|
||
|
tc.assertEqual(root.get('BM'), 2) # Double
|
||
|
tc.assertEqual(root.get('YY'), "none\nsense") # unknown (Text)
|
||
|
tc.assertIs(node1.get('KO'), True) # None
|
||
|
tc.assertEqual(root.get('KM'), 7.5) # Real
|
||
|
tc.assertEqual(root.get('GM'), 1) # Number
|
||
|
tc.assertEqual(root.get('PL'), 'b') # Color
|
||
|
tc.assertEqual(node1.get('B'), (2, 3)) # Point
|
||
|
tc.assertEqual(root.get('AB'),
|
||
|
set([(0, 0), (1, 1), (4, 4)])) # List of Point
|
||
|
tc.assertEqual(root.get('VW'), set()) # Empty elist
|
||
|
tc.assertEqual(root.get('AP'), ("testsuite", "0")) # Application
|
||
|
tc.assertEqual(node1.get('AR'),
|
||
|
[((7, 0), (5, 2)), ((4, 3), (2, 5))]) # Arrow
|
||
|
tc.assertEqual(node1.get('FG'), (515, "first move")) # Figure
|
||
|
tc.assertEqual(node1.get('LB'),
|
||
|
[((6, 0), "lbl"), ((6, 1), "lbl2")]) # Label
|
||
|
# Check we (leniently) treat lists like elists on read
|
||
|
tc.assertEqual(root.get('AE'), set())
|
||
|
tc.assertRaisesRegexp(ValueError, "multiple values", root.get, 'PW')
|
||
|
|
||
|
def test_text_values(tc):
|
||
|
def check(s):
|
||
|
sgf_game = sgf.Sgf_game.from_string(s)
|
||
|
return sgf_game.get_root().get("C")
|
||
|
# Round-trip check of Text values through tokeniser, parser, and
|
||
|
# text_value().
|
||
|
tc.assertEqual(check(r"(;C[abc]KO[])"), r"abc")
|
||
|
tc.assertEqual(check(r"(;C[a\\bc]KO[])"), r"a\bc")
|
||
|
tc.assertEqual(check(r"(;C[a\\bc\]KO[])"), r"a\bc]KO[")
|
||
|
tc.assertEqual(check(r"(;C[abc\\]KO[])"), r"abc" + "\\")
|
||
|
tc.assertEqual(check(r"(;C[abc\\\]KO[])"), r"abc\]KO[")
|
||
|
tc.assertEqual(check(r"(;C[abc\\\\]KO[])"), r"abc" + "\\\\")
|
||
|
tc.assertEqual(check(r"(;C[abc\\\\\]KO[])"), r"abc\\]KO[")
|
||
|
tc.assertEqual(check(r"(;C[xxx :\) yyy]KO[])"), r"xxx :) yyy")
|
||
|
tc.assertEqual(check("(;C[ab\\\nc])"), "abc")
|
||
|
tc.assertEqual(check("(;C[ab\nc])"), "ab\nc")
|
||
|
|
||
|
|
||
|
SAMPLE_SGF = """\
|
||
|
(;AP[testsuite:0]CA[utf-8]DT[2009-06-06]FF[4]GM[1]KM[7.5]PB[Black engine]
|
||
|
PL[B]PW[White engine]RE[W+R]SZ[9]AB[ai][bh][ee]AW[fc][gc];B[dg];W[ef]C[comment
|
||
|
on two lines];B[];W[tt]C[Final comment])
|
||
|
"""
|
||
|
|
||
|
SAMPLE_SGF_VAR = """\
|
||
|
(;AP[testsuite:0]CA[utf-8]DT[2009-06-06]FF[4]GM[1]KM[7.5]PB[Black engine]
|
||
|
PL[B]RE[W+R]SZ[9]AB[ai][bh][ee]AW[fd][gc]VW[]
|
||
|
;B[dg]
|
||
|
;W[ef]C[comment
|
||
|
on two lines]
|
||
|
;B[]
|
||
|
;C[Nonfinal comment]VW[aa:bb]
|
||
|
(;B[ia];W[ib];B[ic])
|
||
|
(;B[ib];W[ic]
|
||
|
(;B[id])
|
||
|
(;B[ie])
|
||
|
))
|
||
|
"""
|
||
|
|
||
|
def test_node_string(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(SAMPLE_SGF)
|
||
|
node = sgf_game.get_root()
|
||
|
tc.assertMultiLineEqual(str(node), dedent("""\
|
||
|
AB[ai][bh][ee]
|
||
|
AP[testsuite:0]
|
||
|
AW[fc][gc]
|
||
|
CA[utf-8]
|
||
|
DT[2009-06-06]
|
||
|
FF[4]
|
||
|
GM[1]
|
||
|
KM[7.5]
|
||
|
PB[Black engine]
|
||
|
PL[B]
|
||
|
PW[White engine]
|
||
|
RE[W+R]
|
||
|
SZ[9]
|
||
|
"""))
|
||
|
|
||
|
def test_node_get_move(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(SAMPLE_SGF)
|
||
|
nodes = list(sgf_game.main_sequence_iter())
|
||
|
tc.assertEqual(nodes[0].get_move(), (None, None))
|
||
|
tc.assertEqual(nodes[1].get_move(), ('b', (2, 3)))
|
||
|
tc.assertEqual(nodes[2].get_move(), ('w', (3, 4)))
|
||
|
tc.assertEqual(nodes[3].get_move(), ('b', None))
|
||
|
tc.assertEqual(nodes[4].get_move(), ('w', None))
|
||
|
|
||
|
def test_node_get_setup_stones(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(
|
||
|
r"(;KM[6.5]SZ[9]C[sample\: comment]AB[ai][bh][ee]AE[bb];B[dg])")
|
||
|
node0 = sgf_game.get_root()
|
||
|
node1 = list(sgf_game.main_sequence_iter())[1]
|
||
|
tc.assertIs(node0.has_setup_stones(), True)
|
||
|
tc.assertIs(node1.has_setup_stones(), False)
|
||
|
tc.assertEqual(node0.get_setup_stones(),
|
||
|
(set([(0, 0), (1, 1), (4, 4)]), set(), set([(7, 1)])))
|
||
|
tc.assertEqual(node1.get_setup_stones(),
|
||
|
(set(), set(), set()))
|
||
|
|
||
|
def test_sgf_game(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(SAMPLE_SGF_VAR)
|
||
|
nodes = list(sgf_game.main_sequence_iter())
|
||
|
tc.assertEqual(sgf_game.get_size(), 9)
|
||
|
tc.assertEqual(sgf_game.get_komi(), 7.5)
|
||
|
tc.assertIs(sgf_game.get_handicap(), None)
|
||
|
tc.assertEqual(sgf_game.get_player_name('b'), "Black engine")
|
||
|
tc.assertIs(sgf_game.get_player_name('w'), None)
|
||
|
tc.assertEqual(sgf_game.get_winner(), 'w')
|
||
|
tc.assertEqual(nodes[2].get('C'), "comment\non two lines")
|
||
|
tc.assertEqual(nodes[4].get('C'), "Nonfinal comment")
|
||
|
|
||
|
g2 = sgf.Sgf_game.from_string("(;)")
|
||
|
tc.assertEqual(g2.get_size(), 19)
|
||
|
tc.assertEqual(g2.get_komi(), 0.0)
|
||
|
tc.assertIs(g2.get_handicap(), None)
|
||
|
tc.assertIs(g2.get_player_name('b'), None)
|
||
|
tc.assertIs(g2.get_player_name('w'), None)
|
||
|
tc.assertEqual(g2.get_winner(), None)
|
||
|
|
||
|
def test_tree_view(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(SAMPLE_SGF_VAR)
|
||
|
root = sgf_game.get_root()
|
||
|
tc.assertIsInstance(root, sgf.Tree_node)
|
||
|
tc.assertIs(root.parent, None)
|
||
|
tc.assertIs(root.owner, sgf_game)
|
||
|
tc.assertEqual(len(root), 1)
|
||
|
tc.assertEqual(root[0].get_raw('B'), "dg")
|
||
|
tc.assertTrue(root)
|
||
|
tc.assertEqual(root.index(root[0]), 0)
|
||
|
|
||
|
branchnode = root[0][0][0][0]
|
||
|
tc.assertIsInstance(branchnode, sgf.Tree_node)
|
||
|
tc.assertIs(branchnode.parent, root[0][0][0])
|
||
|
tc.assertIs(branchnode.owner, sgf_game)
|
||
|
tc.assertEqual(len(branchnode), 2)
|
||
|
tc.assertIs(branchnode[1], branchnode[-1])
|
||
|
tc.assertEqual(branchnode[:1], [branchnode[0]])
|
||
|
tc.assertEqual([node for node in branchnode],
|
||
|
[branchnode[0], branchnode[1]])
|
||
|
with tc.assertRaises(IndexError):
|
||
|
branchnode[2]
|
||
|
tc.assertEqual(branchnode[0].get_raw('B'), "ia")
|
||
|
tc.assertEqual(branchnode[1].get_raw('B'), "ib")
|
||
|
tc.assertEqual(branchnode.index(branchnode[0]), 0)
|
||
|
tc.assertEqual(branchnode.index(branchnode[1]), 1)
|
||
|
|
||
|
tc.assertEqual(len(branchnode[1][0]), 2)
|
||
|
|
||
|
leaf = branchnode[1][0][1]
|
||
|
tc.assertIs(leaf.parent, branchnode[1][0])
|
||
|
tc.assertEqual(len(leaf), 0)
|
||
|
tc.assertFalse(leaf)
|
||
|
|
||
|
tc.assertIs(sgf_game.get_last_node(), root[0][0][0][0][0][0][0])
|
||
|
|
||
|
# check nothing breaks when first retrieval is by index
|
||
|
game2 = sgf.Sgf_game.from_string(SAMPLE_SGF)
|
||
|
root2 = game2.get_root()
|
||
|
tc.assertEqual(root2[0].get_raw('B'), "dg")
|
||
|
|
||
|
def test_serialise(tc):
|
||
|
# Doesn't cover transcoding
|
||
|
sgf_game = sgf.Sgf_game.from_string(SAMPLE_SGF_VAR)
|
||
|
serialised = sgf_game.serialise()
|
||
|
tc.assertEqual(serialised, dedent("""\
|
||
|
(;FF[4]AB[ai][bh][ee]AP[testsuite:0]AW[fd][gc]CA[utf-8]DT[2009-06-06]GM[1]
|
||
|
KM[7.5]PB[Black engine]PL[B]RE[W+R]SZ[9]VW[];B[dg];C[comment
|
||
|
on two lines]W[ef]
|
||
|
;B[];C[Nonfinal comment]VW[aa:bb](;B[ia];W[ib];B[ic])(;B[ib];W[ic](;B[id])(;
|
||
|
B[ie])))
|
||
|
"""))
|
||
|
sgf_game2 = sgf.Sgf_game.from_string(serialised)
|
||
|
tc.assertEqual(map(str, sgf_game.get_main_sequence()),
|
||
|
map(str, sgf_game2.get_main_sequence()))
|
||
|
|
||
|
def test_serialise_wrap(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(SAMPLE_SGF_VAR)
|
||
|
serialised = sgf_game.serialise(wrap=None)
|
||
|
tc.assertEqual(serialised, dedent("""\
|
||
|
(;FF[4]AB[ai][bh][ee]AP[testsuite:0]AW[fd][gc]CA[utf-8]DT[2009-06-06]GM[1]KM[7.5]PB[Black engine]PL[B]RE[W+R]SZ[9]VW[];B[dg];C[comment
|
||
|
on two lines]W[ef];B[];C[Nonfinal comment]VW[aa:bb](;B[ia];W[ib];B[ic])(;B[ib];W[ic](;B[id])(;B[ie])))
|
||
|
"""))
|
||
|
sgf_game2 = sgf.Sgf_game.from_string(serialised)
|
||
|
tc.assertEqual(map(str, sgf_game.get_main_sequence()),
|
||
|
map(str, sgf_game2.get_main_sequence()))
|
||
|
|
||
|
def test_encoding(tc):
|
||
|
g1 = sgf.Sgf_game(19)
|
||
|
tc.assertEqual(g1.get_charset(), "UTF-8")
|
||
|
root = g1.get_root()
|
||
|
tc.assertEqual(root.get_encoding(), "UTF-8")
|
||
|
root.set("C", "£")
|
||
|
tc.assertEqual(root.get("C"), "£")
|
||
|
tc.assertEqual(root.get_raw("C"), "£")
|
||
|
tc.assertEqual(g1.serialise(), dedent("""\
|
||
|
(;FF[4]C[£]CA[UTF-8]GM[1]SZ[19])
|
||
|
"""))
|
||
|
|
||
|
g2 = sgf.Sgf_game(19, encoding="iso-8859-1")
|
||
|
tc.assertEqual(g2.get_charset(), "ISO-8859-1")
|
||
|
root = g2.get_root()
|
||
|
tc.assertEqual(root.get_encoding(), "ISO-8859-1")
|
||
|
root.set("C", "£")
|
||
|
tc.assertEqual(root.get("C"), "£")
|
||
|
tc.assertEqual(root.get_raw("C"), "\xa3")
|
||
|
tc.assertEqual(g2.serialise(), dedent("""\
|
||
|
(;FF[4]C[\xa3]CA[ISO-8859-1]GM[1]SZ[19])
|
||
|
"""))
|
||
|
|
||
|
tc.assertRaisesRegexp(ValueError, "unknown encoding: unknownencoding",
|
||
|
sgf.Sgf_game, 19, "unknownencoding")
|
||
|
|
||
|
|
||
|
def test_loaded_sgf_game_encoding(tc):
|
||
|
g1 = sgf.Sgf_game.from_string("""
|
||
|
(;FF[4]C[£]CA[utf-8]GM[1]SZ[19])
|
||
|
""")
|
||
|
tc.assertEqual(g1.get_charset(), "UTF-8")
|
||
|
root = g1.get_root()
|
||
|
tc.assertEqual(root.get_encoding(), "UTF-8")
|
||
|
tc.assertEqual(root.get("C"), "£")
|
||
|
tc.assertEqual(root.get_raw("C"), "£")
|
||
|
tc.assertEqual(g1.serialise(), dedent("""\
|
||
|
(;FF[4]C[£]CA[utf-8]GM[1]SZ[19])
|
||
|
"""))
|
||
|
|
||
|
g2 = sgf.Sgf_game.from_string("""
|
||
|
(;FF[4]C[\xa3]CA[iso-8859-1]GM[1]SZ[19])
|
||
|
""")
|
||
|
tc.assertEqual(g2.get_charset(), "ISO-8859-1")
|
||
|
root = g2.get_root()
|
||
|
tc.assertEqual(root.get_encoding(), "ISO-8859-1")
|
||
|
tc.assertEqual(root.get("C"), "£")
|
||
|
tc.assertEqual(root.get_raw("C"), "\xa3")
|
||
|
tc.assertEqual(g2.serialise(), dedent("""\
|
||
|
(;FF[4]C[\xa3]CA[iso-8859-1]GM[1]SZ[19])
|
||
|
"""))
|
||
|
|
||
|
g3 = sgf.Sgf_game.from_string("""
|
||
|
(;FF[4]C[\xa3]GM[1]SZ[19])
|
||
|
""")
|
||
|
tc.assertEqual(g3.get_charset(), "ISO-8859-1")
|
||
|
root = g3.get_root()
|
||
|
tc.assertEqual(root.get_encoding(), "ISO-8859-1")
|
||
|
tc.assertEqual(root.get("C"), "£")
|
||
|
tc.assertEqual(root.get_raw("C"), "\xa3")
|
||
|
tc.assertEqual(g3.serialise(), dedent("""\
|
||
|
(;FF[4]C[\xa3]GM[1]SZ[19])
|
||
|
"""))
|
||
|
|
||
|
# This is invalidly encoded. get() notices, but serialise() doesn't care.
|
||
|
g4 = sgf.Sgf_game.from_string("""
|
||
|
(;FF[4]C[\xa3]CA[utf-8]GM[1]SZ[19])
|
||
|
""")
|
||
|
tc.assertEqual(g4.get_charset(), "UTF-8")
|
||
|
root = g4.get_root()
|
||
|
tc.assertEqual(root.get_encoding(), "UTF-8")
|
||
|
tc.assertRaises(UnicodeDecodeError, root.get, "C")
|
||
|
tc.assertEqual(root.get_raw("C"), "\xa3")
|
||
|
tc.assertEqual(g4.serialise(), dedent("""\
|
||
|
(;FF[4]C[\xa3]CA[utf-8]GM[1]SZ[19])
|
||
|
"""))
|
||
|
|
||
|
tc.assertRaisesRegexp(
|
||
|
ValueError, "unknown encoding: unknownencoding",
|
||
|
sgf.Sgf_game.from_string, """
|
||
|
(;FF[4]CA[unknownencoding]GM[1]SZ[19])
|
||
|
""")
|
||
|
|
||
|
def test_override_encoding(tc):
|
||
|
g1 = sgf.Sgf_game.from_string("""
|
||
|
(;FF[4]C[£]CA[iso-8859-1]GM[1]SZ[19])
|
||
|
""", override_encoding="utf-8")
|
||
|
root = g1.get_root()
|
||
|
tc.assertEqual(root.get_encoding(), "UTF-8")
|
||
|
tc.assertEqual(root.get("C"), "£")
|
||
|
tc.assertEqual(root.get_raw("C"), "£")
|
||
|
tc.assertEqual(g1.serialise(), dedent("""\
|
||
|
(;FF[4]C[£]CA[UTF-8]GM[1]SZ[19])
|
||
|
"""))
|
||
|
|
||
|
g2 = sgf.Sgf_game.from_string("""
|
||
|
(;FF[4]C[\xa3]CA[utf-8]GM[1]SZ[19])
|
||
|
""", override_encoding="iso-8859-1")
|
||
|
root = g2.get_root()
|
||
|
tc.assertEqual(root.get_encoding(), "ISO-8859-1")
|
||
|
tc.assertEqual(root.get("C"), "£")
|
||
|
tc.assertEqual(root.get_raw("C"), "\xa3")
|
||
|
tc.assertEqual(g2.serialise(), dedent("""\
|
||
|
(;FF[4]C[\xa3]CA[ISO-8859-1]GM[1]SZ[19])
|
||
|
"""))
|
||
|
|
||
|
def test_serialise_transcoding(tc):
|
||
|
g1 = sgf.Sgf_game.from_string("""
|
||
|
(;FF[4]C[£]CA[utf-8]GM[1]SZ[19])
|
||
|
""")
|
||
|
tc.assertEqual(g1.serialise(), dedent("""\
|
||
|
(;FF[4]C[£]CA[utf-8]GM[1]SZ[19])
|
||
|
"""))
|
||
|
g1.get_root().set("CA", "latin-1")
|
||
|
tc.assertEqual(g1.serialise(), dedent("""\
|
||
|
(;FF[4]C[\xa3]CA[latin-1]GM[1]SZ[19])
|
||
|
"""))
|
||
|
g1.get_root().set("CA", "unknown")
|
||
|
tc.assertRaisesRegexp(ValueError, "unsupported charset: \['unknown']",
|
||
|
g1.serialise)
|
||
|
|
||
|
# improperly-encoded from the start
|
||
|
g2 = sgf.Sgf_game.from_string("""
|
||
|
(;FF[4]C[£]CA[ascii]GM[1]SZ[19])
|
||
|
""")
|
||
|
tc.assertEqual(g2.serialise(), dedent("""\
|
||
|
(;FF[4]C[£]CA[ascii]GM[1]SZ[19])
|
||
|
"""))
|
||
|
g2.get_root().set("CA", "utf-8")
|
||
|
tc.assertRaises(UnicodeDecodeError, g2.serialise)
|
||
|
|
||
|
g3 = sgf.Sgf_game.from_string("""
|
||
|
(;FF[4]C[Δ]CA[utf-8]GM[1]SZ[19])
|
||
|
""")
|
||
|
g3.get_root().unset("CA")
|
||
|
tc.assertRaises(UnicodeEncodeError, g3.serialise)
|
||
|
|
||
|
def test_tree_mutation(tc):
|
||
|
sgf_game = sgf.Sgf_game(9)
|
||
|
root = sgf_game.get_root()
|
||
|
n1 = root.new_child()
|
||
|
n1.set("N", "n1")
|
||
|
n2 = root.new_child()
|
||
|
n2.set("N", "n2")
|
||
|
n3 = n1.new_child()
|
||
|
n3.set("N", "n3")
|
||
|
n4 = root.new_child(1)
|
||
|
n4.set("N", "n4")
|
||
|
tc.assertEqual(
|
||
|
sgf_game.serialise(),
|
||
|
"(;FF[4]CA[UTF-8]GM[1]SZ[9](;N[n1];N[n3])(;N[n4])(;N[n2]))\n")
|
||
|
tc.assertEqual(
|
||
|
[node.get_raw_property_map() for node in sgf_game.main_sequence_iter()],
|
||
|
[node.get_raw_property_map() for node in root, root[0], n3])
|
||
|
tc.assertIs(sgf_game.get_last_node(), n3)
|
||
|
|
||
|
n1.delete()
|
||
|
tc.assertEqual(
|
||
|
sgf_game.serialise(),
|
||
|
"(;FF[4]CA[UTF-8]GM[1]SZ[9](;N[n4])(;N[n2]))\n")
|
||
|
tc.assertRaises(ValueError, root.delete)
|
||
|
|
||
|
def test_tree_mutation_from_coarse_game(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string("(;SZ[9](;N[n1];N[n3])(;N[n2]))")
|
||
|
root = sgf_game.get_root()
|
||
|
n4 = root.new_child()
|
||
|
n4.set("N", "n4")
|
||
|
n3 = root[0][0]
|
||
|
tc.assertEqual(n3.get("N"), "n3")
|
||
|
n5 = n3.new_child()
|
||
|
n5.set("N", "n5")
|
||
|
tc.assertEqual(sgf_game.serialise(),
|
||
|
"(;SZ[9](;N[n1];N[n3];N[n5])(;N[n2])(;N[n4]))\n")
|
||
|
tc.assertEqual(
|
||
|
[node.get_raw_property_map() for node in sgf_game.main_sequence_iter()],
|
||
|
[node.get_raw_property_map() for node in root, root[0], n3, n5])
|
||
|
tc.assertIs(sgf_game.get_last_node(), n5)
|
||
|
n3.delete()
|
||
|
tc.assertEqual(sgf_game.serialise(),
|
||
|
"(;SZ[9](;N[n1])(;N[n2])(;N[n4]))\n")
|
||
|
tc.assertRaises(ValueError, root.delete)
|
||
|
|
||
|
def test_reparent(tc):
|
||
|
g1 = sgf.Sgf_game.from_string("(;SZ[9](;N[n1];N[n3])(;N[n2]))")
|
||
|
root = g1.get_root()
|
||
|
# Test with unexpanded root
|
||
|
tc.assertRaisesRegexp(ValueError, "would create a loop",
|
||
|
root.reparent, root)
|
||
|
n1 = root[0]
|
||
|
n2 = root[1]
|
||
|
n3 = root[0][0]
|
||
|
tc.assertEqual(n1.get("N"), "n1")
|
||
|
tc.assertEqual(n2.get("N"), "n2")
|
||
|
tc.assertEqual(n3.get("N"), "n3")
|
||
|
n3.reparent(n2)
|
||
|
tc.assertEqual(g1.serialise(), "(;SZ[9](;N[n1])(;N[n2];N[n3]))\n")
|
||
|
n3.reparent(n2)
|
||
|
tc.assertEqual(g1.serialise(), "(;SZ[9](;N[n1])(;N[n2];N[n3]))\n")
|
||
|
tc.assertRaisesRegexp(ValueError, "would create a loop",
|
||
|
root.reparent, n3)
|
||
|
tc.assertRaisesRegexp(ValueError, "would create a loop",
|
||
|
n3.reparent, n3)
|
||
|
g2 = sgf.Sgf_game(9)
|
||
|
tc.assertRaisesRegexp(
|
||
|
ValueError, "new parent doesn't belong to the same game",
|
||
|
n3.reparent, g2.get_root())
|
||
|
|
||
|
def test_reparent_index(tc):
|
||
|
g1 = sgf.Sgf_game.from_string("(;SZ[9](;N[n1];N[n3])(;N[n2]))")
|
||
|
root = g1.get_root()
|
||
|
n1 = root[0]
|
||
|
n2 = root[1]
|
||
|
n3 = root[0][0]
|
||
|
tc.assertEqual(n1.get("N"), "n1")
|
||
|
tc.assertEqual(n2.get("N"), "n2")
|
||
|
tc.assertEqual(n3.get("N"), "n3")
|
||
|
n3.reparent(root, index=1)
|
||
|
tc.assertEqual(g1.serialise(), "(;SZ[9](;N[n1])(;N[n3])(;N[n2]))\n")
|
||
|
n3.reparent(root, index=1)
|
||
|
tc.assertEqual(g1.serialise(), "(;SZ[9](;N[n1])(;N[n3])(;N[n2]))\n")
|
||
|
n3.reparent(root, index=2)
|
||
|
tc.assertEqual(g1.serialise(), "(;SZ[9](;N[n1])(;N[n2])(;N[n3]))\n")
|
||
|
|
||
|
def test_extend_main_sequence(tc):
|
||
|
g1 = sgf.Sgf_game(9)
|
||
|
for i in xrange(6):
|
||
|
g1.extend_main_sequence().set("N", "e%d" % i)
|
||
|
tc.assertEqual(
|
||
|
g1.serialise(),
|
||
|
"(;FF[4]CA[UTF-8]GM[1]SZ[9];N[e0];N[e1];N[e2];N[e3];N[e4];N[e5])\n")
|
||
|
g2 = sgf.Sgf_game.from_string("(;SZ[9](;N[n1];N[n3])(;N[n2]))")
|
||
|
for i in xrange(6):
|
||
|
g2.extend_main_sequence().set("N", "e%d" % i)
|
||
|
tc.assertEqual(
|
||
|
g2.serialise(),
|
||
|
"(;SZ[9](;N[n1];N[n3];N[e0];N[e1];N[e2];N[e3];N[e4];N[e5])(;N[n2]))\n")
|
||
|
|
||
|
|
||
|
def test_get_sequence_above(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(SAMPLE_SGF_VAR)
|
||
|
root = sgf_game.get_root()
|
||
|
branchnode = root[0][0][0][0]
|
||
|
leaf = branchnode[1][0][1]
|
||
|
tc.assertEqual(sgf_game.get_sequence_above(root), [])
|
||
|
|
||
|
tc.assertEqual(sgf_game.get_sequence_above(branchnode),
|
||
|
[root, root[0], root[0][0], root[0][0][0]])
|
||
|
|
||
|
tc.assertEqual(sgf_game.get_sequence_above(leaf),
|
||
|
[root, root[0], root[0][0], root[0][0][0],
|
||
|
branchnode, branchnode[1], branchnode[1][0]])
|
||
|
|
||
|
sgf_game2 = sgf.Sgf_game.from_string(SAMPLE_SGF_VAR)
|
||
|
tc.assertRaisesRegexp(ValueError, "node doesn't belong to this game",
|
||
|
sgf_game2.get_sequence_above, leaf)
|
||
|
|
||
|
def test_get_main_sequence_below(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(SAMPLE_SGF_VAR)
|
||
|
root = sgf_game.get_root()
|
||
|
branchnode = root[0][0][0][0]
|
||
|
leaf = branchnode[1][0][1]
|
||
|
tc.assertEqual(sgf_game.get_main_sequence_below(leaf), [])
|
||
|
|
||
|
tc.assertEqual(sgf_game.get_main_sequence_below(branchnode),
|
||
|
[branchnode[0], branchnode[0][0], branchnode[0][0][0]])
|
||
|
|
||
|
tc.assertEqual(sgf_game.get_main_sequence_below(root),
|
||
|
[root[0], root[0][0], root[0][0][0], branchnode,
|
||
|
branchnode[0], branchnode[0][0], branchnode[0][0][0]])
|
||
|
|
||
|
sgf_game2 = sgf.Sgf_game.from_string(SAMPLE_SGF_VAR)
|
||
|
tc.assertRaisesRegexp(ValueError, "node doesn't belong to this game",
|
||
|
sgf_game2.get_main_sequence_below, branchnode)
|
||
|
|
||
|
def test_main_sequence(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(SAMPLE_SGF_VAR)
|
||
|
root = sgf_game.get_root()
|
||
|
|
||
|
nodes = list(sgf_game.main_sequence_iter())
|
||
|
tc.assertEqual(len(nodes), 8)
|
||
|
tc.assertIs(root.get_raw_property_map(),
|
||
|
nodes[0].get_raw_property_map())
|
||
|
# Check that main_sequence_iter() optimisation has been used.
|
||
|
# (Have to call this before making the tree expand.)
|
||
|
with tc.assertRaises(AttributeError):
|
||
|
nodes[1].parent
|
||
|
|
||
|
tree_nodes = sgf_game.get_main_sequence()
|
||
|
tc.assertEqual(len(tree_nodes), 8)
|
||
|
tc.assertIs(root.get_raw_property_map(),
|
||
|
tree_nodes[0].get_raw_property_map())
|
||
|
tc.assertIs(tree_nodes[0], root)
|
||
|
tc.assertIs(tree_nodes[2].parent, tree_nodes[1])
|
||
|
tc.assertIs(sgf_game.get_last_node(), tree_nodes[-1])
|
||
|
|
||
|
tree_node = root
|
||
|
for node in nodes:
|
||
|
tc.assertIs(tree_node.get_raw_property_map(),
|
||
|
node.get_raw_property_map())
|
||
|
if tree_node:
|
||
|
tree_node = tree_node[0]
|
||
|
|
||
|
def test_find(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(SAMPLE_SGF_VAR)
|
||
|
root = sgf_game.get_root()
|
||
|
branchnode = root[0][0][0][0]
|
||
|
leaf = branchnode[1][0][1]
|
||
|
|
||
|
tc.assertEqual(root.get("VW"), set())
|
||
|
tc.assertIs(root.find("VW"), root)
|
||
|
tc.assertRaises(KeyError, root[0].get, "VW")
|
||
|
tc.assertEqual(root[0].find_property("VW"), set())
|
||
|
tc.assertIs(root[0].find("VW"), root)
|
||
|
|
||
|
tc.assertEqual(branchnode.get("VW"),
|
||
|
set([(7, 0), (7, 1), (8, 0), (8, 1)]))
|
||
|
tc.assertIs(branchnode.find("VW"), branchnode)
|
||
|
tc.assertEqual(branchnode.find_property("VW"),
|
||
|
set([(7, 0), (7, 1), (8, 0), (8, 1)]))
|
||
|
|
||
|
tc.assertRaises(KeyError, leaf.get, "VW")
|
||
|
tc.assertIs(leaf.find("VW"), branchnode)
|
||
|
tc.assertEqual(leaf.find_property("VW"),
|
||
|
set([(7, 0), (7, 1), (8, 0), (8, 1)]))
|
||
|
|
||
|
tc.assertIs(leaf.find("XX"), None)
|
||
|
tc.assertRaises(KeyError, leaf.find_property, "XX")
|
||
|
|
||
|
def test_node_set_raw(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string(dedent(r"""
|
||
|
(;AP[testsuite:0]CA[utf-8]DT[2009-06-06]FF[4]GM[1]KM[7.5]
|
||
|
PB[Black engine]PW[White engine]RE[W+R]SZ[9]
|
||
|
AB[ai][bh][ee]AW[fd][gc]BM[2]VW[]
|
||
|
PL[B]
|
||
|
C[123abc]
|
||
|
;B[dg]C[first move])
|
||
|
"""))
|
||
|
root = sgf_game.get_root()
|
||
|
tc.assertEqual(root.get_raw('RE'), "W+R")
|
||
|
root.set_raw('RE', "W+2.5")
|
||
|
tc.assertEqual(root.get_raw('RE'), "W+2.5")
|
||
|
tc.assertRaises(KeyError, root.get_raw, 'XX')
|
||
|
root.set_raw('XX', "xyz")
|
||
|
tc.assertEqual(root.get_raw('XX'), "xyz")
|
||
|
|
||
|
root.set_raw_list('XX', ("abc", "def"))
|
||
|
tc.assertEqual(root.get_raw('XX'), "abc")
|
||
|
tc.assertEqual(root.get_raw_list('XX'), ["abc", "def"])
|
||
|
|
||
|
tc.assertRaisesRegexp(ValueError, "empty property list",
|
||
|
root.set_raw_list, 'B', [])
|
||
|
|
||
|
values = ["123", "456"]
|
||
|
root.set_raw_list('YY', values)
|
||
|
tc.assertEqual(root.get_raw_list('YY'), ["123", "456"])
|
||
|
values.append("789")
|
||
|
tc.assertEqual(root.get_raw_list('YY'), ["123", "456"])
|
||
|
|
||
|
tc.assertRaisesRegexp(ValueError, "ill-formed property identifier",
|
||
|
root.set_raw, 'Black', "aa")
|
||
|
tc.assertRaisesRegexp(ValueError, "ill-formed property identifier",
|
||
|
root.set_raw_list, 'Black', ["aa"])
|
||
|
|
||
|
root.set_raw('C', "foo\\]bar")
|
||
|
tc.assertEqual(root.get_raw('C'), "foo\\]bar")
|
||
|
root.set_raw('C', "abc\\\\")
|
||
|
tc.assertEqual(root.get_raw('C'), "abc\\\\")
|
||
|
tc.assertRaisesRegexp(ValueError, "ill-formed raw property value",
|
||
|
root.set_raw, 'C', "foo]bar")
|
||
|
tc.assertRaisesRegexp(ValueError, "ill-formed raw property value",
|
||
|
root.set_raw, 'C', "abc\\")
|
||
|
tc.assertRaisesRegexp(ValueError, "ill-formed raw property value",
|
||
|
root.set_raw_list, 'C', ["abc", "de]f"])
|
||
|
|
||
|
root.set_raw('C', "foo\\]bar\\\nbaz")
|
||
|
tc.assertEqual(root.get('C'), "foo]barbaz")
|
||
|
|
||
|
|
||
|
def test_node_aliasing(tc):
|
||
|
# Check that node objects retrieved by different means use the same
|
||
|
# property map.
|
||
|
|
||
|
sgf_game = sgf.Sgf_game.from_string(dedent(r"""
|
||
|
(;C[root];C[node 1])
|
||
|
"""))
|
||
|
root = sgf_game.get_root()
|
||
|
plain_node = list(sgf_game.main_sequence_iter())[1]
|
||
|
tree_node = root[0]
|
||
|
# Check the main_sequence_iter() optimisation was used, otherwise this test
|
||
|
# isn't checking what it's supposed to.
|
||
|
tc.assertIsNot(tree_node, plain_node)
|
||
|
tc.assertIs(tree_node.__class__, sgf.Tree_node)
|
||
|
tc.assertIs(plain_node.__class__, sgf.Node)
|
||
|
|
||
|
tc.assertEqual(tree_node.get_raw('C'), "node 1")
|
||
|
tree_node.set_raw('C', r"test\value")
|
||
|
tc.assertEqual(tree_node.get_raw('C'), r"test\value")
|
||
|
tc.assertEqual(plain_node.get_raw('C'), r"test\value")
|
||
|
|
||
|
plain_node.set_raw_list('XX', ["1", "2", "3"])
|
||
|
tc.assertEqual(tree_node.get_raw_list('XX'), ["1", "2", "3"])
|
||
|
|
||
|
def test_node_set(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string("(;FF[4]GM[1]SZ[9])")
|
||
|
root = sgf_game.get_root()
|
||
|
root.set("KO", True)
|
||
|
root.set("KM", 0.5)
|
||
|
root.set('DD', [(3, 4), (5, 6)])
|
||
|
root.set('AB', set([(0, 0), (1, 1), (4, 4)]))
|
||
|
root.set('TW', set())
|
||
|
root.set('XX', "nonsense [none]sense more n\\onsens\\e")
|
||
|
|
||
|
tc.assertEqual(sgf_game.serialise(), dedent("""\
|
||
|
(;FF[4]AB[ai][bh][ee]DD[ef][gd]GM[1]KM[0.5]KO[]SZ[9]TW[]
|
||
|
XX[nonsense [none\\]sense more n\\\\onsens\\\\e])
|
||
|
"""))
|
||
|
|
||
|
def test_node_unset(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string("(;FF[4]GM[1]SZ[9]HA[3])")
|
||
|
root = sgf_game.get_root()
|
||
|
tc.assertEqual(root.get('HA'), 3)
|
||
|
root.unset('HA')
|
||
|
tc.assertRaises(KeyError, root.unset, 'PL')
|
||
|
tc.assertEqual(sgf_game.serialise(),
|
||
|
"(;FF[4]GM[1]SZ[9])\n")
|
||
|
|
||
|
def test_set_and_unset_size(tc):
|
||
|
g1 = sgf.Sgf_game.from_string("(;FF[4]GM[1]SZ[9]HA[3])")
|
||
|
root1 = g1.get_root()
|
||
|
tc.assertRaisesRegexp(ValueError, "changing size is not permitted",
|
||
|
root1.set, "SZ", 19)
|
||
|
root1.set("SZ", 9)
|
||
|
tc.assertRaisesRegexp(ValueError, "changing size is not permitted",
|
||
|
root1.unset, "SZ")
|
||
|
g2 = sgf.Sgf_game.from_string("(;FF[4]GM[1]SZ[19]HA[3])")
|
||
|
root2 = g2.get_root()
|
||
|
root2.unset("SZ")
|
||
|
root2.set("SZ", 19)
|
||
|
|
||
|
def test_set_and_unset_charset(tc):
|
||
|
g1 = sgf.Sgf_game.from_string("(;FF[4]CA[utf-8]GM[1]SZ[9]HA[3])")
|
||
|
tc.assertEqual(g1.get_charset(), "UTF-8")
|
||
|
root1 = g1.get_root()
|
||
|
root1.unset("CA")
|
||
|
tc.assertEqual(g1.get_charset(), "ISO-8859-1")
|
||
|
root1.set("CA", "iso-8859-1")
|
||
|
tc.assertEqual(g1.get_charset(), "ISO-8859-1")
|
||
|
root1.set("CA", "ascii")
|
||
|
tc.assertEqual(g1.get_charset(), "ASCII")
|
||
|
root1.set("CA", "unknownencoding")
|
||
|
tc.assertRaisesRegexp(ValueError,
|
||
|
"no codec available for CA unknownencoding",
|
||
|
g1.get_charset)
|
||
|
|
||
|
def test_node_set_move(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string("(;FF[4]GM[1]SZ[9];B[aa];B[bb])")
|
||
|
root, n1, n2 = sgf_game.get_main_sequence()
|
||
|
tc.assertEqual(root.get_move(), (None, None))
|
||
|
root.set_move('b', (1, 1))
|
||
|
n1.set_move('w', (1, 2))
|
||
|
n2.set_move('b', None)
|
||
|
tc.assertEqual(root.get('B'), (1, 1))
|
||
|
tc.assertRaises(KeyError, root.get, 'W')
|
||
|
tc.assertEqual(n1.get('W'), (1, 2))
|
||
|
tc.assertRaises(KeyError, n1.get, 'B')
|
||
|
tc.assertEqual(n2.get('B'), None)
|
||
|
tc.assertRaises(KeyError, n2.get, 'W')
|
||
|
|
||
|
def test_node_setup_stones(tc):
|
||
|
sgf_game = sgf.Sgf_game.from_string("(;FF[4]GM[1]SZ[9]AW[aa:bb])")
|
||
|
root = sgf_game.get_root()
|
||
|
root.set_setup_stones(
|
||
|
[(1, 2), (3, 4)],
|
||
|
set(),
|
||
|
[(1, 3), (4, 5)],
|
||
|
)
|
||
|
tc.assertEqual(root.get('AB'), set([(1, 2), (3, 4)]))
|
||
|
tc.assertRaises(KeyError, root.get, 'AW')
|
||
|
tc.assertEqual(root.get('AE'), set([(1, 3), (4, 5)]))
|
||
|
|
||
|
def test_add_comment_text(tc):
|
||
|
sgf_game = sgf.Sgf_game(9)
|
||
|
root = sgf_game.get_root()
|
||
|
root.add_comment_text("hello\nworld")
|
||
|
tc.assertEqual(root.get('C'), "hello\nworld")
|
||
|
root.add_comment_text("hello\naga]in")
|
||
|
tc.assertEqual(root.get('C'), "hello\nworld\n\nhello\naga]in")
|
||
|
|