StatementIteratorTest.php 3.07 KB
Newer Older
1 2 3 4
<?php

namespace Doctrine\Tests\DBAL\Driver;

5 6 7 8 9
use Doctrine\DBAL\Driver\IBMDB2\DB2Statement;
use Doctrine\DBAL\Driver\Mysqli\MysqliStatement;
use Doctrine\DBAL\Driver\OCI8\OCI8Statement;
use Doctrine\DBAL\Driver\SQLAnywhere\SQLAnywhereStatement;
use Doctrine\DBAL\Driver\SQLSrv\SQLSrvStatement;
10 11
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\StatementIterator;
12
use Doctrine\DBAL\Portability\Statement as PortabilityStatement;
Sergei Morozov's avatar
Sergei Morozov committed
13
use Doctrine\Tests\DbalTestCase;
14 15
use PHPUnit\Framework\MockObject\MockObject;
use Traversable;
16

17
use function extension_loaded;
18

Sergei Morozov's avatar
Sergei Morozov committed
19
class StatementIteratorTest extends DbalTestCase
20
{
21 22 23
    /**
     * @dataProvider statementProvider()
     */
24
    public function testGettingIteratorDoesNotCallFetch(string $class): void
25
    {
26
        $stmt = $this->createPartialMock($class, ['fetch', 'fetchAll', 'fetchColumn']);
27 28 29 30
        $stmt->expects($this->never())->method('fetch');
        $stmt->expects($this->never())->method('fetchAll');
        $stmt->expects($this->never())->method('fetchColumn');

31 32 33
        $stmt->getIterator();
    }

34
    public function testIteratorIterationCallsFetchOncePerStep(): void
35 36 37 38 39 40
    {
        $stmt = $this->createMock(Statement::class);

        $calls = 0;
        $this->configureStatement($stmt, $calls);

41
        $stmtIterator = new StatementIterator($stmt);
42 43

        $this->assertIterationCallsFetchOncePerStep($stmtIterator, $calls);
44 45
    }

46
    /**
47 48
     * @param class-string<Statement> $class
     *
49 50
     * @dataProvider statementProvider()
     */
51
    public function testStatementIterationCallsFetchOncePerStep(string $class): void
52 53 54 55 56 57 58 59
    {
        $stmt = $this->createPartialMock($class, ['fetch']);

        $calls = 0;
        $this->configureStatement($stmt, $calls);
        $this->assertIterationCallsFetchOncePerStep($stmt, $calls);
    }

60
    private function configureStatement(MockObject $stmt, int &$calls): void
61 62
    {
        $values = ['foo', '', 'bar', '0', 'baz', 0, 'qux', null, 'quz', false, 'impossible'];
Sergei Morozov's avatar
Sergei Morozov committed
63
        $calls  = 0;
64 65 66

        $stmt->expects($this->exactly(10))
            ->method('fetch')
Sergei Morozov's avatar
Sergei Morozov committed
67
            ->willReturnCallback(static function () use ($values, &$calls) {
68 69
                $value = $values[$calls];
                $calls++;
70

71 72
                return $value;
            });
73
    }
74

Grégoire Paris's avatar
Grégoire Paris committed
75 76 77
    /**
     * @param Traversable<int, mixed> $iterator
     */
78
    private function assertIterationCallsFetchOncePerStep(Traversable $iterator, int &$calls): void
79 80
    {
        foreach ($iterator as $i => $_) {
81 82 83
            $this->assertEquals($i + 1, $calls);
        }
    }
84 85

    /**
86
     * @return iterable<array{0: class-string<Statement>}>
87
     */
88
    public static function statementProvider(): iterable
89 90 91 92 93 94 95 96 97 98 99 100 101 102
    {
        if (extension_loaded('ibm_db2')) {
            yield [DB2Statement::class];
        }

        yield [MysqliStatement::class];

        if (extension_loaded('oci8')) {
            yield [OCI8Statement::class];
        }

        yield [PortabilityStatement::class];
        yield [SQLAnywhereStatement::class];

103 104
        if (! extension_loaded('sqlsrv')) {
            return;
105
        }
106 107

        yield [SQLSrvStatement::class];
108
    }
109
}