Skip to content

Commit 514a958

Browse files
committed
test(process): add process manager unit tests
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent d20ae50 commit 514a958

1 file changed

Lines changed: 223 additions & 0 deletions

File tree

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\Tests\Unit\Service\Process;
10+
11+
use OCA\Libresign\AppInfo\Application;
12+
use OCA\Libresign\Service\Process\ProcessManager;
13+
use OCA\Libresign\Tests\Unit\TestCase;
14+
use OCP\IAppConfig;
15+
use PHPUnit\Framework\MockObject\MockObject;
16+
use Psr\Log\LoggerInterface;
17+
18+
class ProcessManagerTest extends TestCase {
19+
private const INSTALL_SOURCE = 'install';
20+
private const WORKER_SOURCE = 'worker';
21+
22+
private IAppConfig&MockObject $appConfig;
23+
private LoggerInterface&MockObject $logger;
24+
25+
public function setUp(): void {
26+
parent::setUp();
27+
$this->appConfig = $this->createMock(IAppConfig::class);
28+
$this->logger = $this->createMock(LoggerInterface::class);
29+
}
30+
31+
/**
32+
* @return array{0: ProcessManager, 1: string}
33+
*/
34+
private function makeManagerWithStoredRegistry(?callable $factory = null): array {
35+
$stored = '{}';
36+
$this->appConfig->method('getValueString')
37+
->willReturnCallback(function (string $_appId, string $_key, string $default) use (&$stored): string {
38+
return $stored ?: $default;
39+
});
40+
41+
$this->appConfig->method('setValueString')
42+
->willReturnCallback(function (string $_appId, string $_key, string $value) use (&$stored): bool {
43+
$stored = $value;
44+
return true;
45+
});
46+
47+
$manager = $factory
48+
? $factory($this->appConfig, $this->logger)
49+
: new ProcessManager($this->appConfig, $this->logger);
50+
51+
return [$manager, $stored];
52+
}
53+
54+
public function testListRunningPurgesStoppedPids(): void {
55+
$stored = '{}';
56+
$this->appConfig->method('getValueString')
57+
->willReturnCallback(function (string $appId, string $key, string $default) use (&$stored): string {
58+
$this->assertSame(Application::APP_ID, $appId);
59+
$this->assertSame('process_registry', $key);
60+
return $stored ?: $default;
61+
});
62+
63+
$this->appConfig->method('setValueString')
64+
->willReturnCallback(function (string $_appId, string $_key, string $value) use (&$stored): bool {
65+
$stored = $value;
66+
return true;
67+
});
68+
69+
$manager = new class($this->appConfig, $this->logger) extends ProcessManager {
70+
public function isRunning(int $pid): bool {
71+
return $pid === 111;
72+
}
73+
};
74+
75+
$manager->register(self::INSTALL_SOURCE, 111, ['resource' => 'cfssl']);
76+
$manager->register(self::INSTALL_SOURCE, 222, ['resource' => 'java']);
77+
78+
$running = $manager->listRunning(self::INSTALL_SOURCE);
79+
80+
$this->assertCount(1, $running);
81+
$this->assertSame(111, $running[0]['pid']);
82+
}
83+
84+
public function testRegisterIgnoresInvalidPid(): void {
85+
[$manager] = $this->makeManagerWithStoredRegistry(fn (IAppConfig $appConfig, LoggerInterface $logger): ProcessManager => new class($appConfig, $logger) extends ProcessManager {
86+
public function isRunning(int $pid): bool {
87+
return true;
88+
}
89+
});
90+
91+
$manager->register(self::WORKER_SOURCE, 0);
92+
$manager->register(self::WORKER_SOURCE, -9);
93+
94+
$this->assertSame(0, $manager->countRunning(self::WORKER_SOURCE));
95+
}
96+
97+
public function testFindRunningPidReturnsFirstWhenNoFilterProvided(): void {
98+
[$manager] = $this->makeManagerWithStoredRegistry(fn (IAppConfig $appConfig, LoggerInterface $logger): ProcessManager => new class($appConfig, $logger) extends ProcessManager {
99+
public function isRunning(int $pid): bool {
100+
return true;
101+
}
102+
});
103+
104+
$manager->register(self::INSTALL_SOURCE, 111, ['resource' => 'cfssl']);
105+
$manager->register(self::INSTALL_SOURCE, 222, ['resource' => 'java']);
106+
107+
$this->assertSame(111, $manager->findRunningPid(self::INSTALL_SOURCE));
108+
}
109+
110+
public function testFindRunningPidAppliesFilterAgainstContext(): void {
111+
[$manager] = $this->makeManagerWithStoredRegistry(fn (IAppConfig $appConfig, LoggerInterface $logger): ProcessManager => new class($appConfig, $logger) extends ProcessManager {
112+
public function isRunning(int $pid): bool {
113+
return true;
114+
}
115+
});
116+
117+
$manager->register(self::INSTALL_SOURCE, 111, ['resource' => 'cfssl']);
118+
$manager->register(self::INSTALL_SOURCE, 222, ['resource' => 'java']);
119+
120+
$actual = $manager->findRunningPid(
121+
self::INSTALL_SOURCE,
122+
fn (array $entry): bool => ($entry['context']['resource'] ?? '') === 'java',
123+
);
124+
125+
$this->assertSame(222, $actual);
126+
}
127+
128+
public function testFindRunningPidReturnsZeroWhenNothingMatchesFilter(): void {
129+
[$manager] = $this->makeManagerWithStoredRegistry(fn (IAppConfig $appConfig, LoggerInterface $logger): ProcessManager => new class($appConfig, $logger) extends ProcessManager {
130+
public function isRunning(int $pid): bool {
131+
return true;
132+
}
133+
});
134+
135+
$manager->register(self::INSTALL_SOURCE, 111, ['resource' => 'cfssl']);
136+
137+
$actual = $manager->findRunningPid(
138+
self::INSTALL_SOURCE,
139+
fn (array $entry): bool => ($entry['context']['resource'] ?? '') === 'java',
140+
);
141+
142+
$this->assertSame(0, $actual);
143+
}
144+
145+
public function testIsRunningReturnsFalseForInvalidPid(): void {
146+
$manager = new ProcessManager($this->appConfig, $this->logger);
147+
148+
$this->assertFalse($manager->isRunning(0));
149+
$this->assertFalse($manager->isRunning(-1));
150+
}
151+
152+
public function testStopAllTerminatesAndUnregistersTrackedPids(): void {
153+
$stored = '{}';
154+
$this->appConfig->method('getValueString')
155+
->willReturnCallback(function (string $_appId, string $_key, string $default) use (&$stored): string {
156+
return $stored ?: $default;
157+
});
158+
159+
$this->appConfig->method('setValueString')
160+
->willReturnCallback(function (string $_appId, string $_key, string $value) use (&$stored): bool {
161+
$stored = $value;
162+
return true;
163+
});
164+
165+
$manager = new class($this->appConfig, $this->logger) extends ProcessManager {
166+
/** @var int[] */
167+
public array $terminated = [];
168+
169+
public function isRunning(int $pid): bool {
170+
return true;
171+
}
172+
173+
protected function terminate(int $pid, int $signal): bool {
174+
$this->terminated[] = $pid;
175+
return true;
176+
}
177+
};
178+
179+
$manager->register(self::WORKER_SOURCE, 333);
180+
$manager->register(self::WORKER_SOURCE, 444);
181+
182+
$stopped = $manager->stopAll(self::WORKER_SOURCE);
183+
184+
$this->assertSame(2, $stopped);
185+
$this->assertSame([333, 444], $manager->terminated);
186+
$this->assertSame(0, $manager->countRunning(self::WORKER_SOURCE));
187+
}
188+
189+
public function testStopAllUsesProvidedSignal(): void {
190+
$stored = '{}';
191+
$this->appConfig->method('getValueString')
192+
->willReturnCallback(function (string $_appId, string $_key, string $default) use (&$stored): string {
193+
return $stored ?: $default;
194+
});
195+
196+
$this->appConfig->method('setValueString')
197+
->willReturnCallback(function (string $_appId, string $_key, string $value) use (&$stored): bool {
198+
$stored = $value;
199+
return true;
200+
});
201+
202+
$manager = new class($this->appConfig, $this->logger) extends ProcessManager {
203+
/** @var int[] */
204+
public array $signals = [];
205+
206+
public function isRunning(int $pid): bool {
207+
return true;
208+
}
209+
210+
protected function terminate(int $pid, int $signal): bool {
211+
$this->signals[] = $signal;
212+
return true;
213+
}
214+
};
215+
216+
$manager->register(self::WORKER_SOURCE, 333);
217+
$manager->register(self::WORKER_SOURCE, 444);
218+
219+
$manager->stopAll(self::WORKER_SOURCE, SIGKILL);
220+
221+
$this->assertSame([SIGKILL, SIGKILL], $manager->signals);
222+
}
223+
}

0 commit comments

Comments
 (0)