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