99namespace OCA \Libresign \Tests \Unit \Service \Process ;
1010
1111use OCA \Libresign \AppInfo \Application ;
12+ use OCA \Libresign \Service \Process \ListeningPidResolver ;
1213use OCA \Libresign \Service \Process \ProcessManager ;
14+ use OCA \Libresign \Service \Process \ProcessSignaler ;
1315use OCA \Libresign \Tests \Unit \TestCase ;
1416use OCP \IAppConfig ;
17+ use PHPUnit \Framework \Attributes \DataProvider ;
1518use PHPUnit \Framework \MockObject \MockObject ;
1619use Psr \Log \LoggerInterface ;
1720
@@ -29,40 +32,46 @@ public function setUp(): void {
2932 }
3033
3134 /**
32- * @return array{0: ProcessManager, 1: string}
35+ * @return array{0: ProcessManager, 1: array< string, string> }
3336 */
3437 private function makeManagerWithStoredRegistry (?callable $ factory = null ): array {
35- $ stored = '{} ' ;
38+ $ storedByKey = [
39+ 'process_registry ' => '{} ' ,
40+ 'process_registry_hints ' => '{} ' ,
41+ ];
3642 $ this ->appConfig ->method ('getValueString ' )
37- ->willReturnCallback (function (string $ _appId , string $ _key , string $ default ) use (&$ stored ): string {
38- return $ stored ?: $ default ;
43+ ->willReturnCallback (function (string $ _appId , string $ key , string $ default ) use (&$ storedByKey ): string {
44+ return $ storedByKey [ $ key ] ?? $ default ;
3945 });
4046
4147 $ this ->appConfig ->method ('setValueString ' )
42- ->willReturnCallback (function (string $ _appId , string $ _key , string $ value ) use (&$ stored ): bool {
43- $ stored = $ value ;
48+ ->willReturnCallback (function (string $ _appId , string $ key , string $ value ) use (&$ storedByKey ): bool {
49+ $ storedByKey [ $ key ] = $ value ;
4450 return true ;
4551 });
4652
4753 $ manager = $ factory
4854 ? $ factory ($ this ->appConfig , $ this ->logger )
4955 : new ProcessManager ($ this ->appConfig , $ this ->logger );
5056
51- return [$ manager , $ stored ];
57+ return [$ manager , $ storedByKey ];
5258 }
5359
5460 public function testListRunningPurgesStoppedPids (): void {
55- $ stored = '{} ' ;
61+ $ storedByKey = [
62+ 'process_registry ' => '{} ' ,
63+ 'process_registry_hints ' => '{} ' ,
64+ ];
5665 $ this ->appConfig ->method ('getValueString ' )
57- ->willReturnCallback (function (string $ appId , string $ key , string $ default ) use (&$ stored ): string {
66+ ->willReturnCallback (function (string $ appId , string $ key , string $ default ) use (&$ storedByKey ): string {
5867 $ this ->assertSame (Application::APP_ID , $ appId );
59- $ this ->assertSame ( 'process_registry ' , $ key );
60- return $ stored ?: $ default ;
68+ $ this ->assertContains ( $ key , [ 'process_registry ' , ' process_registry_hints ' ] );
69+ return $ storedByKey [ $ key ] ?? $ default ;
6170 });
6271
6372 $ this ->appConfig ->method ('setValueString ' )
64- ->willReturnCallback (function (string $ _appId , string $ _key , string $ value ) use (&$ stored ): bool {
65- $ stored = $ value ;
73+ ->willReturnCallback (function (string $ _appId , string $ key , string $ value ) use (&$ storedByKey ): bool {
74+ $ storedByKey [ $ key ] = $ value ;
6675 return true ;
6776 });
6877
@@ -81,19 +90,29 @@ public function isRunning(int $pid): bool {
8190 $ this ->assertSame (111 , $ running [0 ]['pid ' ]);
8291 }
8392
84- public function testRegisterIgnoresInvalidPid (): void {
93+ #[DataProvider('provideInvalidPids ' )]
94+ public function testRegisterIgnoresInvalidPid (int $ invalidPid ): void {
8595 [$ manager ] = $ this ->makeManagerWithStoredRegistry (fn (IAppConfig $ appConfig , LoggerInterface $ logger ): ProcessManager => new class ($ appConfig , $ logger ) extends ProcessManager {
8696 public function isRunning (int $ pid ): bool {
8797 return true ;
8898 }
8999 });
90100
91- $ manager ->register (self ::WORKER_SOURCE , 0 );
92- $ manager ->register (self ::WORKER_SOURCE , -9 );
101+ $ manager ->register (self ::WORKER_SOURCE , $ invalidPid );
93102
94103 $ this ->assertSame (0 , $ manager ->countRunning (self ::WORKER_SOURCE ));
95104 }
96105
106+ /**
107+ * @return array<string, array{0: int}>
108+ */
109+ public static function provideInvalidPids (): array {
110+ return [
111+ 'zero ' => [0 ],
112+ 'negative ' => [-9 ],
113+ ];
114+ }
115+
97116 public function testFindRunningPidReturnsFirstWhenNoFilterProvided (): void {
98117 [$ manager ] = $ this ->makeManagerWithStoredRegistry (fn (IAppConfig $ appConfig , LoggerInterface $ logger ): ProcessManager => new class ($ appConfig , $ logger ) extends ProcessManager {
99118 public function isRunning (int $ pid ): bool {
@@ -142,6 +161,49 @@ public function isRunning(int $pid): bool {
142161 $ this ->assertSame (0 , $ actual );
143162 }
144163
164+ public function testFindRunningPidHydratesRegistryWhenHintExists (): void {
165+ $ resolver = $ this ->createMock (ListeningPidResolver::class);
166+ $ resolver ->expects ($ this ->once ())
167+ ->method ('findListeningPids ' )
168+ ->with (8888 )
169+ ->willReturn ([777 ]);
170+
171+ $ signaler = $ this ->createMock (ProcessSignaler::class);
172+ $ signaler ->method ('isRunning ' )
173+ ->willReturn (true );
174+
175+ [$ manager ] = $ this ->makeManagerWithStoredRegistry (
176+ fn (IAppConfig $ appConfig , LoggerInterface $ logger ): ProcessManager
177+ => new ProcessManager ($ appConfig , $ logger , $ signaler , $ resolver )
178+ );
179+
180+ $ manager ->setSourceHint (self ::INSTALL_SOURCE , [
181+ 'uri ' => 'http://127.0.0.1:8888/api/v1/cfssl/ ' ,
182+ 'port ' => 8888 ,
183+ ]);
184+
185+ $ actual = $ manager ->findRunningPid (self ::INSTALL_SOURCE );
186+
187+ $ this ->assertSame (777 , $ actual );
188+ }
189+
190+ public function testFindRunningPidSkipsFallbackWhenNoHintExists (): void {
191+ $ resolver = $ this ->createMock (ListeningPidResolver::class);
192+ $ resolver ->expects ($ this ->never ())
193+ ->method ('findListeningPids ' );
194+
195+ $ signaler = $ this ->createMock (ProcessSignaler::class);
196+ $ signaler ->method ('isRunning ' )
197+ ->willReturn (true );
198+
199+ [$ manager ] = $ this ->makeManagerWithStoredRegistry (
200+ fn (IAppConfig $ appConfig , LoggerInterface $ logger ): ProcessManager
201+ => new ProcessManager ($ appConfig , $ logger , $ signaler , $ resolver )
202+ );
203+
204+ $ this ->assertSame (0 , $ manager ->findRunningPid (self ::INSTALL_SOURCE ));
205+ }
206+
145207 public function testIsRunningReturnsFalseForInvalidPid (): void {
146208 $ manager = new ProcessManager ($ this ->appConfig , $ this ->logger );
147209
@@ -150,15 +212,18 @@ public function testIsRunningReturnsFalseForInvalidPid(): void {
150212 }
151213
152214 public function testStopAllTerminatesAndUnregistersTrackedPids (): void {
153- $ stored = '{} ' ;
215+ $ storedByKey = [
216+ 'process_registry ' => '{} ' ,
217+ 'process_registry_hints ' => '{} ' ,
218+ ];
154219 $ this ->appConfig ->method ('getValueString ' )
155- ->willReturnCallback (function (string $ _appId , string $ _key , string $ default ) use (&$ stored ): string {
156- return $ stored ?: $ default ;
220+ ->willReturnCallback (function (string $ _appId , string $ key , string $ default ) use (&$ storedByKey ): string {
221+ return $ storedByKey [ $ key ] ?? $ default ;
157222 });
158223
159224 $ this ->appConfig ->method ('setValueString ' )
160- ->willReturnCallback (function (string $ _appId , string $ _key , string $ value ) use (&$ stored ): bool {
161- $ stored = $ value ;
225+ ->willReturnCallback (function (string $ _appId , string $ key , string $ value ) use (&$ storedByKey ): bool {
226+ $ storedByKey [ $ key ] = $ value ;
162227 return true ;
163228 });
164229
@@ -170,7 +235,7 @@ public function isRunning(int $pid): bool {
170235 return true ;
171236 }
172237
173- protected function terminate (int $ pid , int $ signal ): bool {
238+ public function stopPid (int $ pid , int $ signal = SIGTERM ): bool {
174239 $ this ->terminated [] = $ pid ;
175240 return true ;
176241 }
@@ -187,15 +252,18 @@ protected function terminate(int $pid, int $signal): bool {
187252 }
188253
189254 public function testStopAllUsesProvidedSignal (): void {
190- $ stored = '{} ' ;
255+ $ storedByKey = [
256+ 'process_registry ' => '{} ' ,
257+ 'process_registry_hints ' => '{} ' ,
258+ ];
191259 $ this ->appConfig ->method ('getValueString ' )
192- ->willReturnCallback (function (string $ _appId , string $ _key , string $ default ) use (&$ stored ): string {
193- return $ stored ?: $ default ;
260+ ->willReturnCallback (function (string $ _appId , string $ key , string $ default ) use (&$ storedByKey ): string {
261+ return $ storedByKey [ $ key ] ?? $ default ;
194262 });
195263
196264 $ this ->appConfig ->method ('setValueString ' )
197- ->willReturnCallback (function (string $ _appId , string $ _key , string $ value ) use (&$ stored ): bool {
198- $ stored = $ value ;
265+ ->willReturnCallback (function (string $ _appId , string $ key , string $ value ) use (&$ storedByKey ): bool {
266+ $ storedByKey [ $ key ] = $ value ;
199267 return true ;
200268 });
201269
@@ -207,7 +275,7 @@ public function isRunning(int $pid): bool {
207275 return true ;
208276 }
209277
210- protected function terminate (int $ pid , int $ signal ): bool {
278+ public function stopPid (int $ pid , int $ signal = SIGTERM ): bool {
211279 $ this ->signals [] = $ signal ;
212280 return true ;
213281 }
@@ -220,4 +288,42 @@ protected function terminate(int $pid, int $signal): bool {
220288
221289 $ this ->assertSame ([SIGKILL , SIGKILL ], $ manager ->signals );
222290 }
291+
292+ public function testFindListeningPidsDelegatesToResolver (): void {
293+ $ resolver = $ this ->createMock (ListeningPidResolver::class);
294+ $ resolver ->expects ($ this ->once ())
295+ ->method ('findListeningPids ' )
296+ ->with (8888 )
297+ ->willReturn ([111 , 222 ]);
298+
299+ $ signaler = $ this ->createMock (ProcessSignaler::class);
300+
301+ $ manager = new ProcessManager ($ this ->appConfig , $ this ->logger , $ signaler , $ resolver );
302+
303+ $ this ->assertSame ([111 , 222 ], $ manager ->findListeningPids (8888 ));
304+ }
305+
306+ #[DataProvider('provideIsRunningDelegation ' )]
307+ public function testIsRunningDelegatesToSignaler (int $ pid , bool $ expected ): void {
308+ $ resolver = $ this ->createMock (ListeningPidResolver::class);
309+ $ signaler = $ this ->createMock (ProcessSignaler::class);
310+ $ signaler ->expects ($ this ->once ())
311+ ->method ('isRunning ' )
312+ ->with ($ pid )
313+ ->willReturn ($ expected );
314+
315+ $ manager = new ProcessManager ($ this ->appConfig , $ this ->logger , $ signaler , $ resolver );
316+
317+ $ this ->assertSame ($ expected , $ manager ->isRunning ($ pid ));
318+ }
319+
320+ /**
321+ * @return array<string, array{0: int, 1: bool}>
322+ */
323+ public static function provideIsRunningDelegation (): array {
324+ return [
325+ 'running pid ' => [111 , true ],
326+ 'not running pid ' => [222 , false ],
327+ ];
328+ }
223329}
0 commit comments