Completed
Push — master ( 21fef4...f7e2e6 )
by Tomasz
05:34
created

GeneratorTest::testGenerate32bit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 9
rs 9.6666
cc 1
eloc 7
nc 1
nop 0
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');
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0

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.

Loading history...
95
        $this->machineId = '1';
0 ignored issues
show
Documentation Bug introduced by
The property $machineId was declared of type integer, but '1' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
96
        $cf = $this->buildSystemUnderTest();
0 ignored issues
show
Unused Code introduced by
$cf is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
97
    }
98
    
99
    public function testFailsWithBadMachineIdNegative()
100
    {
101
        $this->setExpectedException('\InvalidArgumentException');
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0

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.

Loading history...
102
        $this->machineId = -1;
103
        $cf = $this->buildSystemUnderTest();
0 ignored issues
show
Unused Code introduced by
$cf is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
104
    }
105
106
    public function testFailsWithBadMachineIdTooBig()
107
    {
108
        $this->setExpectedException('\InvalidArgumentException');
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0

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.

Loading history...
109
        $this->machineId = 1024;
110
        $cf = $this->buildSystemUnderTest();
0 ignored issues
show
Unused Code introduced by
$cf is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
111
    }
112
113
    public function testFailsWithBadMachineIdFloat()
114
    {
115
        $this->setExpectedException('\InvalidArgumentException');
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0

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.

Loading history...
116
        $this->machineId = 1.1;
0 ignored issues
show
Documentation Bug introduced by
The property $machineId was declared of type integer, but 1.1 is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
117
        $cf = $this->buildSystemUnderTest();
0 ignored issues
show
Unused Code introduced by
$cf is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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();
0 ignored issues
show
Unused Code introduced by
$id1 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
196
        } catch (UnexpectedValueException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
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');
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
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();
0 ignored issues
show
Unused Code introduced by
$id1 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
216
        
217
        $e = NULL;
218
        try {
219
            $id2 = $cf->generate();
0 ignored issues
show
Unused Code introduced by
$id2 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
220
        } catch (UnexpectedValueException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
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();
0 ignored issues
show
Unused Code introduced by
$id2 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
259
        } catch (OverflowException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
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();
0 ignored issues
show
Unused Code introduced by
$id2 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
329
        } catch (OverflowException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
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
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
367
        $expectedId2 = $value = (0 << 22) | (2 << 12) | 1; //timestamp 0, machine 2, sequence 1
0 ignored issues
show
Unused Code introduced by
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Unused Code Comprehensibility introduced by
39% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
368
        $this->assertEquals($expectedId1, $id1);
369
        $this->assertEquals($expectedId2, $id2);
370
    }
371
}
372