/test/Lazy/Test/SequenceTest.php
https://bitbucket.org/mkjpryor/lazy-sequence · PHP · 369 lines · 206 code · 78 blank · 85 comment · 5 complexity · 88dd79ec263ebfbb570a38acef6fd0ef MD5 · raw file
- <?php
- namespace Lazy\Test;
- use Lazy\Sequence as S;
- /**
- * Stub callback
- */
- class CallableStub {
- public function __invoke() {}
- }
- class SequenceTest extends \PHPUnit_Framework_TestCase {
-
- /**
- * Returns a callable that expects to be called $amount times
- * If given, $params is an array of parameter values expected for each
- * invocation
- *
- * @param integer $amount
- * @param array $args
- */
- protected function expectCallable($amount, array $args = null) {
- $mock = $this->getMock('\Lazy\Test\CallableStub');
-
- // Set the expectation for the number of calls
- $mock->expects($this->exactly($amount))->method('__invoke');
-
- // If $params is not given, that is all we need to do
- if( is_null($args) ) return $mock;
-
- // Otherwise $params is an array
- // Check that $params has the correct size (i.e. one set of parameters per
- // expected call)
- if( count($args) != $amount )
- throw new \BadMethodCallException(
- 'A set of parameters for each expected call must be given'
- );
-
- // Set the expectations for each call to the callable
- $count = 0;
- foreach($args as $with) {
- // Make sure that $with is an array, even if it has a single element
- $with = is_array($with) ? $with : array($with);
- // Capture the expectation so we can use call_user_func_array with it
- $expectation = $mock->expects($this->at($count))->method('__invoke');
- // Call with with $with as positional arguments
- call_user_func_array(array($expectation, 'with'), $with);
-
- $count++;
- }
-
- return $mock;
- }
-
-
- public function testHead() {
- // Test that head returns correctly for a non-empty sequence
- $this->assertEquals(5, S::head(S::create(5, 6, 7, 8)));
-
- // Test that head returns correctly for an empty sequence
- $this->assertNull(S::head(S::create()));
- }
-
- public function testTail() {
- // Test that tail returns correctly for a sequence with >= 2 elements
- $this->assertEquals([6, 7, 8], S::toArray(S::tail(S::create(5, 6, 7, 8))));
-
- // Test that tail returns correctly for a sequence with 1 item
- $this->assertEmpty(S::toArray(S::tail(S::create(5))));
-
- // Test that tail returns correctly for an empty sequence
- $this->assertEmpty(S::toArray(S::tail(S::create())));
- }
-
- public function testLast() {
- // Test that last returns correctly for a sequence with >= 2 items
- $this->assertEquals(8, S::last(S::create(5, 6, 7, 8)));
-
- // Test that last returns correctly for a sequence with 1 item
- $this->assertEquals(5, S::last(S::create(5)));
-
- // Test that last returns correctly for an empty sequence
- $this->assertNull(S::last(S::create()));
- }
-
- public function testIsEmpty() {
- // Test that isEmpty returns false for a non-empty sequence
- $this->assertFalse(S::isEmpty(S::create(5, 6, 7)));
-
- // Test that isEmpty returns true for an empty sequence
- $this->assertTrue(S::isEmpty(S::create()));
- }
-
- public function testCons() {
- // Just test cons prepends the element to the given sequence
- $this->assertEquals([4, 5, 6, 7], S::toArray(S::cons(S::create(5, 6, 7), 4)));
- }
-
- public function testAppend() {
- // Test that append joins 2 non-empty sequences correctly
- $this->assertEquals(
- [5, 6, 7, 8],
- S::toArray(S::append(S::create(5, 6), S::create(7, 8)))
- );
-
- // Test that append joins an empty and a non-empty sequence correctly
- $this->assertEquals(
- [5, 6],
- S::toArray(S::append(S::create(5, 6), S::create()))
- );
- $this->assertEquals(
- [7, 8],
- S::toArray(S::append(S::create(), S::create(7, 8)))
- );
-
- // Test that append joins two empty sequences successfully
- $this->assertEmpty(S::toArray(S::append(S::create(), S::create())));
- }
-
- public function testMap() {
- // Test that map works as expected on a non-empty sequence
- $this->assertEquals(
- [2, 4, 6],
- S::toArray(S::map(S::create(1, 2, 3), function($x) { return $x * 2; }))
- );
-
- // Test that map works as expected on an empty sequence
- $this->assertEmpty(S::toArray(S::map(S::create(), function($x) { return $x * 2; })));
- }
-
- public function testFilter() {
- // Test that filter works as expected on a non-empty sequence
- $this->assertEquals(
- [2, 4],
- S::toArray(S::filter(S::create(1, 2, 3, 4), function($x) { return $x % 2 == 0; }))
- );
-
- // Test that filter works as expected on an empty sequence
- $this->assertEmpty(
- S::toArray(S::filter(S::create(), function($x) { return $x % 2 == 0; }))
- );
- }
-
- public function testTake() {
- // Test that take works with an empty sequence
- $this->assertEmpty(S::toArray(S::take(S::create(), 3)));
-
- // Test that take works with a sequence with <= 3 elements
- $this->assertEquals([1, 2, 3], S::toArray(S::take(S::create(1, 2, 3), 3)));
-
- // Test that take works with a sequence with > 3 elements
- $this->assertEquals([1, 2, 3], S::toArray(S::take(S::create(1, 2, 3, 4, 5), 3)));
-
- // Test that take can be used with an infinite sequence and returns
- $this->assertEquals([1, 2, 3], S::toArray(S::take(S::range(1), 3)));
- }
-
- public function testTakeWhile() {
- $lessThan3 = function($x) { return $x < 3; };
- // Test that takeWhile works with an empty sequence
- $this->assertEmpty(S::toArray(S::takeWhile(S::create(), $lessThan3)));
-
- // Test that takeWhile works with no true elements
- $this->assertEmpty(
- S::toArray(S::takeWhile(S::create(3, 4, 5, 6), $lessThan3))
- );
-
- // Test that takeWhile works with no false elements
- $this->assertEquals(
- [1, 2, 0, 1, 2],
- S::toArray(S::takeWhile(S::create(1, 2, 0, 1, 2), $lessThan3))
- );
-
- // Test that takeWhile works when predicate becomes true again after
- // becoming false
- $this->assertEquals(
- [1, 2],
- S::toArray(S::takeWhile(S::create(1, 2, 3, 1, 2), $lessThan3))
- );
-
- // Test that takeWhile can be used with an infinite sequence and returns
- $this->assertEquals([1, 2], S::toArray(S::takeWhile(S::range(1), $lessThan3)));
- }
-
- public function testSkip() {
- // Test that skip works with an empty sequence
- $this->assertEmpty(S::toArray(S::skip(S::create(), 3)));
-
- // Test that skip works with a sequence with <= 3 elements
- $this->assertEmpty(S::toArray(S::skip(S::create(1, 2, 3), 3)));
-
- // Test that skip works with a sequence with > 3 elements
- $this->assertEquals([4, 5], S::toArray(S::skip(S::create(1, 2, 3, 4, 5), 3)));
- }
-
- public function testSkipWhile() {
- $lessThan3 = function($x) { return $x < 3; };
- // Test that skipWhile works with an empty sequence
- $this->assertEmpty(S::toArray(S::skipWhile(S::create(), $lessThan3)));
-
- // Test that skipWhile works with no false elements
- $this->assertEmpty(
- S::toArray(S::skipWhile(S::create(1, 2, 0, 1, 2), $lessThan3))
- );
-
- // Test that skipWhile works with no true elements
- $this->assertEquals(
- [3, 4, 5, 6],
- S::toArray(S::skipWhile(S::create(3, 4, 5, 6), $lessThan3))
- );
-
- // Test that skipWhile works when predicate becomes true again after
- // becoming false
- $this->assertEquals(
- [3, 1, 2],
- S::toArray(S::skipWhile(S::create(1, 2, 3, 1, 2), $lessThan3))
- );
- }
-
- public function testReduce() {
- $sum = function($x, $y) { return $x + $y; };
-
- // Test that reduce works with an empty sequence
- $this->assertEquals(0, S::reduce(S::create(), $sum, 0));
-
- // Test that reduce works with a non-empty sequence
- $this->assertEquals(6, S::reduce(S::create(1, 2, 3), $sum, 0));
- }
-
- public function testReverse() {
- // Test that the reverse of an empty sequence is the empty sequence
- $this->assertEmpty(S::toArray(S::reverse(S::create())));
-
- // Test that a non-empty sequence reverses correctly
- $this->assertEquals([7, 6, 5], S::toArray(S::reverse(S::create(5, 6, 7))));
- }
-
- public function testZip() {
- $zipper = function($x, $y) { return array($x, $y); };
-
- // Test that zipping two empty sequences works
- $this->assertEmpty(
- S::toArray(S::zip(S::create(), S::create(), $zipper))
- );
-
- // Test that zipping any other sequence with the empty sequence works
- $this->assertEmpty(
- S::toArray(S::zip(S::create(1, 2, 3), S::create(), $zipper))
- );
- $this->assertEmpty(
- S::toArray(S::zip(S::create(), S::create(4, 5, 6), $zipper))
- );
-
- // Test that zipping two equal size sequences works
- $this->assertEquals(
- [[1, 4], [2, 5], [3, 6]],
- S::toArray(S::zip(S::create(1, 2, 3), S::create(4, 5, 6), $zipper))
- );
-
- // Test that zipping sequences with different sizes works
- $this->assertEquals(
- [[1, 4], [2, 5]],
- S::toArray(S::zip(S::create(1, 2), S::create(4, 5, 6), $zipper))
- );
- $this->assertEquals(
- [[1, 4], [2, 5]],
- S::toArray(S::zip(S::create(1, 2, 3), S::create(4, 5), $zipper))
- );
-
- // Test that zipping with plain arrays works
- $this->assertEquals(
- [[1, 4], [2, 5]],
- S::toArray(S::zip([1, 2], [4, 5, 6], $zipper))
- );
- }
-
- public function testEach() {
- // Test that each works correctly with an empty sequence
- // I.e. the given callback is never called
- S::each(S::create(), $this->expectCallable(0));
-
- // Test that each works correctly with a non-empty sequence
- // I.e. the given callback is called with successive elements
- S::each(S::create(1, 2, 3, 4), $this->expectCallable(4, [[1], [2], [3], [4]]));
- }
-
- public function testToArray() {
- // Test that toArray works with an empty sequence
- $this->assertEmpty(S::toArray(S::create()));
-
- // Test that toArray works with a non-empty sequence
- $this->assertEquals([1, 2, 3], S::toArray(S::create(1, 2, 3)));
- }
-
- public function testCount() {
- // Test that count works with an empty array
- $this->assertEquals(0, S::count(S::create()));
-
- // Test that count works with a non-empty array
- $this->assertEquals(3, S::count(S::create(1, 2, 3)));
- }
-
- public function testCreate() {
- // Test that create can create an empty sequence
- foreach( S::create() as $item )
- $this->fail('Should not be reachable');
-
- // Test that create can create non-empty sequences
- S::each(S::create(1, 2, 3), $this->expectCallable(3, [[1], [2], [3]]));
- }
-
- public function testRange() {
- // Test that range can create empty sequences with +ve and -ve steps
- $this->assertEmpty(S::toArray(S::range(2, 1, 1)));
- $this->assertEmpty(S::toArray(S::range(1, 2, -1)));
-
- // Test that range can create single element sequences with +ve and -ve steps
- $this->assertEquals([1], S::toArray(S::range(1, 1, 1)));
- $this->assertEquals([1], S::toArray(S::range(1, 1, -1)));
-
- // Test that range can create multi-element array with different +ve and -ve steps
- $this->assertEquals([1, 2, 3], S::toArray(S::range(1, 3)));
- $this->assertEquals([1, 3, 5, 7, 9], S::toArray(S::range(1, 10, 2)));
- $this->assertEquals([2, 4, 6, 8, 10], S::toArray(S::range(2, 10, 2)));
-
- $this->assertEquals([3, 2, 1], S::toArray(S::range(3, 1, -1)));
- $this->assertEquals([10, 8, 6, 4, 2], S::toArray(S::range(10, 1, -2)));
- $this->assertEquals([10, 8, 6, 4, 2, 0], S::toArray(S::range(10, 0, -2)));
-
- // Check that range can create infinite sequences (i.e. the same range command
- // can be used to create arrays of different sizes by taking more or less items)
- $this->assertEquals([1, 3, 5], S::toArray(S::take(S::range(1, null, 2), 3)));
- $this->assertEquals([1, 3, 5, 7, 9], S::toArray(S::take(S::range(1, null, 2), 5)));
- }
-
- public function testRepeat() {
- // Test that repeat creates an infinite sequence containing the same value
- // repeated
- $this->assertEquals([3, 3, 3], S::toArray(S::take(S::repeat(3), 3)));
- $this->assertEquals([3, 3, 3, 3, 3], S::toArray(S::take(S::repeat(3), 5)));
- }
-
- public function testReplicate() {
- // Test that replicate can create an empty sequence
- $this->assertEmpty(S::toArray(S::replicate(0, 3)));
-
- // Test that replicate can create non-empty sequences
- $this->assertEquals([3, 3, 3], S::toArray(S::replicate(3, 3)));
- $this->assertEquals([3, 3, 3, 3, 3], S::toArray(S::replicate(5, 3)));
- }
-
- public function testIterate() {
- // Test that iterate can create infinite sequences
- $this->assertEquals(
- [1, 2, 3],
- S::toArray(S::take(S::iterate(function($i) { return $i + 1; }, 1), 3))
- );
- $this->assertEquals(
- [1, 2, 3, 4, 5],
- S::toArray(S::take(S::iterate(function($i) { return $i + 1; }, 1), 5))
- );
- }
- }