1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Particletree\Pqp; |
4
|
|
|
|
5
|
|
|
use PHPUnit_Framework_Testcase; |
6
|
|
|
use ReflectionClass; |
7
|
|
|
use ReflectionMethod; |
8
|
|
|
|
9
|
|
|
class PhpQuickProfilerTest extends PHPUnit_Framework_TestCase |
10
|
|
|
{ |
11
|
|
|
|
12
|
|
|
protected static $dbConnection; |
13
|
|
|
|
14
|
|
|
public static function setUpBeforeClass() |
15
|
|
|
{ |
16
|
|
|
self::$dbConnection = new LoggingPdo('sqlite::memory:'); |
17
|
|
|
$createTable = " |
18
|
|
|
CREATE TABLE IF NOT EXISTS `testing` ( |
19
|
|
|
`id` integer PRIMARY KEY AUTOINCREMENT, |
20
|
|
|
`title` varchar(60) NOT NULL |
21
|
|
|
);"; |
22
|
|
|
self::$dbConnection->exec($createTable); |
23
|
|
|
|
24
|
|
|
$hydrateTable = " |
25
|
|
|
INSERT INTO `testing` |
26
|
|
|
(`title`) |
27
|
|
|
VALUES |
28
|
|
|
('alpha'), |
29
|
|
|
('beta'), |
30
|
|
|
('charlie'), |
31
|
|
|
('delta');"; |
32
|
|
|
self::$dbConnection->exec($hydrateTable); |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
public function testConstruct() |
36
|
|
|
{ |
37
|
|
|
$startTime = microtime(true); |
38
|
|
|
|
39
|
|
|
$profiler = new PhpQuickProfiler(); |
40
|
|
|
$this->assertAttributeEquals($startTime, 'startTime', $profiler); |
41
|
|
|
|
42
|
|
|
$profiler = new PhpQuickProfiler($startTime); |
43
|
|
|
$this->assertAttributeEquals($startTime, 'startTime', $profiler); |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
public function testSetConsole() |
47
|
|
|
{ |
48
|
|
|
$console = new Console(); |
49
|
|
|
$profiler = new PhpQuickProfiler(); |
50
|
|
|
$profiler->setConsole($console); |
51
|
|
|
|
52
|
|
|
$this->assertAttributeSame($console, 'console', $profiler); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
public function testSetDisplay() |
56
|
|
|
{ |
57
|
|
|
$display = new Display(); |
58
|
|
|
$profiler = new PhpQuickProfiler(); |
59
|
|
|
$profiler->setDisplay($display); |
60
|
|
|
|
61
|
|
|
$this->assertAttributeSame($display, 'display', $profiler); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
public function testGatherFileData() |
65
|
|
|
{ |
66
|
|
|
$files = get_included_files(); |
67
|
|
|
$profiler = new PhpQuickProfiler(); |
68
|
|
|
$gatheredFileData = $profiler->gatherFileData(); |
69
|
|
|
|
70
|
|
|
$this->assertInternalType('array', $gatheredFileData); |
71
|
|
|
$this->assertEquals(count($files), count($gatheredFileData)); |
72
|
|
|
foreach ($gatheredFileData as $fileData) { |
73
|
|
|
$this->assertInternalType('array', $fileData); |
74
|
|
|
$this->assertArrayHasKey('name', $fileData); |
75
|
|
|
$this->assertContains($fileData['name'], $files); |
76
|
|
|
$this->assertArrayHasKey('size', $fileData); |
77
|
|
|
$this->assertEquals($fileData['size'], filesize($fileData['name'])); |
78
|
|
|
} |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
public function testGatherMemoryData() |
82
|
|
|
{ |
83
|
|
|
$memoryUsage = memory_get_peak_usage(); |
84
|
|
|
$allowedLimit = ini_get('memory_limit'); |
85
|
|
|
$profiler = new PhpQuickProfiler(); |
86
|
|
|
$gatheredMemoryData = $profiler->gatherMemoryData(); |
87
|
|
|
|
88
|
|
|
$this->assertInternalType('array', $gatheredMemoryData); |
89
|
|
|
$this->assertEquals(2, count($gatheredMemoryData)); |
90
|
|
|
$this->assertArrayHasKey('used', $gatheredMemoryData); |
91
|
|
|
$this->assertEquals($memoryUsage, $gatheredMemoryData['used']); |
92
|
|
|
$this->assertArrayHasKey('allowed', $gatheredMemoryData); |
93
|
|
|
$this->assertEquals($allowedLimit, $gatheredMemoryData['allowed']); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
public function testSetProfiledQueries() |
97
|
|
|
{ |
98
|
|
|
$profiledQueries = $this->dataProfiledQueries(); |
99
|
|
|
$profiler = new PhpQuickProfiler(); |
100
|
|
|
$profiler->setProfiledQueries($profiledQueries); |
101
|
|
|
|
102
|
|
|
$this->assertAttributeEquals($profiledQueries, 'profiledQueries', $profiler); |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
public function testGatherQueryData() |
106
|
|
|
{ |
107
|
|
|
$profiledQueries = $this->dataProfiledQueries(); |
108
|
|
|
$profiledQueriesSql = array(); |
109
|
|
|
$profiledQueriesTime = array(); |
110
|
|
|
foreach ($profiledQueries as $queryData) { |
111
|
|
|
array_push($profiledQueriesSql, $queryData['sql']); |
112
|
|
|
array_push($profiledQueriesTime, $queryData['time']); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
$profiler = new PhpQuickProfiler(); |
116
|
|
|
$profiler->setProfiledQueries($profiledQueries); |
117
|
|
|
$gatheredQueryData = $profiler->gatherQueryData(self::$dbConnection); |
118
|
|
|
|
119
|
|
|
$this->assertInternalType('array', $gatheredQueryData); |
120
|
|
|
$this->assertEquals(count($profiledQueries), count($gatheredQueryData)); |
121
|
|
|
foreach ($gatheredQueryData as $queryData) { |
122
|
|
|
$this->assertInternalType('array', $queryData); |
123
|
|
|
$this->assertArrayHasKey('sql', $queryData); |
124
|
|
|
$this->assertContains($queryData['sql'], $profiledQueriesSql); |
125
|
|
|
$this->assertArrayHasKey('explain', $queryData); |
126
|
|
|
$this->assertInternaltype('array', $queryData['explain']); |
127
|
|
|
$this->assertGreaterThan(0, count($queryData['explain'])); |
128
|
|
|
$this->assertArrayHasKey('time', $queryData); |
129
|
|
|
$this->assertContains($queryData['time'], $profiledQueriesTime); |
130
|
|
|
} |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
public function testGatherQueryDataInternalProfiler() |
134
|
|
|
{ |
135
|
|
|
$profiledQueries = $this->dataProfiledQueries(); |
136
|
|
|
$dbConnection = self::$dbConnection; |
137
|
|
|
$dbConnection->queries = $profiledQueries; |
138
|
|
|
$profiler = new PhpQuickProfiler(); |
139
|
|
|
$profiler->gatherQueryData($dbConnection); |
140
|
|
|
|
141
|
|
|
$this->assertAttributeSame($profiledQueries, 'profiledQueries', $profiler); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* @dataProvider dataProfiledQueries |
146
|
|
|
*/ |
147
|
|
|
public function testExplainQuery($sql, $parameters) |
148
|
|
|
{ |
149
|
|
|
$profiler = new PhpQuickProfiler(); |
150
|
|
|
$reflectedMethod = $this->getAccessibleMethod($profiler, 'explainQuery'); |
151
|
|
|
|
152
|
|
|
$explainedQuery = $reflectedMethod->invokeArgs( |
153
|
|
|
$profiler, |
154
|
|
|
array(self::$dbConnection, $sql, $parameters) |
155
|
|
|
); |
156
|
|
|
$this->assertInternalType('array', $explainedQuery); |
157
|
|
|
$this->assertGreaterThan(0, count($explainedQuery)); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* @expectedException Exception |
162
|
|
|
*/ |
163
|
|
|
public function testExplainQueryBadQueryException() |
164
|
|
|
{ |
165
|
|
|
$invalidQuery = 'SELECT * FROM `fake_table`'; |
166
|
|
|
$profiler = new PhpQuickProfiler(); |
167
|
|
|
$reflectedMethod = $this->getAccessibleMethod($profiler, 'explainQuery'); |
168
|
|
|
|
169
|
|
|
$reflectedMethod->invokeArgs( |
170
|
|
|
$profiler, |
171
|
|
|
array(self::$dbConnection, $invalidQuery) |
172
|
|
|
); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* @expectedException Exception |
177
|
|
|
*/ |
178
|
|
View Code Duplication |
public function testExplainQueryBadParametersException() |
|
|
|
|
179
|
|
|
{ |
180
|
|
|
$query = 'SELECT * FROM `testing` WHERE `title` = :title'; |
181
|
|
|
$invalidParams = array('id' => 1); |
182
|
|
|
$profiler = new PhpQuickProfiler(); |
183
|
|
|
$reflectedMethod = $this->getAccessibleMethod($profiler, 'explainQuery'); |
184
|
|
|
|
185
|
|
|
$reflectedMethod->invokeArgs( |
186
|
|
|
$profiler, |
187
|
|
|
array(self::$dbConnection, $query, $invalidParams) |
188
|
|
|
); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* @dataProvider dataConnectionDrivers |
193
|
|
|
*/ |
194
|
|
|
public function testGetExplainQuery($driver, $prefix) |
195
|
|
|
{ |
196
|
|
|
$query = 'SELECT * FROM `testing`'; |
197
|
|
|
$profiler = new PhpQuickProfiler(); |
198
|
|
|
$reflectedMethod = $this->getAccessibleMethod($profiler, 'getExplainQuery'); |
199
|
|
|
|
200
|
|
|
$explainQuery = $reflectedMethod->invokeArgs( |
201
|
|
|
$profiler, |
202
|
|
|
array($query, $driver) |
203
|
|
|
); |
204
|
|
|
|
205
|
|
|
$explainPrefix = str_replace($query, '', $explainQuery); |
206
|
|
|
$explainPrefix = trim($explainPrefix); |
207
|
|
|
$this->assertEquals($prefix, $explainPrefix); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* @expectedException Exception |
212
|
|
|
*/ |
213
|
|
View Code Duplication |
public function testGetExplainQueryUnsupportedDriver() |
|
|
|
|
214
|
|
|
{ |
215
|
|
|
$query = 'SELECT * FROM `testing`'; |
216
|
|
|
$unsupportedDriver = 'zz'; |
217
|
|
|
$profiler = new PhpQuickProfiler(); |
218
|
|
|
$reflectedMethod = $this->getAccessibleMethod($profiler, 'getExplainQuery'); |
219
|
|
|
|
220
|
|
|
$reflectedMethod->invokeArgs( |
221
|
|
|
$profiler, |
222
|
|
|
array($query, $unsupportedDriver) |
223
|
|
|
); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
public function testGatherSpeedData() |
227
|
|
|
{ |
228
|
|
|
$elapsedTime = 1.234; |
229
|
|
|
$startTime = microtime(true) - $elapsedTime; |
230
|
|
|
$allowedTime = ini_get('max_execution_time'); |
231
|
|
|
$profiler = new PhpQuickProfiler($startTime); |
232
|
|
|
$gatheredSpeedData = $profiler->gatherSpeedData(); |
233
|
|
|
|
234
|
|
|
$this->assertInternalType('array', $gatheredSpeedData); |
235
|
|
|
$this->assertEquals(2, count($gatheredSpeedData)); |
236
|
|
|
$this->assertArrayHasKey('elapsed', $gatheredSpeedData); |
237
|
|
|
$this->assertEquals($elapsedTime, $gatheredSpeedData['elapsed']); |
238
|
|
|
$this->assertArrayHasKey('allowed', $gatheredSpeedData); |
239
|
|
|
$this->assertEquals($allowedTime, $gatheredSpeedData['allowed']); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
public function testDisplay() |
243
|
|
|
{ |
244
|
|
|
$console = new Console(); |
245
|
|
|
$profiler = new PhpQuickProfiler(); |
246
|
|
|
|
247
|
|
|
$expectedDisplay = new Display(); |
248
|
|
|
$expectedDisplay->setConsole($console); |
249
|
|
|
$expectedDisplay->setFileData($profiler->gatherFileData()); |
250
|
|
|
$expectedDisplay->setMemoryData($profiler->gatherMemoryData()); |
251
|
|
|
$expectedDisplay->setQueryData($profiler->gatherQueryData()); |
252
|
|
|
$expectedDisplay->setSpeedData($profiler->gatherSpeedData()); |
253
|
|
|
ob_start(); |
254
|
|
|
$expectedDisplay->__invoke(); |
255
|
|
|
ob_end_clean(); |
256
|
|
|
|
257
|
|
|
$display = new Display(); |
258
|
|
|
$profiler->setConsole($console); |
259
|
|
|
$profiler->setDisplay($display); |
260
|
|
|
ob_start(); |
261
|
|
|
$profiler->display(); |
262
|
|
|
ob_end_clean(); |
263
|
|
|
|
264
|
|
|
$this->assertAttributeEquals($expectedDisplay, 'display', $profiler); |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* @expectedException Exception |
269
|
|
|
*/ |
270
|
|
|
public function testDisplayNothingSetException() |
271
|
|
|
{ |
272
|
|
|
$profiler = new PhpQuickProfiler(); |
273
|
|
|
$profiler->display(); |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* @expectedException Exception |
278
|
|
|
*/ |
279
|
|
|
public function testDisplayNoConsoleException() |
280
|
|
|
{ |
281
|
|
|
$display = new Display(); |
282
|
|
|
$profiler = new PhpQuickProfiler(); |
283
|
|
|
$profiler->setDisplay($display); |
284
|
|
|
$profiler->display(); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* @expectedException Exception |
289
|
|
|
*/ |
290
|
|
|
public function testDisplayNoDisplayException() |
291
|
|
|
{ |
292
|
|
|
$console = new Console(); |
293
|
|
|
$profiler = new PhpQuickProfiler(); |
294
|
|
|
$profiler->setConsole($console); |
295
|
|
|
$profiler->display(); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
public function dataProfiledQueries() |
299
|
|
|
{ |
300
|
|
|
return array( |
301
|
|
|
array( |
302
|
|
|
'sql' => "SELECT * FROM testing", |
303
|
|
|
'parameters' => array(), |
304
|
|
|
'time' => 25 |
305
|
|
|
), |
306
|
|
|
array( |
307
|
|
|
'sql' => "SELECT id FROM testing WHERE title = :title", |
308
|
|
|
'parameters' => array('title' => 'beta'), |
309
|
|
|
'time' => 5 |
310
|
|
|
) |
311
|
|
|
); |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
public function dataConnectionDrivers() |
315
|
|
|
{ |
316
|
|
|
return array( |
317
|
|
|
array( |
318
|
|
|
'driver' => 'mysql', |
319
|
|
|
'prefix' => 'EXPLAIN' |
320
|
|
|
), |
321
|
|
|
array( |
322
|
|
|
'driver' => 'sqlite', |
323
|
|
|
'prefix' => 'EXPLAIN QUERY PLAN' |
324
|
|
|
) |
325
|
|
|
); |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
protected function getAccessibleMethod(PhpQuickProfiler $profiler, $methodName) |
329
|
|
|
{ |
330
|
|
|
$reflectedConsole = new ReflectionClass(get_class($profiler)); |
331
|
|
|
$reflectedMethod = $reflectedConsole->getMethod($methodName); |
332
|
|
|
$reflectedMethod->setAccessible(true); |
333
|
|
|
return $reflectedMethod; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
public static function tearDownAfterClass() |
337
|
|
|
{ |
338
|
|
|
self::$dbConnection = null; |
339
|
|
|
} |
340
|
|
|
} |
341
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.