SgfParser 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SGF::Gametree do
4
+
5
+ let(:game) { get_first_game_from 'spec/data/ff4_ex.sgf' }
6
+ let(:root_node) {
7
+ SGF::Node.new({FF: '4', AP: 'Primiview:3.1',
8
+ GM: '1', SZ: '19', GN: 'Gametree 1: properties',
9
+ US: 'Arno Hollosi'})
10
+ }
11
+ subject { game }
12
+
13
+ it 'should throw up if initialized with a non-Node argument' do
14
+ expect { SGF::Gametree.new('I am a string') }.to raise_error(ArgumentError)
15
+ expect { SGF::Gametree.new(SGF::Node.new) }.not_to raise_error
16
+ end
17
+
18
+ it 'has game-level information' do
19
+ expect(subject.name).to eq 'Gametree 1: properties'
20
+ expect(subject.data_entry).to eq 'Arno Hollosi'
21
+ end
22
+
23
+ it 'should raise errors' do
24
+ expect { subject.opening }.to raise_error(SGF::NoIdentityError)
25
+ expect { subject.nonexistent_identity }.to raise_error(SGF::NoIdentityError)
26
+ end
27
+
28
+ it 'has meta information' do
29
+ expect(subject.inspect).to match(/SGF::Gametree/)
30
+ expect(subject.inspect).to match(/#{game.object_id}/)
31
+ end
32
+
33
+ context "When talking about nodes" do
34
+ it "begins on the root node" do
35
+ expect(subject.current_node).to eq root_node
36
+ end
37
+
38
+ it "should have a nice way to go to children[0]" do
39
+ subject.next_node
40
+ expect(subject.current_node).to eq subject.root.children[0]
41
+ end
42
+
43
+ it "should have a way of setting an arbitrary node to the current node" do
44
+ subject.current_node = subject.root.children[3]
45
+ expect(subject.current_node.properties.keys).to match_array %w(B C N)
46
+ expect(subject.current_node.children.size).to eq 6
47
+ end
48
+
49
+ end
50
+
51
+ context "blocks" do
52
+ it "should use preorder traversal for each" do
53
+ game = get_first_game_from 'spec/data/example1.sgf'
54
+ array = []
55
+ game.each { |node| array << node }
56
+ expect(array[0].c).to eq "root"
57
+ expect(array[1].c).to eq "a"
58
+ expect(array[2].c).to eq "b"
59
+ end
60
+
61
+ it "should go through all nodes, even if block returns 'nil' (puts, anyone?)" do
62
+ game = SGF::Gametree.new root_node
63
+ game.root.add_children SGF::Node.new(B: "dd")
64
+ nodes = []
65
+ game.each { |node| nodes << node; nil }
66
+ expect(nodes.size).to eq 2
67
+ end
68
+ end
69
+
70
+ context "Slices" do
71
+
72
+ it "should return a node-only gametree if slice is [0..0]" do
73
+ game = SGF::Gametree.new root_node
74
+ slice = game.slice(0..0)
75
+ expect(slice).to be_instance_of SGF::Gametree
76
+ expect(slice.root).to eq root_node
77
+ end
78
+
79
+ it "should slice through a one-branch list" do
80
+ node = SGF::Node.new
81
+ game = SGF::Gametree.new node
82
+ node.pw = 'Aldric'
83
+ child1 = SGF::Node.new b: "qq"
84
+ child2 = SGF::Node.new a: "rn"
85
+ child3 = SGF::Node.new b: "nr"
86
+ node.add_children child1
87
+ child1.add_children child2
88
+ child2.add_children child3
89
+ slice = game.slice(1..3)
90
+ expect(slice).to be_instance_of SGF::Gametree
91
+ root = slice.root
92
+ expect(root.b).to eq 'qq'
93
+ expect(root.parent).to be_nil
94
+ expect(root.children[0].a).to eq 'rn'
95
+ expect(root.children[0].children[0].b).to eq 'nr'
96
+ end
97
+
98
+ end
99
+ end
@@ -1,90 +1,196 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
- describe "SGF::Node" do
3
+ RSpec.describe SGF::Node do
4
4
 
5
- before :each do
6
- @node = SGF::Node.new
7
- end
5
+ let(:node) { SGF::Node.new }
6
+ subject { node }
8
7
 
9
- it "should be a valid node" do
10
- @node.class.should == SGF::Node
11
- @node.properties.should == {}
12
- @node.parent.should == nil
13
- @node.children.should == []
14
- end
8
+ context 'inspect' do
9
+ subject { node.inspect }
10
+ it { is_expected.to match(/#{node.object_id}/) }
11
+ it { is_expected.to match(/SGF::Node/) }
12
+ it { is_expected.to match(/Has no parent/) }
15
13
 
16
- it "should store properties" do
17
- @node.add_properties "PB" => "Dosaku"
18
- @node.properties.should == {"PB" => "Dosaku"}
19
- end
14
+ it "should give you a relatively useful inspect" do
15
+ node.add_properties({C: "Oh hi", PB: "Dosaku", AE: "[dd][gh]"})
16
+ is_expected.to match(/3 Properties/)
20
17
 
21
- it "should link to a parent" do
22
- parent = SGF::Node.new
23
- @node.parent = parent
24
- @node.parent.should == parent
25
- end
18
+ node.add_children SGF::Node.new, SGF::Node.new
19
+ expect(node.inspect).to match(/2 Children/)
26
20
 
27
- it "should link to children" do
28
- child1 = SGF::Node.new
29
- child2 = SGF::Node.new
30
- child3 = SGF::Node.new
31
- @node.add_children child1, child2, child3
32
- @node.children.should == [child1, child2, child3]
33
- end
21
+ node.parent = SGF::Node.new
22
+ expect(node.inspect).to match(/Has a parent/)
23
+ end
34
24
 
35
- it "should link to children, who should get new parents" do
36
- child1 = SGF::Node.new
37
- child2 = SGF::Node.new
38
- child3 = SGF::Node.new
39
- @node.add_children child1, child2, child3
40
- @node.children.each { |child| child.parent.should == @node }
41
25
  end
42
26
 
43
- it "should allow concatenation of properties" do
44
- @node.add_properties "TC" => "Hello,"
45
- @node.add_properties "TC" => " world!"
46
- @node.properties["TC"].should == "Hello, world!"
27
+ it "should properly show a string version of the node" do
28
+ subject.add_properties({"C" => "Oh hi]", "PB" => "Dosaku"})
29
+ expect(subject.to_s).to eq ";C[Oh hi\\]]\nPB[Dosaku]"
47
30
  end
48
31
 
49
- it "should give you the properties based on method given" do
50
- @node.add_properties "PW" => "The Tick"
51
- @node.add_properties "PB" => "Batmanuel"
52
- @node.pw.should == "The Tick"
53
- @node.pb.should == "Batmanuel"
32
+ it "should properly show a string version of the node if identities are symbols" do
33
+ subject.add_properties({C: "Oh hi]", PB: "Dosaku"})
34
+ expect(subject.to_s).to eq ";C[Oh hi\\]]\nPB[Dosaku]"
54
35
  end
55
36
 
56
- it "should allow you to change a property completely" do
57
- @node.add_properties "RE" => "This is made up"
58
- @node.properties["RE"] = "This is also made up"
59
- @node.re.should == "This is also made up"
60
- @node.re = "And that too"
61
- @node.re.should == "And that too"
37
+ context "Heredity" do
38
+
39
+ let(:parent) { SGF::Node.new }
40
+ let(:child1) { SGF::Node.new }
41
+ let(:child3) { SGF::Node.new }
42
+ let(:child2) { SGF::Node.new }
43
+
44
+ it "should link to a parent" do
45
+ subject.parent = parent
46
+ expect(subject.parent).to eq parent
47
+ end
48
+
49
+ it "should link to children" do
50
+ node.add_children child1, child2, child3
51
+ expect(node.children).to eq [child1, child2, child3]
52
+ end
53
+
54
+ it "should link to children, who should get new parents" do
55
+ node.add_children child1, child2, child3
56
+ node.children.each { |child| expect(child.parent).to eq node }
57
+ end
58
+
59
+ it "should not be the child of many nodes" do
60
+ parent2 = SGF::Node.new
61
+ parent.add_children node
62
+ parent2.add_children node
63
+ expect(node.parent).to eq(parent2)
64
+ expect(parent2.children).to include(node)
65
+ expect(parent.children).to_not include(node)
66
+ end
67
+
68
+ it "should become a child of its new parent" do
69
+ node.parent = parent
70
+ expect(parent.children).to include node
71
+ end
72
+
62
73
  end
63
74
 
64
- it "should implement [] as a shortcut to read properties" do
65
- @node.add_properties "PB" => "Dosaku"
66
- @node["PB"].should == "Dosaku"
67
- @node[:PB].should == "Dosaku"
75
+ context "Properties" do
76
+
77
+ it "should store properties" do
78
+ node.add_properties PB: "Dosaku"
79
+ expect(node.properties).to eq({"PB" => "Dosaku"})
80
+ end
81
+
82
+ it "should allow concatenation of properties" do
83
+ node.add_properties "TC" => "Hello,"
84
+ node.add_properties "TC" => " world!"
85
+ expect(node.properties["TC"]).to eq "Hello, world!"
86
+ end
87
+
88
+ it "should give you the properties based on method given" do
89
+ node.add_properties "PW" => "The Tick"
90
+ node.add_properties "PB" => "Batmanuel"
91
+ expect(node.pw).to eq "The Tick"
92
+ expect(node.pb).to eq "Batmanuel"
93
+ end
94
+
95
+ it "should allow you to change a property completely" do
96
+ node.add_properties "RE" => "This is made up"
97
+ node.properties["RE"] = "This is also made up"
98
+ expect(node.re).to eq "This is also made up"
99
+ node.re = "And that too"
100
+ expect(node.re).to eq "And that too"
101
+ node[:RE] = 'kokolegorille'
102
+ expect(node[:RE]).to eq 'kokolegorille'
103
+ end
104
+
105
+ it "should implement [] as a shortcut to read properties" do
106
+ node.add_properties "PB" => "Dosaku"
107
+ expect(node["PB"]).to eq "Dosaku"
108
+ expect(node[:PB]).to eq "Dosaku"
109
+ end
110
+
68
111
  end
69
112
 
70
- it "should give you a relatively useful inspect" do
71
- @node.inspect.should match /#{@node.object_id}/
72
- @node.inspect.should match /SGF::Node/
113
+ context "Node depth" do
114
+
115
+ it "should get a node depth number one more by the parent when attached to a parent" do
116
+ child = SGF::Node.new
117
+ node.add_children child
118
+ expect(node.depth).to eq 0
119
+ expect(child.depth).to eq 1
120
+ end
121
+
122
+ it "should properly set depth if a parent is passed to initializer" do
123
+ node2 = SGF::Node.new parent: node
124
+ expect(node2.parent).to eq node
125
+ expect(node2.depth).to eq 1
126
+ end
127
+
128
+ it "should properly update depth when parentage changes" do
129
+ link1 = SGF::Node.new
130
+ link2 = SGF::Node.new
131
+ link2.parent = link1
132
+ link1.parent = node
133
+ expect(node.parent).to be_nil
134
+ expect(link1.parent).to eq node
135
+ expect(link2.parent).to eq link1
136
+ expect(node.depth).to eq 0
137
+ expect(link1.depth).to eq 1
138
+ expect(link2.depth).to eq 2
139
+ end
140
+
141
+ it "should properly update depth if parent is set to nil / parent is removed" do
142
+ parent = SGF::Node.new
143
+ child = SGF::Node.new
144
+
145
+ node.add_children child
146
+ node.parent = parent
147
+ expect(node.depth).to eq 1
148
+ expect(child.depth).to eq 2
149
+
150
+ node.parent = nil
151
+ expect(node.depth).to eq 0
152
+ expect(child.depth).to eq 1
153
+
154
+ node.parent = parent
155
+ expect(node.depth).to eq 1
156
+ expect(child.depth).to eq 2
157
+
158
+ node.remove_parent
159
+ expect(node.depth).to eq 0
160
+ expect(child.depth).to eq 1
161
+ end
162
+
163
+ it "should properly update depth when childhood changes" do
164
+ link1 = SGF::Node.new
165
+ link2 = SGF::Node.new
166
+ link3 = SGF::Node.new
167
+ link1.add_children link2
168
+ link2.add_children link3
169
+ node.add_children link1
170
+ node.add_children link3
171
+ expect(node.depth).to eq 0
172
+ expect(link1.depth).to eq 1
173
+ expect(link2.depth).to eq 2
174
+ expect(link3.depth).to eq 1
175
+ end
176
+ end
73
177
 
74
- @node.add_properties({"C" => "Oh hi", "PB" => "Dosaku", "AE" => "[dd][gh]"})
75
- @node.inspect.should match /3 Properties/
178
+ context "self-consistency" do
179
+ it "should only track changes from its current parent" do
180
+ link1 = SGF::Node.new
181
+ link2 = SGF::Node.new
76
182
 
77
- @node.add_children SGF::Node.new, SGF::Node.new
78
- @node.inspect.should match /2 Children/
183
+ link3 = SGF::Node.new
184
+ link1.add_children link2
185
+ node.parent = link2
186
+ expect(node.depth).to eq 2
79
187
 
80
- @node.inspect.should match /Has no parent/
81
- @node.parent = SGF::Node.new
82
- @node.inspect.should match /Has a parent/
83
- end
188
+ link1.depth = 2
189
+ expect(node.depth).to eq 4
84
190
 
85
- it "should properly show a string version of the node" do
86
- @node.add_properties({"C" => "Oh hi]", "PB" => "Dosaku"})
87
- @node.to_str.should == ";C[Oh hi\\]]\nPB[Dosaku]"
191
+ node.parent = link3
192
+ link1.depth = 6
193
+ expect(node.depth).to eq 1
194
+ end
88
195
  end
89
-
90
- end
196
+ end
@@ -1,117 +1,119 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
- describe "SGF::Parser" do
3
+ RSpec.describe SGF::Parser do
4
4
 
5
- it "should give an error if the first two character are not (;" do
6
- parser = sgf_parser
7
- expect { parser.parse ';)' }.to raise_error SGF::MalformedDataError
8
- end
5
+ let(:parser) { SGF::Parser.new }
6
+
7
+ describe "when given invalid SGF files" do
8
+ it "should give an error if the first two character are not (;" do
9
+ expect { parser.parse ';)' }.to raise_error SGF::MalformedDataError
10
+ end
11
+
12
+ it "should not give an error if it is told to sit down and shut up" do
13
+ expect { parser.parse(';)', false) }.to_not raise_error
14
+ end
15
+
16
+ it "should properly parse a file that has many AB/AW/AE values in a single node" do
17
+ invalid_sgf = "(;AB[dd]AB[aa])"
18
+ collection = parser.parse invalid_sgf
19
+
20
+ node = SGF::Node.new AB: %w[dd aa]
21
+ expected = SGF::Collection.new
22
+ expected.root.add_children node
9
23
 
10
- it "should not give an error if it is told to sit down and shut up" do
11
- parser = sgf_parser
12
- expect { parser.parse ';)', false}.to_not raise_error
24
+ expect(collection).to eq expected
25
+ expect(collection.errors).to include "Multiple AB identities are present in a single node. A property should only exist once per node."
26
+ end
13
27
  end
14
28
 
15
29
  it "should parse a simple node" do
16
- parser = sgf_parser
17
- tree = parser.parse ";PW[5]", false
18
- tree.root.children[0].pw.should == "5"
30
+ collection = parser.parse ";PW[Dosaku]", false
31
+ expect(collection.root.children[0].pw).to eq "Dosaku"
19
32
  end
20
33
 
21
34
  it "should parse a node with two properties" do
22
- parser = sgf_parser
23
- tree = parser.parse ";PB[Aldric]PW[Bob]", false
24
- tree.root.children[0].pb.should == "Aldric"
25
- tree.root.children[0].pw.should == "Bob"
35
+ collection = parser.parse ";PB[Aldric]PW[Bob]", false
36
+ expect(collection.root.children[0].pb).to eq "Aldric"
37
+ expect(collection.root.children[0].pw).to eq "Bob"
38
+ end
39
+
40
+ it "should apparently ignore line breaks in the middle of a property name -- is this right?" do
41
+ collection = parser.parse ";P\nB[Aldric]P\n\n\nW[Bob]", false
42
+ expect(collection.root.children[0].pb).to eq "Aldric"
43
+ expect(collection.root.children[0].pw).to eq "Bob"
26
44
  end
27
45
 
28
46
  it "should parse two nodes with one property each" do
29
- parser = sgf_parser
30
- tree = parser.parse ";PB[Aldric];PW[Bob]", false
31
- node = tree.root.children[0]
32
- node.pb.should == "Aldric"
33
- node.children[0].pw.should == "Bob"
47
+ collection = parser.parse ";PB[Aldric];PW[Bob]", false
48
+ node = collection.root.children[0]
49
+ expect(node.pb).to eq "Aldric"
50
+ expect(node.children[0].pw).to eq "Bob"
34
51
  end
35
52
 
36
53
  it "should parse a tree with a single branch" do
37
- parser = sgf_parser
38
- tree = parser.parse "(;FF[4]PW[Aldric]PB[Bob];B[qq])"
39
- node = tree.root.children[0]
40
- node.pb.should == "Bob"
41
- node.pw.should == "Aldric"
42
- node.children[0].b.should == "qq"
54
+ collection = parser.parse "(;FF[4]PW[Aldric]PB[Bob];B[qq])"
55
+ node = collection.root.children[0]
56
+ expect(node.pb).to eq "Bob"
57
+ expect(node.pw).to eq "Aldric"
58
+ expect(node.children[0].b).to eq "qq"
43
59
  end
44
60
 
45
61
  it "should parse a tree with two branches" do
46
- parser = sgf_parser
47
- tree = parser.parse "(;FF[4](;C[main])(;C[branch]))"
48
- node = tree.root.children[0]
49
- node.ff.should == "4"
50
- node.children.size.should == 2
51
- node.children[0].c.should == "main"
52
- node.children[1].c.should == "branch"
62
+ collection = parser.parse "(;FF[4](;C[main])(;C[branch]))"
63
+ node = collection.root.children[0]
64
+ expect(node.ff).to eq "4"
65
+ expect(node.children.size).to eq 2
66
+ expect( node.children[0].c).to eq "main"
67
+ expect(node.children[1].c).to eq "branch"
53
68
  end
54
69
 
55
- it "should parse a comment with a ] inside" do
56
- parser = sgf_parser
57
- tree = parser.parse "(;C[Oh hi\\] there])"
58
- tree.root.children[0].c.should == "Oh hi] there"
70
+ it "should parse a comment with a \\] inside" do
71
+ collection = parser.parse "(;C[Oh hi\\] there])"
72
+ expect(collection.root.children[0].c).to eq "Oh hi] there"
59
73
  end
60
74
 
61
75
  it "should parse a multi-property identity well" do
62
- parser = sgf_parser
63
- tree = parser.parse "(;FF[4];AW[bd][cc][qr])"
64
- tree.root.children[0].children[0].aw.should == ["bd", "cc", "qr"]
76
+ collection = parser.parse "(;FF[4];AW[bd][cc][qr])"
77
+ expect(collection.root.children[0].children[0].aw).to eq %w(bd cc qr)
65
78
  end
66
79
 
67
80
  it "should parse multiple trees" do
68
- parser = sgf_parser
69
- tree = parser.parse "(;FF[4]PW[Aldric];AW[dd][cc])(;FF[4]PW[Hi];PB[ad])"
70
- tree.root.children.size.should == 2
71
- tree.root.children[0].children[0].aw.should == ["dd", "cc"]
72
- tree.root.children[1].children[0].pb.should == "ad"
81
+ collection = parser.parse "(;FF[4]PW[Aldric];AW[dd][cc])(;FF[4]PW[Hi];PB[ad])"
82
+ expect(collection.root.children.size).to eq 2
83
+ expect(collection.root.children[0].children[0].aw).to eq %w(dd cc)
84
+ expect(collection.root.children[1].children[0].pb).to eq "ad"
73
85
  end
74
86
 
75
87
  it "should not put (; into the identity when separated by line breaks" do
76
- parser = sgf_parser
77
- tree = parser.parse "(;FF[4]\n\n(;B[dd])(;B[da]))"
78
- game = tree.root.children[0]
79
- game.children.size.should == 2
80
- game.children[0].b.should == "dd"
81
- game.children[1].b.should == "da"
88
+ collection = parser.parse "(;FF[4]\n\n(;B[dd])(;B[da]))"
89
+ game = collection.root.children[0]
90
+ expect(game.children.size).to eq 2
91
+ expect(game.children[0].b).to eq "dd"
92
+ expect( game.children[1].b).to eq "da"
82
93
  end
83
94
 
84
95
  it "should parse the simplified sample SGF" do
85
- parser = sgf_parser
86
- tree = parser.parse SIMPLIFIED_SAMPLE_SGF
87
- root = tree.root
88
- root.children.size.should == 2
96
+ collection = parser.parse SIMPLIFIED_SAMPLE_SGF
97
+ root = collection.root
98
+ expect(root.children.size).to eq 2
89
99
  node = root.children[0].children[0]
90
- node.ar.should == ["aa:sc", "sa:ac", "aa:sa", "aa:ac", "cd:cj", "gd:md", "fh:ij", "kj:nh"]
100
+ expect(node.ar).to eq %w(aa:sc sa:ac aa:sa aa:ac cd:cj gd:md fh:ij kj:nh)
91
101
  end
92
102
 
93
103
  it "should parse a file if given a file handler as input" do
94
- parser = sgf_parser
95
104
  file = File.open 'spec/data/simple.sgf'
96
- tree = parser.parse file
97
- game = tree.games.first
98
- game.white_player.should == "redrose"
99
- game.black_player.should == "tartrate"
105
+ collection = parser.parse file
106
+ game = collection.gametrees.first
107
+ expect(game.white_player).to eq "redrose"
108
+ expect(game.black_player).to eq "tartrate"
100
109
  end
101
110
 
102
- it "should parse a file if given a local path as input" do
103
- parser = sgf_parser
111
+ it "should parse a file if given a local path as input" do
104
112
  local_path = 'spec/data/simple.sgf'
105
- tree = parser.parse local_path
106
- game = tree.games.first
107
- game.white_player.should == "redrose"
108
- game.black_player.should == "tartrate"
109
- end
110
-
111
- private
112
-
113
- def sgf_parser
114
- SGF::Parser.new
113
+ collection = parser.parse local_path
114
+ game = collection.gametrees.first
115
+ expect(game.white_player).to eq "redrose"
116
+ expect(game.black_player).to eq "tartrate"
115
117
  end
116
118
 
117
119
  end