Passed
Push — master ( f93278...ce4ce7 )
by Martin
05:18 queued 02:52
created

ServiceTest::setUp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the systemctl PHP library.
5
 *
6
 * (c) Martin Janser <[email protected]>
7
 *
8
 * This source file is subject to the GPL license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace SystemCtl\tests;
13
14
use Symfony\Component\Process\Process;
15
use SystemCtl\CommandFailedException;
16
use SystemCtl\Service;
17
18
/**
19
 * @covers SystemCtl\Service::__construct
20
 * @covers SystemCtl\Service::setCommand
21
 * @covers SystemCtl\Service::sudo
22
 * @covers SystemCtl\Service::<private>
23
 */
24
class ServiceTest extends \PHPUnit_Framework_TestCase
25
{
26
    /**
27
     * @var string
28
     */
29
    private $commandFilename;
30
31
    /**
32
     * @var string
33
     */
34
    private $callCountFilename;
35
36
    /**
37
     * @var int
38
     */
39
    private $callCount = 1;
40
41
    protected function setUp()
42
    {
43
        $this->callCount = 1;
44
    }
45
46
    protected function tearDown()
47
    {
48
        if ($this->commandFilename) {
49
            unlink($this->commandFilename);
50
            $this->commandFilename = null;
51
        }
52
        if ($this->callCountFilename) {
53
            unlink($this->callCountFilename);
54
            $this->callCountFilename = null;
55
        }
56
    }
57
58
    /**
59
     * @covers SystemCtl\Service::__toString
60
     */
61
    public function testServiceName()
62
    {
63
        $service = $this->getMockedService('test-service');
64
65
        $this->expectNoOtherCalls();
66
67
        $this->assertSame('test-service', (string) $service, 'Service name should match');
68
    }
69
70
    /**
71
     * @covers SystemCtl\Service::isRunning
72
     */
73
    public function testIsRunning()
74
    {
75
        $service = $this->getMockedService('test-service');
76
77
        $this->expectCall(['--lines=0', 'status', 'test-service'], 0);
78
        $this->expectNoOtherCalls();
79
80
        $this->assertTrue($service->isRunning(), 'Service should be running');
81
    }
82
83
    /**
84
     * @covers SystemCtl\Service::isRunning
85
     */
86
    public function testIsNotRunning()
87
    {
88
        $service = $this->getMockedService('test-service');
89
90
        $this->expectCall(['--lines=0', 'status', 'test-service'], Service::STATUS_STOPPED);
91
        $this->expectNoOtherCalls();
92
93
        $this->assertFalse($service->isRunning(), 'Service should not be running');
94
    }
95
96
    /**
97
     * @covers SystemCtl\Service::isRunning
98
     * @covers SystemCtl\CommandFailedException
99
     */
100
    public function testCheckInvalidServiceThrowsException()
101
    {
102
        $service = $this->getMockedService('test-service');
103
104
        $this->expectCall(['status', 'test-service'], 6);
105
        $this->expectNoOtherCalls();
106
        $this->expectException(CommandFailedException::class);
107
108
        $service->isRunning();
109
    }
110
111
    /**
112
     * @covers SystemCtl\Service::isRunning
113
     * @covers SystemCtl\Service::start
114
     */
115
    public function testStart()
116
    {
117
        $service = $this->getMockedService('test-service');
118
119
        $this->expectCall(['--lines=0', 'status', 'test-service'], Service::STATUS_STOPPED);
120
        $this->expectCall(['start', 'test-service'], 0);
121
        $this->expectNoOtherCalls();
122
123
        $service->start();
124
125
        $this->assertTrue(true);
126
    }
127
128
    /**
129
     * @covers SystemCtl\Service::isRunning
130
     * @covers SystemCtl\Service::start
131
     */
132
    public function testStartAlreadyRunning()
133
    {
134
        $service = $this->getMockedService('test-service');
135
136
        $this->expectCall(['--lines=0', 'status', 'test-service'], 0);
137
        $this->expectNoOtherCalls();
138
139
        $service->start();
140
141
        $this->assertTrue(true);
142
    }
143
144
    /**
145
     * @covers SystemCtl\Service::isRunning
146
     * @covers SystemCtl\Service::start
147
     * @covers SystemCtl\CommandFailedException
148
     */
149
    public function testStartInvalidServiceThrowsException()
150
    {
151
        $service = $this->getMockedService('test-service');
152
153
        $this->expectCall(['--lines=0', 'status', 'test-service'], Service::STATUS_STOPPED);
154
        $this->expectCall(['start', 'test-service'], 6);
155
        $this->expectNoOtherCalls();
156
        $this->expectException(CommandFailedException::class);
157
158
        $service->start();
159
    }
160
161
    /**
162
     * @covers SystemCtl\Service::isRunning
163
     * @covers SystemCtl\Service::stop
164
     */
165
    public function testStop()
166
    {
167
        $service = $this->getMockedService('test-service');
168
169
        $this->expectCall(['--lines=0', 'status', 'test-service'], 0);
170
        $this->expectCall(['stop', 'test-service'], 0);
171
        $this->expectNoOtherCalls();
172
173
        $service->stop();
174
175
        $this->assertTrue(true);
176
    }
177
178
    /**
179
     * @covers SystemCtl\Service::isRunning
180
     * @covers SystemCtl\Service::stop
181
     */
182
    public function testStopWhenNotRunning()
183
    {
184
        $service = $this->getMockedService('test-service');
185
186
        $this->expectCall(['--lines=0', 'status', 'test-service'], Service::STATUS_STOPPED);
187
        $this->expectNoOtherCalls();
188
189
        $service->stop();
190
191
        $this->assertTrue(true);
192
    }
193
194
    /**
195
     * @covers SystemCtl\Service::isRunning
196
     * @covers SystemCtl\Service::stop
197
     * @covers SystemCtl\CommandFailedException
198
     */
199
    public function testStopInvalidServiceThrowsException()
200
    {
201
        $service = $this->getMockedService('test-service');
202
203
        $this->expectCall(['--lines=0', 'status', 'test-service'], 0);
204
        $this->expectCall(['stop', 'test-service'], 6);
205
        $this->expectNoOtherCalls();
206
        $this->expectException(CommandFailedException::class);
207
208
        $service->stop();
209
    }
210
211
    /**
212
     * @covers SystemCtl\Service::restart
213
     */
214
    public function testRestart()
215
    {
216
        $service = $this->getMockedService('test-service');
217
218
        $this->expectCall(['restart', 'test-service'], 0);
219
        $this->expectNoOtherCalls();
220
221
        $service->restart();
222
223
        $this->assertTrue(true);
224
    }
225
226
    /**
227
     * @covers SystemCtl\Service::isRunning
228
     * @covers SystemCtl\Service::restart
229
     * @covers SystemCtl\CommandFailedException
230
     */
231
    public function testRestartInvalidServiceThrowsException()
232
    {
233
        $service = $this->getMockedService('test-service');
234
235
        $this->expectCall(['restart', 'test-service'], 6);
236
        $this->expectNoOtherCalls();
237
        $this->expectException(CommandFailedException::class);
238
239
        $service->restart();
240
    }
241
242
    /**
243
     * @covers SystemCtl\Service::isRunning
244
     * @covers SystemCtl\Service::restart
245
     * @covers SystemCtl\CommandFailedException
246
     */
247
    public function testExceptionHasProcess()
248
    {
249
        $service = $this->getMockedService('test-service');
250
251
        $this->expectCall(['restart', 'test-service'], 6);
252
        $this->expectNoOtherCalls();
253
254
        try {
255
            $service->restart();
256
257
            $this->fail('Command should fail');
258
        } catch (CommandFailedException $e) {
259
            $this->assertInstanceOf(Process::class, $e->getProcess());
260
        }
261
    }
262
263
    /**
264
     * Returns a service instance with a mocked systemctl command.
265
     *
266
     * @param string $name Service name
267
     *
268
     * @return Service
269
     */
270
    private function getMockedService($name)
271
    {
272
        $this->commandFilename = tempnam(sys_get_temp_dir(), 'systemctl');
273
        $this->callCountFilename = tempnam(sys_get_temp_dir(), 'systemctl');
274
275
        file_put_contents($this->callCountFilename, '0');
276
277
        file_put_contents($this->commandFilename, '<?php'."\n");
278
        file_put_contents($this->commandFilename, sprintf(
279
            '$c = file_get_contents(\'%s\');'."\n",
280
            $this->callCountFilename
281
        ), FILE_APPEND);
282
        file_put_contents($this->commandFilename, sprintf(
283
            'file_put_contents(\'%s\', ++$c);'."\n",
284
            $this->callCountFilename
285
        ), FILE_APPEND);
286
287
        Service::setCommand('php '.$this->commandFilename);
288
        Service::sudo(false);
289
290
        return new Service($name);
291
    }
292
293
    /**
294
     * Adds an expected call to the systemctl command.
295
     *
296
     * @param string[] $arguments List of expected arguments
297
     * @param int      $exitCode  Exit code which the command should return
298
     */
299
    private function expectCall(array $arguments, $exitCode)
300
    {
301
        $conditions = [];
302
        $index = 1;
303
        foreach ($arguments as $argument) {
304
            $conditions[] = sprintf(
305
                'isset($argv[%1$d]) && $argv[%1$d] === \'%2$s\'',
306
                $index++,
307
                $argument
308
            );
309
        }
310
311
        $code = sprintf(
312
            'if (%d == $c && %s) { exit(%d); }'."\n",
313
            $this->callCount++,
314
            implode(' && ', $conditions),
315
            $exitCode
316
        );
317
318
        file_put_contents($this->commandFilename, $code, FILE_APPEND);
319
    }
320
321
    /**
322
     * Sets no more expected calls to the systemctl command.
323
     */
324
    private function expectNoOtherCalls()
325
    {
326
        $code = 'fwrite(STDERR, "Invalid call count or arguments specified: ".$c.", ".var_export($argv, true)); exit(250);'."\n";
327
328
        file_put_contents($this->commandFilename, $code, FILE_APPEND);
329
    }
330
}
331