icu_tournament 0.8.9

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.
@@ -0,0 +1,313 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ module ICU
4
+ describe Player do
5
+ context "a typical player" do
6
+ it "should have a name, number and some results" do
7
+ lambda do
8
+ player = Player.new('Mark', 'Orr', 1)
9
+ player.add_result(Result.new(1, 1, 'W', :opponent => 37, :score => 'W', :colour => 'W'))
10
+ player.add_result(Result.new(2, 1, 'W', :opponent => 13, :score => 'W', :colour => 'B'))
11
+ player.add_result(Result.new(3, 1, 'W', :opponent => 7, :score => 'D', :colour => 'W'))
12
+ end.should_not raise_error
13
+ end
14
+ end
15
+
16
+ context "names" do
17
+ it "should be specified in constructor" do
18
+ p = Player.new('Mark', 'Orr', 1)
19
+ p.first_name.should == 'Mark'
20
+ p.last_name.should == 'Orr'
21
+ end
22
+
23
+ it "should be resettable via accessors" do
24
+ p = Player.new('Mark', 'Orr', 1)
25
+ p.first_name= 'Gary'
26
+ p.last_name= 'Kasparov'
27
+ p.first_name.should == 'Gary'
28
+ p.last_name.should == 'Kasparov'
29
+ end
30
+
31
+ it "should not contain invalid characters" do
32
+ lambda { Player.new('12', 'Orr', 1) }.should raise_error(/invalid first name/)
33
+ lambda { Player.new('Mark', '*!', 1) }.should raise_error(/invalid last name/)
34
+ end
35
+
36
+ it "should not have empty last name" do
37
+ lambda { Player.new('Mark', '', 1) }.should raise_error(/invalid last name/)
38
+ lambda { Player.new('', 'Orr', 1) }.should raise_error(/invalid first name/)
39
+ end
40
+
41
+ it "both names can be returned together" do
42
+ p = Player.new('Mark', 'Orr', 1)
43
+ p.name.should == 'Orr, Mark'
44
+ end
45
+
46
+ it "names should be automatically canonicalised" do
47
+ p = Player.new(' maRk J l ', ' ORR', 1)
48
+ p.name.should == 'Orr, Mark J. L.'
49
+ p.first_name = 'z'
50
+ p.name.should == 'Orr, Z.'
51
+ p.last_name = " o meFiSto "
52
+ p.name.should == "O'Mefisto, Z."
53
+ end
54
+ end
55
+
56
+ context "number" do
57
+ it "should just be an integer" do
58
+ Player.new('Mark', 'Orr', 3).num.should == 3
59
+ Player.new('Mark', 'Orr', -7).num.should == -7
60
+ Player.new('Mark', 'Orr', ' -4 ').num.should == -4
61
+ Player.new('Mark', 'Orr', '0').num.should == 0
62
+ lambda { Player.new('Mark', 'Orr', ' ') }.should raise_error(/invalid integer/)
63
+ end
64
+ end
65
+
66
+ context "ID" do
67
+ it "defaults to nil" do
68
+ Player.new('Mark', 'Orr', 3).id.should be_nil
69
+ end
70
+
71
+ it "should be a positive integer" do
72
+ Player.new('Mark', 'Orr', 3, :id => 1350).id.should == 1350
73
+ Player.new('Gary', 'Kasparov', 4, :id => '4100018').id.should == 4100018
74
+ lambda { Player.new('Mark', 'Orr', 3, :id => ' 0 ') }.should raise_error(/invalid positive integer/)
75
+ end
76
+ end
77
+
78
+ context "federation" do
79
+ it "defaults to nil" do
80
+ Player.new('Mark', 'Orr', 3).fed.should be_nil
81
+ Player.new('Mark', 'Orr', 3, :fed => ' ').fed.should be_nil
82
+ end
83
+
84
+ it "should consist of at least three letters" do
85
+ Player.new('Gary', 'Kasparov', 1, :fed => 'RUS').fed.should == 'RUS'
86
+ Player.new('Mark', 'Orr', 3, :fed => ' Ireland ').fed.should == 'IRL'
87
+ lambda { Player.new('Danny', 'Kopec', 3, :fed => 'US') }.should raise_error(/invalid federation/)
88
+ end
89
+ end
90
+
91
+ context "title" do
92
+ it "defaults to nil" do
93
+ Player.new('Mark', 'Orr', 3).title.should be_nil
94
+ Player.new('Mark', 'Orr', 3, :title => ' ').title.should be_nil
95
+ end
96
+
97
+ it "should be one of national, candidate, FIDE, international or grand master" do
98
+ Player.new('Gary', 'Kasparov', 1, :title => 'GM').title.should == 'GM'
99
+ Player.new('Mark', 'Orr', 2, :title => ' im ').title.should == 'IM'
100
+ Player.new('Mark', 'Quinn', 2, :title => 'm').title.should == 'IM'
101
+ Player.new('Pia', 'Cramling', 3, :title => ' wg ').title.should == 'WGM'
102
+ Player.new('Philip', 'Short', 4, :title => 'F ').title.should == 'FM'
103
+ Player.new('Gearoidin', 'Ui Laighleis', 5, :title => 'wc').title.should == 'WCM'
104
+ Player.new('Gearoidin', 'Ui Laighleis', 7, :title => 'wm').title.should == 'WIM'
105
+ Player.new('Eamon', 'Keogh', 6, :title => 'nm').title.should == 'NM'
106
+ lambda { Player.new('Mark', 'Orr', 3, :title => 'Dr') }.should raise_error(/invalid chess title/)
107
+ end
108
+ end
109
+
110
+ context "rating" do
111
+ it "defaults to nil" do
112
+ Player.new('Mark', 'Orr', 3).rating.should be_nil
113
+ Player.new('Mark', 'Orr', 3, :rating => ' ').rating.should be_nil
114
+ end
115
+
116
+ it "should be a positive integer" do
117
+ Player.new('Gary', 'Kasparov', 1, :rating => 2800).rating.should == 2800
118
+ Player.new('Mark', 'Orr', 2, :rating => ' 2100 ').rating.should == 2100
119
+ lambda { Player.new('Mark', 'Orr', 3, :rating => -2100) }.should raise_error(/invalid positive integer/)
120
+ lambda { Player.new('Mark', 'Orr', 3, :rating => 'IM') }.should raise_error(/invalid positive integer/)
121
+ end
122
+ end
123
+
124
+ context "rank" do
125
+ it "defaults to nil" do
126
+ Player.new('Mark', 'Orr', 3).rank.should be_nil
127
+ end
128
+
129
+ it "should be a positive integer" do
130
+ Player.new('Mark', 'Orr', 3, :rank => 1).rank.should == 1
131
+ Player.new('Gary', 'Kasparov', 4, :rank => ' 29 ').rank.should == 29
132
+ lambda { Player.new('Mark', 'Orr', 3, :rank => 0) }.should raise_error(/invalid positive integer/)
133
+ lambda { Player.new('Mark', 'Orr', 3, :rank => ' -1 ') }.should raise_error(/invalid positive integer/)
134
+ end
135
+ end
136
+
137
+ context "date of birth" do
138
+ it "defaults to nil" do
139
+ Player.new('Mark', 'Orr', 3).dob.should be_nil
140
+ Player.new('Mark', 'Orr', 3, :dob => ' ').dob.should be_nil
141
+ end
142
+
143
+ it "should be a yyyy-mm-dd date" do
144
+ Player.new('Mark', 'Orr', 3, :dob => '1955-11-09').dob.should == '1955-11-09'
145
+ lambda { Player.new('Mark', 'Orr', 3, :dob => 'X') }.should raise_error(/invalid.*dob/)
146
+ end
147
+ end
148
+
149
+ context "gender" do
150
+ it "defaults to nil" do
151
+ Player.new('Mark', 'Orr', 3).gender.should be_nil
152
+ Player.new('Mark', 'Orr', 3, :gender => ' ').gender.should be_nil
153
+ end
154
+
155
+ it "should be either M or F" do
156
+ Player.new('Mark', 'Orr', 3, :gender => 'male').gender.should == 'M'
157
+ Player.new('April', 'Cronin', 3, :gender => 'woman').gender.should == 'F'
158
+ end
159
+
160
+ it "should raise an exception if the gender is not specified properly" do
161
+ lambda { Player.new('Mark', 'Orr', 3, :gender => 'X') }.should raise_error(/invalid gender/)
162
+ end
163
+ end
164
+
165
+ context "results and points" do
166
+ it "should initialise to an empty array" do
167
+ results = Player.new('Mark', 'Orr', 3).results
168
+ results.should be_instance_of Array
169
+ results.size.should == 0
170
+ end
171
+
172
+ it "can be added to" do
173
+ player = Player.new('Mark', 'Orr', 3)
174
+ player.add_result(Result.new(1, 3, 'W', :opponent => 1))
175
+ player.add_result(Result.new(2, 3, 'D', :opponent => 2))
176
+ player.add_result(Result.new(3, 3, 'L', :opponent => 4))
177
+ results = player.results
178
+ results.should be_instance_of Array
179
+ results.size.should == 3
180
+ player.points.should == 1.5
181
+ end
182
+
183
+ it "should not allow mismatched player numbers" do
184
+ player = Player.new('Mark', 'Orr', 3)
185
+ lambda { player.add_result(Result.new(1, 4, 'W', :opponent => 1)) }.should raise_error(/player number .* matched/)
186
+ end
187
+
188
+ it "should enforce unique round numbers" do
189
+ player = Player.new('Mark', 'Orr', 3)
190
+ player.add_result(Result.new(1, 3, 'W', :opponent => 1))
191
+ player.add_result(Result.new(2, 3, 'D', :opponent => 2))
192
+ lambda { player.add_result(Result.new(2, 3, 'L', :opponent => 4)) }.should raise_error(/round number .* unique/)
193
+ end
194
+ end
195
+
196
+ context "looking up results" do
197
+ before(:all) do
198
+ @p = Player.new('Mark', 'Orr', 1)
199
+ @p.add_result(Result.new(1, 1, 'W', :opponent => 37, :score => 'W', :colour => 'W'))
200
+ @p.add_result(Result.new(2, 1, 'W', :opponent => 13, :score => 'W', :colour => 'B'))
201
+ @p.add_result(Result.new(3, 1, 'W', :opponent => 7, :score => 'D', :colour => 'W'))
202
+ end
203
+
204
+ it "should find results by round number" do
205
+ @p.find_result(1).opponent.should == 37
206
+ @p.find_result(2).opponent.should == 13
207
+ @p.find_result(3).opponent.should == 7
208
+ @p.find_result(4).should be_nil
209
+ end
210
+ end
211
+
212
+ context "merge" do
213
+ before(:each) do
214
+ @p1 = Player.new('Mark', 'Orr', 1, :id => 1350)
215
+ @p2 = Player.new('Mark', 'Orr', 2, :rating => 2100, :title => 'IM', :fed => 'IRL')
216
+ @p3 = Player.new('Gearoidin', 'Ui Laighleis', 3, :rating => 1600, :title => 'WIM', :fed => 'IRL')
217
+ end
218
+
219
+ it "takes on the ID, rating, title and fed of the other player but not the player number" do
220
+ @p1.merge(@p2)
221
+ @p1.num.should == 1
222
+ @p1.id.should == 1350
223
+ @p1.rating.should == 2100
224
+ @p1.title.should == 'IM'
225
+ @p1.fed.should == 'IRL'
226
+ end
227
+
228
+ it "should have a kind of symmetry" do
229
+ p1 = @p1.dup
230
+ p2 = @p2.dup
231
+ p1.merge(p2).eql?(@p2.merge(@p1))
232
+ end
233
+
234
+ it "cannot be done with unequal objects" do
235
+ lambda { @p1.merge(@p3) }.should raise_error(/cannot merge.*not equal/)
236
+ end
237
+ end
238
+
239
+ context "renumber the player numbers" do
240
+ before(:each) do
241
+ @p = Player.new('Mark', 'Orr', 10)
242
+ @p.add_result(Result.new(1, 10, 'W', :opponent => 20))
243
+ @p.add_result(Result.new(2, 10, 'W', :opponent => 30))
244
+ end
245
+
246
+ it "should renumber successfully if the map has the relevant player numbers" do
247
+ map = { 10 => 1, 20 => 2, 30 => 3 }
248
+ @p.renumber(map).num.should == 1
249
+ @p.results.map{ |r| r.opponent }.sort.join('').should == '23'
250
+ end
251
+
252
+ it "should raise exception if a player number is not in the map" do
253
+ lambda { @p.renumber({ 100 => 1, 20 => 2, 30 => 3 }) }.should raise_error(/player.*10.*not found/)
254
+ lambda { @p.renumber({ 10 => 1, 200 => 2, 30 => 3 }) }.should raise_error(/opponent.*20.*not found/)
255
+ end
256
+ end
257
+
258
+ context "loose equality" do
259
+ before(:all) do
260
+ @mark1 = Player.new('Mark', 'Orr', 1)
261
+ @mark2 = Player.new('Mark', 'Orr', 2, :fed => 'IRL')
262
+ @mark3 = Player.new('Mark', 'Orr', 3, :fed => 'USA')
263
+ @mark4 = Player.new('Mark', 'Sax', 4, :def => 'HUN')
264
+ @john1 = Player.new('John', 'Orr', 5, :fed => 'IRL')
265
+ end
266
+
267
+ it "any player is equal to itself" do
268
+ (@mark1 == @mark1).should be_true
269
+ end
270
+
271
+ it "two players are equal if their names are the same and their federations do not conflict" do
272
+ (@mark1 == @mark2).should be_true
273
+ end
274
+
275
+ it "two players cannot be equal if they have different names" do
276
+ (@mark1 == @mark4).should be_false
277
+ (@mark1 == @john1).should be_false
278
+ end
279
+
280
+ it "two players cannot be equal if they have different federations" do
281
+ (@mark2 == @mark3).should be_false
282
+ (@mark1 == @mark3).should be_true
283
+ end
284
+ end
285
+
286
+ context "strict equality" do
287
+ before(:all) do
288
+ @mark1 = Player.new('Mark', 'Orr', 1)
289
+ @mark2 = Player.new('Mark', 'Orr', 2, :id => 1350, :rating => 2100, :title => 'IM')
290
+ @mark3 = Player.new('Mark', 'Orr', 3, :id => 1530)
291
+ @mark4 = Player.new('Mark', 'Orr', 4, :rating => 2200)
292
+ @mark5 = Player.new('Mark', 'Orr', 5, :title => 'GM')
293
+ end
294
+
295
+ it "any player is equal to itself" do
296
+ @mark1.eql?(@mark1).should be_true
297
+ @mark1.eql?(@mark1).should be_true
298
+ end
299
+
300
+ it "two players are equal as long as their ID, rating and title do not conflict" do
301
+ @mark1.eql?(@mark2).should be_true
302
+ @mark3.eql?(@mark4).should be_true
303
+ @mark4.eql?(@mark5).should be_true
304
+ end
305
+
306
+ it "two players are not equal if their ID, rating or title conflict" do
307
+ @mark2.eql?(@mark3).should be_false
308
+ @mark2.eql?(@mark4).should be_false
309
+ @mark2.eql?(@mark5).should be_false
310
+ end
311
+ end
312
+ end
313
+ end
@@ -0,0 +1,203 @@
1
+ # encoding: utf-8
2
+
3
+ require File.dirname(__FILE__) + '/spec_helper'
4
+
5
+ module ICU
6
+ describe Result do
7
+ context "a typical result" do
8
+ it "should have a round, player number and score plus optional opponent number, colour and rateable flag" do
9
+ lambda { Result.new(3, 5, 'W', :opponent => 11, :colour => 'W') }.should_not raise_error
10
+ end
11
+ end
12
+
13
+ context "round number" do
14
+ it "should be a positive integer" do
15
+ lambda { Result.new(-2, 2, 0) }.should raise_error(/invalid positive integer/)
16
+ lambda { Result.new(0, 2, 0) }.should raise_error(/invalid positive integer/)
17
+ Result.new(1, 2, 0).round.should == 1
18
+ Result.new(' 3 ', 2, 0).round.should == 3
19
+ end
20
+ end
21
+
22
+ context "player number" do
23
+ it "should be an integer" do
24
+ lambda { Result.new(1, ' ', 0) }.should raise_error(/invalid integer/)
25
+ Result.new(1, 2, 0).player.should == 2
26
+ Result.new(1, ' 0 ', 0).player.should == 0
27
+ Result.new(1, -5, 0).player.should == -5
28
+ Result.new(1, " 9 ", 0).player.should == 9
29
+ end
30
+ end
31
+
32
+ context "score" do
33
+ [1, 1.0, 'W', 'w', '+'].each do |score|
34
+ it "should be 'W' for #{score}, #{score.class}" do
35
+ Result.new(1, 2, score).score.should == 'W'
36
+ end
37
+ end
38
+ [0, 0.0, 'L', 'l', '-'].each do |score|
39
+ it "should be 'L' for #{score}, #{score.class}" do
40
+ Result.new(1, 2, score).score.should == 'L'
41
+ end
42
+ end
43
+ ['½', 0.5, 'D', 'd', '='].each do |score|
44
+ it "should be 'L' for #{score}, #{score.class}" do
45
+ Result.new(1, 2, score).score.should == 'D'
46
+ end
47
+ end
48
+ ['', ' ', 'x', 2, -1.0, nil].each do |score|
49
+ it "should raise error for invalid score (#{score})" do
50
+ lambda { Result.new(1, 2, score) }.should raise_error(/invalid score/)
51
+ end
52
+ end
53
+ it "should be expressable as a number" do
54
+ Result.new(1, 2, 'W').points.should == 1.0
55
+ Result.new(1, 2, 'L').points.should == 0.0
56
+ Result.new(1, 2, 'D').points.should == 0.5
57
+ end
58
+ end
59
+
60
+ context "colour" do
61
+ it "should be 'W' or 'B' or nil (unknown)" do
62
+ Result.new(4, 1, 0).colour.should be_nil
63
+ Result.new(4, 1, 0, :colour => 'W').colour.should == 'W'
64
+ Result.new(4, 1, 0, :colour => 'white').colour.should == 'W'
65
+ Result.new(4, 1, 0, :colour => ' b ').colour.should == 'B'
66
+ Result.new(4, 1, 0, :colour => ' BLACK ').colour.should == 'B'
67
+ lambda { Result.new(4, 1, 0, :colour => 'red') }.should raise_error(/invalid colour/)
68
+ end
69
+ end
70
+
71
+ context "opponent number" do
72
+ it "should be nil (the default) or an integer" do
73
+ Result.new(4, 1, 0).opponent.should be_nil
74
+ Result.new(4, 1, 0, :opponent => nil).opponent.should be_nil
75
+ Result.new(4, 1, 0, :opponent => ' ').opponent.should be_nil
76
+ Result.new(4, 1, 0, :opponent => 0).opponent.should == 0
77
+ Result.new(4, 1, 0, :opponent => 2).opponent.should == 2
78
+ Result.new(4, 1, 0, :opponent => -6).opponent.should == -6
79
+ Result.new(4, 1, 0, :opponent => ' 10 ').opponent.should == 10
80
+ lambda { Result.new(4, 1, 0, :opponent => 'X') }.should raise_error(/invalid opponent number/)
81
+ end
82
+
83
+ it "should be different to player number" do
84
+ lambda { Result.new(4, 1, 0, :opponent => 1) }.should raise_error(/opponent .* player .* different/)
85
+ end
86
+ end
87
+
88
+ context "rateable flag" do
89
+ it "should default to false if there is no opponent" do
90
+ Result.new(4, 1, 0).rateable.should be_false
91
+ end
92
+
93
+ it "should default to true if there is an opponent" do
94
+ Result.new(4, 1, 0, :opponent => 10).rateable.should be_true
95
+ end
96
+
97
+ it "should change if an opponent is added" do
98
+ r = Result.new(4, 1, 0)
99
+ r.opponent = 5;
100
+ r.rateable.should be_true
101
+ end
102
+
103
+ it "should be settable to false from the constructor" do
104
+ Result.new(4, 1, 0, :opponent => 10, :rateable => false).rateable.should be_false
105
+ end
106
+
107
+ it "should be settable to false using the accessor" do
108
+ r = Result.new(4, 1, 0, :opponent => 10)
109
+ r.rateable= false
110
+ r.rateable.should be_false
111
+ end
112
+
113
+ it "should not be settable to true if there is no opponent" do
114
+ r = Result.new(4, 1, 0)
115
+ r.rateable= true
116
+ r.rateable.should be_false
117
+ end
118
+ end
119
+
120
+ context "reversed result" do
121
+ it "should be nil if there is no opponent" do
122
+ Result.new(4, 1, 0).reverse.should be_nil
123
+ end
124
+
125
+ it "should have player and opponent swapped" do
126
+ r = Result.new(4, 1, 0, :opponent => 2).reverse
127
+ r.player.should == 2
128
+ r.opponent.should == 1
129
+ end
130
+
131
+ it "should have reversed result" do
132
+ Result.new(4, 1, 0, :opponent => 2).reverse.score.should == 'W'
133
+ Result.new(4, 1, 1, :opponent => 2).reverse.score.should == 'L'
134
+ Result.new(4, 1, '=', :opponent => 2).reverse.score.should == 'D'
135
+ end
136
+
137
+ it "should preserve rateability" do
138
+ Result.new(4, 1, 0, :opponent => 2).reverse.rateable.should be_true
139
+ Result.new(4, 1, 0, :opponent => 2, :rateable => false).reverse.rateable.should be_false
140
+ end
141
+ end
142
+
143
+ context "renumber the player numbers in a result" do
144
+ before(:each) do
145
+ @r1 = Result.new(1, 4, 0)
146
+ @r2 = Result.new(2, 3, 1, :opponent => 4, :color => 'B')
147
+ end
148
+
149
+ it "should renumber successfully if the map has the relevant player numbers" do
150
+ map = { 4 => 1, 3 => 2 }
151
+ @r1.renumber(map).player.should == 1
152
+ @r2.renumber(map).player.should == 2
153
+ @r1.opponent.should be_nil
154
+ @r2.opponent.should == 1
155
+ end
156
+
157
+ it "should raise exception if a player number is not in the map" do
158
+ lambda { @r1.renumber({ 5 => 1, 3 => 2 }) }.should raise_error(/player.*4.*not found/)
159
+ end
160
+ end
161
+
162
+ context "loose equality" do
163
+ before(:each) do
164
+ @r1 = Result.new(1, 1, 0, :opponent => 2, :colour => 'W')
165
+ @r2 = Result.new(1, 1, 0, :opponent => 2, :colour => 'W')
166
+ @r3 = Result.new(2, 1, 0, :opponent => 2, :colour => 'W')
167
+ @r4 = Result.new(1, 3, 0, :opponent => 2, :colour => 'W')
168
+ @r5 = Result.new(1, 1, 1, :opponent => 2, :colour => 'W')
169
+ @r6 = Result.new(1, 1, 0, :opponent => 3, :colour => 'W')
170
+ @r7 = Result.new(1, 1, 0, :opponent => 2, :colour => 'B')
171
+ end
172
+
173
+ it "should be equal if the round, player numbers, result and colour all match" do
174
+ (@r1 == @r1).should be_true
175
+ (@r1 == @r2).should be_true
176
+ end
177
+
178
+ it "should not be equal if the round, player numbers, result or colour do not match" do
179
+ (@r1 == @r3).should be_false
180
+ (@r1 == @r4).should be_false
181
+ (@r1 == @r5).should be_false
182
+ (@r1 == @r6).should be_false
183
+ (@r1 == @r7).should be_false
184
+ end
185
+ end
186
+
187
+ context "strict equality" do
188
+ before(:each) do
189
+ @r1 = Result.new(1, 1, 0, :opponent => 2, :colour => 'W')
190
+ @r2 = Result.new(1, 1, 0, :opponent => 2, :colour => 'W')
191
+ @r3 = Result.new(1, 1, 0, :opponent => 2, :colour => 'W', :rateable => false)
192
+ @r4 = Result.new(2, 1, 0, :opponent => 2, :colour => 'W')
193
+ end
194
+
195
+ it "should only be equal if everything is the same" do
196
+ @r1.eql?(@r1).should be_true
197
+ @r1.eql?(@r2).should be_true
198
+ @r1.eql?(@r3).should be_false
199
+ @r1.eql?(@r4).should be_false
200
+ end
201
+ end
202
+ end
203
+ end