10
10
use PhpSchool \PhpWorkshop \Exercise \ExerciseInterface ;
11
11
use PhpSchool \PhpWorkshop \Exercise \ExerciseType ;
12
12
use PhpSchool \PhpWorkshop \Exercise \Scenario \CliScenario ;
13
- use PhpSchool \PhpWorkshop \ExerciseDispatcher ;
14
13
use PhpSchool \PhpWorkshop \Output \OutputInterface ;
15
14
use PhpSchool \PhpWorkshop \Result \ComparisonFailure ;
16
15
use PhpSchool \PhpWorkshop \Result \Failure ;
@@ -31,71 +30,101 @@ public function getDescription(): string
31
30
32
31
public function defineListeners (EventDispatcher $ eventDispatcher ): void
33
32
{
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
+ );
38
52
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 );
42
58
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 );
45
62
46
- //wait for server to boot
47
- usleep (100000 );
63
+ socket_close ($ socket );
48
64
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
+ );
52
69
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 );
56
75
57
- $ eventDispatcher ->insertVerifier ('cli.verify.student.executing ' , function (CliExecuteEvent $ event ) {
58
- $ args = $ event ->getArgs ()->getArrayCopy ();
76
+ $ socket = $ this ->createSocket ();
59
77
60
- //wait for server to boot
61
- usleep (100000 );
78
+ $ result = @socket_connect ($ socket , '0.0.0.0 ' , $ studentPort );
62
79
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. " ;
73
83
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
+ }
75
90
76
- //wait for shutdown
77
- usleep (100000 );
91
+ $ out = (string ) socket_read ($ socket , 2048 , PHP_NORMAL_READ );
92
+
93
+ socket_close ($ socket );
78
94
79
- $ date = new \DateTime ();
95
+ //wait for shutdown
96
+ usleep (100000 );
80
97
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 ());
85
106
}
86
- return new Success ($ this ->getName ());
87
- });
107
+ );
88
108
89
- $ eventDispatcher ->listen ('cli.run.student.executing ' , function (CliExecuteEvent $ event ) {
109
+ $ eventDispatcher ->listen ('cli.run.student.executing ' , function (CliExecuteEvent $ event ) use ( $ studentPort ) {
90
110
/** @var OutputInterface $output */
91
111
$ output = $ event ->getParameter ('output ' );
92
- $ args = $ event ->getArgs ()->getArrayCopy ();
93
112
94
113
//wait for server to boot
95
- usleep ( 100000 );
114
+ sleep ( 1 );
96
115
97
116
$ 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
+ }
99
128
$ out = (string ) socket_read ($ socket , 2048 , PHP_NORMAL_READ );
100
129
101
130
//wait for shutdown
@@ -105,9 +134,18 @@ public function defineListeners(EventDispatcher $eventDispatcher): void
105
134
});
106
135
}
107
136
108
- private function getRandomPort (): string
137
+ private function getRandomPort (): int
109
138
{
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 ;
111
149
}
112
150
113
151
public function getType (): ExerciseType
@@ -117,7 +155,8 @@ public function getType(): ExerciseType
117
155
118
156
public function defineTestScenario (): CliScenario
119
157
{
120
- return (new CliScenario ())->withExecution ();
158
+ return (new CliScenario ())
159
+ ->withExecution ();
121
160
}
122
161
123
162
private function createSocket (): Socket
@@ -128,6 +167,8 @@ private function createSocket(): Socket
128
167
throw new RuntimeException ('Cannot create socket ' );
129
168
}
130
169
170
+ socket_set_option ($ socket , SOL_SOCKET , SO_RCVTIMEO , ["sec " => 5 , "usec " => 0 ]);
171
+
131
172
return $ socket ;
132
173
}
133
174
}
0 commit comments