1010use PhpSchool \PhpWorkshop \Exercise \ExerciseInterface ;
1111use PhpSchool \PhpWorkshop \Exercise \ExerciseType ;
1212use PhpSchool \PhpWorkshop \Exercise \Scenario \CliScenario ;
13- use PhpSchool \PhpWorkshop \ExerciseDispatcher ;
1413use PhpSchool \PhpWorkshop \Output \OutputInterface ;
1514use PhpSchool \PhpWorkshop \Result \ComparisonFailure ;
1615use PhpSchool \PhpWorkshop \Result \Failure ;
@@ -31,71 +30,101 @@ public function getDescription(): string
3130
3231 public function defineListeners (EventDispatcher $ eventDispatcher ): void
3332 {
34- $ appendArgsListener = function (CliExecuteEvent $ event ) {
35- $ event ->appendArg ('127.0.0.1 ' );
36- $ event ->appendArg ($ this ->getRandomPort ());
37- };
33+ $ referencePort = $ this ->getRandomPort ();
34+ $ studentPort = $ this ->getRandomPort ();
35+
36+ $ eventDispatcher ->listen (
37+ 'cli.verify.reference-execute.pre ' ,
38+ function (CliExecuteEvent $ event ) use ($ referencePort ) {
39+ $ event ->appendArg ('0.0.0.0 ' );
40+ $ event ->appendArg ((string ) $ referencePort );
41+ $ event ->getScenario ()->exposePort ($ referencePort );
42+ }
43+ );
44+ $ eventDispatcher ->listen (
45+ ['cli.verify.student-execute.pre ' , 'cli.run.student-execute.pre ' ],
46+ function (CliExecuteEvent $ event ) use ($ studentPort ) {
47+ $ event ->appendArg ('0.0.0.0 ' );
48+ $ event ->appendArg ((string ) $ studentPort );
49+ $ event ->getScenario ()->exposePort ($ studentPort );
50+ }
51+ );
3852
39- $ eventDispatcher ->listen ('cli.verify.reference-execute.pre ' , $ appendArgsListener );
40- $ eventDispatcher ->listen ('cli.verify.student-execute.pre ' , $ appendArgsListener );
41- $ eventDispatcher ->listen ('cli.run.student-execute.pre ' , $ appendArgsListener );
53+ $ eventDispatcher ->listen (
54+ 'cli.verify.reference.executing ' ,
55+ function (CliExecuteEvent $ event ) use ($ referencePort ) {
56+ //wait for server to boot
57+ sleep (1 );
4258
43- $ eventDispatcher ->listen ('cli.verify.reference.executing ' , function (CliExecuteEvent $ event ) {
44- $ args = $ event ->getArgs ()->getArrayCopy ();
59+ $ socket = $ this ->createSocket ();
60+ @socket_connect ($ socket , '0.0.0.0 ' , $ referencePort );
61+ @socket_read ($ socket , 2048 , PHP_NORMAL_READ );
4562
46- //wait for server to boot
47- usleep (100000 );
63+ socket_close ($ socket );
4864
49- $ socket = $ this ->createSocket ();
50- socket_connect ($ socket , $ args [0 ], (int ) $ args [1 ]);
51- socket_read ($ socket , 2048 , PHP_NORMAL_READ );
65+ //wait for shutdown
66+ usleep (100000 );
67+ }
68+ );
5269
53- //wait for shutdown
54- usleep (100000 );
55- });
70+ $ eventDispatcher ->insertVerifier (
71+ 'cli.verify.student.executing ' ,
72+ function (CliExecuteEvent $ event ) use ($ studentPort ) {
73+ //wait for server to boot
74+ sleep (1 );
5675
57- $ eventDispatcher ->insertVerifier ('cli.verify.student.executing ' , function (CliExecuteEvent $ event ) {
58- $ args = $ event ->getArgs ()->getArrayCopy ();
76+ $ socket = $ this ->createSocket ();
5977
60- //wait for server to boot
61- usleep (100000 );
78+ $ result = @socket_connect ($ socket , '0.0.0.0 ' , $ studentPort );
6279
63- $ socket = $ this ->createSocket ();
64- $ connectResult = @socket_connect ($ socket , $ args [0 ], (int ) $ args [1 ]);
65-
66- if (!$ connectResult ) {
67- return Failure::fromNameAndReason ($ this ->getName (), sprintf (
68- "Client returns an error (number %d): Connection refused while trying to join tcp://127.0.0.1:%d. " ,
69- socket_last_error ($ socket ),
70- $ args [1 ]
71- ));
72- }
80+ if (!$ result ) {
81+ $ error = "Client returns an error (number %d): Connection refused " ;
82+ $ error .= "while trying to join tcp://0.0.0.0:%d. " ;
7383
74- $ out = (string ) socket_read ($ socket , 2048 , PHP_NORMAL_READ );
84+ return Failure::fromNameAndReason ($ this ->getName (), sprintf (
85+ $ error ,
86+ socket_last_error ($ socket ),
87+ $ studentPort
88+ ));
89+ }
7590
76- //wait for shutdown
77- usleep (100000 );
91+ $ out = (string ) socket_read ($ socket , 2048 , PHP_NORMAL_READ );
92+
93+ socket_close ($ socket );
7894
79- $ date = new \DateTime ();
95+ //wait for shutdown
96+ usleep (100000 );
8097
81- //match the current date but any seconds
82- //since we can't mock time in PHP easily
83- if (!preg_match (sprintf ('/^%s:([0-5][0-9]|60)\n$/ ' , $ date ->format ('Y-m-d H:i ' )), $ out )) {
84- return ComparisonFailure::fromNameAndValues ($ this ->getName (), $ date ->format ("Y-m-d H:i:s \n" ), $ out );
98+ $ date = new \DateTime ();
99+
100+ //match the current date but any seconds
101+ //since we can't mock time in PHP easily
102+ if (!preg_match (sprintf ('/^%s:([0-5][0-9]|60)\n$/ ' , $ date ->format ('Y-m-d H:i ' )), $ out )) {
103+ return ComparisonFailure::fromNameAndValues ($ this ->getName (), $ date ->format ("Y-m-d H:i:s \n" ), $ out );
104+ }
105+ return new Success ($ this ->getName ());
85106 }
86- return new Success ($ this ->getName ());
87- });
107+ );
88108
89- $ eventDispatcher ->listen ('cli.run.student.executing ' , function (CliExecuteEvent $ event ) {
109+ $ eventDispatcher ->listen ('cli.run.student.executing ' , function (CliExecuteEvent $ event ) use ( $ studentPort ) {
90110 /** @var OutputInterface $output */
91111 $ output = $ event ->getParameter ('output ' );
92- $ args = $ event ->getArgs ()->getArrayCopy ();
93112
94113 //wait for server to boot
95- usleep ( 100000 );
114+ sleep ( 1 );
96115
97116 $ socket = $ this ->createSocket ();
98- socket_connect ($ socket , $ args [0 ], (int ) $ args [1 ]);
117+ try {
118+ $ connectResult = @socket_connect ($ socket , '0.0.0.0 ' , $ studentPort );
119+ } catch (\ErrorException $ e ) {
120+ $ output ->write ('Cannot connect ' );
121+ return ;
122+ }
123+
124+ if (false === $ connectResult ) {
125+ $ output ->write ('Cannot connect ' );
126+ return ;
127+ }
99128 $ out = (string ) socket_read ($ socket , 2048 , PHP_NORMAL_READ );
100129
101130 //wait for shutdown
@@ -105,9 +134,18 @@ public function defineListeners(EventDispatcher $eventDispatcher): void
105134 });
106135 }
107136
108- private function getRandomPort (): string
137+ private function getRandomPort (): int
109138 {
110- return (string ) mt_rand (1025 , 65535 );
139+ $ sock = socket_create_listen (0 );
140+
141+ if ($ sock === false ) {
142+ throw new RuntimeException ('Cannot create socket ' );
143+ }
144+
145+ socket_getsockname ($ sock , $ addr , $ port );
146+ socket_close ($ sock );
147+
148+ return $ port ;
111149 }
112150
113151 public function getType (): ExerciseType
@@ -117,7 +155,8 @@ public function getType(): ExerciseType
117155
118156 public function defineTestScenario (): CliScenario
119157 {
120- return (new CliScenario ())->withExecution ();
158+ return (new CliScenario ())
159+ ->withExecution ();
121160 }
122161
123162 private function createSocket (): Socket
@@ -128,6 +167,8 @@ private function createSocket(): Socket
128167 throw new RuntimeException ('Cannot create socket ' );
129168 }
130169
170+ socket_set_option ($ socket , SOL_SOCKET , SO_RCVTIMEO , ["sec " => 5 , "usec " => 0 ]);
171+
131172 return $ socket ;
132173 }
133174}
0 commit comments