summaryrefslogtreecommitdiff
path: root/tests/phpunit/includes/libs/XhprofTest.php
diff options
context:
space:
mode:
Diffstat (limited to 'tests/phpunit/includes/libs/XhprofTest.php')
-rw-r--r--tests/phpunit/includes/libs/XhprofTest.php320
1 files changed, 320 insertions, 0 deletions
diff --git a/tests/phpunit/includes/libs/XhprofTest.php b/tests/phpunit/includes/libs/XhprofTest.php
new file mode 100644
index 00000000..2440fc08
--- /dev/null
+++ b/tests/phpunit/includes/libs/XhprofTest.php
@@ -0,0 +1,320 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * @uses Xhprof
+ * @uses AutoLoader
+ * @author Bryan Davis <bd808@wikimedia.org>
+ * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
+ * @since 1.25
+ */
+class XhprofTest extends PHPUnit_Framework_TestCase {
+
+ public function setUp() {
+ if ( !function_exists( 'xhprof_enable' ) ) {
+ $this->markTestSkipped( 'No xhprof support detected.' );
+ }
+ }
+
+ /**
+ * @covers Xhprof::splitKey
+ * @dataProvider provideSplitKey
+ */
+ public function testSplitKey( $key, $expect ) {
+ $this->assertSame( $expect, Xhprof::splitKey( $key ) );
+ }
+
+ public function provideSplitKey() {
+ return array(
+ array( 'main()', array( null, 'main()' ) ),
+ array( 'foo==>bar', array( 'foo', 'bar' ) ),
+ array( 'bar@1==>bar@2', array( 'bar@1', 'bar@2' ) ),
+ array( 'foo==>bar==>baz', array( 'foo', 'bar==>baz' ) ),
+ array( '==>bar', array( '', 'bar' ) ),
+ array( '', array( null, '' ) ),
+ );
+ }
+
+ /**
+ * @covers Xhprof::__construct
+ * @covers Xhprof::stop
+ * @covers Xhprof::getRawData
+ * @dataProvider provideRawData
+ */
+ public function testRawData( $flags, $keys ) {
+ $xhprof = new Xhprof( array( 'flags' => $flags ) );
+ $raw = $xhprof->getRawData();
+ $this->assertArrayHasKey( 'main()', $raw );
+ foreach ( $keys as $key ) {
+ $this->assertArrayHasKey( $key, $raw['main()'] );
+ }
+ }
+
+ public function provideRawData() {
+ $tests = array(
+ array( 0, array( 'ct', 'wt' ) ),
+ );
+
+ if ( defined( 'XHPROF_FLAGS_CPU' ) && defined( 'XHPROF_FLAGS_CPU' ) ) {
+ $tests[] = array( XHPROF_FLAGS_MEMORY, array(
+ 'ct', 'wt', 'mu', 'pmu',
+ ) );
+ $tests[] = array( XHPROF_FLAGS_CPU, array(
+ 'ct', 'wt', 'cpu',
+ ) );
+ $tests[] = array( XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_CPU, array(
+ 'ct', 'wt', 'mu', 'pmu', 'cpu',
+ ) );
+ }
+
+ return $tests;
+ }
+
+ /**
+ * @covers Xhprof::pruneData
+ */
+ public function testInclude() {
+ $xhprof = $this->getXhprofFixture( array(
+ 'include' => array( 'main()' ),
+ ) );
+ $raw = $xhprof->getRawData();
+ $this->assertArrayHasKey( 'main()', $raw );
+ $this->assertArrayHasKey( 'main()==>foo', $raw );
+ $this->assertArrayHasKey( 'main()==>xhprof_disable', $raw );
+ $this->assertSame( 3, count( $raw ) );
+ }
+
+ /**
+ * Validate the structure of data returned by
+ * Xhprof::getInclusiveMetrics(). This acts as a guard against unexpected
+ * structural changes to the returned data in lieu of using a more heavy
+ * weight typed response object.
+ *
+ * @covers Xhprof::getInclusiveMetrics
+ */
+ public function testInclusiveMetricsStructure() {
+ $metricStruct = array(
+ 'ct' => 'int',
+ 'wt' => 'array',
+ 'cpu' => 'array',
+ 'mu' => 'array',
+ 'pmu' => 'array',
+ );
+ $statStruct = array(
+ 'total' => 'numeric',
+ 'min' => 'numeric',
+ 'mean' => 'numeric',
+ 'max' => 'numeric',
+ 'variance' => 'numeric',
+ 'percent' => 'numeric',
+ );
+
+ $xhprof = $this->getXhprofFixture();
+ $metrics = $xhprof->getInclusiveMetrics();
+
+ foreach ( $metrics as $name => $metric ) {
+ $this->assertArrayStructure( $metricStruct, $metric );
+
+ foreach ( $metricStruct as $key => $type ) {
+ if ( $type === 'array' ) {
+ $this->assertArrayStructure( $statStruct, $metric[$key] );
+ if ( $name === 'main()' ) {
+ $this->assertEquals( 100, $metric[$key]['percent'] );
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Validate the structure of data returned by
+ * Xhprof::getCompleteMetrics(). This acts as a guard against unexpected
+ * structural changes to the returned data in lieu of using a more heavy
+ * weight typed response object.
+ *
+ * @covers Xhprof::getCompleteMetrics
+ */
+ public function testCompleteMetricsStructure() {
+ $metricStruct = array(
+ 'ct' => 'int',
+ 'wt' => 'array',
+ 'cpu' => 'array',
+ 'mu' => 'array',
+ 'pmu' => 'array',
+ 'calls' => 'array',
+ 'subcalls' => 'array',
+ );
+ $statsMetrics = array( 'wt', 'cpu', 'mu', 'pmu' );
+ $statStruct = array(
+ 'total' => 'numeric',
+ 'min' => 'numeric',
+ 'mean' => 'numeric',
+ 'max' => 'numeric',
+ 'variance' => 'numeric',
+ 'percent' => 'numeric',
+ 'exclusive' => 'numeric',
+ );
+
+ $xhprof = $this->getXhprofFixture();
+ $metrics = $xhprof->getCompleteMetrics();
+
+ foreach ( $metrics as $name => $metric ) {
+ $this->assertArrayStructure( $metricStruct, $metric, $name );
+
+ foreach ( $metricStruct as $key => $type ) {
+ if ( in_array( $key, $statsMetrics ) ) {
+ $this->assertArrayStructure(
+ $statStruct, $metric[$key], $key
+ );
+ $this->assertLessThanOrEqual(
+ $metric[$key]['total'], $metric[$key]['exclusive']
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * @covers Xhprof::getCallers
+ * @covers Xhprof::getCallees
+ * @uses Xhprof
+ */
+ public function testEdges() {
+ $xhprof = $this->getXhprofFixture();
+ $this->assertSame( array(), $xhprof->getCallers( 'main()' ) );
+ $this->assertSame( array( 'foo', 'xhprof_disable' ),
+ $xhprof->getCallees( 'main()' )
+ );
+ $this->assertSame( array( 'main()' ),
+ $xhprof->getCallers( 'foo' )
+ );
+ $this->assertSame( array(), $xhprof->getCallees( 'strlen' ) );
+ }
+
+ /**
+ * @covers Xhprof::getCriticalPath
+ * @uses Xhprof
+ */
+ public function testCriticalPath() {
+ $xhprof = $this->getXhprofFixture();
+ $path = $xhprof->getCriticalPath();
+
+ $last = null;
+ foreach ( $path as $key => $value ) {
+ list( $func, $call ) = Xhprof::splitKey( $key );
+ $this->assertSame( $last, $func );
+ $last = $call;
+ }
+ $this->assertSame( $last, 'bar@1' );
+ }
+
+ /**
+ * Get an Xhprof instance that has been primed with a set of known testing
+ * data. Tests for the Xhprof class should laregly be concerned with
+ * evaluating the manipulations of the data collected by xhprof rather
+ * than the data collection process itself.
+ *
+ * The returned Xhprof instance primed will be with a data set created by
+ * running this trivial program using the PECL xhprof implementation:
+ * @code
+ * function bar( $x ) {
+ * if ( $x > 0 ) {
+ * bar($x - 1);
+ * }
+ * }
+ * function foo() {
+ * for ( $idx = 0; $idx < 2; $idx++ ) {
+ * bar( $idx );
+ * $x = strlen( 'abc' );
+ * }
+ * }
+ * xhprof_enable( XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY );
+ * foo();
+ * $x = xhprof_disable();
+ * var_export( $x );
+ * @endcode
+ *
+ * @return Xhprof
+ */
+ protected function getXhprofFixture( array $opts = array() ) {
+ $xhprof = new Xhprof( $opts );
+ $xhprof->loadRawData( array (
+ 'foo==>bar' => array (
+ 'ct' => 2,
+ 'wt' => 57,
+ 'cpu' => 92,
+ 'mu' => 1896,
+ 'pmu' => 0,
+ ),
+ 'foo==>strlen' => array (
+ 'ct' => 2,
+ 'wt' => 21,
+ 'cpu' => 141,
+ 'mu' => 752,
+ 'pmu' => 0,
+ ),
+ 'bar==>bar@1' => array (
+ 'ct' => 1,
+ 'wt' => 18,
+ 'cpu' => 19,
+ 'mu' => 752,
+ 'pmu' => 0,
+ ),
+ 'main()==>foo' => array (
+ 'ct' => 1,
+ 'wt' => 304,
+ 'cpu' => 307,
+ 'mu' => 4008,
+ 'pmu' => 0,
+ ),
+ 'main()==>xhprof_disable' => array (
+ 'ct' => 1,
+ 'wt' => 8,
+ 'cpu' => 10,
+ 'mu' => 768,
+ 'pmu' => 392,
+ ),
+ 'main()' => array (
+ 'ct' => 1,
+ 'wt' => 353,
+ 'cpu' => 351,
+ 'mu' => 6112,
+ 'pmu' => 1424,
+ ),
+ ) );
+ return $xhprof;
+ }
+
+ /**
+ * Assert that the given array has the described structure.
+ *
+ * @param array $struct Array of key => type mappings
+ * @param array $actual Array to check
+ * @param string $label
+ */
+ protected function assertArrayStructure( $struct, $actual, $label = null ) {
+ $this->assertInternalType( 'array', $actual, $label );
+ $this->assertCount( count($struct), $actual, $label );
+ foreach ( $struct as $key => $type ) {
+ $this->assertArrayHasKey( $key, $actual );
+ $this->assertInternalType( $type, $actual[$key] );
+ }
+ }
+}