Completed
Push — ezp25366-copy_content_relation... ( 7d5327...4b97d3 )
by André
74:42 queued 12:30
created

TestCase::getDatabaseConnection()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
dl 0
loc 8
rs 9.4285
c 1
b 1
f 0
cc 2
eloc 4
nc 2
nop 0
1
<?php
2
3
/**
4
 * File contains: eZ\Publish\Core\Persistence\Legacy\Tests\TestCase class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 *
9
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\Persistence\Legacy\Tests;
12
13
use eZ\Publish\Core\Persistence\Doctrine\ConnectionHandler;
14
use eZ\Publish\Core\Persistence\Database\SelectQuery;
15
use PHPUnit_Framework_TestCase;
16
use InvalidArgumentException;
17
use PDOException;
18
use Exception;
19
20
/**
21
 * Base test case for database related tests.
22
 */
23
abstract class TestCase extends PHPUnit_Framework_TestCase
24
{
25
    /**
26
     * DSN used for the DB backend.
27
     *
28
     * @var string
29
     */
30
    protected $dsn;
31
32
    /**
33
     * Name of the DB, extracted from DSN.
34
     *
35
     * @var string
36
     */
37
    protected $db;
38
39
    /**
40
     * Database handler -- to not be constructed twice for one test.
41
     *
42
     * @var \eZ\Publish\Core\Persistence\Database\DatabaseHandler
43
     */
44
    protected $handler;
45
46
    /**
47
     * Doctrine Database connection -- to not be constructed twice for one test.
48
     *
49
     * @var \Doctrine\DBAL\Connection
50
     */
51
    protected $connection;
52
53
    /**
54
     * Property which holds the state if this is the initial test run, so that
55
     * we should set up the database, or if this is any of the following test
56
     * runs, where it is sufficient to reset the database.
57
     */
58
    protected static $initial = true;
59
60
    /**
61
     * Get data source name.
62
     *
63
     * The database connection string is read from an optional environment
64
     * variable "DATABASE" and defaults to an in-memory SQLite database.
65
     *
66
     * @return string
67
     */
68
    protected function getDsn()
69
    {
70
        if (!$this->dsn) {
71
            $this->dsn = getenv('DATABASE');
72
            if (!$this->dsn) {
73
                $this->dsn = 'sqlite://:memory:';
74
            }
75
            $this->db = preg_replace('(^([a-z]+).*)', '\\1', $this->dsn);
76
        }
77
78
        return $this->dsn;
79
    }
80
81
    /**
82
     * Get a eZ Doctrine database connection handler.
83
     *
84
     * Get a ConnectionHandler, which can be used to interact with the configured
85
     * database. The database connection string is read from an optional
86
     * environment variable "DATABASE" and defaults to an in-memory SQLite
87
     * database.
88
     *
89
     * @return \eZ\Publish\Core\Persistence\Doctrine\ConnectionHandler
90
     */
91
    public function getDatabaseHandler()
92
    {
93
        if (!$this->handler) {
94
            $this->handler = ConnectionHandler::createFromConnection($this->getDatabaseConnection());
95
            $this->db = $this->handler->getName();
96
        }
97
98
        return $this->handler;
99
    }
100
101
    /**
102
     * Get native Doctrine database connection.
103
     *
104
     * @return \Doctrine\DBAL\Connection
105
     */
106
    public function getDatabaseConnection()
107
    {
108
        if (!$this->connection) {
109
            $this->connection = ConnectionHandler::createConnectionFromDSN($this->getDsn());
110
        }
111
112
        return $this->connection;
113
    }
114
115
    /**
116
     * Resets the database on test setup, so we always operate on a clean
117
     * database.
118
     */
119
    public function setUp()
120
    {
121
        try {
122
            $handler = $this->getDatabaseHandler();
123
        } catch (PDOException $e) {
124
            $this->markTestSkipped(
125
                'PDO session could not be created: ' . $e->getMessage()
126
            );
127
        }
128
129
        $schema = __DIR__ . '/_fixtures/schema.' . $this->db . '.sql';
130
131
        $queries = array_filter(preg_split('(;\\s*$)m', file_get_contents($schema)));
132
        foreach ($queries as $query) {
133
            $handler->exec($query);
134
        }
135
136
        $this->resetSequences();
137
138
        // Set "global" static var, that we are behind the initial run
139
        self::$initial = false;
140
    }
141
142
    protected function tearDown()
143
    {
144
        unset($this->handler);
145
    }
146
147
    /**
148
     * Get a text representation of a result set.
149
     *
150
     * @param array $result
151
     *
152
     * @return string
153
     */
154
    protected static function getResultTextRepresentation(array $result)
155
    {
156
        return implode(
157
            "\n",
158
            array_map(
159
                function ($row) {
160
                    return implode(', ', $row);
161
                },
162
                $result
163
            )
164
        );
165
    }
166
167
    /**
168
     * Inserts database fixture from $file.
169
     *
170
     * @param string $file
171
     */
172
    protected function insertDatabaseFixture($file)
173
    {
174
        $data = require $file;
175
        $db = $this->getDatabaseHandler();
176
177
        foreach ($data as $table => $rows) {
178
            // Check that at least one row exists
179
            if (!isset($rows[0])) {
180
                continue;
181
            }
182
183
            $q = $db->createInsertQuery();
184
            $q->insertInto($db->quoteIdentifier($table));
185
186
            // Contains the bound parameters
187
            $values = array();
188
189
            // Binding the parameters
190
            foreach ($rows[0] as $col => $val) {
191
                $q->set(
192
                    $db->quoteIdentifier($col),
193
                    $q->bindParam($values[$col])
194
                );
195
            }
196
197
            $stmt = $q->prepare();
198
199
            foreach ($rows as $row) {
200
                try {
201
                    // This CANNOT be replaced by:
202
                    // $values = $row
203
                    // each $values[$col] is a PHP reference which should be
204
                    // kept for parameters binding to work
205
                    foreach ($row as $col => $val) {
206
                        $values[$col] = $val;
207
                    }
208
209
                    $stmt->execute();
210
                } catch (Exception $e) {
211
                    echo "$table ( ", implode(', ', $row), " )\n";
212
                    throw $e;
213
                }
214
            }
215
        }
216
217
        $this->resetSequences();
218
    }
219
220
    /**
221
     * Reset DB sequences.
222
     */
223
    public function resetSequences()
224
    {
225
        switch ($this->db) {
226
            case 'pgsql':
227
                // Update PostgreSQL sequences
228
                $handler = $this->getDatabaseHandler();
229
230
                $queries = array_filter(preg_split('(;\\s*$)m',
231
                    file_get_contents(__DIR__ . '/_fixtures/setval.pgsql.sql')));
232
                foreach ($queries as $query) {
233
                    $handler->exec($query);
234
                }
235
        }
236
    }
237
238
    /**
239
     * Assert query result as correct.
240
     *
241
     * Builds text representations of the asserted and fetched query result,
242
     * based on a eZ\Publish\Core\Persistence\Database\SelectQuery object. Compares them using classic diff for
243
     * maximum readability of the differences between expectations and real
244
     * results.
245
     *
246
     * The expectation MUST be passed as a two dimensional array containing
247
     * rows of columns.
248
     *
249
     * @param array $expectation
250
     * @param \eZ\Publish\Core\Persistence\Database\SelectQuery $query
251
     * @param string $message
252
     */
253
    public static function assertQueryResult(array $expectation, SelectQuery $query, $message = null)
254
    {
255
        $statement = $query->prepare();
256
        $statement->execute();
257
258
        $result = array();
259
        while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
260
            $result[] = $row;
261
        }
262
263
        return self::assertEquals(
264
            self::getResultTextRepresentation($expectation),
265
            self::getResultTextRepresentation($result),
266
            $message
267
        );
268
    }
269
270
    /**
271
     * Asserts correct property values on $object.
272
     *
273
     * Asserts that for all keys in $properties a corresponding property
274
     * exists in $object with the *same* value as in $properties.
275
     *
276
     * @param array $properties
277
     * @param object $object
278
     */
279
    protected function assertPropertiesCorrect(array $properties, $object)
280
    {
281
        if (!is_object($object)) {
282
            throw new InvalidArgumentException(
283
                'Expected object as second parameter, received ' . gettype($object)
284
            );
285
        }
286
        foreach ($properties as $propName => $propVal) {
287
            $this->assertSame(
288
                $propVal,
289
                $object->$propName,
290
                "Incorrect value for \${$propName}"
291
            );
292
        }
293
    }
294
295
    /**
296
     * Asserts $expStruct equals $actStruct in at least $propertyNames.
297
     *
298
     * Asserts that properties of $actStruct equal properties of $expStruct (not
299
     * vice versa!). If $propertyNames is null, all properties are checked.
300
     * Otherwise, $propertyNames provides a white list.
301
     *
302
     * @param object $expStruct
303
     * @param object $actStruct
304
     * @param array $propertyNames
305
     */
306
    protected function assertStructsEqual(
307
        $expStruct,
308
        $actStruct,
309
        array $propertyNames = null
310
    ) {
311
        if ($propertyNames === null) {
312
            $propertyNames = $this->getPublicPropertyNames($expStruct);
313
        }
314
        foreach ($propertyNames as $propName) {
315
            $this->assertEquals(
316
                $expStruct->$propName,
317
                $actStruct->$propName,
318
                "Properties \${$propName} not same"
319
            );
320
        }
321
    }
322
323
    /**
324
     * Returns public property names in $object.
325
     *
326
     * @param object $object
327
     *
328
     * @return array
329
     */
330
    protected function getPublicPropertyNames($object)
331
    {
332
        $refl = new ReflectionObject($object);
333
334
        return array_map(
335
            function ($prop) {
336
                return $prop->getName();
337
            },
338
            $refl->getProperties(ReflectionProperty::IS_PUBLIC)
339
        );
340
    }
341
342
    /**
343
     * @return string
344
     */
345 View Code Duplication
    protected static function getInstallationDir()
346
    {
347
        static $installDir = null;
348
        if ($installDir === null) {
349
            $config = require 'config.php';
350
            $installDir = $config['install_dir'];
351
        }
352
353
        return $installDir;
354
    }
355
}
356