Skip to content

Commit a9439d3

Browse files
jmikolasebastianbergmann
authored andcommitted
Unpack nested IteratorAggregate objects for Count
This allows generators (#2149) and internal Traversables (#2642) to be properly detected before attempting to restore the Iterator's position after counting (#1125).
1 parent 7727a49 commit a9439d3

File tree

3 files changed

+42
-4
lines changed

3 files changed

+42
-4
lines changed

src/Framework/Constraint/Count.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ protected function getCountOf($other)
5656
}
5757

5858
if ($other instanceof Traversable) {
59-
if ($other instanceof IteratorAggregate) {
60-
$iterator = $other->getIterator();
61-
} else {
62-
$iterator = $other;
59+
while ($other instanceof IteratorAggregate) {
60+
$other = $other->getIterator();
6361
}
6462

63+
$iterator = $other;
64+
6565
if ($iterator instanceof Generator) {
6666
return $this->getCountOfGenerator($iterator);
6767
}

tests/Framework/Constraint/CountTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ public function testCount()
2525
$countConstraint = new Count(2);
2626
$it = new \TestIterator([1, 2]);
2727
$ia = new \TestIteratorAggregate($it);
28+
$ia2 = new \TestIteratorAggregate2($ia);
2829

2930
$this->assertTrue($countConstraint->evaluate($it, '', true));
3031
$this->assertTrue($countConstraint->evaluate($ia, '', true));
32+
$this->assertTrue($countConstraint->evaluate($ia2, '', true));
3133
}
3234

3335
public function testCountDoesNotChangeIteratorKey()
@@ -78,6 +80,23 @@ public function testCountDoesNotChangeIteratorKey()
7880
$it->next();
7981
$countConstraint->evaluate($ia, '', true);
8082
$this->assertFalse($it->valid());
83+
84+
// test with nested IteratorAggregate
85+
$it = new \TestIterator([1, 2]);
86+
$ia = new \TestIteratorAggregate($it);
87+
$ia2 = new \TestIteratorAggregate2($ia);
88+
89+
$countConstraint = new Count(2);
90+
$countConstraint->evaluate($ia2, '', true);
91+
$this->assertEquals(1, $it->current());
92+
93+
$it->next();
94+
$countConstraint->evaluate($ia2, '', true);
95+
$this->assertEquals(2, $it->current());
96+
97+
$it->next();
98+
$countConstraint->evaluate($ia2, '', true);
99+
$this->assertFalse($it->valid());
81100
}
82101

83102
public function testCountGeneratorsDoNotRewind()
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/* This class is used for testing a chain of IteratorAggregate objects, since
4+
* PHP does allow IteratorAggregate::getIterator() to return an instance of the
5+
* same class. */
6+
class TestIteratorAggregate2 implements IteratorAggregate
7+
{
8+
private $traversable;
9+
10+
public function __construct(\Traversable $traversable)
11+
{
12+
$this->traversable = $traversable;
13+
}
14+
15+
public function getIterator()
16+
{
17+
return $this->traversable;
18+
}
19+
}

0 commit comments

Comments
 (0)