sgf_parser 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.irbrc +3 -0
- data/.rvmrc +4 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +17 -0
- data/LICENSE +20 -0
- data/README.textile +27 -0
- data/Rakefile +20 -0
- data/TODO +0 -0
- data/VERSION +1 -0
- data/bin/sgf +28 -0
- data/bin/stm2dot +18 -0
- data/doc/sgf_state_machine.dot +58 -0
- data/doc/sgf_state_machine.svg +269 -0
- data/lib/sgf.rb +15 -0
- data/lib/sgf/binary_file_error.rb +4 -0
- data/lib/sgf/debugger.rb +15 -0
- data/lib/sgf/default_event_listener.rb +37 -0
- data/lib/sgf/model/constants.rb +19 -0
- data/lib/sgf/model/event_listener.rb +72 -0
- data/lib/sgf/model/game.rb +52 -0
- data/lib/sgf/model/label.rb +12 -0
- data/lib/sgf/model/node.rb +115 -0
- data/lib/sgf/model/property_handler.rb +51 -0
- data/lib/sgf/more/state_machine_presenter.rb +43 -0
- data/lib/sgf/more/stm_dot_converter.rb +139 -0
- data/lib/sgf/parse_error.rb +25 -0
- data/lib/sgf/parser.rb +56 -0
- data/lib/sgf/renderer.rb +25 -0
- data/lib/sgf/sgf_helper.rb +55 -0
- data/lib/sgf/sgf_state_machine.rb +174 -0
- data/lib/sgf/state_machine.rb +76 -0
- data/sgf_parser.gemspec +95 -0
- data/spec/fixtures/2009-11-01-1.sgf +24 -0
- data/spec/fixtures/2009-11-01-2.sgf +23 -0
- data/spec/fixtures/chinese_gb.sgf +9 -0
- data/spec/fixtures/chinese_utf.sgf +9 -0
- data/spec/fixtures/example.sgf +18 -0
- data/spec/fixtures/good.sgf +55 -0
- data/spec/fixtures/good1.sgf +167 -0
- data/spec/fixtures/kgs.sgf +723 -0
- data/spec/fixtures/test.png +0 -0
- data/spec/sgf/model/event_listener_spec.rb +97 -0
- data/spec/sgf/model/game_spec.rb +29 -0
- data/spec/sgf/model/node_spec.rb +84 -0
- data/spec/sgf/more/state_machine_presenter_spec.rb +29 -0
- data/spec/sgf/parse_error_spec.rb +10 -0
- data/spec/sgf/parser_spec.rb +210 -0
- data/spec/sgf/sgf_helper_spec.rb +68 -0
- data/spec/sgf/sgf_state_machine_spec.rb +166 -0
- data/spec/sgf/state_machine_spec.rb +137 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +47 -0
- metadata +150 -0
Binary file
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
module SGF
|
4
|
+
module Model
|
5
|
+
describe EventListener do
|
6
|
+
before :each do
|
7
|
+
@listener = EventListener.new
|
8
|
+
end
|
9
|
+
|
10
|
+
(DefaultEventListener.instance_methods - DefaultEventListener.superclass.instance_methods).each do |method|
|
11
|
+
it "should implement #{method} from SGF::DefaultEventListener" do
|
12
|
+
@listener.methods.should include(method)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should start a new game on start_game" do
|
17
|
+
@listener.start_game
|
18
|
+
game1 = @listener.game
|
19
|
+
@listener.start_game
|
20
|
+
game2 = @listener.game
|
21
|
+
game1.should_not == game2
|
22
|
+
end
|
23
|
+
|
24
|
+
it "start_variation should create a new node as variation root" do
|
25
|
+
@listener.start_game
|
26
|
+
@listener.start_node
|
27
|
+
node = @listener.node
|
28
|
+
@listener.start_variation
|
29
|
+
@listener.node.should_not == node
|
30
|
+
@listener.node.variation_root?.should be_true
|
31
|
+
end
|
32
|
+
|
33
|
+
it "end_variation should set current node as parent of current variation's root" do
|
34
|
+
@listener.start_game
|
35
|
+
@listener.start_node
|
36
|
+
node = @listener.node
|
37
|
+
@listener.start_variation
|
38
|
+
@listener.node.should_not == node
|
39
|
+
@listener.end_variation
|
40
|
+
@listener.node.should == node
|
41
|
+
end
|
42
|
+
|
43
|
+
SGF::Model::GAME_PROPERTY_MAPPINGS.each do |prop_name, attr_name|
|
44
|
+
it "call property_name=#{prop_name} and property_value=something should set #{attr_name} on game" do
|
45
|
+
mock(@listener.game).send(attr_name, '123')
|
46
|
+
@listener.property_name = prop_name
|
47
|
+
@listener.property_value = '123'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should set node as game's root node on first call to start_node" do
|
52
|
+
@listener.start_game
|
53
|
+
@listener.start_node
|
54
|
+
@listener.node.should == @listener.game.root_node
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should create child node on current node when call start_node" do
|
58
|
+
@listener.start_game
|
59
|
+
@listener.start_node
|
60
|
+
node = @listener.node
|
61
|
+
@listener.start_node
|
62
|
+
@listener.node.should_not == node
|
63
|
+
@listener.node.parent.should == node
|
64
|
+
node.children.should == [@listener.node]
|
65
|
+
end
|
66
|
+
|
67
|
+
it "property_name='C' and property_value=something should add comment to node" do
|
68
|
+
@listener.start_game
|
69
|
+
@listener.start_node
|
70
|
+
@listener.property_name = "C"
|
71
|
+
@listener.property_value = "comment"
|
72
|
+
@listener.game.root_node.comment.should == 'comment'
|
73
|
+
end
|
74
|
+
|
75
|
+
it "game misc properties" do
|
76
|
+
@listener.start_game
|
77
|
+
@listener.start_node
|
78
|
+
@listener.property_name = "US"
|
79
|
+
@listener.property_value = "A"
|
80
|
+
@listener.game.misc_properties["US"].should == "A"
|
81
|
+
@listener.property_value = "B"
|
82
|
+
@listener.game.misc_properties["US"].should == ["A", "B"]
|
83
|
+
end
|
84
|
+
|
85
|
+
it "node misc properties" do
|
86
|
+
@listener.start_game
|
87
|
+
@listener.start_node
|
88
|
+
@listener.property_name = "TR"
|
89
|
+
@listener.property_value = "AB"
|
90
|
+
@listener.node.misc_properties["TR"].should == "AB"
|
91
|
+
@listener.property_value = "AC"
|
92
|
+
@listener.node.misc_properties["TR"].should == ["AB", "AC"]
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
module SGF
|
4
|
+
module Model
|
5
|
+
describe Game do
|
6
|
+
it "moves should return number of moves" do
|
7
|
+
game = Game.new
|
8
|
+
n1 = Node.new game.root_node
|
9
|
+
n1.sgf_play_black 'AB'
|
10
|
+
n2 = Node.new n1
|
11
|
+
n2.sgf_play_white 'BC'
|
12
|
+
|
13
|
+
game.moves.should == 2
|
14
|
+
end
|
15
|
+
|
16
|
+
it "moves should not count non-move nodes" do
|
17
|
+
game = Game.new
|
18
|
+
n1 = Node.new game.root_node
|
19
|
+
n1.sgf_play_black 'AB'
|
20
|
+
dummy1 = Node.new(n1)
|
21
|
+
n2 = Node.new dummy1
|
22
|
+
n2.sgf_play_white 'BC'
|
23
|
+
dummy2 = Node.new(n2)
|
24
|
+
|
25
|
+
game.moves.should == 2
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
|
3
|
+
module SGF
|
4
|
+
module Model
|
5
|
+
describe Node do
|
6
|
+
describe "move_no" do
|
7
|
+
it "should return parent node's move number + 1 if current node is a move" do
|
8
|
+
parent = Node.new
|
9
|
+
mock(parent).move_no{ 10 }
|
10
|
+
node = Node.new(parent)
|
11
|
+
node.sgf_play_black 'AB'
|
12
|
+
node.move_no.should == 11
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return parent node's move number if current node is not a move" do
|
16
|
+
parent = Node.new
|
17
|
+
mock(parent).move_no{ 10 }
|
18
|
+
node = Node.new(parent)
|
19
|
+
node.move_no.should == 10
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return 0 if parent is nil and current node is not a move" do
|
23
|
+
node = Node.new
|
24
|
+
node.move_no.should == 0
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should return 1 if parent is nil and current node is a move" do
|
28
|
+
node = Node.new
|
29
|
+
node.sgf_play_black "AB"
|
30
|
+
node.move_no.should == 1
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "sgf_setup_black should add an entry to black_moves" do
|
35
|
+
node = Node.new
|
36
|
+
node.sgf_setup_black "AB"
|
37
|
+
node.black_moves.first.should == [0, 1]
|
38
|
+
end
|
39
|
+
|
40
|
+
it "sgf_setup_white should add an entry to white_moves" do
|
41
|
+
node = Node.new
|
42
|
+
node.sgf_setup_white "AB"
|
43
|
+
node.white_moves.first.should == [0, 1]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "sgf_play_black should set node type to MOVE and save move information" do
|
47
|
+
node = Node.new
|
48
|
+
node.sgf_play_black "AB"
|
49
|
+
node.node_type.should == Constants::NODE_MOVE
|
50
|
+
node.color.should == Constants::BLACK
|
51
|
+
node.move.should == [0, 1]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "sgf_play_black should set node type to PASS on empty move coordinate" do
|
55
|
+
node = Node.new
|
56
|
+
node.sgf_play_black " "
|
57
|
+
node.node_type.should == Constants::NODE_PASS
|
58
|
+
node.color.should == Constants::BLACK
|
59
|
+
end
|
60
|
+
|
61
|
+
it "sgf_play_white should set node type to MOVE and save move information" do
|
62
|
+
node = Node.new
|
63
|
+
node.sgf_play_white "AB"
|
64
|
+
node.node_type.should == Constants::NODE_MOVE
|
65
|
+
node.color.should == Constants::WHITE
|
66
|
+
node.move.should == [0, 1]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "sgf_play_white should set node type to PASS on empty move coordinate" do
|
70
|
+
node = Node.new
|
71
|
+
node.sgf_play_white " "
|
72
|
+
node.node_type.should == Constants::NODE_PASS
|
73
|
+
node.color.should == Constants::WHITE
|
74
|
+
end
|
75
|
+
|
76
|
+
it "child returns first child" do
|
77
|
+
node = Node.new
|
78
|
+
first_child = Node.new(node)
|
79
|
+
second_child = Node.new(node)
|
80
|
+
node.child.should == first_child
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../../lib/sgf/more/state_machine_presenter')
|
3
|
+
|
4
|
+
describe SGF::More::StateMachinePresenter do
|
5
|
+
it "should create nodes for start state and all reachable states" do
|
6
|
+
stm = SGF::StateMachine.new(:start)
|
7
|
+
stm.transition(:start, /a/, :state_a)
|
8
|
+
|
9
|
+
presenter = SGF::More::StateMachinePresenter.new
|
10
|
+
presenter.process(stm)
|
11
|
+
|
12
|
+
presenter.nodes.should include("start")
|
13
|
+
presenter.nodes.should include("state_a")
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should create edges for all transitions" do
|
17
|
+
stm = SGF::StateMachine.new(:start)
|
18
|
+
stm.desc (desc1 = "start + /a/ => state_a")
|
19
|
+
stm.transition(:start, /a/, :state_a)
|
20
|
+
stm.desc (desc2 = "start + /b/ => state_b")
|
21
|
+
stm.transition(:start, /b/, :state_b)
|
22
|
+
|
23
|
+
presenter = SGF::More::StateMachinePresenter.new
|
24
|
+
presenter.process(stm)
|
25
|
+
|
26
|
+
presenter.edges.should include(["start", "state_a", desc1])
|
27
|
+
presenter.edges.should include(["start", "state_b", desc2])
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
module SGF
|
4
|
+
describe ParseError do
|
5
|
+
it "message should return content up to the bad input" do
|
6
|
+
error = ParseError.new("(;AB]", 4)
|
7
|
+
error.message.should include("(;AB] <=== SGF parse error occurred here")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
module SGF
|
4
|
+
describe Parser, 'with DefaultEventListener' do
|
5
|
+
before :each do
|
6
|
+
@listener = DefaultEventListener.new
|
7
|
+
@parser = Parser.new @listener
|
8
|
+
end
|
9
|
+
|
10
|
+
{'nil' => nil, '' => '', ' ' => ' '}.each do |name, value|
|
11
|
+
it "should throw error on bad input - '#{name}'" do
|
12
|
+
begin
|
13
|
+
@parser.parse(value)
|
14
|
+
fail("Should raise error")
|
15
|
+
rescue => e
|
16
|
+
e.class.should == ArgumentError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should call start_game" do
|
22
|
+
mock(@listener).start_game
|
23
|
+
@parser.parse("(")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should call end_game" do
|
27
|
+
mock(@listener).end_game
|
28
|
+
@parser.parse("(;)")
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should call start_node" do
|
32
|
+
mock(@listener).start_node
|
33
|
+
@parser.parse("(;")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should call end_variation" do
|
37
|
+
mock(@listener).end_variation
|
38
|
+
@parser.parse("(;)")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should call property_name= with name" do
|
42
|
+
mock(@listener).property_name = 'GN'
|
43
|
+
@parser.parse("(;GN[)")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should call property_value= with value" do
|
47
|
+
mock(@listener).property_value = 'a game'
|
48
|
+
@parser.parse("(;GN[a game])")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should call property_value= for each value" do
|
52
|
+
mock(@listener).property_value = 'DB'
|
53
|
+
mock(@listener).property_value = 'KS'
|
54
|
+
@parser.parse("(;AB[DB][KS])")
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should call start_variation" do
|
58
|
+
mock(@listener).start_variation
|
59
|
+
@parser.parse("(;(")
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should call end_variation" do
|
63
|
+
mock(@listener).end_variation
|
64
|
+
@parser.parse("(;(;)")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe Parser, 'with SGF::Model::EventListener' do
|
69
|
+
before :each do
|
70
|
+
@listener = SGF::Model::EventListener.new
|
71
|
+
@parser = Parser.new @listener
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should parse game in file" do
|
75
|
+
@parser.parse_file File.expand_path(File.dirname(__FILE__) + '/../fixtures/good.sgf')
|
76
|
+
game = @listener.game
|
77
|
+
game.name.should == 'White (W) vs. Black (B)'
|
78
|
+
game.rule.should == 'Japanese'
|
79
|
+
game.board_size.should == 19
|
80
|
+
game.handicap.should == 0
|
81
|
+
game.komi.should == 5.5
|
82
|
+
game.played_on.should == "1999-07-28"
|
83
|
+
game.white_player.should == 'White'
|
84
|
+
game.black_player.should == 'Black'
|
85
|
+
game.program.should == 'Cgoban 1.9.2'
|
86
|
+
game.time_rule.should == '30:00(5x1:00)'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should raise BinaryFileError on parsing binary file" do
|
90
|
+
pending 'check if file is binary is problematic'
|
91
|
+
lambda {
|
92
|
+
@parser.parse_file File.expand_path(File.dirname(__FILE__) + '/../fixtures/test.png')
|
93
|
+
}.should raise_error(SGF::BinaryFileError)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should parse game without moves" do
|
97
|
+
@parser.parse <<-INPUT
|
98
|
+
(;GM[1]FF[3]
|
99
|
+
GN[White (W) vs. Black (B)];
|
100
|
+
)
|
101
|
+
INPUT
|
102
|
+
game = @listener.game
|
103
|
+
game.name.should == 'White (W) vs. Black (B)'
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should raise error on invalid input" do
|
107
|
+
lambda { @parser.parse("123") }.should raise_error
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should parse a complete game" do
|
111
|
+
@parser.parse <<-INPUT
|
112
|
+
(;GM[1]FF[3]
|
113
|
+
RU[Japanese]SZ[19]HA[0]KM[5.5]
|
114
|
+
PW[White]
|
115
|
+
PB[Black]
|
116
|
+
GN[White (W) vs. Black (B)]
|
117
|
+
DT[1999-07-28]
|
118
|
+
RE[W+R]
|
119
|
+
SY[Cgoban 1.9.2]TM[30:00(5x1:00)];
|
120
|
+
AW[ea][eb][ec][bd][dd][ae][ce][de][cf][ef][cg][dg][eh][ci][di][bj][ej]
|
121
|
+
AB[da][db][cc][dc][cd][be][bf][ag][bg][bh][ch][dh]LB[bd:A]PL[2]
|
122
|
+
C[guff plays A and adum tenukis to fill a 1-point ko. white to kill.
|
123
|
+
]
|
124
|
+
(;W[bc];B[bb]
|
125
|
+
(;W[ca];B[cb]
|
126
|
+
(;W[ab];B[ba]
|
127
|
+
(;W[bi]
|
128
|
+
C[RIGHT black can't push (but no such luck in the actual game)
|
129
|
+
])
|
130
|
+
)))
|
131
|
+
)
|
132
|
+
INPUT
|
133
|
+
game = @listener.game
|
134
|
+
game.name.should == 'White (W) vs. Black (B)'
|
135
|
+
game.rule.should == 'Japanese'
|
136
|
+
game.board_size.should == 19
|
137
|
+
game.handicap.should == 0
|
138
|
+
game.komi.should == 5.5
|
139
|
+
game.result.should == 'W+R'
|
140
|
+
game.played_on.should == "1999-07-28"
|
141
|
+
game.white_player.should == 'White'
|
142
|
+
game.black_player.should == 'Black'
|
143
|
+
game.program.should == 'Cgoban 1.9.2'
|
144
|
+
game.time_rule.should == '30:00(5x1:00)'
|
145
|
+
|
146
|
+
root_node = game.root_node
|
147
|
+
root_node.node_type.should == SGF::Model::Constants::NODE_SETUP
|
148
|
+
|
149
|
+
node2 = root_node.child
|
150
|
+
node2.node_type.should == SGF::Model::Constants::NODE_SETUP
|
151
|
+
node2.trunk?.should be_true
|
152
|
+
node2.comment.should == "guff plays A and adum tenukis to fill a 1-point ko. white to kill."
|
153
|
+
node2.black_moves.should include([3, 0])
|
154
|
+
node2.black_moves.should include([3, 1])
|
155
|
+
node2.white_moves.should include([4, 0])
|
156
|
+
node2.white_moves.should include([4, 1])
|
157
|
+
node2.labels[0].should == SGF::Model::Label.new([1, 3], "A")
|
158
|
+
|
159
|
+
var1_root = node2.children[0]
|
160
|
+
var1_root.trunk?.should be_false
|
161
|
+
var1_root.variation_root?.should be_true
|
162
|
+
var1_root.color.should == SGF::Model::Constants::WHITE
|
163
|
+
var1_root.move.should == [1, 2]
|
164
|
+
|
165
|
+
var1_node2 = var1_root.child
|
166
|
+
var1_node2.trunk?.should be_false
|
167
|
+
var1_node2.variation_root?.should_not be_true
|
168
|
+
var1_node2.color.should == SGF::Model::Constants::BLACK
|
169
|
+
var1_node2.move.should == [1, 1]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe "class methods" do
|
174
|
+
it "parse should take a String and parse it and return the game" do
|
175
|
+
game = SGF::Parser.parse <<-INPUT
|
176
|
+
(;GM[1]FF[3]
|
177
|
+
GN[White (W) vs. Black (B)];
|
178
|
+
)
|
179
|
+
INPUT
|
180
|
+
game.name.should == 'White (W) vs. Black (B)'
|
181
|
+
end
|
182
|
+
|
183
|
+
it "parse should pass debug parameter to event listener" do
|
184
|
+
event_listener = SGF::Model::EventListener.new(false)
|
185
|
+
mock(SGF::Model::EventListener).new(true) {event_listener}
|
186
|
+
SGF::Parser.parse "(;)", true
|
187
|
+
end
|
188
|
+
|
189
|
+
it "parse_file should take a sgf file and parse it and return the game" do
|
190
|
+
game = SGF::Parser.parse_file File.expand_path(File.dirname(__FILE__) + '/../fixtures/good.sgf')
|
191
|
+
game.name.should == 'White (W) vs. Black (B)'
|
192
|
+
end
|
193
|
+
|
194
|
+
it "parse_file should pass debug parameter to event listener" do
|
195
|
+
event_listener = SGF::Model::EventListener.new(false)
|
196
|
+
mock(SGF::Model::EventListener).new(true) {event_listener}
|
197
|
+
game = SGF::Parser.parse_file(File.expand_path(File.dirname(__FILE__) + '/../fixtures/good.sgf'), true)
|
198
|
+
game.name.should == 'White (W) vs. Black (B)'
|
199
|
+
end
|
200
|
+
|
201
|
+
it "should parse game with escaped []" do
|
202
|
+
game = SGF::Parser.parse_file(File.expand_path(File.dirname(__FILE__) + '/../fixtures/good1.sgf'))
|
203
|
+
end
|
204
|
+
|
205
|
+
it "is_binary? should return true for binary file" do
|
206
|
+
pending 'check if file is binary is problematic'
|
207
|
+
SGF::Parser.is_binary?(File.expand_path(File.dirname(__FILE__) + '/../fixtures/test.png')).should be_true
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|