Skip to content

Commit

Permalink
Fix ImportSubjectClassPass; Split RevealPass from InitializeTestSubje…
Browse files Browse the repository at this point in the history
…ctPass.
  • Loading branch information
adamelso committed Jun 11, 2024
1 parent 99f745c commit e890388
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 78 deletions.
2 changes: 1 addition & 1 deletion lib/Transunit/Pass/AddTestMethodPrefixPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private function addPrefix(Node\Stmt\ClassMethod $node): void

if (
0 !== strpos($methodName, 'it_')
|| 0 !== strpos($methodName, 'its_')
&& 0 !== strpos($methodName, 'its_')
) {
return;
}
Expand Down
18 changes: 13 additions & 5 deletions lib/Transunit/Pass/AssertionPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ public function rewrite(Node $node): void
return;
}

$assertion = $node->expr->name->toString();
$mappedAssertions = [
'shouldBe' => 'assertSame',
'shouldReturn' => 'assertSame',
Expand All @@ -41,26 +40,35 @@ public function rewrite(Node $node): void
'shouldImplement' => 'assertInstanceOf',
];

$mappedConstantAssertions = [
'null' => 'assertNull',
'true' => 'assertTrue',
'false' => 'assertFalse',
];

$assertion = $node->expr->name->toString();

if (!isset($mappedAssertions[$assertion])) {
return;
}

$expectation = $node->expr->args[0]->value;
$call = $node->expr->var;


if (
$expectation instanceof Node\Expr\ConstFetch
&& $expectation->name->toString() === 'null'
&& isset($mappedConstantAssertions[$expectation->name->toString()])
) {
// static::assertNull($call);
$assertionMethod = $mappedConstantAssertions[$expectation->name->toString()] ?? null;

$rewrittenAssertion = new Node\Expr\StaticCall(
new Node\Name('static'),
'assertNull',
$assertionMethod,
[
new Node\Arg($call)
]
);

} else {
// static::assertSame($expectation, $call);
$rewrittenAssertion = new Node\Expr\StaticCall(
Expand Down
4 changes: 0 additions & 4 deletions lib/Transunit/Pass/DeclareTestSubjectPropertyPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@ public function rewrite(Node $node): void
return;
}

$useProphecyTrait = array_shift($node->stmts);

$this->declareTestSubjectAsClassProperty($node);

array_unshift($node->stmts, $useProphecyTrait);
}

private function declareTestSubjectAsClassProperty(Node\Stmt\Class_ $node): void
Expand Down
20 changes: 11 additions & 9 deletions lib/Transunit/Pass/ImportSubjectClassPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ private function importSubjectClass(Namespace_ $node): void
if ($ns[0] === 'tests' && $ns[1] === 'unit') {
array_shift($ns);
array_shift($ns);
} elseif ($ns[0] === 'spec') {
array_shift($ns);
}

foreach ($node->stmts as $stmt) {
if ($stmt instanceof Node\Stmt\Class_) {
$testClassname = $stmt->name->toString();
$ns[] = substr($testClassname, 0, -4);

break;
}
}

$fcqn = implode('\\', $ns);
Expand All @@ -59,15 +70,6 @@ private function importSubjectClass(Namespace_ $node): void
}
}

foreach ($node->stmts as $stmt) {
if ($stmt instanceof Node\Stmt\Class_) {
$testClassname = $stmt->name->toString();
$ns[] = substr($testClassname, 0, -4);

break;
}
}

$use = new Node\Stmt\Use_([
new Node\Stmt\UseUse(new Name($fcqn)),
]);
Expand Down
78 changes: 29 additions & 49 deletions lib/Transunit/Pass/InitializeTestSubjectPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,10 @@

/**
* ```
* class TestSubjectTest extends TestCase
* {
* use ProphecyTrait;
*
* private TestSubject $_testSubject;
*
* - function let(AgentRepository $agentRepository, EventDispatcher $eventDispatcher)
* + function let()
* function let(AgentRepository $agentRepository, EventDispatcher $eventDispatcher)
* {
* - $this->beConstructedWith($agentRepository, $eventDispatcher);
* + $this->_testSubject = new TestSubject($this->agentRepository->reveal(), $this->eventDispatcher->reveal());
* - $this->beConstructedWith($agentRepository, $eventDispatcher, 'chicken');
* + $this->_testSubject = new TestSubject($this->agentRepository->reveal(), $this->eventDispatcher->reveal(), 'chicken');
* }
* ```
*/
Expand All @@ -43,7 +36,10 @@ public function rewrite(Node $node): void

$useProphecyTrait = array_shift($node->stmts);

$this->instantiateTestSubject($setupMethod);
$testClassname = $node->name->toString();
$subjectClassname = substr($testClassname, 0, -4);

$this->instantiateTestSubject($setupMethod, $subjectClassname);

array_unshift($node->stmts, $useProphecyTrait);
}
Expand All @@ -62,69 +58,53 @@ private function findSetupMethod(Node\Stmt\Class_ $node): ?Node\Stmt\ClassMethod
return null;
}

private function instantiateTestSubject(Node\Stmt\ClassMethod $node): void
private function instantiateTestSubject(Node\Stmt\ClassMethod $node, string $subjectClassname): void
{
$testClassname = $node->name->toString();
$subjectClassname = substr($testClassname, 0, -4);

$collaboratorNames = [];
/** @var Node\Arg[] $args */
$args = [];

foreach ($node->stmts as $stmt) {
// Check if expression is a property assignment
if (! $stmt instanceof Node\Stmt\Expression) {
continue;
}

if (! $stmt->expr instanceof Node\Expr\Assign) {
continue;
}

if (! $stmt->expr->var instanceof Node\Expr\PropertyFetch) {
continue;
}

if (! $stmt->expr->var->var instanceof Node\Expr\Variable) {
if (
$stmt->expr instanceof Node\Expr\Assign
&& $stmt->expr->expr instanceof Node\Expr\New_
&& $stmt->expr->var instanceof Node\Expr\PropertyFetch
&& $stmt->expr->var->name instanceof Node\Identifier
&& '_testSubject' === $stmt->expr->var->name->name
) {
// Already instantiated.
continue;
}

$collaboratorVariableName = $stmt->expr->var->var->name;

if ($collaboratorVariableName === 'this') {
continue;
if (
$stmt->expr instanceof Node\Expr\MethodCall
&& $stmt->expr->name instanceof Node\Identifier
&& 'beConstructedWith' === $stmt->expr->name->name
) {
$args = $stmt->expr->args;
break;
}

$collaboratorNames[$collaboratorVariableName] = true;
}

$globalCollaborators = [];

foreach ($collaboratorNames as $collaborator => $v) {
// call $this->{$collaborator}->reveal()
$globalCollaborators[] = new Node\Arg(new Node\Expr\MethodCall(
new Node\Expr\PropertyFetch(
new Node\Expr\Variable('this'),
$collaborator
),
'reveal'
));
}

$node->stmts = array_map(function ($stmt) use ($subjectClassname, $globalCollaborators) {
$node->stmts = array_map(function ($stmt) use ($subjectClassname, $args) {
if ($stmt instanceof Node\Stmt\Expression
&& $stmt->expr instanceof Node\Expr\MethodCall
&& $stmt->expr->var instanceof Node\Expr\Variable
&& $stmt->expr->var->name === 'this'
&& $stmt->expr->name->toString() === 'beConstructedWith'
) {
// replace $this->beConstructedWith(...) with $this->_testSubject = new TestSubject(...)
return $this->writeInstantiation($subjectClassname, $globalCollaborators);
return $this->writeInstantiation($subjectClassname, $args);
}

return $stmt;
}, $node->stmts);
}

private function writeInstantiation(string $subjectClassname, array $globalCollaborators = []): Node\Stmt\Expression
private function writeInstantiation(string $subjectClassname, array $args = []): Node\Stmt\Expression
{
return new Node\Stmt\Expression(
new Node\Expr\Assign(
Expand All @@ -134,7 +114,7 @@ private function writeInstantiation(string $subjectClassname, array $globalColla
),
new Node\Expr\New_(
new Node\Name($subjectClassname),
$globalCollaborators
$args
)
)
);
Expand Down
5 changes: 4 additions & 1 deletion lib/Transunit/Pass/RenameSetupPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ class RenameSetupPass implements Pass
public function find(NodeFinder $nodeFinder, $ast): array
{
return $nodeFinder->find($ast, function (Node $node) {
if ($node instanceof Node\Stmt\ClassMethod && !in_array($node->name->toString(), ['setUp', 'let'],true)) {
if (
$node instanceof Node\Stmt\ClassMethod
&& 'let' === $node->name->toString()
) {
return $node;
}

Expand Down
103 changes: 103 additions & 0 deletions lib/Transunit/Pass/RevealPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

namespace Transunit\Pass;

use PhpParser\Node;
use PhpParser\NodeFinder;
use Transunit\Pass;

class RevealPass implements Pass
{
public function find(NodeFinder $nodeFinder, $ast): array
{
return [$nodeFinder->findFirstInstanceOf($ast, Node\Stmt\Class_::class)];
}

public function rewrite(Node $node): void
{
if (!$node instanceof Node\Stmt\Class_) {
return;
}

$setupMethod = $this->findSetupMethod($node);

if (!$setupMethod instanceof Node\Stmt\ClassMethod) {
return;
}

$useProphecyTrait = array_shift($node->stmts);

$testClassname = $node->name->toString();
$subjectClassname = substr($testClassname, 0, -4);

$this->instantiateTestSubject($setupMethod, $subjectClassname);

array_unshift($node->stmts, $useProphecyTrait);
}

private function findSetupMethod(Node\Stmt\Class_ $node): ?Node\Stmt\ClassMethod
{
foreach ($node->stmts as $stmt) {
if (
$stmt instanceof Node\Stmt\ClassMethod
&& in_array($stmt->name->toString(), ['setUp', 'let'], true)
) {
return $stmt;
}
}

return null;
}

private function instantiateTestSubject(Node\Stmt\ClassMethod $node, string $subjectClassname): void
{
$rewrittenNode = null;

foreach ($node->stmts as $stmt) {
if (! $stmt instanceof Node\Stmt\Expression) {
continue;
}

if (
$stmt->expr instanceof Node\Expr\Assign
&& $stmt->expr->expr instanceof Node\Expr\New_
&& $stmt->expr->var instanceof Node\Expr\PropertyFetch
&& $stmt->expr->var->name instanceof Node\Identifier
&& '_testSubject' === $stmt->expr->var->name->name
) {
$rewrittenNode = $stmt->expr->expr;
break;
}

if (
$stmt->expr instanceof Node\Expr\MethodCall
&& $stmt->expr->name instanceof Node\Identifier
&& 'beConstructedWith' === $stmt->expr->name->name
) {
$rewrittenNode = $stmt->expr;
break;
}
}

$newArgs = [];

foreach ($rewrittenNode->args as $arg) {
if (!$arg->value instanceof Node\Expr\Variable) {
$newArgs[] = $arg;
continue;
}

// @todo Confirm the variable was declared as an argument of the let() method

$newArgs[] = new Node\Arg(new Node\Expr\MethodCall(
new Node\Expr\PropertyFetch(
new Node\Expr\Variable('this'),
$arg->value->name
),
'reveal'
));
}

$rewrittenNode->args = $newArgs;
}
}
19 changes: 10 additions & 9 deletions lib/Transunit/Transunit.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,18 @@ private static function processFile(string $path): string
new Pass\ImportMockingLibraryPass(),
new Pass\RenameClassPass(),
new Pass\ChangeExtendedClassPass(),
new Pass\UseProphecyTraitPass(),
new Pass\GlobalCollaboratorPass(),
new Pass\CreateSetupIfNoneExistsPass(),
new Pass\InitializeTestSubjectPass(),
new Pass\DeclareTestSubjectPropertyPass(),
new Pass\CallTestSubjectPass(),
new Pass\AssertionPass(),
new Pass\DeclareTestSubjectPropertyPass(), // run before CreateSetupIfNoneExistsPass.
new Pass\CreateSetupIfNoneExistsPass(), // run after DeclareTestSubjectPropertyPass and before UseProphecyTraitPass
new Pass\UseProphecyTraitPass(), // run after CreateSetupIfNoneExistsPass
new Pass\RenameSetupPass(),
new Pass\AddTestMethodPrefixPass(),
new Pass\ProphesizeGlobalCollaboratorsPass(),
new Pass\ProphesizeLocalCollaboratorsPass(),
// new Pass\GlobalCollaboratorPass(),
new Pass\InitializeTestSubjectPass(),
new Pass\RevealPass(),
new Pass\CallTestSubjectPass(), // run before CallTestSubjectPass
new Pass\AssertionPass(), // run after CallTestSubjectPass.
// new Pass\ProphesizeGlobalCollaboratorsPass(), // run before ProphesizeLocalCollaboratorsPass
// new Pass\ProphesizeLocalCollaboratorsPass(), // run after ProphesizeGlobalCollaboratorsPass
];

/** @var Pass $pass */
Expand Down

0 comments on commit e890388

Please sign in to comment.