Work around test provider running before setUp()
[lhc/web/wiklou.git] / tests / phpunit / includes / libs / XhprofTest.php
1 <?php
2 /**
3 * @section LICENSE
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 * http://www.gnu.org/copyleft/gpl.html
18 *
19 * @file
20 */
21
22 /**
23 * @uses Xhprof
24 * @uses AutoLoader
25 * @author Bryan Davis <bd808@wikimedia.org>
26 * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
27 * @since 1.25
28 */
29 class XhprofTest extends PHPUnit_Framework_TestCase {
30
31 public function setUp() {
32 if ( !function_exists( 'xhprof_enable' ) ) {
33 $this->markTestSkipped( 'No xhprof support detected.' );
34 }
35 }
36
37 /**
38 * @covers Xhprof::splitKey
39 * @dataProvider provideSplitKey
40 */
41 public function testSplitKey( $key, $expect ) {
42 $this->assertSame( $expect, Xhprof::splitKey( $key ) );
43 }
44
45 public function provideSplitKey() {
46 return array(
47 array( 'main()', array( null, 'main()' ) ),
48 array( 'foo==>bar', array( 'foo', 'bar' ) ),
49 array( 'bar@1==>bar@2', array( 'bar@1', 'bar@2' ) ),
50 array( 'foo==>bar==>baz', array( 'foo', 'bar==>baz' ) ),
51 array( '==>bar', array( '', 'bar' ) ),
52 array( '', array( null, '' ) ),
53 );
54 }
55
56 /**
57 * @covers Xhprof::__construct
58 * @covers Xhprof::stop
59 * @covers Xhprof::getRawData
60 * @dataProvider provideRawData
61 */
62 public function testRawData( $flags, $keys ) {
63 $xhprof = new Xhprof( array( 'flags' => $flags ) );
64 $raw = $xhprof->getRawData();
65 $this->assertArrayHasKey( 'main()', $raw );
66 foreach ( $keys as $key ) {
67 $this->assertArrayHasKey( $key, $raw['main()'] );
68 }
69 }
70
71 public function provideRawData() {
72 $tests = array(
73 array( 0, array( 'ct', 'wt' ) ),
74 );
75
76 if ( defined( 'XHPROF_FLAGS_CPU' ) && defined( 'XHPROF_FLAGS_CPU' ) ) {
77 $tests[] = array( XHPROF_FLAGS_MEMORY, array(
78 'ct', 'wt', 'mu', 'pmu',
79 ) );
80 $tests[] = array( XHPROF_FLAGS_CPU, array(
81 'ct', 'wt', 'cpu',
82 ) );
83 $tests[] = array( XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_CPU, array(
84 'ct', 'wt', 'mu', 'pmu', 'cpu',
85 ) );
86 }
87
88 return $tests;
89 }
90
91 /**
92 * @covers Xhprof::pruneData
93 */
94 public function testInclude() {
95 $xhprof = $this->getXhprofFixture( array(
96 'include' => array( 'main()' ),
97 ) );
98 $raw = $xhprof->getRawData();
99 $this->assertArrayHasKey( 'main()', $raw );
100 $this->assertArrayHasKey( 'main()==>foo', $raw );
101 $this->assertArrayHasKey( 'main()==>xhprof_disable', $raw );
102 $this->assertSame( 3, count( $raw ) );
103 }
104
105 /**
106 * Validate the structure of data returned by
107 * Xhprof::getInclusiveMetrics(). This acts as a guard against unexpected
108 * structural changes to the returned data in lieu of using a more heavy
109 * weight typed response object.
110 *
111 * @covers Xhprof::getInclusiveMetrics
112 */
113 public function testInclusiveMetricsStructure() {
114 $metricStruct = array(
115 'ct' => 'int',
116 'wt' => 'array',
117 'cpu' => 'array',
118 'mu' => 'array',
119 'pmu' => 'array',
120 );
121 $statStruct = array(
122 'total' => 'numeric',
123 'min' => 'numeric',
124 'mean' => 'numeric',
125 'max' => 'numeric',
126 'variance' => 'numeric',
127 'percent' => 'numeric',
128 );
129
130 $xhprof = $this->getXhprofFixture();
131 $metrics = $xhprof->getInclusiveMetrics();
132
133 foreach ( $metrics as $name => $metric ) {
134 $this->assertArrayStructure( $metricStruct, $metric );
135
136 foreach ( $metricStruct as $key => $type ) {
137 if ( $type === 'array' ) {
138 $this->assertArrayStructure( $statStruct, $metric[$key] );
139 if ( $name === 'main()' ) {
140 $this->assertEquals( 100, $metric[$key]['percent'] );
141 }
142 }
143 }
144 }
145 }
146
147 /**
148 * Validate the structure of data returned by
149 * Xhprof::getCompleteMetrics(). This acts as a guard against unexpected
150 * structural changes to the returned data in lieu of using a more heavy
151 * weight typed response object.
152 *
153 * @covers Xhprof::getCompleteMetrics
154 */
155 public function testCompleteMetricsStructure() {
156 $metricStruct = array(
157 'ct' => 'int',
158 'wt' => 'array',
159 'cpu' => 'array',
160 'mu' => 'array',
161 'pmu' => 'array',
162 'calls' => 'array',
163 'subcalls' => 'array',
164 );
165 $statsMetrics = array( 'wt', 'cpu', 'mu', 'pmu' );
166 $statStruct = array(
167 'total' => 'numeric',
168 'min' => 'numeric',
169 'mean' => 'numeric',
170 'max' => 'numeric',
171 'variance' => 'numeric',
172 'percent' => 'numeric',
173 'exclusive' => 'numeric',
174 );
175
176 $xhprof = $this->getXhprofFixture();
177 $metrics = $xhprof->getCompleteMetrics();
178
179 foreach ( $metrics as $name => $metric ) {
180 $this->assertArrayStructure( $metricStruct, $metric, $name );
181
182 foreach ( $metricStruct as $key => $type ) {
183 if ( in_array( $key, $statsMetrics ) ) {
184 $this->assertArrayStructure(
185 $statStruct, $metric[$key], $key
186 );
187 $this->assertLessThanOrEqual(
188 $metric[$key]['total'], $metric[$key]['exclusive']
189 );
190 }
191 }
192 }
193 }
194
195 /**
196 * @covers Xhprof::getCallers
197 * @covers Xhprof::getCallees
198 * @uses Xhprof
199 */
200 public function testEdges() {
201 $xhprof = $this->getXhprofFixture();
202 $this->assertSame( array(), $xhprof->getCallers( 'main()' ) );
203 $this->assertSame( array( 'foo', 'xhprof_disable' ),
204 $xhprof->getCallees( 'main()' )
205 );
206 $this->assertSame( array( 'main()' ),
207 $xhprof->getCallers( 'foo' )
208 );
209 $this->assertSame( array(), $xhprof->getCallees( 'strlen' ) );
210 }
211
212 /**
213 * @covers Xhprof::getCriticalPath
214 * @uses Xhprof
215 */
216 public function testCriticalPath() {
217 $xhprof = $this->getXhprofFixture();
218 $path = $xhprof->getCriticalPath();
219
220 $last = null;
221 foreach ( $path as $key => $value ) {
222 list( $func, $call ) = Xhprof::splitKey( $key );
223 $this->assertSame( $last, $func );
224 $last = $call;
225 }
226 $this->assertSame( $last, 'bar@1' );
227 }
228
229 /**
230 * Get an Xhprof instance that has been primed with a set of known testing
231 * data. Tests for the Xhprof class should laregly be concerned with
232 * evaluating the manipulations of the data collected by xhprof rather
233 * than the data collection process itself.
234 *
235 * The returned Xhprof instance primed will be with a data set created by
236 * running this trivial program using the PECL xhprof implementation:
237 * @code
238 * function bar( $x ) {
239 * if ( $x > 0 ) {
240 * bar($x - 1);
241 * }
242 * }
243 * function foo() {
244 * for ( $idx = 0; $idx < 2; $idx++ ) {
245 * bar( $idx );
246 * $x = strlen( 'abc' );
247 * }
248 * }
249 * xhprof_enable( XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY );
250 * foo();
251 * $x = xhprof_disable();
252 * var_export( $x );
253 * @endcode
254 *
255 * @return Xhprof
256 */
257 protected function getXhprofFixture( array $opts = array() ) {
258 $xhprof = new Xhprof( $opts );
259 $xhprof->loadRawData( array (
260 'foo==>bar' => array (
261 'ct' => 2,
262 'wt' => 57,
263 'cpu' => 92,
264 'mu' => 1896,
265 'pmu' => 0,
266 ),
267 'foo==>strlen' => array (
268 'ct' => 2,
269 'wt' => 21,
270 'cpu' => 141,
271 'mu' => 752,
272 'pmu' => 0,
273 ),
274 'bar==>bar@1' => array (
275 'ct' => 1,
276 'wt' => 18,
277 'cpu' => 19,
278 'mu' => 752,
279 'pmu' => 0,
280 ),
281 'main()==>foo' => array (
282 'ct' => 1,
283 'wt' => 304,
284 'cpu' => 307,
285 'mu' => 4008,
286 'pmu' => 0,
287 ),
288 'main()==>xhprof_disable' => array (
289 'ct' => 1,
290 'wt' => 8,
291 'cpu' => 10,
292 'mu' => 768,
293 'pmu' => 392,
294 ),
295 'main()' => array (
296 'ct' => 1,
297 'wt' => 353,
298 'cpu' => 351,
299 'mu' => 6112,
300 'pmu' => 1424,
301 ),
302 ) );
303 return $xhprof;
304 }
305
306 /**
307 * Assert that the given array has the described structure.
308 *
309 * @param array $struct Array of key => type mappings
310 * @param array $actual Array to check
311 * @param string $label
312 */
313 protected function assertArrayStructure( $struct, $actual, $label = null ) {
314 $this->assertInternalType( 'array', $actual, $label );
315 $this->assertCount( count($struct), $actual, $label );
316 foreach ( $struct as $key => $type ) {
317 $this->assertArrayHasKey( $key, $actual );
318 $this->assertInternalType( $type, $actual[$key] );
319 }
320 }
321 }