From ccf7e7a195091df369b09847ea6c7be7f6a8ecef Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Tue, 7 Apr 2020 12:49:09 +0200 Subject: [PATCH] Add IterableEntityIdPager This can be used to page through an iterable of entity IDs without having to load it into memory all at once, as an alternative to: new InMemoryEntityIdPager( ...$iterable ) --- RELEASE-NOTES.md | 3 + src/EntityId/IterableEntityIdPager.php | 51 ++++++++++ .../EntityId/IterableEntityIdPagerTest.php | 99 +++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 src/EntityId/IterableEntityIdPager.php create mode 100644 tests/unit/EntityId/IterableEntityIdPagerTest.php diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 07607f3f..328293ac 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,8 @@ # Wikibase DataModel Services release notes +## Version 5.3.0 (dev) +* Added `IterableEntityIdPager` + ## Version 5.2.0 (2020-03-10) * Allow installing with wikimedia/assert 0.5.0 diff --git a/src/EntityId/IterableEntityIdPager.php b/src/EntityId/IterableEntityIdPager.php new file mode 100644 index 00000000..a05f11b9 --- /dev/null +++ b/src/EntityId/IterableEntityIdPager.php @@ -0,0 +1,51 @@ + $iterable + */ + public function __construct( iterable $iterable ) { + if ( $iterable instanceof Iterator ) { + $this->iterator = $iterable; + } elseif ( is_array( $iterable ) ) { + $this->iterator = new ArrayIterator( $iterable ); + } else { + $this->iterator = new IteratorIterator( $iterable ); + } + $this->iterator->rewind(); + } + + /** + * @see EntityIdPager::fetchIds + * + * @param int $limit + * + * @return EntityId[] + */ + public function fetchIds( $limit ) { + $ids = []; + while ( $limit-- > 0 && $this->iterator->valid() ) { + $ids[] = $this->iterator->current(); + $this->iterator->next(); + } + return $ids; + } + +} diff --git a/tests/unit/EntityId/IterableEntityIdPagerTest.php b/tests/unit/EntityId/IterableEntityIdPagerTest.php new file mode 100644 index 00000000..f5510223 --- /dev/null +++ b/tests/unit/EntityId/IterableEntityIdPagerTest.php @@ -0,0 +1,99 @@ + [ self::ONE_THROUGH_TEN ]; + yield 'ArrayIterator' => [ new ArrayIterator( self::ONE_THROUGH_TEN ) ]; + yield 'generator I' => [ $this->yieldOneThroughTenIndividually() ]; + yield 'generator II' => [ $this->yieldOneThroughTenAsYieldFrom() ]; + $aggregate = $this->createMock( IteratorAggregate::class ); + $aggregate->method( 'getIterator' )->willReturn( new ArrayIterator( self::ONE_THROUGH_TEN ) ); + yield 'IteratorAggregate' => [ $aggregate ]; + } + + public function providePagers() { + foreach ( $this->provideIterables() as $key => $iterable ) { + yield $key => [ new IterableEntityIdPager( $iterable[0] ) ]; + } + } + + /** @dataProvider providePagers */ + public function testOneBatchLimit10( IterableEntityIdPager $pager ) { + $this->assertSame( self::ONE_THROUGH_TEN, $pager->fetchIds( 10 ) ); + $this->assertSame( [], $pager->fetchIds( 1 ) ); + $this->assertSame( [], $pager->fetchIds( 10 ) ); + } + + /** @dataProvider providePagers */ + public function testOneBatchLimit100( IterableEntityIdPager $pager ) { + $this->assertSame( self::ONE_THROUGH_TEN, $pager->fetchIds( 100 ) ); + $this->assertSame( [], $pager->fetchIds( 1 ) ); + $this->assertSame( [], $pager->fetchIds( 10 ) ); + } + + /** @dataProvider providePagers */ + public function testTwoBatchesLimits5And5( IterableEntityIdPager $pager ) { + $this->assertSame( [ 1, 2, 3, 4, 5 ], $pager->fetchIds( 5 ) ); + $this->assertSame( [ 6, 7, 8, 9, 10 ], $pager->fetchIds( 5 ) ); + $this->assertSame( [], $pager->fetchIds( 5 ) ); + $this->assertSame( [], $pager->fetchIds( 1 ) ); + $this->assertSame( [], $pager->fetchIds( 0 ) ); + } + + /** @dataProvider providePagers */ + public function testThreeBatchesLimits0And5And5( IterableEntityIdPager $pager ) { + $this->assertSame( [], $pager->fetchIds( 0 ) ); + $this->assertSame( [ 1, 2, 3, 4, 5 ], $pager->fetchIds( 5 ) ); + $this->assertSame( [ 6, 7, 8, 9, 10 ], $pager->fetchIds( 5 ) ); + $this->assertSame( [], $pager->fetchIds( 5 ) ); + $this->assertSame( [], $pager->fetchIds( 1 ) ); + $this->assertSame( [], $pager->fetchIds( 0 ) ); + } + + /** @dataProvider providePagers */ + public function testFourBatchesLimits1And2And3And4( IterableEntityIdPager $pager ) { + $this->assertSame( [ 1 ], $pager->fetchIds( 1 ) ); + $this->assertSame( [ 2, 3 ], $pager->fetchIds( 2 ) ); + $this->assertSame( [ 4, 5, 6 ], $pager->fetchIds( 3 ) ); + $this->assertSame( [ 7, 8, 9, 10 ], $pager->fetchIds( 4 ) ); + $this->assertSame( [], $pager->fetchIds( 5 ) ); + } + + /** @dataProvider providePagers */ + public function testTenBatchesEachLimit1( IterableEntityIdPager $pager ) { + foreach ( self::ONE_THROUGH_TEN as $i ) { + $this->assertSame( [ $i ], $pager->fetchIds( 1 ) ); + } + $this->assertSame( [], $pager->fetchIds( 1 ) ); + } + +}