33
44use const DIRECTORY_SEPARATOR ;
55use const JSON_THROW_ON_ERROR ;
6- use function array_is_list ;
6+ use function array_column ;
7+ use function array_merge ;
78use function array_pop ;
9+ use function array_unique ;
10+ use function array_values ;
811use function assert ;
912use function exec ;
1013use function explode ;
1114use function file_put_contents ;
1215use function in_array ;
13- use function is_array ;
1416use function is_dir ;
15- use function is_string ;
1617use function json_decode ;
1718use function mkdir ;
1819use function sprintf ;
1920use function sys_get_temp_dir ;
2021use function tempnam ;
2122use function unlink ;
23+ use PHPUnit \Event \Code \TestMethod ;
2224use PHPUnit \Event \Test \AdditionalInformationProvided ;
2325use PHPUnit \Runner \Extension \Extension as ExtensionInterface ;
2426use PHPUnit \Runner \Extension \Facade as ExtensionFacade ;
@@ -37,6 +39,16 @@ final class Extension implements ExtensionInterface
3739 */
3840 private string $ format ;
3941
42+ /**
43+ * @var array<class-string, array{uses: list<class-string>, emits: list<class-string>}>
44+ */
45+ private array $ commands ;
46+
47+ /**
48+ * @var list<array{test: TestMethod, given: list<non-empty-string>, when: non-empty-string, then: list<non-empty-string>}>
49+ */
50+ private array $ tests ;
51+
4052 public function bootstrap (Configuration $ configuration , ExtensionFacade $ facade , ParameterCollection $ parameters ): void
4153 {
4254 $ targetDirectory = '/tmp ' ;
@@ -61,64 +73,99 @@ public function bootstrap(Configuration $configuration, ExtensionFacade $facade,
6173
6274 $ this ->format = $ format ;
6375
64- $ facade ->registerSubscriber (new AdditionalInformationProvidedSubscriber ($ this ));
76+ $ facade ->registerSubscribers (
77+ new AdditionalInformationProvidedSubscriber ($ this ),
78+ new TestRunnerFinishedSubscriber ($ this ),
79+ );
6580 }
6681
6782 public function testProvidedAdditionalInformation (AdditionalInformationProvided $ event ): void
6883 {
84+ /**
85+ * @var array{given: list<array{className: class-string, description: non-empty-string}>, when: array{className: class-string, description: non-empty-string}, then: list<array{className: class-string, description: non-empty-string}>} $data
86+ */
6987 $ data = json_decode ($ event ->additionalInformation (), true , flags: JSON_THROW_ON_ERROR );
7088
71- assert (is_array ($ data ));
72- assert (isset ($ data ['given ' ]));
73- assert (is_array ($ data ['given ' ]));
74- assert (array_is_list ($ data ['given ' ]));
75- assert (isset ($ data ['when ' ]));
76- assert (is_string ($ data ['when ' ]));
77- assert (isset ($ data ['then ' ]));
78- assert (is_array ($ data ['then ' ]));
79- assert (array_is_list ($ data ['then ' ]));
80-
81- $ tmp = explode ('\\' , $ event ->test ()->className ());
82- $ className = array_pop ($ tmp );
83-
84- $ dot = (new DotRenderer )->render (
85- /** @phpstan-ignore argument.type */
86- $ data ['given ' ],
87- /** @phpstan-ignore argument.type */
88- $ data ['when ' ],
89- /** @phpstan-ignore argument.type */
90- $ data ['then ' ],
89+ if (!isset ($ this ->commands [$ data ['when ' ]['className ' ]])) {
90+ $ this ->commands [$ data ['when ' ]['className ' ]] = [
91+ 'uses ' => [],
92+ 'emits ' => [],
93+ ];
94+ }
95+
96+ $ this ->commands [$ data ['when ' ]['className ' ]]['uses ' ] = array_values (
97+ array_unique (
98+ array_merge (
99+ $ this ->commands [$ data ['when ' ]['className ' ]]['uses ' ],
100+ array_column ($ data ['given ' ], 'className ' ),
101+ ),
102+ ),
91103 );
92104
93- $ target = sprintf (
94- ' %s%s%s_%s.%s ' ,
95- $ this -> targetDirectory ,
96- DIRECTORY_SEPARATOR ,
97- $ className ,
98- $ event -> test ()-> methodName ( ),
99- $ this -> format ,
105+ $ this -> commands [ $ data [ ' when ' ][ ' className ' ]][ ' emits ' ] = array_values (
106+ array_unique (
107+ array_merge (
108+ $ this -> commands [ $ data [ ' when ' ][ ' className ' ]][ ' emits ' ] ,
109+ array_column ( $ data [ ' then ' ], ' className ' ) ,
110+ ),
111+ ) ,
100112 );
101113
102- if ($ this ->format === 'dot ' ) {
103- file_put_contents ($ target , $ dot );
114+ $ this ->tests [] = [
115+ 'test ' => $ event ->test (),
116+ 'given ' => array_column ($ data ['given ' ], 'description ' ),
117+ 'when ' => $ data ['when ' ]['description ' ],
118+ 'then ' => array_column ($ data ['then ' ], 'description ' ),
119+ ];
120+ }
104121
105- return ;
106- }
122+ public function testRunnerFinished (): void
123+ {
124+ $ this ->renderTests ();
125+ }
107126
108- $ tmpFile = tempnam (sys_get_temp_dir (), 'graphviz ' );
127+ private function renderTests (): void
128+ {
129+ foreach ($ this ->tests as $ test ) {
130+ $ tmp = explode ('\\' , $ test ['test ' ]->className ());
131+ $ className = array_pop ($ tmp );
132+
133+ $ dot = (new DotRenderer )->render (
134+ $ test ['given ' ],
135+ $ test ['when ' ],
136+ $ test ['then ' ],
137+ );
138+
139+ $ target = sprintf (
140+ '%s%s%s_%s.%s ' ,
141+ $ this ->targetDirectory ,
142+ DIRECTORY_SEPARATOR ,
143+ $ className ,
144+ $ test ['test ' ]->methodName (),
145+ $ this ->format ,
146+ );
109147
110- file_put_contents ($ tmpFile , $ dot );
148+ if ($ this ->format === 'dot ' ) {
149+ file_put_contents ($ target , $ dot );
111150
112- exec (
113- sprintf (
114- 'dot -T%s -o %s %s > /dev/null 2>&1 ' ,
115- $ this ->format ,
116- $ target ,
117- $ tmpFile ,
118- ),
119- );
151+ return ;
152+ }
153+
154+ $ tmpFile = tempnam (sys_get_temp_dir (), 'graphviz ' );
120155
121- unlink ($ tmpFile );
156+ file_put_contents ($ tmpFile , $ dot );
157+
158+ exec (
159+ sprintf (
160+ 'dot -T%s -o %s %s > /dev/null 2>&1 ' ,
161+ $ this ->format ,
162+ $ target ,
163+ $ tmpFile ,
164+ ),
165+ );
166+
167+ unlink ($ tmpFile );
168+ }
122169 }
123170
124171 /**
0 commit comments