Completed
Push — master ( 0fc226...e4c266 )
by Michał
02:50
created

FuncTest::testCompose()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 0
1
<?php namespace nyx\utils\tests;
2
3
// Internal dependencies
4
use nyx\utils\Func;
5
6
/**
7
 * Func Tests
8
 *
9
 * @package     Nyx\Utils\Tests
10
 * @version     0.0.1
11
 * @author      Michal Chojnacki <[email protected]>
12
 * @copyright   2012-2016 Nyx Dev Team
13
 * @link        http://docs.muyo.io/nyx/utils/index.html
14
 */
15
class FuncTest extends \PHPUnit\Framework\TestCase
16
{
17
    // Used by the when/unless tests.
18
    private $whenExists;
19
20
    // Func::after()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
21
    public function testAfter()
22
    {
23
        $times = 5;
24
        $runs  = 11;
25
26
        // Let's create a simple helper.
27
        $test = function (callable $wrapper, callable $function, $value) use ($times, $runs) {
28
29
            // Invoke the function once to determine the expected value.
30
            $expected = $function($value);
31
32
            for ($i = 1; $i <= $runs; $i++) {
33
                if ($i < $times) {
34
                    $this->assertNull($wrapper($value));
35
                } else {
36
                    $this->assertEquals($expected, $wrapper($value));
37
                }
38
            }
39
        };
40
41
        // Prepare our functions.
42
        $func1 = function ($value) {return 'foo'.$value;};
43
        $func2 = function ($value) {return $value.'bar';};
44
45
        // Run a few loops.
46
        $wrapper = Func::after($times, $func1);
47
        $test($wrapper, $func1, 'bar');
48
49
        $wrapper = Func::after($times, $func2);
50
        $test($wrapper, $func2, 'foo');
51
    }
52
53
    /**
54
     * @see Func::compose()
55
     * ----------------------------------------------------------------------------------------------------------------
56
     */
57
58
    public function testCompose()
59
    {
60
        $countFiltered = Func::compose('count', 'array_filter');
61
        $data = [1, 'foo', 2, 'bar', 3, [1, 2, 3]];
62
63
        static::assertEquals(3, $countFiltered($data, 'is_int'));
64
        static::assertEquals(1, $countFiltered($data, 'is_array'));
65
        static::assertEquals(2, $countFiltered($data, 'is_string'));
66
    }
67
68
    /**
69
     * @see Func::hash()
70
     * ----------------------------------------------------------------------------------------------------------------
71
     */
72
73
    public function testHash()
74
    {
75
        // Prepare our functions.
76
        $func1 = function ($arg1, $arg2) {return true;};
0 ignored issues
show
Unused Code introduced by
The parameter $arg1 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $arg2 is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
77
78
        // Run some tests.
79
        $this->assertEquals('8bdb4ed5e8d60590121851789f7c5366', Func::hash($func1, ['arg1', 'arg2']));
80
        $this->assertEquals('d18a838748b014d758771d5dc497f78f', Func::hash($func1, ['arg1', ['arg2', 'arg3']]));
81
82
        // Same callable, different arguments.
83
        $this->assertEquals('0322135bc1a0c8e574fc5c3d380eb8d7', Func::hash([$this, 'compute'], ['arg1', ['arg2', 'arg3']]));
84
        $this->assertEquals('bd1d96a1996666394149e71b1f18352c', Func::hash([$this, 'compute'], ['arg1']));
85
86
        // Both should boil down to the same hash - same arguments, just a different callable notation.
87
        $this->assertEquals('2edbc7fd908ff5a29f88330640479c08', Func::hash(['\nyx\utils\tests\FuncTest', 'staticCompute'], ['arg1', ['arg2', 'arg3']]));
88
        $this->assertEquals('2edbc7fd908ff5a29f88330640479c08', Func::hash('\nyx\utils\tests\FuncTest::staticCompute', ['arg1', ['arg2', 'arg3']]));
89
90
        // Same callable notation, different arguments though.
91
        $this->assertEquals('2e287dc68fecd9998c0fcc8e15dbe3aa', Func::hash(['\nyx\utils\tests\FuncTest', 'staticCompute'], ['arg1']));
92
        $this->assertEquals('95d485a14f7431598b81c70e92999745', Func::hash('\nyx\utils\tests\FuncTest::staticCompute', ['arg2']));
93
    }
94
95
    // Func::memoize()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
96
    public function testMemoize()
97
    {
98
        // Prepare our functions.
99
        $func = function ($arg1, $arg2) {return $arg1.$arg2;};
100
101
        // Without resolver (automatic cache key).
102
        $memoized = Func::memoize($func);
103
104
        $this->assertEquals('foobar', $memoized('foo', 'bar'));
105
        $this->assertNotEquals('barfoo', $memoized('foo', 'bar'));
106
        $this->assertEquals('barfoo', $memoized('bar', 'foo'));
107
        $this->assertNotEquals('foobar', $memoized('bar', 'foo'));
108
109
        // With resolver (key depends on args).
110
        $resolver = function (callable $func, $args) {
111
            return 'key_'.$args[0].$args[1];
112
        };
113
        $memoized = Func::memoize($func, $resolver);
114
115
        $this->assertEquals('foobar', $memoized('foo', 'bar'));
116
        $this->assertNotEquals('barfoo', $memoized('foo', 'bar'));
117
        $this->assertEquals('barfoo', $memoized('bar', 'foo'));
118
        $this->assertNotEquals('foobar', $memoized('bar', 'foo'));
119
120
        // With resolver (fixed key) - all calls should return the first result, regardless of the arguments
121
        // passed, since the result will be fetched from the fixed key.
122
        $resolver = function (callable $func, $args) {return 'key';};
0 ignored issues
show
Unused Code introduced by
The parameter $func is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
123
        $memoized = Func::memoize($func, $resolver);
124
125
        $this->assertEquals('foobar', $memoized('foo', 'bar'));
126
        $this->assertEquals('foobar', $memoized('bar', 'foo'));
127
        $this->assertEquals('foobar', $memoized('test', 'omnomnom'));
128
129
        // Same as above but external resolver callable.
130
        $memoized = Func::memoize($func, [$this, 'compute']);
131
132
        $this->assertEquals('foobar', $memoized('foo', 'bar'));
133
        $this->assertEquals('foobar', $memoized('bar', 'foo'));
134
        $this->assertEquals('foobar', $memoized('test', 'omnomnom'));
135
    }
136
137
    // Func::once()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
138
    public function testOnce()
139
    {
140
        // Prepare our functions.
141
        $func1 = function ($value) {return 'foo'.$value;};
142
        $func2 = function ($value) {return $value.'bar';};
143
144
        // Run some tests.
145
        $wrapper = Func::once($func1);
146
        $this->assertEquals('foobar', $wrapper('bar'));
147
        $this->assertNull($wrapper('bar'));
148
        $this->assertNull($wrapper('baz'));
149
150
        $wrapper = Func::once($func2);
151
        $this->assertEquals('foobar', $wrapper('foo'));
152
        $this->assertNull($wrapper('bar'));
153
        $this->assertNull($wrapper('baz'));
154
    }
155
156
    // Func::once()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
157
    public function testOnly()
158
    {
159
        // Prepare our functions.
160
        $func1 = function ($value) {return 'foo'.$value;};
161
        $func2 = function ($value) {return $value.'bar';};
162
163
        // Run some tests.
164
        $wrapper = Func::only(3, $func1);
165
        $this->assertEquals('foobar', $wrapper('bar'));
166
        $this->assertEquals('foobaz', $wrapper('baz'));
167
        $this->assertEquals('fooxyz', $wrapper('xyz'));
168
        $this->assertNull($wrapper('bar'));
169
        $this->assertNull($wrapper('baz'));
170
171
        $wrapper = Func::only(2, $func2);
172
        $this->assertEquals('foobar', $wrapper('foo'));
173
        $this->assertEquals('bazbar', $wrapper('baz'));
174
        $this->assertNull($wrapper('bar'));
175
        $this->assertNull($wrapper('baz'));
176
    }
177
178
    // Func::partial()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
179 View Code Duplication
    public function testPartial()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
180
    {
181
        // Prepare our function.
182
        $func = function ($val1, $val2, $val3) {return $val1.$val2.$val3;};
183
184
        // Run some tests.
185
        $wrapper = Func::partial($func, 'foo', 'bar');
186
        $this->assertEquals('foobarbaz', $wrapper('baz'));
187
        $this->assertEquals('foobarzeta', $wrapper('zeta'));
188
189
        $wrapper = Func::partial($func, 'hello', ' world');
190
        $this->assertEquals('hello world of doom.', $wrapper(' of doom.'));
191
        $this->assertEquals('hello world of mine.', $wrapper(' of mine.'));
192
    }
193
194
    // Func::partial()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
195 View Code Duplication
    public function testPartialRight()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
196
    {
197
        // Prepare our function.
198
        $func = function ($val1, $val2, $val3) {return $val1.$val2.$val3;};
199
200
        // Run some tests.
201
        $wrapper = Func::partialRight($func, 'foo', 'bar');
202
        $this->assertEquals('bazfoobar', $wrapper('baz'));
203
        $this->assertEquals('zetafoobar', $wrapper('zeta'));
204
205
        $wrapper = Func::partialRight($func, 'hello', ' world');
206
        $this->assertEquals(' of doom.hello world', $wrapper(' of doom.'));
207
        $this->assertEquals(' of mine.hello world', $wrapper(' of mine.'));
208
    }
209
210
    // Func::throttle()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
211
    public function testThrottle()
212
    {
213
        $counter  = 0;
214
        $function = function () use (&$counter) { $counter++; };
215
        $wrapper  = Func::throttle($function, 100);
216
217
        // Time it to invoke the function 5 times at most, even though we call the wrapper 7 times.
218
        $wrapper();
219
        $wrapper();
220
        $wrapper();
221
        usleep(120 * 1000);
222
        $wrapper();
223
        usleep(140 * 1000);
224
        $wrapper();
225
        usleep(220 * 1000);
226
        $wrapper();
227
        usleep(240 * 1000);
228
        $wrapper();
229
        $this->assertEquals(5, $counter, 'function was throttled');
230
231
        usleep(500 * 1000);
232
233
        // Single call.
234
        $counter  = 0;
235
        $function = function () use (&$counter) { $counter++; };
236
        $wrapper  = Func::throttle($function, 100);
237
238
        $wrapper();
239
        usleep(220 * 1000);
240
        $this->assertEquals(1, $counter, 'function called once');
241
242
        usleep(500 * 1000);
243
244
        // Double call.
245
        $counter  = 0;
246
        $function = function () use (&$counter) { $counter++; };
247
        $wrapper  = Func::throttle($function, 100);
248
249
        $wrapper();
250
        $wrapper();
251
        usleep(220 * 1000);
252
        $this->assertEquals(1, $counter, 'function called twice');
253
    }
254
255
    /**
256
     * @see Func::retry()
257
     * ----------------------------------------------------------------------------------------------------------------
258
     */
259
260
    public function testRetryAlwaysSuccessful()
261
    {
262
        $expected = 'foo';
263
        $result   = Func::retry(function () use ($expected) {
264
            return $expected;
265
        });
266
267
        static::assertEquals('foo', $result);
268
    }
269
270
    public function testRetryAlwaysFailing()
271
    {
272
        $this->expectException('InvalidArgumentException');
273
274
        Func::retry(function () {
275
            throw new \InvalidArgumentException;
276
        });
277
    }
278
279
    public function testRetryWithSingleException()
280
    {
281
        $shouldThrow = true;
282
        $result = Func::retry(function () use (&$shouldThrow) {
283
            if ($shouldThrow) {
284
                $shouldThrow = false;
285
                throw new \Exception;
286
            }
287
288
            return 'foo';
289
        });
290
291
        static::assertEquals('foo', $result);
292
    }
293
294
    public function testRetryAttemptsCount()
295
    {
296
        $count = 0;
297
        try {
298
            Func::retry(function () use (&$count) {
299
                $count++;
300
                throw new \Exception;
301
            }, 5);
302
        } catch (\Exception $e) { }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
303
304
        // We expect to have counted to 6, since we requested 5 retries (+1 initial attempt).
305
        static::assertEquals(6, $count);
306
    }
307
308
    public function testRetryWithDelay()
309
    {
310
        $startTime = microtime(true);
311
312
        try {
313
            Func::retry(function () use (&$count) {
314
                throw new \Exception;
315
            }, 4, 0.25);
316
        } catch (\Exception $e) {
317
            // We expect at least 1 second to have passed (1 initial attempt + 4 retries after 0.25 seconds each).
318
            static::assertGreaterThan(1, (microtime(true) - $startTime));
319
        }
320
    }
321
322
    /**
323
     * @see Func::unless()
324
     * ----------------------------------------------------------------------------------------------------------------
325
     */
326
327 View Code Duplication
    public function testUnless()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
328
    {
329
        // -- Basic test. No params for the test nor the callable.
330
        $test = function () {
331
            return true === $this->getWhenExists();
332
        };
333
334
        $callable = function () {return 1;};
335
336
        $unless = Func::unless($test, $callable);
337
        $this->setWhenExists(true);
338
        $this->assertNull($unless());
339
        $this->setWhenExists(false);
340
        $this->assertEquals(1, $unless());
341
342
        // -- Params for the callable.
343
        $callable = function ($val) {return $val;};
344
        $unless = Func::unless($test, $callable);
345
        $this->setWhenExists(true);
346
        $this->assertNull($unless('foo'));
347
        $this->setWhenExists(false);
348
        $this->assertEquals('foo', $unless('foo'));
349
350
        // -- Same params for the callable and the test.
351
        $test = function ($bool) {
352
            return $bool === $this->getWhenExists();
353
        };
354
355
        $unless = Func::unless($test, $callable, Func::PASSTHROUGH);
356
        $this->setWhenExists(true);
357
        $this->assertNull($unless(true));
358
        $this->assertFalse($unless(false));
359
        $this->assertEquals('foo', $unless('foo'));
360
        $this->setWhenExists(false);
361
        $this->assertTrue($unless(true));
362
363
        // -- Different params for the callable and the test.
364
        $test = function ($bool) {
365
            return $bool === $this->getWhenExists();
366
        };
367
368
        // Pass 'true' to the test.
369
        $unless = Func::unless($test, $callable, true);
370
        $this->setWhenExists(true);
371
        $this->assertNull($unless('foo'));
372
        $this->assertNull($unless(false));
373
        $this->setWhenExists(false);
374
        $this->assertEquals('foo', $unless('foo'));
375
        $this->assertTrue($unless(true));
376
    }
377
378
    // Func::when()
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
379 View Code Duplication
    public function testWhen()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
380
    {
381
        // -- Basic test. No params for the test nor the callable.
382
        $test = function () {
383
            return true === $this->getWhenExists();
384
        };
385
386
        $callable = function () {return 1;};
387
        $when = Func::when($test, $callable);
388
        $this->setWhenExists(true);
389
        $this->assertEquals(1, $when());
390
        $this->setWhenExists(false);
391
        $this->assertNull($when());
392
393
        // -- Params for the callable.
394
        $callable = function ($val) {return $val;};
395
        $when = Func::when($test, $callable);
396
        $this->setWhenExists(true);
397
        $this->assertEquals('foo', $when('foo'));
398
        $this->setWhenExists(false);
399
        $this->assertNull($when('foo'));
400
401
        // -- Same params for the callable and the test.
402
        $test = function ($bool) {
403
            return $bool === $this->getWhenExists();
404
        };
405
406
        $when = Func::when($test, $callable, Func::PASSTHROUGH);
407
        $this->setWhenExists(true);
408
        $this->assertTrue($when(true));
409
        $this->assertNull($when(false));
410
        $this->assertNull($when('foo'));
411
        $this->setWhenExists(false);
412
        $this->assertFalse($when(false));
413
414
        // -- Different params for the callable and the test.
415
        $test = function ($bool) {
416
            return $bool === $this->getWhenExists();
417
        };
418
419
        // Pass 'true' to the test.
420
        $when = Func::when($test, $callable, true);
421
        $this->setWhenExists(true);
422
        $this->assertEquals('foo', $when('foo'));
423
        $this->assertFalse($when(false));
424
        $this->setWhenExists(false);
425
        $this->assertNull($when('foo'));
426
        $this->assertNull($when(false));
427
    }
428
429
    // Used by the when/unless tests.
430
    protected function getWhenExists()
431
    {
432
        return $this->whenExists;
433
    }
434
435
    // Used by the when/unless tests.
436
    protected function setWhenExists($exists)
437
    {
438
        $this->whenExists = (bool) $exists;
439
    }
440
441
    // Used by the memoize and hash tests.
442
    public function compute()
443
    {
444
        return 'foobar';
445
    }
446
447
    // Used by the hash tests.
448
    public static function staticCompute()
449
    {
450
        return 'foobar';
451
    }
452
}
453