Skip to content

Commit

Permalink
MDL-79285 xhprof: Cover the new functions with unit tests
Browse files Browse the repository at this point in the history
This covers the 2 new functions with unit tests:
- xhprof_topo_sort()
- reduce_run_data()

Note that the example graph used in the provider is the
one shown in the issue to explain the reduction procedure.
  • Loading branch information
stronk7 committed Sep 26, 2023
1 parent bc70bd6 commit 8ef2974
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 8 deletions.
176 changes: 171 additions & 5 deletions lib/tests/xhprof_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,17 @@
*/
class xhprof_test extends \advanced_testcase {

public static function setUpBeforeClass(): void {
global $CFG;
require_once($CFG->libdir . '/xhprof/xhprof_moodle.php');
}

/**
* Data provider for string matches
*
* @return array
*/
public function profiling_string_matches_provider() {
public static function profiling_string_matches_provider(): array {
return [
['/index.php', '/index.php', true],
['/some/dir/index.php', '/index.php', false],
Expand Down Expand Up @@ -61,19 +66,180 @@ public function profiling_string_matches_provider() {
/**
* Test the matching syntax
*
* @covers ::profiling_string_matches
* @dataProvider profiling_string_matches_provider
* @param string $string
* @param string $patterns
* @param bool $expected
*/
public function test_profiling_string_matches($string, $patterns, $expected) {

global $CFG;
require_once($CFG->libdir . '/xhprof/xhprof_moodle.php');

$result = profiling_string_matches($string, $patterns);
$this->assertSame($result, $expected);
}

/**
* Data provider for both the topological sort and the data reduction tests.
*
* @return array
*/
public static function run_data_provider(): array {
// This data corresponds to the runs used as example @ MDL-79285.
return [
'sorted_case' => [
'rundata' => array_flip([
'A',
'A==>B',
'A==>C',
'A==>__Mustache4',
'B==>__Mustache1',
'__Mustache1==>__Mustache2',
'__Mustache4==>__Mustache2',
'__Mustache4==>E',
'E==>F',
'C==>F',
'__Mustache2==>F',
'__Mustache2==>D',
'D==>__Mustache3',
'__Mustache3==>F',
]),
'expectations' => [
'topofirst' => 'A',
'topolast' => '__Mustache3==>F',
'topocount' => 14,
'topoorder' => [
// Before and after pairs to verify they are ordered.
['before' => 'A==>C', 'after' => 'C==>F'],
['before' => 'D==>__Mustache3', 'after' => '__Mustache3==>F'],
],
'reducecount' => 8,
'reduceremoved' => [
// Elements that will be removed by the reduction.
'__Mustache1==>__Mustache2',
'__Mustache4==>__Mustache2',
'__Mustache2==>F',
'__Mustache2==>D',
'__Mustache2==>D',
'__Mustache3==>F',
],
],
],
'unsorted_case' => [
'rundata' => array_flip([
'A==>__Mustache4',
'__Mustache3==>F',
'A==>B',
'A==>C',
'B==>__Mustache1',
'__Mustache1==>__Mustache2',
'__Mustache4==>__Mustache2',
'__Mustache4==>E',
'E==>F',
'C==>F',
'__Mustache2==>F',
'__Mustache2==>D',
'D==>__Mustache3',
'A',
]),
'expectations' => [
'topofirst' => 'A',
'topolast' => '__Mustache3==>F',
'topocount' => 14,
'topoorder' => [
// Before and after pairs to verify they are ordered.
['before' => 'A==>C', 'after' => 'C==>F'],
['before' => 'D==>__Mustache3', 'after' => '__Mustache3==>F'],
],
'reducecount' => 8,
'reduceremoved' => [
// Elements that will be removed by the reduction.
'__Mustache1==>__Mustache2',
'__Mustache4==>__Mustache2',
'__Mustache2==>F',
'__Mustache2==>D',
'__Mustache2==>D',
'__Mustache3==>F',
],
],
],
];
}

/**
* Test that topologically sorting the run data works as expected
*
* @covers \moodle_xhprofrun::xhprof_topo_sort
* @dataProvider run_data_provider
*
* @param array $rundata The run data to be sorted.
* @param array $expectations The expected results.
*/
public function test_xhprof_topo_sort(array $rundata, array $expectations) {
// Make sure all the examples in the provider are the same size.
$this->assertSame($expectations['topocount'], count($rundata));

// Make moodle_xhprofrun::xhprof_topo_sort() accessible.
$reflection = new \ReflectionClass('\moodle_xhprofrun');
$method = $reflection->getMethod('xhprof_topo_sort');
$method->setAccessible(true);
// Sort the data.
$result = $method->invokeArgs(new \moodle_xhprofrun(), [$rundata]);
$this->assertIsArray($result);
$this->assertSame($expectations['topocount'], count($result));
// Convert the array to a list of keys, so we can assert values by position.
$resultkeys = array_keys($result);

// This is the elements that should be first.
$this->assertSame($expectations['topofirst'], $resultkeys[0]);
// This is the element that should be last.
$this->assertSame($expectations['topolast'], $resultkeys[$expectations['topocount'] - 1]);
// This relative ordering should be respected.
foreach ($expectations['topoorder'] as $order) {
// All the elements in the expectations should be present.
$this->assertArrayHasKey($order['before'], $result);
$this->assertArrayHasKey($order['after'], $result);
// And they should be in the correct relative order.
$this->assertGreaterThan(
array_search($order['before'], $resultkeys),
array_search($order['after'], $resultkeys)
);
}

// Final check, if we sort it again, nothing changes (it's already topologically sorted).
$result2 = $method->invokeArgs(new \moodle_xhprofrun(), [$result]);
$this->assertSame($result, $result2);
}

/**
* Test that reducing the data complexity works as expected
*
* @covers \moodle_xhprofrun::reduce_run_data
* @dataProvider run_data_provider
*
* @param array $rundata The run data to be reduced.
* @param array $expectations The expected results.
*/
public function test_reduce_run_data(array $rundata, array $expectations) {
// Make sure that the expected keys that will be removed are present.
foreach ($expectations['reduceremoved'] as $key) {
$this->assertArrayHasKey($key, $rundata);
}

// Make moodle_xhprofrun::reduce_run_data() accessible.
$reflection = new \ReflectionClass('\moodle_xhprofrun');
$method = $reflection->getMethod('reduce_run_data');
$method->setAccessible(true);
// Reduce the data.
$result = $method->invokeArgs(new \moodle_xhprofrun(), [$rundata]);
$this->assertIsArray($result);
$this->assertSame($expectations['reducecount'], count($result));
// These have been the removed elements.
foreach ($expectations['reduceremoved'] as $key) {
$this->assertArrayNotHasKey($key, $result);
}

// Final check, if we reduce it again, nothing changes (it's already reduced).
$result2 = $method->invokeArgs(new \moodle_xhprofrun(), [$result]);
$this->assertSame($result, $result2);
}
}

6 changes: 3 additions & 3 deletions lib/xhprof/xhprof_moodle.php
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ protected function reduce_run_data(array $info): array {
$parent = 'NULL';
$child = $key;
} else {
list ($parent, $child) = explode('==>', $key); // TODO: Consider caching this in a property.
[$parent, $child] = explode('==>', $key); // TODO: Consider caching this in a property.
}

if (preg_match($regexp, $key)) {
Expand All @@ -1035,7 +1035,7 @@ protected function reduce_run_data(array $info): array {
$parent = 'NULL';
$child = $key;
} else {
list ($parent, $child) = explode('==>', $key); // TODO: Consider caching this in a property.
[$parent, $child] = explode('==>', $key); // TODO: Consider caching this in a property.
}

if (isset($removed[$parent]) && !isset($remaining[$parent])) {
Expand Down Expand Up @@ -1081,7 +1081,7 @@ protected function xhprof_topo_sort(array $info): array {
$sorted[$key] = $info[$key];
$visited[$key] = true;
} else {
list ($parent, $child) = explode('==>', $key); // TODO: Consider caching this in a property.
[$parent, $child] = explode('==>', $key); // TODO: Consider caching this in a property.
if (isset($visited[$parent])) {
// Parent already visited, we can add any children to the sorted array.
$sorted[$key] = $info[$key];
Expand Down

0 comments on commit 8ef2974

Please sign in to comment.