1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Gendoria\CruftFlake; |
4
|
|
|
|
5
|
|
|
use Gendoria\CruftFlake\Generator\Generator; |
6
|
|
|
use OverflowException; |
7
|
|
|
use PHPUnit_Framework_TestCase; |
8
|
|
|
use UnexpectedValueException; |
9
|
|
|
|
10
|
|
|
class GeneratorTest extends PHPUnit_Framework_TestCase |
11
|
|
|
{ |
12
|
|
|
private $machineId = 1; |
13
|
|
|
|
14
|
|
|
private $timer; |
15
|
|
|
private $config; |
16
|
|
|
|
17
|
|
|
public function setUp() |
18
|
|
|
{ |
19
|
|
|
$this->timer = $this->getMockBuilder('\Gendoria\CruftFlake\Timer\TimerInterface') |
20
|
|
|
->disableOriginalConstructor() |
21
|
|
|
->getMock(); |
22
|
|
|
$this->config = $this->getMockBuilder('\Gendoria\CruftFlake\Config\ConfigInterface') |
23
|
|
|
->disableOriginalConstructor() |
24
|
|
|
->getMock(); |
25
|
|
|
} |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Get generator for normal tests. |
29
|
|
|
* |
30
|
|
|
* @return Generator |
31
|
|
|
*/ |
32
|
|
|
private function buildSystemUnderTest() |
33
|
|
|
{ |
34
|
|
|
$this->config->expects($this->once()) |
35
|
|
|
->method('getMachine') |
36
|
|
|
->will($this->returnValue($this->machineId)); |
37
|
|
|
return new Generator($this->config, $this->timer); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Get generator for 32 bit tests. |
42
|
|
|
* |
43
|
|
|
* @return Generator |
44
|
|
|
*/ |
45
|
|
|
private function buildSystemUnderTest32Bit() |
46
|
|
|
{ |
47
|
|
|
$this->config->expects($this->once()) |
48
|
|
|
->method('getMachine') |
49
|
|
|
->will($this->returnValue($this->machineId)); |
50
|
|
|
$generator = $this->getMock('Gendoria\CruftFlake\Generator\Generator', array('is32Bit'), array($this->config, $this->timer)); |
51
|
|
|
$generator->expects($this->any()) |
52
|
|
|
->method('is32Bit') |
53
|
|
|
->will($this->returnValue(true)); |
54
|
|
|
return $generator; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Get generator for normal tests. |
59
|
|
|
* |
60
|
|
|
* @return Generator |
61
|
|
|
*/ |
62
|
|
|
private function buildSystemUnderTestHeartbeat($newMachineId) |
63
|
|
|
{ |
64
|
|
|
$this->config->expects($this->exactly(2)) |
65
|
|
|
->method('getMachine') |
66
|
|
|
->will($this->onConsecutiveCalls($this->machineId, $newMachineId)); |
67
|
|
|
$this->config->expects($this->once()) |
68
|
|
|
->method('heartbeat') |
69
|
|
|
->will($this->returnValue(true)); |
70
|
|
|
return new Generator($this->config, $this->timer); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
private function assertId($id) |
74
|
|
|
{ |
75
|
|
|
$this->assertTrue(is_string($id)); |
76
|
|
|
$this->assertTrue(ctype_digit($id)); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
private function assertReallyNotEquals($v1, $v2) |
80
|
|
|
{ |
81
|
|
|
$this->assertTrue($v1 !== $v2); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
// --- |
85
|
|
|
|
86
|
|
|
public function testConstructs() |
87
|
|
|
{ |
88
|
|
|
$cf = $this->buildSystemUnderTest(); |
89
|
|
|
$this->assertInstanceOf('\Gendoria\CruftFlake\Generator\Generator', $cf); |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
public function testFailsWithBadMachineIdString() |
93
|
|
|
{ |
94
|
|
|
$this->setExpectedException('\InvalidArgumentException'); |
|
|
|
|
95
|
|
|
$this->machineId = '1'; |
|
|
|
|
96
|
|
|
$cf = $this->buildSystemUnderTest(); |
|
|
|
|
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
public function testFailsWithBadMachineIdNegative() |
100
|
|
|
{ |
101
|
|
|
$this->setExpectedException('\InvalidArgumentException'); |
|
|
|
|
102
|
|
|
$this->machineId = -1; |
103
|
|
|
$cf = $this->buildSystemUnderTest(); |
|
|
|
|
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
public function testFailsWithBadMachineIdTooBig() |
107
|
|
|
{ |
108
|
|
|
$this->setExpectedException('\InvalidArgumentException'); |
|
|
|
|
109
|
|
|
$this->machineId = 1024; |
110
|
|
|
$cf = $this->buildSystemUnderTest(); |
|
|
|
|
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
public function testFailsWithBadMachineIdFloat() |
114
|
|
|
{ |
115
|
|
|
$this->setExpectedException('\InvalidArgumentException'); |
|
|
|
|
116
|
|
|
$this->machineId = 1.1; |
|
|
|
|
117
|
|
|
$cf = $this->buildSystemUnderTest(); |
|
|
|
|
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
public function testLargestPossibleMachineId() |
121
|
|
|
{ |
122
|
|
|
$this->machineId = 1023; |
123
|
|
|
$cf = $this->buildSystemUnderTest(); |
124
|
|
|
$this->assertInstanceOf('\Gendoria\CruftFlake\Generator\Generator', $cf); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
public function testGenerate() |
128
|
|
|
{ |
129
|
|
|
$this->timer->expects($this->once()) |
130
|
|
|
->method('getUnixTimestamp') |
131
|
|
|
->will($this->returnValue(1341246960000)); |
132
|
|
|
$cf = $this->buildSystemUnderTest(); |
133
|
|
|
$id = $cf->generate(); |
134
|
|
|
$this->assertId($id); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
public function testGenerate32bit() |
138
|
|
|
{ |
139
|
|
|
$this->timer->expects($this->once()) |
140
|
|
|
->method('getUnixTimestamp') |
141
|
|
|
->will($this->returnValue(1341246960000)); |
142
|
|
|
$cf = $this->buildSystemUnderTest32Bit(); |
143
|
|
|
$id = $cf->generate(); |
144
|
|
|
$this->assertId($id); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
|
148
|
|
|
public function testGenerateForPerMillisecondCollisions() |
149
|
|
|
{ |
150
|
|
|
$this->timer->expects($this->any()) |
151
|
|
|
->method('getUnixTimestamp') |
152
|
|
|
->will($this->returnValue(1341246960000)); |
153
|
|
|
$cf = $this->buildSystemUnderTest(); |
154
|
|
|
|
155
|
|
|
$id1 = $cf->generate(); |
156
|
|
|
$id2 = $cf->generate(); |
157
|
|
|
$id3 = $cf->generate(); |
158
|
|
|
|
159
|
|
|
$this->assertId($id1); |
160
|
|
|
$this->assertId($id2); |
161
|
|
|
$this->assertId($id3); |
162
|
|
|
|
163
|
|
|
$this->assertReallyNotEquals($id1, $id2); |
164
|
|
|
$this->assertReallyNotEquals($id2, $id3); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
public function testGenerateAsksForTimeEachTime() |
168
|
|
|
{ |
169
|
|
|
$this->timer->expects($this->at(0)) |
170
|
|
|
->method('getUnixTimestamp') |
171
|
|
|
->will($this->returnValue(1341246960000)); |
172
|
|
|
$this->timer->expects($this->at(1)) |
173
|
|
|
->method('getUnixTimestamp') |
174
|
|
|
->will($this->returnValue(1341246960001)); |
175
|
|
|
$cf = $this->buildSystemUnderTest(); |
176
|
|
|
|
177
|
|
|
$id1 = $cf->generate(); |
178
|
|
|
$id2 = $cf->generate(); |
179
|
|
|
|
180
|
|
|
$this->assertId($id1); |
181
|
|
|
$this->assertId($id2); |
182
|
|
|
|
183
|
|
|
$this->assertReallyNotEquals($id1, $id2); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
public function testFailsWithTimestampBeforeEpoch() |
187
|
|
|
{ |
188
|
|
|
$this->timer->expects($this->at(0)) |
189
|
|
|
->method('getUnixTimestamp') |
190
|
|
|
->will($this->returnValue(1325375999999)); |
191
|
|
|
$cf = $this->buildSystemUnderTest(); |
192
|
|
|
|
193
|
|
|
$e = NULL; |
194
|
|
|
try { |
195
|
|
|
$id1 = $cf->generate(); |
|
|
|
|
196
|
|
|
} catch (UnexpectedValueException $e) { |
|
|
|
|
197
|
|
|
|
198
|
|
|
} |
199
|
|
|
$this->assertInstanceOf('\UnexpectedValueException', $e); |
200
|
|
|
$this->assertEquals('Time is currently set before our epoch - unable to generate IDs for 1 milliseconds', $e->getMessage()); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
public function testFailsIfTimeRunsBackwards() |
204
|
|
|
{ |
205
|
|
|
//$this->setExpectedException('\UnexpectedValueException'); |
|
|
|
|
206
|
|
|
|
207
|
|
|
$this->timer->expects($this->at(0)) |
208
|
|
|
->method('getUnixTimestamp') |
209
|
|
|
->will($this->returnValue(1341246960001)); |
210
|
|
|
$this->timer->expects($this->at(1)) |
211
|
|
|
->method('getUnixTimestamp') |
212
|
|
|
->will($this->returnValue(1341246960000)); |
213
|
|
|
$cf = $this->buildSystemUnderTest(); |
214
|
|
|
|
215
|
|
|
$id1 = $cf->generate(); |
|
|
|
|
216
|
|
|
|
217
|
|
|
$e = NULL; |
218
|
|
|
try { |
219
|
|
|
$id2 = $cf->generate(); |
|
|
|
|
220
|
|
|
} catch (UnexpectedValueException $e) { |
|
|
|
|
221
|
|
|
|
222
|
|
|
} |
223
|
|
|
$this->assertInstanceOf('\UnexpectedValueException', $e); |
224
|
|
|
$this->assertEquals('Time moved backwards. We cannot generate IDs for 1 milliseconds', $e->getMessage()); |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
public function testFullSequenceRange() |
228
|
|
|
{ |
229
|
|
|
$this->timer->expects($this->any()) |
230
|
|
|
->method('getUnixTimestamp') |
231
|
|
|
->will($this->returnValue(1341246960000)); |
232
|
|
|
$cf = $this->buildSystemUnderTest(); |
233
|
|
|
|
234
|
|
|
$ids = array(); |
235
|
|
|
for ($i=0; $i<4095; $i++) { |
236
|
|
|
$id = $cf->generate(); |
237
|
|
|
$ids[$id] = 1; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
$this->assertEquals(4095, count($ids)); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
public function testFailsIfSequenceOverflow() |
244
|
|
|
{ |
245
|
|
|
$this->timer->expects($this->any()) |
246
|
|
|
->method('getUnixTimestamp') |
247
|
|
|
->will($this->returnValue(1341246960000)); |
248
|
|
|
$cf = $this->buildSystemUnderTest(); |
249
|
|
|
|
250
|
|
|
$ids = array(); |
251
|
|
|
for ($i=0; $i<4096; $i++) { |
252
|
|
|
$id = $cf->generate(); |
253
|
|
|
$ids[$id] = 1; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
$e = NULL; |
257
|
|
|
try { |
258
|
|
|
$id2 = $cf->generate(); |
|
|
|
|
259
|
|
|
} catch (OverflowException $e) { |
|
|
|
|
260
|
|
|
|
261
|
|
|
} |
262
|
|
|
$this->assertInstanceOf('\OverflowException', $e); |
263
|
|
|
$this->assertEquals('Sequence overflow (too many IDs generated) - unable to generate IDs for 1 milliseconds', $e->getMessage()); |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
public function testSmallestTimestampId() |
267
|
|
|
{ |
268
|
|
|
$this->machineId = 0; |
269
|
|
|
$this->timer->expects($this->any()) |
270
|
|
|
->method('getUnixTimestamp') |
271
|
|
|
->will($this->returnValue(1325376000000)); |
272
|
|
|
$cf = $this->buildSystemUnderTest(); |
273
|
|
|
|
274
|
|
|
$id1 = $cf->generate(); |
275
|
|
|
$id2 = $cf->generate(); |
276
|
|
|
|
277
|
|
|
$this->assertId($id1); |
278
|
|
|
$this->assertId($id2); |
279
|
|
|
$this->assertReallyNotEquals($id1, $id2); |
280
|
|
|
|
281
|
|
|
$this->assertEquals(0, $id1); |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
public function testSmallestTimestampWithMachine() |
285
|
|
|
{ |
286
|
|
|
$this->timer->expects($this->any()) |
287
|
|
|
->method('getUnixTimestamp') |
288
|
|
|
->will($this->returnValue(1325376000000)); |
289
|
|
|
$cf = $this->buildSystemUnderTest(); |
290
|
|
|
|
291
|
|
|
$id1 = $cf->generate(); |
292
|
|
|
$id2 = $cf->generate(); |
293
|
|
|
|
294
|
|
|
$this->assertId($id1); |
295
|
|
|
$this->assertId($id2); |
296
|
|
|
$this->assertReallyNotEquals($id1, $id2); |
297
|
|
|
|
298
|
|
|
$this->assertEquals(1 << 12, $id1); |
299
|
|
|
$this->assertEquals(1 << 12 | 1, $id2); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
public function testSmallTimestampWithMachine() |
303
|
|
|
{ |
304
|
|
|
$this->timer->expects($this->any()) |
305
|
|
|
->method('getUnixTimestamp') |
306
|
|
|
->will($this->returnValue(1325376000001)); |
307
|
|
|
$cf = $this->buildSystemUnderTest(); |
308
|
|
|
|
309
|
|
|
$id1 = $cf->generate(); |
310
|
|
|
$id2 = $cf->generate(); |
311
|
|
|
|
312
|
|
|
$this->assertId($id1); |
313
|
|
|
$this->assertId($id2); |
314
|
|
|
$this->assertReallyNotEquals($id1, $id2); |
315
|
|
|
|
316
|
|
|
$this->assertEquals(1 << 22 | 1 << 12, $id1); |
317
|
|
|
$this->assertEquals(1 << 22 | 1 << 12 | 1, $id2); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
public function testFailsOnTimestampOverflow() |
321
|
|
|
{ |
322
|
|
|
$this->timer->expects($this->any()) |
323
|
|
|
->method('getUnixTimestamp') |
324
|
|
|
->will($this->returnValue(3524399255552)); |
325
|
|
|
$cf = $this->buildSystemUnderTest(); |
326
|
|
|
$e = NULL; |
327
|
|
|
try { |
328
|
|
|
$id2 = $cf->generate(); |
|
|
|
|
329
|
|
|
} catch (OverflowException $e) { |
|
|
|
|
330
|
|
|
|
331
|
|
|
} |
332
|
|
|
$this->assertInstanceOf('\OverflowException', $e); |
333
|
|
|
$this->assertEquals('Timestamp overflow (past end of lifespan) - unable to generate any more IDs', $e->getMessage()); |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
public function testLargestTimestampWithLargestEverythingElse() |
337
|
|
|
{ |
338
|
|
|
$this->machineId = 1023; |
339
|
|
|
$this->timer->expects($this->any()) |
340
|
|
|
->method('getUnixTimestamp') |
341
|
|
|
->will($this->returnValue(3524399255551)); |
342
|
|
|
$cf = $this->buildSystemUnderTest(); |
343
|
|
|
|
344
|
|
|
$id1 = $cf->generate(); |
345
|
|
|
$id2 = $cf->generate(); |
346
|
|
|
|
347
|
|
|
$this->assertId($id1); |
348
|
|
|
$this->assertId($id2); |
349
|
|
|
$this->assertReallyNotEquals($id1, $id2); |
350
|
|
|
$this->assertEquals('9223372036854771712', $id1); |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
public function testHeartbeat() |
354
|
|
|
{ |
355
|
|
|
$this->machineId = 1; |
356
|
|
|
$this->timer->expects($this->any()) |
357
|
|
|
->method('getUnixTimestamp') |
358
|
|
|
->will($this->returnValue(1325376000000)); |
359
|
|
|
$cf = $this->buildSystemUnderTestHeartbeat(2); |
360
|
|
|
|
361
|
|
|
//Test. First ID, heartbeat changing machine ID to 2, second ID |
362
|
|
|
$id1 = $cf->generate(); |
363
|
|
|
$cf->heartbeat(); |
364
|
|
|
$id2 = $cf->generate(); |
365
|
|
|
|
366
|
|
|
$expectedId1 = $value = (0 << 22) | (1 << 12) | 0; //timestamp 0, machine 1, sequence 0 |
|
|
|
|
367
|
|
|
$expectedId2 = $value = (0 << 22) | (2 << 12) | 1; //timestamp 0, machine 2, sequence 1 |
|
|
|
|
368
|
|
|
$this->assertEquals($expectedId1, $id1); |
369
|
|
|
$this->assertEquals($expectedId2, $id2); |
370
|
|
|
} |
371
|
|
|
} |
372
|
|
|
|
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.