Skip to content

Commit

Permalink
Add ability to log backtraces
Browse files Browse the repository at this point in the history
  • Loading branch information
stratedge committed Feb 4, 2018
1 parent 264f6cf commit a1759f9
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 3 deletions.
5 changes: 2 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Added
- Backtrace limiting options, including a default and per-test
- Ability to limit backtrace records logged, with an override-able default
- Ability to log backtraces for statements run, either for all tests or a single test
- `BacktraceCollection` class to contain backtrace logs
- `Collection::map()` method
- Backtrace container property to the PDOStatement class
- Backtrace logging options to the Wye container

## [0.6.0] - 2018-01-28
### Added
Expand Down
51 changes: 51 additions & 0 deletions src/Collections/BacktraceCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,56 @@

class BacktraceCollection extends Collection implements BacktraceCollectionInterface
{
/**
* Return a new collection of strings formatted generally into the
* print_debug_backtrace() format.
*
* @return CollectionInterface
*/
public function getTrace()
{
return $this->map(function ($item, $key) {
// Convert arguments into something more readable
if (!empty($item['args'])) {
$args = array_map(function ($item) {
switch (true) {
case $item === null:
return 'null';
case $item === true:
return 'true';
case $item === false:
return 'false';
case is_array($item):
if (!count($item)) {
return 'array(0)';
} elseif (array_keys($item) !== range(0, count($item) -1)) {
return 'associative-array(' . count($item) . ')';
}
return 'sequential-array(' . count($item) . ')';
case is_object($item):
return get_class($item);
case is_string($item):
return "\"{$item}\"";
case is_numeric($item):
return $item;
}

return "\"{$item}\"";
}, $item['args']);
} else {
$args = [];
}

return sprintf(
'#%d %s%s%s(%s) called at [%s:%d]',
$key,
!empty($item['class']) ? $item['class'] : null,
!empty($item['type']) ? $item['type'] : null,
!empty($item['function']) ? $item['function'] : null,
implode(', ', $args),
!empty($item['file']) ? $item['file'] : null,
!empty($item['line']) ? $item['line'] : null
);
});
}
}
11 changes: 11 additions & 0 deletions src/Wye.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,17 @@ public static function executeStatement(

//Increment number of queries run
static::incrementNumQueries();

if (static::shouldLogBacktrace()) {
$statement->setBacktrace(
static::makeBacktraceCollection(
debug_backtrace(
DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS,
static::resolveBacktraceLimit()
)
)
);
}
}


Expand Down
132 changes: 132 additions & 0 deletions tests/Feature/StoreBacktracesForStatementsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

namespace Tests\Feature;

use Stratedge\Wye\Collections\BacktraceCollectionInterface;
use Stratedge\Wye\Wye;

class StoreBacktracesForStatementsTest extends \Tests\TestCase
{
public function testTurnedOffStoresNoBacktraces()
{
$stmt = Wye::makeStatement('SELECT * FROM table_name', []);

$stmt->execute();

$this->assertNull($stmt->getBacktrace());
}

public function testOnForAllTestsStoresBacktrace()
{
Wye::logBacktraceForAllTests();

$stmt = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt2 = Wye::makeStatement('SELECT * FROM table_name', []);

$stmt->execute();
$stmt2->execute();

$this->assertInstanceOf(BacktraceCollectionInterface::class, $stmt->getBacktrace());
$this->assertInstanceOf(BacktraceCollectionInterface::class, $stmt2->getBacktrace());
}

public function testOnForTestStoresBacktrace()
{
Wye::logBacktraceForTest();

$stmt = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt2 = Wye::makeStatement('SELECT * FROM table_name', []);

$stmt->execute();
$stmt2->execute();

$this->assertInstanceOf(BacktraceCollectionInterface::class, $stmt->getBacktrace());
$this->assertInstanceOf(BacktraceCollectionInterface::class, $stmt2->getBacktrace());
}

public function testOnForAllTestsSpansResets()
{
Wye::logBacktraceForAllTests();

$stmt = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt->execute();
$this->assertInstanceOf(BacktraceCollectionInterface::class, $stmt->getBacktrace());

Wye::reset();

$stmt2 = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt2->execute();
$this->assertInstanceOf(BacktraceCollectionInterface::class, $stmt2->getBacktrace());
}

public function testOnForOneTestDoesNotSpanResets()
{
Wye::logBacktraceForTest();

$stmt = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt->execute();
$this->assertInstanceOf(BacktraceCollectionInterface::class, $stmt->getBacktrace());

Wye::reset();

$stmt2 = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt2->execute();
$this->assertNull($stmt2->getBacktrace());
}

public function testBacktraceDefaultLimitAppliedIfNeeded()
{
Wye::logBacktraceForTest();
Wye::setBacktraceDefaultLimit(3);

$stmt = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt->execute();

$this->assertCount(3, $stmt->getBacktrace());
}

public function testPerTestBacktraceLimitApplied()
{
Wye::logBacktraceForTest();
Wye::setBacktraceDefaultLimit(3);
Wye::setBacktraceLimit(2);

$stmt = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt->execute();

$this->assertCount(2, $stmt->getBacktrace());
}

public function testBacktraceDefaultLimitSpansResets()
{
Wye::logBacktraceForAllTests();
Wye::setBacktraceDefaultLimit(3);

$stmt = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt->execute();
$this->assertCount(3, $stmt->getBacktrace());

Wye::reset();

$stmt2 = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt2->execute();
$this->assertCount(3, $stmt2->getBacktrace());
}

public function testBacktraceLimitDoesNotSpanResets()
{
Wye::logBacktraceForAllTests();
Wye::setBacktraceDefaultLimit(3);
Wye::setBacktraceLimit(2);

$stmt = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt->execute();
$this->assertCount(2, $stmt->getBacktrace());

Wye::reset();

$stmt2 = Wye::makeStatement('SELECT * FROM table_name', []);
$stmt2->execute();
$this->assertCount(3, $stmt2->getBacktrace());
}
}
1 change: 1 addition & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ public function setUp()
{
Wye::reset();
Wye::resetBacktraceForAllTests();
Wye::resetBacktraceDefaultLimit();
}
}
105 changes: 105 additions & 0 deletions tests/Unit/Collections/BacktraceCollection/GetTraceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php

namespace Tests\Unit\Collections\BacktraceCollection;

use Stratedge\Wye\Collections\BacktraceCollection;
use Stratedge\Wye\Collections\CollectionInterface;
use Stratedge\Wye\Wye;

class GetTraceTest extends \Tests\TestCase
{
public function testReturnsNewCollection()
{
$collection = Wye::makeBacktraceCollection();

$new = $collection->getTrace();

$this->assertNotSame($new, $collection);
$this->assertInstanceOf(CollectionInterface::class, $new);
}

public function testFormatsItemsCorrectly()
{
$all = [
'class' => 'a',
'type' => 'b',
'function' => 'c',
'file' => 'd',
'line' => '1'
];

$keys = [
'class',
'type',
'function',
'file',
'line',
];

$data[] = $all;

foreach ($keys as $key) {
$data[] = array_merge($all, [$key => null]);
}

$collection = Wye::makeBacktraceCollection($data);

$expected = [
'#0 abc() called at [d:1]',
'#1 bc() called at [d:1]',
'#2 ac() called at [d:1]',
'#3 ab() called at [d:1]',
'#4 abc() called at [:1]',
'#5 abc() called at [d:0]',
];

$this->assertSame($expected, $collection->getTrace()->all());
}

public function testFormatsArgsCorrectly()
{
$values = [
[true],
[false],
[null],
[[]],
[[1, 2]],
[['test' => 1]],
[Wye::makeBacktraceCollection()],
['123'],
['1.2'],
['-8'],
['cool'],
[123],
[1.2],
[-8],
['test', 'multiple', 'values'],
];

foreach ($values as $value) {
$data[] = ['args' => $value];
}

$expected = [
'#0 (true) called at [:0]',
'#1 (false) called at [:0]',
'#2 (null) called at [:0]',
'#3 (array(0)) called at [:0]',
'#4 (sequential-array(2)) called at [:0]',
'#5 (associative-array(1)) called at [:0]',
'#6 (' . BacktraceCollection::class . ') called at [:0]',
'#7 ("123") called at [:0]',
'#8 ("1.2") called at [:0]',
'#9 ("-8") called at [:0]',
'#10 ("cool") called at [:0]',
'#11 (123) called at [:0]',
'#12 (1.2) called at [:0]',
'#13 (-8) called at [:0]',
'#14 ("test", "multiple", "values") called at [:0]',
];

$collection = Wye::makeBacktraceCollection($data);

$this->assertSame($expected, $collection->getTrace()->all());
}
}

0 comments on commit a1759f9

Please sign in to comment.