├── .docker
├── php7.3
│ └── Dockerfile
├── php7.4
│ └── Dockerfile
├── php8.0
│ └── Dockerfile
├── php8.1
│ └── Dockerfile
└── php8.2
│ └── Dockerfile
├── .gitattributes
├── .github
└── workflows
│ └── continuous-integration.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── MIGRATION.md
├── Makefile
├── README.md
├── Vagrantfile
├── composer.json
├── docker-compose.yml
├── phpcs.xml
├── phpstan.neon
├── phpunit.xml
├── src
├── Command
│ ├── CommandDispatcherInterface.php
│ ├── CommandInterface.php
│ ├── SymfonyCommand.php
│ └── SymfonyCommandDispatcher.php
├── Exception
│ ├── CommandFailedException.php
│ ├── ExceptionInterface.php
│ ├── UnitNotFoundException.php
│ └── UnitTypeNotSupportedException.php
├── SystemCtl.php
├── Unit
│ ├── AbstractUnit.php
│ ├── Automount.php
│ ├── Device.php
│ ├── Mount.php
│ ├── Scope.php
│ ├── Service.php
│ ├── Slice.php
│ ├── Socket.php
│ ├── Swap.php
│ ├── Target.php
│ ├── Timer.php
│ └── UnitInterface.php
└── Utils
│ └── OutputFetcher.php
└── test
├── Integration
├── Command
│ └── SymfonyCommandDispatcherTest.php
├── SystemCtlTest.php
└── Unit
│ └── UnitTest.php
└── Unit
├── Command
└── SymfonyCommandTest.php
├── SystemCtlTest.php
├── Unit
├── AbstractUnitTest.php
└── UnitStub.php
└── Utils
└── OutputFetcherTest.php
/.docker/php7.3/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.3-alpine
2 |
3 | COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
4 |
5 | COPY --from=composer/composer:latest-bin /composer /usr/bin/composer
6 | ENV COMPOSER_ALLOW_SUPERUSER 1
7 |
8 | WORKDIR /docker
9 | # Workaround to keep container running
10 | CMD ["tail", "-f", "/dev/null"]
11 |
--------------------------------------------------------------------------------
/.docker/php7.4/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.4-alpine
2 |
3 | COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
4 |
5 | COPY --from=composer/composer:latest-bin /composer /usr/bin/composer
6 | ENV COMPOSER_ALLOW_SUPERUSER 1
7 |
8 | WORKDIR /docker
9 | # Workaround to keep container running
10 | CMD ["tail", "-f", "/dev/null"]
11 |
--------------------------------------------------------------------------------
/.docker/php8.0/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:8.0-alpine
2 |
3 | COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
4 |
5 | COPY --from=composer/composer:latest-bin /composer /usr/bin/composer
6 | ENV COMPOSER_ALLOW_SUPERUSER 1
7 |
8 | WORKDIR /docker
9 | # Workaround to keep container running
10 | CMD ["tail", "-f", "/dev/null"]
11 |
--------------------------------------------------------------------------------
/.docker/php8.1/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:8.1-alpine
2 |
3 | COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
4 |
5 | COPY --from=composer/composer:latest-bin /composer /usr/bin/composer
6 | ENV COMPOSER_ALLOW_SUPERUSER 1
7 |
8 | WORKDIR /docker
9 | # Workaround to keep container running
10 | CMD ["tail", "-f", "/dev/null"]
11 |
--------------------------------------------------------------------------------
/.docker/php8.2/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:8.2.0RC5-alpine
2 |
3 | COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
4 |
5 | COPY --from=composer/composer:latest-bin /composer /usr/bin/composer
6 | ENV COMPOSER_ALLOW_SUPERUSER 1
7 |
8 | WORKDIR /docker
9 | # Workaround to keep container running
10 | CMD ["tail", "-f", "/dev/null"]
11 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.github/workflows/continuous-integration.yml:
--------------------------------------------------------------------------------
1 | name: CI Pipeline
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - "[0-9]+.[0-9]+.x"
7 | pull_request:
8 |
9 | jobs:
10 |
11 | coding-standard:
12 | name: "Coding Standard"
13 | runs-on: "${{ matrix.operating-system }}"
14 | strategy:
15 | fail-fast: true
16 | matrix:
17 | operating-system: ['ubuntu-latest']
18 | php-version: ['8.1']
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v2
22 |
23 | - name: Setup PHP
24 | uses: shivammathur/setup-php@v2
25 | with:
26 | php-version: ${{ matrix.php-version }}
27 | tools: "composer:v2"
28 |
29 | - name: Install dependencies
30 | run: composer install --no-progress --prefer-dist --optimize-autoloader
31 |
32 | - name: Check codestyle
33 | run: vendor/bin/phpcs -s
34 |
35 | static-analysis:
36 | name: "Static Analysis"
37 | runs-on: "${{ matrix.operating-system }}"
38 |
39 | strategy:
40 | fail-fast: true
41 | matrix:
42 | php-version: ['8.1']
43 | operating-system: ['ubuntu-latest']
44 |
45 | steps:
46 | - name: Checkout
47 | uses: actions/checkout@v2
48 |
49 | - name: Setup PHP
50 | uses: shivammathur/setup-php@v2
51 | with:
52 | php-version: ${{ matrix.php-version }}
53 | tools: "composer:v2"
54 |
55 | - name: Install dependencies
56 | run: composer install --no-progress --prefer-dist --optimize-autoloader
57 |
58 | - name: Analyze code with static-analysis
59 | run: vendor/bin/phpstan analyse --no-progress
60 |
61 | unit-tests:
62 | name: "Unit Tests"
63 |
64 | runs-on: "${{ matrix.operating-system }}"
65 | continue-on-error: "${{ matrix.experimental }}"
66 |
67 | strategy:
68 | fail-fast: false
69 | matrix:
70 | php-version: ["8.1","8.2"]
71 | operating-system: ["ubuntu-latest"]
72 | experimental: [false]
73 |
74 | steps:
75 | - name: Checkout
76 | uses: actions/checkout@v2
77 |
78 | - name: Setup PHP
79 | uses: shivammathur/setup-php@v2
80 | with:
81 | php-version: ${{ matrix.php-version }}
82 | tools: "composer:v2"
83 |
84 | - name: Install dependencies
85 | run: composer install --no-progress --prefer-dist --optimize-autoloader --ignore-platform-req=php
86 |
87 | - name: Execute tests
88 | run: vendor/bin/phpunit --colors=always --coverage-text
89 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Composer files
2 | composer.phar
3 | vendor/
4 | composer.lock
5 |
6 | # PHPUnit
7 | build/
8 | .phpunit.result.cache
9 |
10 | # Vagrant
11 | .vagrant
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [Unreleased]
4 |
5 | ## [0.8.2] - 2021-05-27
6 | ### Added
7 | - Added PHP8 Support ([#48](https://github.com/icanhazstring/systemctl-php/pull/48)) (thanks to [@marcingy](https://github.com/marcingy))
8 |
9 | ## [0.8.1] - 2020-11-09
10 | ### Added
11 | - Added `Target` unit ([#38](https://github.com/icanhazstring/systemctl-php/pull/38)) (thanks to [@peter279k](https://github.com/peter279k))
12 | - Added `Swap` unit ([#39](https://github.com/icanhazstring/systemctl-php/pull/39)) (thanks to [@peter279k](https://github.com/peter279k))
13 | - Added `Automount` unit ([#40](https://github.com/icanhazstring/systemctl-php/pull/40)) (thanks to [@peter279k](https://github.com/peter279k))
14 | - Added `Mount` unit ([#41](https://github.com/icanhazstring/systemctl-php/pull/41)) (thanks to [@peter279k](https://github.com/peter279k))
15 |
16 | ### Changed
17 | - Replace Travis CI with GitHub Action status badge ([#44](https://github.com/icanhazstring/systemctl-php/pull/44)) (thanks to [@peter279k](https://github.com/peter279k))
18 | - Migrate `phpunit.xml` for new version ([#42](https://github.com/icanhazstring/systemctl-php/pull/42)) (thanks to [@peter279k](https://github.com/peter279k))
19 |
20 | ## [0.8.0] - 2020-11-05
21 | ### Added
22 | - Added `Slice` unit ([#36](https://github.com/icanhazstring/systemctl-php/pull/36)) (thanks to [@peter279k](https://github.com/peter279k))
23 | - Added method `SystemCtl::reset-failed()` ([#37](https://github.com/icanhazstring/systemctl-php/pull/37)) (thanks to [@icanhazstring](https://github.com/icanhazstring))
24 |
25 | ### Changed
26 | - Dropped support for php7.2
27 | - Dropped support for `symfony/process:^4.4`
28 |
29 | ## [0.7.1] - 2020-05-27
30 | ### Added
31 | - Added `Scope` unit ([#32](https://github.com/icanhazstring/systemctl-php/pull/32)) (thanks to [@peter279k](https://github.com/peter279k))
32 |
33 | ## [0.7.0] - 2020-02-16
34 | ### Changed
35 | - Moved classes to different namespace (`SystemCtl` to `icanhazstring\Systemctl`)
36 | - Dropped support for PHP7.1
37 | - Dropped support for `symfony/process:^3.x`
38 |
39 | ### Added
40 | - Added `CHANGELOG.md` and `MIGRATION.md`
41 | - Added support for `symfony/process:^4.4 || ^5.0`
42 | - Added code quality tools
43 |
44 | ## Previous releases
45 | - No changelog available
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-2020 Andreas Frömer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MIGRATION.md:
--------------------------------------------------------------------------------
1 | # Migration
2 |
3 | ## v0.6.x to 0.7.0
4 | The namespace `SystemCtl\` was moved to `icanhazstring\SystemCtl`.
5 | You can do a search/replace to move you implementation.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CONTAINER=systemctl-php-7.4
2 |
3 | up:
4 | docker-compose up -d
5 |
6 | down:
7 | docker-compose down
8 |
9 | install:
10 | docker exec -it $(CONTAINER) composer install
11 |
12 | update:
13 | docker exec -it $(CONTAINER) composer update
14 |
15 | require:
16 | docker exec -it $(CONTAINER) composer require $(filter-out $@, $(MAKECMDGOALS))
17 |
18 | remove:
19 | docker exec -it $(CONTAINER) composer remove $(filter-out $@, $(MAKECMDGOALS))
20 |
21 | check: csfix cs phpunit analyse
22 |
23 | phpunit:
24 | docker exec -it $(CONTAINER) vendor/bin/phpunit
25 |
26 | analyse:
27 | docker exec -it $(CONTAINER) vendor/bin/phpstan analyse
28 |
29 | cs:
30 | docker exec -it $(CONTAINER) vendor/bin/phpcs
31 |
32 | csfix:
33 | docker exec -it $(CONTAINER) vendor/bin/phpcbf
34 |
35 | %:
36 | @true
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # systemctl-php
2 |  [](https://codeclimate.com/github/icanhazstring/systemctl-php) [](https://codeclimate.com/github/icanhazstring/systemctl-php/coverage) [](https://gitter.im/icanhazstring/systemctl-php?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
3 |
4 | PHP wrapper for systemctl
5 |
6 | # How to install
7 | ```php
8 | $ composer require icanhazstring/systemctl-php
9 | ```
10 |
11 | ## Current supported units
12 | See [Units](src/Unit)
13 |
14 | > If you like to add support for more units, feel free to grab an issue and contribute.
15 |
16 | ## Current supported commands
17 | - start
18 | - stop
19 | - enable
20 | - disable
21 | - reload
22 | - restart
23 | - isEnabled
24 | - isActive
25 |
26 | > If you like to add support for more commands, feel free to contribute.
27 |
28 | ## How to change the binary
29 |
30 | ```php
31 | SystemCtl::setBinary('/bin/systemctl');
32 | ```
33 |
34 | ## How to change command timeout
35 | To change command tmeout simply call the static method `setTimeout`.
36 | ```php
37 | SystemCtl::setTimeout(10);
38 | ```
39 |
40 | > The default timeout is set to `3` seconds
41 |
42 | ## "I need sudo to run commands"
43 | If you need sudo, you should execute the bin executable with sudo.
44 | The incode support was dropped due to security reason.
45 |
46 | ## How do I start/stop/restart a unit?
47 | Simply is that. First we instantiate a `SystemCtl` instance an load a unit from a specific type. Here we use a `Service`. You will always get back `true` if the command succeeded. Otherwise the method will throw a `CommandFailedException`.
48 |
49 | ```php
50 | $systemCtl = new SystemCtl();
51 |
52 | // start/stop/enable/disable/reload/restart
53 | $systemCtl->getService('nginx')->start();
54 | $systemCtl->getService('nginx')->stop();
55 | ```
56 |
57 | # How to Contribute
58 | Clone the repo and install using `composer`
59 |
60 | ```bash
61 | $ composer install
62 | ```
63 |
64 | Make your changes and make sure you run *test*, *codesniffer* and *phpstan*.
65 |
66 | ```bash
67 | $ composer test
68 | > vendor/bin/phpunit
69 | PHPUnit 9.5.3 by Sebastian Bergmann and contributors.
70 |
71 | ............................................................... 63 / 128 ( 49%)
72 | ............................................................... 126 / 128 ( 98%)
73 | .. 128 / 128 (100%)
74 |
75 | Time: 00:00.033, Memory: 10.00 MB
76 |
77 | OK (128 tests, 192 assertions)
78 |
79 | $ composer cs
80 | > vendor/bin/phpcs --standard=PSR2 src/ && vendor/bin/phpcs --standard=PSR2 tests/
81 |
82 | $
83 |
84 | $ composer analyse
85 | > vendor/bin/phpstan analyse --no-progress
86 | Note: Using configuration file /data/systemctl-php/phpstan.neon.
87 |
88 |
89 | [OK] No errors
90 |
91 | $
92 | ```
93 |
94 | # Credits
95 | This library is heavily influenced by [@mjanser](https://github.com/mjanser) [php-systemctl](https://github.com/mjanser/php-systemctl).
96 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | Vagrant.configure("2") do |config|
2 | config.vm.box = "puphpet/ubuntu1604-x64"
3 |
4 | config.vm.provision "shell", inline: <<-SHELL
5 | add-apt-repository ppa:ondrej/php
6 | apt-get update
7 | apt-get install -y php7.2 php7.2-xml php7.2-mbstring php7.2-zip php7.2-curl php7.2-xdebug composer
8 |
9 | # Switch to /vagrant and install packages
10 | cd /vagrant && composer install
11 | SHELL
12 | end
13 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "icanhazstring/systemctl-php",
3 | "description": "PHP wrapper for systemctl",
4 | "type": "library",
5 | "require": {
6 | "php": "^7.3|^8.0",
7 | "symfony/process": "^5.0|^6.0"
8 | },
9 | "require-dev": {
10 | "phpunit/phpunit": "^9.6.9",
11 | "squizlabs/php_codesniffer": "^3.7.2",
12 | "slevomat/coding-standard": "^6.4.1",
13 | "phpstan/phpstan": "^1.10.21",
14 | "phpspec/prophecy-phpunit": "^2.0.2"
15 | },
16 | "autoload": {
17 | "psr-4": {
18 | "icanhazstring\\SystemCtl\\": "src/"
19 | }
20 | },
21 | "autoload-dev": {
22 | "psr-4": {
23 | "icanhazstring\\SystemCtl\\Test\\": "test/"
24 | }
25 | },
26 | "license": "MIT",
27 | "authors": [
28 | {
29 | "name": "icanhazstring",
30 | "email": "blubb0r05+github@gmail.com"
31 | }
32 | ],
33 | "extra": {
34 | "branch-alias": {
35 | "dev-master": "0.8.x-dev"
36 | }
37 | },
38 | "scripts": {
39 | "analyse": "vendor/bin/phpstan analyse --no-progress",
40 | "test": "vendor/bin/phpunit",
41 | "cs": "vendor/bin/phpcs"
42 | },
43 | "config": {
44 | "allow-plugins": {
45 | "dealerdirect/phpcodesniffer-composer-installer": true
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.7"
2 |
3 | services:
4 | php7.3:
5 | build: .docker/php7.3
6 | container_name: systemctl-php-7.3
7 | volumes:
8 | - .:/docker:rw
9 | tty: true
10 |
11 | php7.4:
12 | build: .docker/php7.4
13 | container_name: systemctl-php-7.4
14 | volumes:
15 | - .:/docker:rw
16 | tty: true
17 |
18 | php8.0:
19 | build: .docker/php8.0
20 | container_name: systemctl-php-8.0
21 | volumes:
22 | - .:/docker:rw
23 | tty: true
24 |
25 | php8.1:
26 | build: .docker/php8.1
27 | container_name: systemctl-php-8.1
28 | volumes:
29 | - .:/docker:rw
30 | tty: true
31 |
32 | php8.2:
33 | build: .docker/php8.2
34 | container_name: systemctl-php-8.2
35 | volumes:
36 | - .:/docker:rw
37 | tty: true
38 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | src
21 | test
22 |
23 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | inferPrivatePropertyTypeFromConstructor: true
3 |
4 | level: max
5 | paths:
6 | - src/
7 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./src
6 |
7 |
8 |
9 |
10 | ./test/Unit
11 |
12 |
13 | ./test/Integration
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/Command/CommandDispatcherInterface.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | interface CommandInterface
14 | {
15 | /**
16 | * @return CommandInterface
17 | * @throws CommandFailedException
18 | */
19 | public function run(): CommandInterface;
20 |
21 | /**
22 | * @return string
23 | */
24 | public function getOutput(): string;
25 |
26 | /**
27 | * @return bool
28 | */
29 | public function isSuccessful(): bool;
30 | }
31 |
--------------------------------------------------------------------------------
/src/Command/SymfonyCommand.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class SymfonyCommand implements CommandInterface
15 | {
16 | /** @var Process */
17 | private $process;
18 |
19 | /**
20 | * @param Process $process
21 | */
22 | public function __construct(Process $process)
23 | {
24 | $this->process = $process;
25 | }
26 |
27 | /**
28 | * @inheritdoc
29 | */
30 | public function getOutput(): string
31 | {
32 | return $this->process->getOutput();
33 | }
34 |
35 | /**
36 | * @inheritdoc
37 | */
38 | public function isSuccessful(): bool
39 | {
40 | return $this->process->isSuccessful();
41 | }
42 |
43 | /**
44 | * @inheritdoc
45 | */
46 | public function run(): CommandInterface
47 | {
48 | $this->process->run();
49 |
50 | if (!$this->process->isSuccessful()) {
51 | throw new CommandFailedException($this->process->getErrorOutput());
52 | }
53 |
54 | return $this;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Command/SymfonyCommandDispatcher.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | class SymfonyCommandDispatcher implements CommandDispatcherInterface
14 | {
15 | /** @var string */
16 | private $binary;
17 | /** @var int */
18 | private $timeout;
19 |
20 | /**
21 | * @inheritdoc
22 | */
23 | public function setBinary(string $binary): CommandDispatcherInterface
24 | {
25 | $this->binary = $binary;
26 |
27 | return $this;
28 | }
29 |
30 | /**
31 | * @inheritdoc
32 | */
33 | public function setTimeout(int $timeout): CommandDispatcherInterface
34 | {
35 | $this->timeout = $timeout;
36 |
37 | return $this;
38 | }
39 |
40 | public function dispatch(string ...$commands): CommandInterface
41 | {
42 | $process = new Process(array_merge([$this->binary], $commands));
43 | $process->setTimeout($this->timeout);
44 |
45 | $process = new SymfonyCommand($process);
46 |
47 | return $process->run();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Exception/CommandFailedException.php:
--------------------------------------------------------------------------------
1 | setTimeout(self::$timeout)
94 | ->setBinary(self::$binary);
95 |
96 | // @phpstan-ignore-next-line
97 | return new $unitClass($unitName, $commandDispatcher);
98 | }
99 |
100 | /**
101 | * List all supported units
102 | *
103 | * @param null|string $unitPrefix
104 | * @param string[] $unitTypes
105 | *
106 | * @return array|string[]
107 | * @throws Exception\CommandFailedException
108 | */
109 | public function listUnits(?string $unitPrefix = null, array $unitTypes = self::SUPPORTED_UNITS): array
110 | {
111 | $commands = ['list-units'];
112 |
113 | if ($unitPrefix) {
114 | $commands[] = $unitPrefix . '*';
115 | }
116 |
117 | $output = $this->getCommandDispatcher()->dispatch(...$commands)->getOutput();
118 |
119 | return array_reduce($unitTypes, function ($carry, $unitSuffix) use ($output) {
120 | $result = Utils\OutputFetcher::fetchUnitNames($unitSuffix, $output);
121 |
122 | return array_merge($carry, $result);
123 | }, []);
124 | }
125 |
126 | /**
127 | * @param string $name
128 | *
129 | * @return Service
130 | * @throws Exception\CommandFailedException
131 | */
132 | public function getService(string $name): Service
133 | {
134 | $units = $this->listUnits($name, [Service::UNIT]);
135 |
136 | $unitName = $this->searchForUnitInUnits($name, $units);
137 |
138 | if ($unitName === null) {
139 | throw UnitNotFoundException::create(Service::UNIT, $name);
140 | }
141 |
142 | return new Service($unitName, $this->getCommandDispatcher());
143 | }
144 |
145 | /**
146 | * @param string $unitName
147 | * @param array|string[] $units
148 | *
149 | * @return null|string
150 | */
151 | protected function searchForUnitInUnits(string $unitName, array $units): ?string
152 | {
153 | foreach ($units as $unit) {
154 | if ($unit === $unitName) {
155 | return $unit;
156 | }
157 | }
158 |
159 | return null;
160 | }
161 |
162 | /**
163 | * @param null|string $unitPrefix
164 | *
165 | * @return Service[]
166 | */
167 | public function getServices(?string $unitPrefix = null): array
168 | {
169 | $units = $this->listUnits($unitPrefix, [Service::UNIT]);
170 |
171 | return array_map(function ($unitName) {
172 | return new Service($unitName, $this->getCommandDispatcher());
173 | }, $units);
174 | }
175 |
176 | /**
177 | * @param string $name
178 | *
179 | * @return Timer
180 | */
181 | public function getTimer(string $name): Timer
182 | {
183 | $units = $this->listUnits($name, [Timer::UNIT]);
184 |
185 | $unitName = $this->searchForUnitInUnits($name, $units);
186 |
187 | if (is_null($unitName)) {
188 | throw UnitNotFoundException::create(Timer::UNIT, $name);
189 | }
190 |
191 | return new Timer($unitName, $this->getCommandDispatcher());
192 | }
193 |
194 | /**
195 | * @param null|string $unitPrefix
196 | *
197 | * @return Timer[]
198 | */
199 | public function getTimers(?string $unitPrefix = null): array
200 | {
201 | $units = $this->listUnits($unitPrefix, [Timer::UNIT]);
202 |
203 | return array_map(function ($unitName) {
204 | return new Timer($unitName, $this->getCommandDispatcher());
205 | }, $units);
206 | }
207 |
208 | /**
209 | * @param string $name
210 | *
211 | * @return Socket
212 | */
213 | public function getSocket(string $name): Socket
214 | {
215 | $units = $this->listUnits($name, [Socket::UNIT]);
216 |
217 | $unitName = $this->searchForUnitInUnits($name, $units);
218 |
219 | if (is_null($unitName)) {
220 | throw UnitNotFoundException::create(Socket::UNIT, $name);
221 | }
222 |
223 | return new Socket($unitName, $this->getCommandDispatcher());
224 | }
225 |
226 | /**
227 | * @param null|string $unitPrefix
228 | *
229 | * @return Socket[]
230 | */
231 | public function getSockets(?string $unitPrefix = null): array
232 | {
233 | $units = $this->listUnits($unitPrefix, [Socket::UNIT]);
234 |
235 | return array_map(function ($unitName) {
236 | return new Socket($unitName, $this->getCommandDispatcher());
237 | }, $units);
238 | }
239 |
240 | /**
241 | * @param string $name
242 | *
243 | * @return Scope
244 | */
245 | public function getScope(string $name): Scope
246 | {
247 | $units = $this->listUnits($name, [Scope::UNIT]);
248 |
249 | $unitName = $this->searchForUnitInUnits($name, $units);
250 |
251 | if (is_null($unitName)) {
252 | throw UnitNotFoundException::create(Scope::UNIT, $name);
253 | }
254 |
255 | return new Scope($unitName, $this->getCommandDispatcher());
256 | }
257 |
258 | /**
259 | * @param null|string $unitPrefix
260 | *
261 | * @return Scope[]
262 | */
263 | public function getScopes(?string $unitPrefix = null): array
264 | {
265 | $units = $this->listUnits($unitPrefix, [Scope::UNIT]);
266 |
267 | return array_map(function ($unitName) {
268 | return new Scope($unitName, $this->getCommandDispatcher());
269 | }, $units);
270 | }
271 |
272 | /**
273 | * @param string $name
274 | *
275 | * @return Slice
276 | */
277 | public function getSlice(string $name): Slice
278 | {
279 | $units = $this->listUnits($name, [Slice::UNIT]);
280 |
281 | $unitName = $this->searchForUnitInUnits($name, $units);
282 |
283 | if (is_null($unitName)) {
284 | throw UnitNotFoundException::create(Slice::UNIT, $name);
285 | }
286 |
287 | return new Slice($unitName, $this->getCommandDispatcher());
288 | }
289 |
290 | /**
291 | * @param null|string $unitPrefix
292 | *
293 | * @return Slice[]
294 | */
295 | public function getSlices(?string $unitPrefix = null): array
296 | {
297 | $units = $this->listUnits($unitPrefix, [Slice::UNIT]);
298 |
299 | return array_map(function ($unitName) {
300 | return new Slice($unitName, $this->getCommandDispatcher());
301 | }, $units);
302 | }
303 |
304 | /**
305 | * @param string $name
306 | *
307 | * @return Target
308 | */
309 | public function getTarget(string $name): Target
310 | {
311 | $units = $this->listUnits($name, [Target::UNIT]);
312 |
313 | $unitName = $this->searchForUnitInUnits($name, $units);
314 |
315 | if (is_null($unitName)) {
316 | throw UnitNotFoundException::create(Target::UNIT, $name);
317 | }
318 |
319 | return new Target($unitName, $this->getCommandDispatcher());
320 | }
321 |
322 | /**
323 | * @param null|string $unitPrefix
324 | *
325 | * @return Target[]
326 | */
327 | public function getTargets(?string $unitPrefix = null): array
328 | {
329 | $units = $this->listUnits($unitPrefix, [Target::UNIT]);
330 |
331 | return array_map(function ($unitName) {
332 | return new Target($unitName, $this->getCommandDispatcher());
333 | }, $units);
334 | }
335 |
336 | /**
337 | * @param string $name
338 | *
339 | * @return Swap
340 | */
341 | public function getSwap(string $name): Swap
342 | {
343 | $units = $this->listUnits($name, [Swap::UNIT]);
344 |
345 | $unitName = $this->searchForUnitInUnits($name, $units);
346 |
347 | if (is_null($unitName)) {
348 | throw UnitNotFoundException::create(Swap::UNIT, $name);
349 | }
350 |
351 | return new Swap($unitName, $this->getCommandDispatcher());
352 | }
353 |
354 | /**
355 | * @param null|string $unitPrefix
356 | *
357 | * @return Swap[]
358 | */
359 | public function getSwaps(?string $unitPrefix = null): array
360 | {
361 | $units = $this->listUnits($unitPrefix, [Swap::UNIT]);
362 |
363 | return array_map(function ($unitName) {
364 | return new Swap($unitName, $this->getCommandDispatcher());
365 | }, $units);
366 | }
367 |
368 | /**
369 | * Restart the daemon to reload specs and new units
370 | *
371 | * @return bool
372 | * @throws Exception\CommandFailedException
373 | */
374 | public function daemonReload(): bool
375 | {
376 | return $this->getCommandDispatcher()->dispatch('daemon-reload')->isSuccessful();
377 | }
378 |
379 | /**
380 | * Reset failed state of all unit so they won't be listed using listUnits
381 | *
382 | * @return bool
383 | * @throws Exception\CommandFailedException
384 | */
385 | public function resetFailed(): bool
386 | {
387 | return $this->getCommandDispatcher()->dispatch('reset-failed')->isSuccessful();
388 | }
389 |
390 | /**
391 | * @return CommandDispatcherInterface
392 | */
393 | public function getCommandDispatcher(): CommandDispatcherInterface
394 | {
395 | if ($this->commandDispatcher === null) {
396 | $this->commandDispatcher = (new SymfonyCommandDispatcher())
397 | ->setTimeout(self::$timeout)
398 | ->setBinary(self::$binary);
399 | }
400 |
401 | return $this->commandDispatcher;
402 | }
403 |
404 | /**
405 | * @param CommandDispatcherInterface $dispatcher
406 | *
407 | * @return SystemCtl
408 | */
409 | public function setCommandDispatcher(CommandDispatcherInterface $dispatcher)
410 | {
411 | $this->commandDispatcher = $dispatcher
412 | ->setTimeout(self::$timeout)
413 | ->setBinary(self::$binary);
414 |
415 | return $this;
416 | }
417 |
418 | /**
419 | * @param string $name
420 | *
421 | * @return Device
422 | */
423 | public function getDevice(string $name): Device
424 | {
425 | $units = $this->listUnits($name, [Device::UNIT]);
426 |
427 | $unitName = $this->searchForUnitInUnits($name, $units);
428 |
429 | if (is_null($unitName)) {
430 | throw UnitNotFoundException::create(Device::UNIT, $name);
431 | }
432 |
433 | return new Device($unitName, $this->getCommandDispatcher());
434 | }
435 |
436 | /**
437 | * @param string|null $unitPrefix
438 | *
439 | * @return Device[]
440 | */
441 | public function getDevices(?string $unitPrefix = null): array
442 | {
443 | $units = $this->listUnits($unitPrefix, [Device::UNIT]);
444 |
445 | return array_map(function ($unitName) {
446 | return new Device($unitName, $this->getCommandDispatcher());
447 | }, $units);
448 | }
449 |
450 | /**
451 | * @param string $name
452 | *
453 | * @return Automount
454 | */
455 | public function getAutomount(string $name): Automount
456 | {
457 | $units = $this->listUnits($name, [Automount::UNIT]);
458 |
459 | $unitName = $this->searchForUnitInUnits($name, $units);
460 |
461 | if (is_null($unitName)) {
462 | throw UnitNotFoundException::create(Automount::UNIT, $name);
463 | }
464 |
465 | return new Automount($unitName, $this->getCommandDispatcher());
466 | }
467 |
468 | /**
469 | * @param null|string $unitPrefix
470 | *
471 | * @return Automount[]
472 | */
473 | public function getAutomounts(?string $unitPrefix = null): array
474 | {
475 | $units = $this->listUnits($unitPrefix, [Automount::UNIT]);
476 |
477 | return array_map(function ($unitName) {
478 | return new Automount($unitName, $this->getCommandDispatcher());
479 | }, $units);
480 | }
481 |
482 |
483 | /**
484 | * @param string $name
485 | *
486 | * @return Mount
487 | */
488 | public function getMount(string $name): Mount
489 | {
490 | $units = $this->listUnits($name, [Mount::UNIT]);
491 |
492 | $unitName = $this->searchForUnitInUnits($name, $units);
493 |
494 | if (is_null($unitName)) {
495 | throw UnitNotFoundException::create(Mount::UNIT, $name);
496 | }
497 |
498 | return new Mount($unitName, $this->getCommandDispatcher());
499 | }
500 |
501 | /**
502 | * @param null|string $unitPrefix
503 | *
504 | * @return Mount[]
505 | */
506 | public function getMounts(?string $unitPrefix = null): array
507 | {
508 | $units = $this->listUnits($unitPrefix, [Mount::UNIT]);
509 |
510 | return array_map(function ($unitName) {
511 | return new Mount($unitName, $this->getCommandDispatcher());
512 | }, $units);
513 | }
514 | }
515 |
--------------------------------------------------------------------------------
/src/Unit/AbstractUnit.php:
--------------------------------------------------------------------------------
1 | name = $name;
36 | $this->commandDispatcher = $commandDispatcher;
37 | }
38 |
39 | /**
40 | * @inheritdoc
41 | */
42 | public function getName(): string
43 | {
44 | return $this->name;
45 | }
46 |
47 | /**
48 | * @inheritDoc
49 | */
50 | public function isMultiInstance(): bool
51 | {
52 | return strpos($this->name, '@') !== false;
53 | }
54 |
55 | /**
56 | * @inheritDoc
57 | * @todo: Everything in here should happen inside the constructor and stored
58 | * afterwards.
59 | */
60 | public function getInstanceName(): ?string
61 | {
62 | $instanceName = explode('@', $this->name, 2)[1] ?? null;
63 |
64 | if (is_string($instanceName) && strpos($instanceName, '.') !== false) {
65 | $instanceName = explode('.', $instanceName, 2)[0];
66 | }
67 |
68 | return $instanceName;
69 | }
70 |
71 | /**
72 | * @return string
73 | */
74 | abstract protected function getUnitSuffix(): string;
75 |
76 | /**
77 | * @return CommandInterface
78 | * @throws CommandFailedException
79 | */
80 | public function execute(string ...$commands): CommandInterface
81 | {
82 | $commands[] = implode(
83 | '.',
84 | [
85 | $this->name,
86 | $this->getUnitSuffix(),
87 | ]
88 | );
89 |
90 | return $this->commandDispatcher->dispatch(...$commands);
91 | }
92 |
93 | /**
94 | * @return bool
95 | */
96 | public function start(): bool
97 | {
98 | return $this->execute(__FUNCTION__)->isSuccessful();
99 | }
100 |
101 | /**
102 | * @return bool
103 | */
104 | public function stop(): bool
105 | {
106 | return $this->execute(__FUNCTION__)->isSuccessful();
107 | }
108 |
109 | /**
110 | * @return bool
111 | */
112 | public function disable(): bool
113 | {
114 | return $this->execute(__FUNCTION__)->isSuccessful();
115 | }
116 |
117 | /**
118 | * @return bool
119 | */
120 | public function reload(): bool
121 | {
122 | return $this->execute(__FUNCTION__)->isSuccessful();
123 | }
124 |
125 | /**
126 | * @return bool
127 | */
128 | public function restart(): bool
129 | {
130 | return $this->execute(__FUNCTION__)->isSuccessful();
131 | }
132 |
133 | /**
134 | * @return bool
135 | */
136 | public function enable(): bool
137 | {
138 | return $this->execute(__FUNCTION__)->isSuccessful();
139 | }
140 |
141 | /**
142 | * @return bool
143 | */
144 | public function isEnabled(): bool
145 | {
146 | $output = $this->execute('is-enabled')->getOutput();
147 |
148 | return trim($output) === 'enabled';
149 | }
150 |
151 | /**
152 | * @return bool
153 | */
154 | public function isActive(): bool
155 | {
156 | $output = $this->execute('is-active')->getOutput();
157 |
158 | return trim($output) === 'active';
159 | }
160 |
161 | /**
162 | * @return bool
163 | */
164 | public function isRunning(): bool
165 | {
166 | return $this->isActive();
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/Unit/Automount.php:
--------------------------------------------------------------------------------
1 | .*)\.' . $suffix . '\s.*$/m', $output, $matches);
23 | return $matches['unit'] ?? [];
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/test/Integration/Command/SymfonyCommandDispatcherTest.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class SymfonyCommandDispatcherTest extends TestCase
15 | {
16 | /**
17 | * @test
18 | */
19 | public function itShouldDispatchACorrectCommand(): void
20 | {
21 | $dispatcher = new SymfonyCommandDispatcher();
22 | $dispatcher->setBinary('echo');
23 |
24 | $output = $dispatcher->dispatch('a')->getOutput();
25 | $this->assertSame('a', trim($output));
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test/Integration/SystemCtlTest.php:
--------------------------------------------------------------------------------
1 | prophesize(CommandDispatcherInterface::class);
31 | $commandDispatcher->setTimeout(Argument::any())->willReturn($commandDispatcher);
32 | $commandDispatcher->setBinary(Argument::any())->willReturn($commandDispatcher);
33 |
34 | return $commandDispatcher;
35 | }
36 |
37 | public function testListUnitsWithAvailableUnits(): void
38 | {
39 | $output = <<prophesize(CommandInterface::class);
55 | $command->getOutput()->willReturn($output);
56 |
57 | $dispatcherStub = $this->createCommandDispatcherStub();
58 | $dispatcherStub->dispatch(Argument::cetera())->willReturn($command);
59 |
60 | $systemctl = new SystemCtl();
61 | $systemctl->setCommandDispatcher($dispatcherStub->reveal());
62 |
63 | $units = $systemctl->listUnits(null, SystemCtl::AVAILABLE_UNITS);
64 | self::assertCount(12, $units);
65 | }
66 |
67 | public function testListUnitsWithSupportedUnits(): void
68 | {
69 | $output = <<prophesize(CommandInterface::class);
84 | $command->getOutput()->willReturn($output);
85 |
86 | $dispatcherStub = $this->createCommandDispatcherStub();
87 | $dispatcherStub->dispatch(Argument::cetera())->willReturn($command);
88 |
89 | $systemctl = new SystemCtl();
90 | $systemctl->setCommandDispatcher($dispatcherStub->reveal());
91 |
92 | $units = $systemctl->listUnits();
93 | self::assertCount(5, $units);
94 | }
95 |
96 | public function testCreateUnitFromSupportedSuffixShouldWord(): void
97 | {
98 | $unit = SystemCtl::unitFromSuffix('service', 'SuccessService');
99 | self::assertInstanceOf(UnitInterface::class, $unit);
100 | self::assertInstanceOf(Service::class, $unit);
101 | self::assertSame('SuccessService', $unit->getName());
102 | }
103 |
104 | public function testCreateUnitFromUnsupportedSuffixShouldRaiseException(): void
105 | {
106 | $this->expectException(UnitTypeNotSupportedException::class);
107 | SystemCtl::unitFromSuffix('unsupported', 'FailUnit');
108 | }
109 |
110 | public function testGetServices(): void
111 | {
112 | $output = <<prophesize(CommandInterface::class);
122 | $command->getOutput()->willReturn($output);
123 |
124 | $dispatcherStub = $this->createCommandDispatcherStub();
125 | $dispatcherStub->dispatch(Argument::cetera())->willReturn($command);
126 |
127 | $systemctl = new SystemCtl();
128 | $systemctl->setCommandDispatcher($dispatcherStub->reveal());
129 |
130 | $services = $systemctl->getServices();
131 |
132 | self::assertCount(2, $services);
133 | }
134 |
135 | public function testGetTimers(): void
136 | {
137 | $output = <<prophesize(CommandInterface::class);
147 | $command->getOutput()->willReturn($output);
148 |
149 | $dispatcherStub = $this->createCommandDispatcherStub();
150 | $dispatcherStub->dispatch(Argument::cetera())->willReturn($command);
151 |
152 | $systemctl = new SystemCtl();
153 | $systemctl->setCommandDispatcher($dispatcherStub->reveal());
154 | $timers = $systemctl->getTimers();
155 |
156 | self::assertCount(2, $timers);
157 | }
158 |
159 | /**
160 | * @test
161 | */
162 | public function itShouldReturnTrueOnSuccessfulDaemonReload(): void
163 | {
164 | $command = $this->prophesize(CommandInterface::class);
165 | $command->isSuccessful()->willReturn(true);
166 |
167 | $dispatcher = $this->createCommandDispatcherStub();
168 | $dispatcher->dispatch(Argument::exact('daemon-reload'))->willReturn($command);
169 |
170 | $systemCtl = new SystemCtl();
171 | $systemCtl->setCommandDispatcher($dispatcher->reveal());
172 |
173 | self::assertTrue($systemCtl->daemonReload());
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/test/Integration/Unit/UnitTest.php:
--------------------------------------------------------------------------------
1 | prophesize(CommandInterface::class);
35 | $command->isSuccessful()->willReturn(true);
36 |
37 | $commandDispatcher = $this->createCommandDispatcherStub();
38 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
39 |
40 | $service = new Service('AwesomeService', $commandDispatcher->reveal());
41 |
42 | self::assertTrue($service->start());
43 | self::assertTrue($service->stop());
44 | self::assertTrue($service->enable());
45 | self::assertTrue($service->disable());
46 | self::assertTrue($service->reload());
47 | self::assertTrue($service->restart());
48 | }
49 |
50 | public function createCommandDispatcherStub(): ObjectProphecy
51 | {
52 | $commandDispatcher = $this->prophesize(CommandDispatcherInterface::class);
53 | $commandDispatcher->setTimeout(Argument::any())->willReturn($commandDispatcher);
54 | $commandDispatcher->setBinary(Argument::any())->willReturn($commandDispatcher);
55 |
56 | return $commandDispatcher;
57 | }
58 |
59 | public function testServiceCommandsIfProcessIsUnsuccessFulShouldRaiseException(): void
60 | {
61 | $commandDispatcher = $this->createCommandDispatcherStub();
62 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
63 |
64 | $service = new Service('AwesomeService', $commandDispatcher->reveal());
65 |
66 | $this->expectException(CommandFailedException::class);
67 | $service->start();
68 | }
69 |
70 | public function testTimerCommandsIfProcessIsSuccessfulShouldReturnTrue(): void
71 | {
72 | $command = $this->prophesize(CommandInterface::class);
73 | $command->isSuccessful()->willReturn(true);
74 |
75 | $commandDispatcher = $this->createCommandDispatcherStub();
76 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
77 |
78 | $timer = new Timer('AwesomeService', $commandDispatcher->reveal());
79 |
80 | self::assertTrue($timer->start());
81 | self::assertTrue($timer->stop());
82 | self::assertTrue($timer->enable());
83 | self::assertTrue($timer->disable());
84 | self::assertTrue($timer->reload());
85 | self::assertTrue($timer->restart());
86 | }
87 |
88 | public function testTimerCommandsIfProcessIsUnsuccessFulShouldRaiseException(): void
89 | {
90 | $commandDispatcher = $this->createCommandDispatcherStub();
91 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
92 |
93 | $timer = new Timer('AwesomeTimer', $commandDispatcher->reveal());
94 |
95 | $this->expectException(CommandFailedException::class);
96 | $timer->start();
97 | }
98 |
99 | public function testSocketCommandsIfProcessIsSuccessfulShouldReturnTrue(): void
100 | {
101 | $command = $this->prophesize(CommandInterface::class);
102 | $command->isSuccessful()->willReturn(true);
103 |
104 | $commandDispatcher = $this->createCommandDispatcherStub();
105 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
106 |
107 | $socket = new Socket('AwesomeSocket', $commandDispatcher->reveal());
108 |
109 | self::assertTrue($socket->start());
110 | self::assertTrue($socket->stop());
111 | self::assertTrue($socket->enable());
112 | self::assertTrue($socket->disable());
113 | self::assertTrue($socket->reload());
114 | self::assertTrue($socket->restart());
115 | }
116 |
117 | public function testSocketCommandsIfProcessIsUnsuccessFulShouldRaiseException(): void
118 | {
119 | $commandDispatcher = $this->createCommandDispatcherStub();
120 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
121 |
122 | $socket = new Socket('AwesomeSocket', $commandDispatcher->reveal());
123 |
124 | $this->expectException(CommandFailedException::class);
125 | $socket->start();
126 | }
127 |
128 | public function testScopeCommandsIfProcessIsSuccessfulShouldReturnTrue(): void
129 | {
130 | $command = $this->prophesize(CommandInterface::class);
131 | $command->isSuccessful()->willReturn(true);
132 |
133 | $commandDispatcher = $this->createCommandDispatcherStub();
134 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
135 |
136 | $scope = new Scope('AwesomeScope', $commandDispatcher->reveal());
137 |
138 | self::assertTrue($scope->start());
139 | self::assertTrue($scope->stop());
140 | self::assertTrue($scope->enable());
141 | self::assertTrue($scope->disable());
142 | self::assertTrue($scope->reload());
143 | self::assertTrue($scope->restart());
144 | }
145 |
146 | public function testScopeCommandsIfProcessIsUnsuccessFulShouldRaiseException(): void
147 | {
148 | $commandDispatcher = $this->createCommandDispatcherStub();
149 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
150 |
151 | $scope = new Scope('AwesomeScope', $commandDispatcher->reveal());
152 |
153 | $this->expectException(CommandFailedException::class);
154 | $scope->start();
155 | }
156 |
157 | public function testSliceCommandsIfProcessIsSuccessfulShouldReturnTrue()
158 | {
159 | $command = $this->prophesize(CommandInterface::class);
160 | $command->isSuccessful()->willReturn(true);
161 |
162 | $commandDispatcher = $this->createCommandDispatcherStub();
163 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
164 |
165 | $slice = new Slice('AwesomeSlice', $commandDispatcher->reveal());
166 |
167 | $this->assertTrue($slice->start());
168 | $this->assertTrue($slice->stop());
169 | $this->assertTrue($slice->enable());
170 | $this->assertTrue($slice->disable());
171 | $this->assertTrue($slice->reload());
172 | $this->assertTrue($slice->restart());
173 | }
174 |
175 | public function testSliceCommandsIfProcessIsUnsuccessFulShouldRaiseException()
176 | {
177 | $commandDispatcher = $this->createCommandDispatcherStub();
178 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
179 |
180 | $slice = new Slice('AwesomeSlice', $commandDispatcher->reveal());
181 |
182 | $this->expectException(CommandFailedException::class);
183 | $slice->start();
184 | }
185 |
186 | public function testTargetCommandsIfProcessIsSuccessfulShouldReturnTrue()
187 | {
188 | $command = $this->prophesize(CommandInterface::class);
189 | $command->isSuccessful()->willReturn(true);
190 |
191 | $commandDispatcher = $this->createCommandDispatcherStub();
192 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
193 |
194 | $target = new Target('AwesomeTarget', $commandDispatcher->reveal());
195 |
196 | $this->assertTrue($target->start());
197 | $this->assertTrue($target->stop());
198 | $this->assertTrue($target->enable());
199 | $this->assertTrue($target->disable());
200 | $this->assertTrue($target->reload());
201 | $this->assertTrue($target->restart());
202 | }
203 |
204 | public function testTargetCommandsIfProcessIsUnsuccessFulShouldRaiseException()
205 | {
206 | $commandDispatcher = $this->createCommandDispatcherStub();
207 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
208 |
209 | $target = new Target('AwesomeTarget', $commandDispatcher->reveal());
210 |
211 | $this->expectException(CommandFailedException::class);
212 | $target->start();
213 | }
214 |
215 |
216 | public function testSwapCommandsIfProcessIsSuccessfulShouldReturnTrue()
217 | {
218 | $command = $this->prophesize(CommandInterface::class);
219 | $command->isSuccessful()->willReturn(true);
220 |
221 | $commandDispatcher = $this->createCommandDispatcherStub();
222 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
223 |
224 | $swap = new Swap('AwesomeSwap', $commandDispatcher->reveal());
225 |
226 | $this->assertTrue($swap->start());
227 | $this->assertTrue($swap->stop());
228 | $this->assertTrue($swap->enable());
229 | $this->assertTrue($swap->disable());
230 | $this->assertTrue($swap->reload());
231 | $this->assertTrue($swap->restart());
232 | }
233 |
234 | public function testSwapCommandsIfProcessIsUnsuccessFulShouldRaiseException()
235 | {
236 | $commandDispatcher = $this->createCommandDispatcherStub();
237 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
238 |
239 | $swap = new Swap('AwesomeSwap', $commandDispatcher->reveal());
240 |
241 | $this->expectException(CommandFailedException::class);
242 | $swap->start();
243 | }
244 |
245 | public function testDeviceCommandsIfProcessIsSuccessfulShouldReturnTrue()
246 | {
247 | $command = $this->prophesize(CommandInterface::class);
248 | $command->isSuccessful()->willReturn(true);
249 |
250 | $commandDispatcher = $this->createCommandDispatcherStub();
251 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
252 |
253 | $device = new Device('AwesomeDevice', $commandDispatcher->reveal());
254 |
255 | self::assertTrue($device->start());
256 | self::assertTrue($device->stop());
257 | self::assertTrue($device->enable());
258 | self::assertTrue($device->disable());
259 | self::assertTrue($device->reload());
260 | self::assertTrue($device->restart());
261 | }
262 |
263 | public function testDeviceCommandsIfProcessIsUnsuccessFulShouldRaiseException()
264 | {
265 | $commandDispatcher = $this->createCommandDispatcherStub();
266 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
267 |
268 | $device = new Device('AwesomeDevice', $commandDispatcher->reveal());
269 |
270 | $this->expectException(CommandFailedException::class);
271 | $device->start();
272 | }
273 |
274 | public function testAutomountCommandsIfProcessIsSuccessfulShouldReturnTrue()
275 | {
276 | $command = $this->prophesize(CommandInterface::class);
277 | $command->isSuccessful()->willReturn(true);
278 |
279 | $commandDispatcher = $this->createCommandDispatcherStub();
280 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
281 |
282 | $automount = new Automount('AwesomeAutomount', $commandDispatcher->reveal());
283 |
284 | $this->assertTrue($automount->start());
285 | $this->assertTrue($automount->stop());
286 | $this->assertTrue($automount->enable());
287 | $this->assertTrue($automount->disable());
288 | $this->assertTrue($automount->reload());
289 | $this->assertTrue($automount->restart());
290 | }
291 |
292 | public function testAutomountCommandsIfProcessIsUnsuccessFulShouldRaiseException()
293 | {
294 | $commandDispatcher = $this->createCommandDispatcherStub();
295 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
296 |
297 | $automount = new Automount('AwesomeAutomount', $commandDispatcher->reveal());
298 |
299 | $this->expectException(CommandFailedException::class);
300 | $automount->start();
301 | }
302 |
303 | public function testMountCommandsIfProcessIsSuccessfulShouldReturnTrue()
304 | {
305 | $command = $this->prophesize(CommandInterface::class);
306 | $command->isSuccessful()->willReturn(true);
307 |
308 | $commandDispatcher = $this->createCommandDispatcherStub();
309 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
310 |
311 | $mount = new Mount('AwesomeMount', $commandDispatcher->reveal());
312 |
313 | $this->assertTrue($mount->start());
314 | $this->assertTrue($mount->stop());
315 | $this->assertTrue($mount->enable());
316 | $this->assertTrue($mount->disable());
317 | $this->assertTrue($mount->reload());
318 | $this->assertTrue($mount->restart());
319 | }
320 |
321 | public function testMountCommandsIfProcessIsUnsuccessFulShouldRaiseException()
322 | {
323 | $commandDispatcher = $this->createCommandDispatcherStub();
324 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
325 |
326 | $mount = new Mount('AwesomeMount', $commandDispatcher->reveal());
327 |
328 | $this->expectException(CommandFailedException::class);
329 | $mount->start();
330 | }
331 | }
332 |
--------------------------------------------------------------------------------
/test/Unit/Command/SymfonyCommandTest.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class SymfonyCommandTest extends TestCase
18 | {
19 | use ProphecyTrait;
20 |
21 | /**
22 | * @test
23 | */
24 | public function itShouldCreateValidInstance(): void
25 | {
26 | $process = $this->prophesize(Process::class);
27 | $command = new SymfonyCommand($process->reveal());
28 |
29 | self::assertInstanceOf(SymfonyCommand::class, $command);
30 | }
31 |
32 | /**
33 | * @test
34 | */
35 | public function itShouldReturnOutputFromProcess(): void
36 | {
37 | $process = $this->prophesize(Process::class);
38 | $process->getOutput()->willReturn('test');
39 |
40 | $command = new SymfonyCommand($process->reveal());
41 | self::assertSame('test', $command->getOutput());
42 | }
43 |
44 | /**
45 | * @test
46 | */
47 | public function itShouldReturnSuccessfulFromProcess(): void
48 | {
49 | $process = $this->prophesize(Process::class);
50 | $process->isSuccessful()->willReturn(true);
51 |
52 | $command = new SymfonyCommand($process->reveal());
53 | self::assertTrue($command->isSuccessful());
54 | }
55 |
56 | /**
57 | * @test
58 | */
59 | public function itShouldReturnTheCommandIfCommandRanSuccessFul(): void
60 | {
61 | $process = $this->prophesize(Process::class);
62 | $process->run()->shouldBeCalled();
63 | $process->getErrorOutput()->willReturn('testError');
64 | $process->isSuccessful()->willReturn(true);
65 |
66 | $command = new SymfonyCommand($process->reveal());
67 | self::assertSame($command, $command->run());
68 | }
69 |
70 | /**
71 | * @test
72 | */
73 | public function itShouldRaiseAnExceptionIfProcessWasNotSuccessfull(): void
74 | {
75 | $process = $this->prophesize(Process::class);
76 | $process->run()->shouldBeCalled();
77 | $process->getErrorOutput()->willReturn('testError');
78 | $process->isSuccessful()->willReturn(false);
79 |
80 | $command = new SymfonyCommand($process->reveal());
81 | $this->expectException(CommandFailedException::class);
82 | $this->expectExceptionMessage('testError');
83 |
84 | $command->run();
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/test/Unit/SystemCtlTest.php:
--------------------------------------------------------------------------------
1 | prophesize(CommandDispatcherInterface::class);
39 | $commandDispatcherStub->setTimeout(Argument::type('int'))->willReturn($commandDispatcherStub);
40 | $commandDispatcherStub->setBinary(Argument::type('string'))->willReturn($commandDispatcherStub);
41 |
42 | return $commandDispatcherStub;
43 | }
44 |
45 | /**
46 | * @param string $output
47 | *
48 | * @return ObjectProphecy
49 | */
50 | private function buildCommandStub(string $output): ObjectProphecy
51 | {
52 | $command = $this->prophesize(CommandInterface::class);
53 | $command->getOutput()->willReturn($output);
54 |
55 | return $command;
56 | }
57 |
58 | /**
59 | * @test
60 | */
61 | public function itShouldCallCommandDispatcherWithListUnitsAndUnitPrefixOnServiceGetting(): void
62 | {
63 | $unitName = 'testService';
64 | $output = ' testService.service Active running';
65 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
66 | $commandDispatcherStub
67 | ->dispatch(...['list-units', $unitName . '*'])
68 | ->willReturn($this->buildCommandStub($output));
69 |
70 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
71 |
72 | $service = $systemctl->getService($unitName);
73 | self::assertSame('testService', $service->getName());
74 | }
75 |
76 | /**
77 | * @test
78 | */
79 | public function itShouldReturnAServiceOnServiceGetting(): void
80 | {
81 | $unitName = 'testService';
82 | $output = ' testService.service Active running';
83 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
84 | $commandDispatcherStub
85 | ->dispatch(...['list-units', $unitName . '*'])
86 | ->willReturn($this->buildCommandStub($output));
87 |
88 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
89 |
90 | $service = $systemctl->getService($unitName);
91 | self::assertSame($unitName, $service->getName());
92 | }
93 |
94 | /**
95 | * @test
96 | */
97 | public function itShouldReturnAServiceWithTheCorrectNameOnServiceGetting(): void
98 | {
99 | $unitName = 'testService';
100 | $output = ' testService.service Active running';
101 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
102 | $commandDispatcherStub
103 | ->dispatch(...['list-units', $unitName . '*'])
104 | ->willReturn($this->buildCommandStub($output));
105 |
106 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
107 |
108 | $service = $systemctl->getService($unitName);
109 | self::assertSame('testService', $service->getName());
110 | }
111 |
112 | /**
113 | * @test
114 | */
115 | public function itShouldCallCommandDispatcherWithListUnitsAndUnitPrefixOnMountGetting()
116 | {
117 | $unitName = 'testMount';
118 | $output = ' testMount.mount Active running';
119 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
120 | $commandDispatcherStub
121 | ->dispatch(...['list-units', $unitName . '*'])
122 | ->willReturn($this->buildCommandStub($output));
123 |
124 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
125 |
126 | $mount = $systemctl->getMount($unitName);
127 | $this->assertInstanceOf(Mount::class, $mount);
128 | $this->assertSame($unitName, $mount->getName());
129 | }
130 |
131 | /**
132 | * @test
133 | */
134 | public function itShouldThrowAnExceptionIfNotServicesCouldBeFound(): void
135 | {
136 | $unitName = 'testService';
137 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
138 | $commandDispatcherStub
139 | ->dispatch(...['list-units', $unitName . '*'])
140 | ->willReturn($this->buildCommandStub(''));
141 |
142 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
143 |
144 | $this->expectException(UnitNotFoundException::class);
145 | $systemctl->getTimer($unitName);
146 | }
147 |
148 | /**
149 | * @test
150 | */
151 | public function itShouldCallCommandDispatcherWithListUnitsAndUnitPrefixOnTimerGetting(): void
152 | {
153 | $unitName = 'testTimer';
154 | $output = ' testTimer.timer Active running';
155 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
156 | $commandDispatcherStub
157 | ->dispatch(...['list-units', $unitName . '*'])
158 | ->willReturn($this->buildCommandStub($output));
159 |
160 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
161 |
162 | $timer = $systemctl->getTimer($unitName);
163 | self::assertInstanceOf(Timer::class, $timer);
164 | self::assertSame($unitName, $timer->getName());
165 | }
166 |
167 | /**
168 | * @test
169 | */
170 | public function itShouldCallCommandDispatcherWithListUnitsAndUnitPrefixOnSocketGetting(): void
171 | {
172 | $unitName = 'testSocket';
173 | $output = ' testSocket.socket Active running';
174 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
175 | $commandDispatcherStub
176 | ->dispatch(...['list-units', $unitName . '*'])
177 | ->willReturn($this->buildCommandStub($output));
178 |
179 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
180 |
181 | $socket = $systemctl->getSocket($unitName);
182 | self::assertInstanceOf(Socket::class, $socket);
183 | self::assertSame($unitName, $socket->getName());
184 | }
185 |
186 | /**
187 | * @test
188 | */
189 | public function itShouldCallCommandDispatcherWithListUnitsAndUnitPrefixOnScopeGetting(): void
190 | {
191 | $unitName = 'testScope';
192 | $output = ' testScope.scope Active running';
193 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
194 | $commandDispatcherStub
195 | ->dispatch(...['list-units', $unitName . '*'])
196 | ->willReturn($this->buildCommandStub($output));
197 |
198 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
199 |
200 | $scope = $systemctl->getScope($unitName);
201 | self::assertInstanceOf(Scope::class, $scope);
202 | self::assertSame($unitName, $scope->getName());
203 | }
204 |
205 | /**
206 | * @test
207 | */
208 | public function itShouldCallCommandDispatcherWithListUnitsAndUnitPrefixOnSliceGetting()
209 | {
210 | $unitName = 'testSlice';
211 | $output = ' testSlice.slice Active running';
212 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
213 | $commandDispatcherStub
214 | ->dispatch(...['list-units', $unitName . '*'])
215 | ->willReturn($this->buildCommandStub($output));
216 |
217 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
218 |
219 | $slice = $systemctl->getSlice($unitName);
220 | $this->assertInstanceOf(Slice::class, $slice);
221 | $this->assertSame($unitName, $slice->getName());
222 | }
223 |
224 | /**
225 | * @test
226 | */
227 | public function itShouldCallCommandDispatcherWithListUnitsAndUnitPrefixOnTargetGetting()
228 | {
229 | $unitName = 'testTarget';
230 | $output = ' testTarget.target Active running';
231 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
232 | $commandDispatcherStub
233 | ->dispatch(...['list-units', $unitName . '*'])
234 | ->willReturn($this->buildCommandStub($output));
235 |
236 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
237 |
238 | $target = $systemctl->getTarget($unitName);
239 | $this->assertInstanceOf(Target::class, $target);
240 | $this->assertSame($unitName, $target->getName());
241 | }
242 |
243 | /**
244 | * @test
245 | */
246 | public function itShouldCallCommandDispatcherWithListUnitsAndUnitPrefixOnSwapGetting()
247 | {
248 | $unitName = 'testSwap';
249 | $output = ' testSwap.swap Active running';
250 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
251 | $commandDispatcherStub
252 | ->dispatch(...['list-units', $unitName . '*'])
253 | ->willReturn($this->buildCommandStub($output));
254 |
255 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
256 |
257 | $swap = $systemctl->getSwap($unitName);
258 | $this->assertInstanceOf(Swap::class, $swap);
259 | $this->assertSame($unitName, $swap->getName());
260 | }
261 |
262 | /**
263 | * @test
264 | */
265 | public function itShouldCallCommandDispatcherWithListUnitsAndUnitPrefixOnAutomountGetting()
266 | {
267 | $unitName = 'testAutomount';
268 | $output = ' testAutomount.automount Active running';
269 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
270 | $commandDispatcherStub
271 | ->dispatch(...['list-units', $unitName . '*'])
272 | ->willReturn($this->buildCommandStub($output));
273 |
274 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
275 |
276 | $automount = $systemctl->getAutomount($unitName);
277 | $this->assertInstanceOf(Automount::class, $automount);
278 | $this->assertSame($unitName, $automount->getName());
279 | }
280 |
281 | /**
282 | * @test
283 | */
284 | public function itShouldThrowAnExeceptionIfNotTimerCouldBeFound(): void
285 | {
286 | $unitName = 'testTimer';
287 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
288 | $commandDispatcherStub
289 | ->dispatch(...['list-units', $unitName . '*'])
290 | ->willReturn($this->buildCommandStub(''));
291 |
292 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
293 |
294 | $this->expectException(UnitNotFoundException::class);
295 | $systemctl->getTimer($unitName);
296 | }
297 |
298 | /**
299 | * @test
300 | */
301 | public function itShouldThrowAnExceptionIfNoSocketCouldBeFound(): void
302 | {
303 | $unitName = 'testSocket';
304 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
305 | $commandDispatcherStub
306 | ->dispatch(...['list-units', $unitName . '*'])
307 | ->willReturn($this->buildCommandStub(''));
308 |
309 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
310 |
311 | $this->expectException(UnitNotFoundException::class);
312 | $systemctl->getSocket($unitName);
313 | }
314 |
315 | /**
316 | * @test
317 | */
318 | public function itShouldThrowAnExceptionIfNoTargetCouldBeFound()
319 | {
320 | $unitName = 'testTarget';
321 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
322 | $commandDispatcherStub
323 | ->dispatch(...['list-units', $unitName . '*'])
324 | ->willReturn($this->buildCommandStub(''));
325 |
326 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
327 |
328 | $this->expectException(UnitNotFoundException::class);
329 | $systemctl->getTarget($unitName);
330 | }
331 |
332 | /**
333 | * @test
334 | */
335 | public function itShouldThrowAnExceptionIfNoSwapCouldBeFound()
336 | {
337 | $unitName = 'testSwap';
338 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
339 | $commandDispatcherStub
340 | ->dispatch(...['list-units', $unitName . '*'])
341 | ->willReturn($this->buildCommandStub(''));
342 |
343 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
344 |
345 | $this->expectException(UnitNotFoundException::class);
346 | $systemctl->getSwap($unitName);
347 | }
348 |
349 | /**
350 | * @test
351 | */
352 | public function itShouldThrowAnExceptionIfNoScopeCouldBeFound(): void
353 | {
354 | $unitName = 'testScope';
355 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
356 | $commandDispatcherStub
357 | ->dispatch(...['list-units', $unitName . '*'])
358 | ->willReturn($this->buildCommandStub(''));
359 |
360 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
361 |
362 | $this->expectException(UnitNotFoundException::class);
363 | $systemctl->getScope($unitName);
364 | }
365 |
366 | /**
367 | * @test
368 | */
369 | public function itShouldThrowAnExceptionIfNoSliceCouldBeFound()
370 | {
371 | $unitName = 'testSlice';
372 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
373 | $commandDispatcherStub
374 | ->dispatch(...['list-units', $unitName . '*'])
375 | ->willReturn($this->buildCommandStub(''));
376 |
377 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
378 |
379 | $this->expectException(UnitNotFoundException::class);
380 | $systemctl->getSlice($unitName);
381 | }
382 |
383 | /**
384 | * @test
385 | */
386 | public function itShouldReturnATimerOnTimerGetting(): void
387 | {
388 | $unitName = 'testService';
389 | $output = ' testService.service Active running';
390 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
391 | $commandDispatcherStub
392 | ->dispatch(...['list-units', $unitName . '*'])
393 | ->willReturn($this->buildCommandStub($output));
394 |
395 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
396 |
397 | $service = $systemctl->getService($unitName);
398 | self::assertInstanceOf(Service::class, $service);
399 | }
400 |
401 | /**
402 | * @test
403 | */
404 | public function itShouldReturnATimerWithTheCorrectNameOnTimerGetting(): void
405 | {
406 | $unitName = 'testService';
407 | $output = ' testService.service Active running';
408 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
409 | $commandDispatcherStub
410 | ->dispatch(...['list-units', $unitName . '*'])
411 | ->willReturn($this->buildCommandStub($output));
412 |
413 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
414 |
415 | $service = $systemctl->getService($unitName);
416 | self::assertSame('testService', $service->getName());
417 | }
418 |
419 | /**
420 | * @test
421 | */
422 | public function itShouldCallCommandDispatcherWithListUnitsAndUnitPrefixOnDeviceGetting()
423 | {
424 | $unitName = 'testDevice';
425 | $output = ' testDevice.device Active running';
426 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
427 | $commandDispatcherStub
428 | ->dispatch(...['list-units', $unitName . '*'])
429 | ->willReturn($this->buildCommandStub($output));
430 |
431 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
432 |
433 | $device = $systemctl->getDevice($unitName);
434 | self::assertInstanceOf(Device::class, $device);
435 | self::assertSame($unitName, $device->getName());
436 | }
437 |
438 | /**
439 | * @test
440 | */
441 | public function itShouldThrowAnExceptionIfNoDeviceCouldBeFound()
442 | {
443 | $unitName = 'testDevice';
444 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
445 | $commandDispatcherStub
446 | ->dispatch(...['list-units', $unitName . '*'])
447 | ->willReturn($this->buildCommandStub(''));
448 |
449 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
450 |
451 | $this->expectException(UnitNotFoundException::class);
452 | $systemctl->getSocket($unitName);
453 | }
454 |
455 | /**
456 | * @test
457 | */
458 | public function itShouldThrowAnExceptionIfNoAutomountCouldBeFound()
459 | {
460 | $unitName = 'testAutomount';
461 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
462 | $commandDispatcherStub
463 | ->dispatch(...['list-units', $unitName . '*'])
464 | ->willReturn($this->buildCommandStub(''));
465 |
466 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
467 |
468 | $this->expectException(UnitNotFoundException::class);
469 | $systemctl->getAutomount($unitName);
470 | }
471 |
472 | /**
473 | * @test
474 | */
475 | public function itShouldThrowAnExceptionIfNoMountCouldBeFound()
476 | {
477 | $unitName = 'testMount';
478 | $commandDispatcherStub = $this->buildCommandDispatcherStub();
479 | $commandDispatcherStub
480 | ->dispatch(...['list-units', $unitName . '*'])
481 | ->willReturn($this->buildCommandStub(''));
482 |
483 | $systemctl = (new SystemCtl())->setCommandDispatcher($commandDispatcherStub->reveal());
484 |
485 | $this->expectException(UnitNotFoundException::class);
486 | $systemctl->getMount($unitName);
487 | }
488 | }
489 |
--------------------------------------------------------------------------------
/test/Unit/Unit/AbstractUnitTest.php:
--------------------------------------------------------------------------------
1 | prophesize(CommandDispatcherInterface::class);
35 | $unit = new UnitStub($name, $commandDispatcher->reveal());
36 |
37 | self::assertSame($name, $unit->getName());
38 | }
39 |
40 | /**
41 | * @return array
42 | */
43 | public function itShouldReturnCorrectNameDataProvider(): array
44 | {
45 | return [
46 | [
47 | 'name' => 'test1',
48 | ],
49 | [
50 | 'name' => 'test1.service',
51 | ],
52 | [
53 | 'name' => 'test1.timer',
54 | ],
55 | [
56 | 'name' => 'test1.socket',
57 | ],
58 | [
59 | 'name' => 'test1.scope',
60 | ],
61 | [
62 | 'name' => 'test1.slice',
63 | ],
64 | [
65 | 'name' => 'test1.swap',
66 | ],
67 | [
68 | 'name' => 'test1.target',
69 | ],
70 | [
71 | 'name' => 'test1.mount',
72 | ],
73 | [
74 | 'name' => 'test1.automount',
75 | ],
76 | [
77 | 'name' => 'test1.mount',
78 | ],
79 | [
80 | 'name' => 'test1.device',
81 | ],
82 | [
83 | 'name' => 'test1@2.service',
84 | ],
85 | ];
86 | }
87 |
88 | /**
89 | * @param string $name
90 | * @param bool $isMultiInstance
91 | *
92 | * @test
93 | * @dataProvider itDetectsMultiInstanceUnitsCorrectlyDataProvider
94 | */
95 | public function itDetectsMultiInstanceUnitsCorrectly(string $name, bool $isMultiInstance): void
96 | {
97 | $commandDispatcher = $this->prophesize(CommandDispatcherInterface::class);
98 | $unit = new UnitStub($name, $commandDispatcher->reveal());
99 |
100 | self::assertSame($isMultiInstance, $unit->isMultiInstance());
101 | }
102 |
103 | /**
104 | * @return array
105 | */
106 | public function itDetectsMultiInstanceUnitsCorrectlyDataProvider(): array
107 | {
108 | return [
109 | [
110 | 'name' => 'test1@1',
111 | 'isMultiInstance' => true,
112 | ],
113 | [
114 | 'name' => 'test1@123.service',
115 | 'isMultiInstance' => true,
116 | ],
117 | [
118 | 'name' => 'test1@foo',
119 | 'isMultiInstance' => true,
120 | ],
121 | [
122 | 'name' => 'test1@foo.mount',
123 | 'isMultiInstance' => true,
124 | ],
125 | [
126 | 'name' => 'test1@foo.automount',
127 | 'isMultiInstance' => true,
128 | ],
129 | [
130 | 'name' => 'test1.service',
131 | 'isMultiInstance' => false,
132 | ],
133 | [
134 | 'name' => 'test1.timer',
135 | 'isMultiInstance' => false,
136 | ],
137 | [
138 | 'name' => 'test1.socket',
139 | 'isMultiInstance' => false,
140 | ],
141 | [
142 | 'name' => 'test1.device',
143 | 'isMultiInstance' => false,
144 | ],
145 | [
146 | 'name' => 'test1.scope',
147 | 'isMultiInstance' => false,
148 | ],
149 | [
150 | 'name' => 'test1.slice',
151 | 'isMultiInstance' => false,
152 | ],
153 | [
154 | 'name' => 'test1.swap',
155 | 'isMultiInstance' => false,
156 | ],
157 | [
158 | 'name' => 'test1.target',
159 | 'isMultiInstance' => false,
160 | ],
161 | [
162 | 'name' => 'test1.mount',
163 | 'isMultiInstance' => false,
164 | ],
165 | ];
166 | }
167 |
168 | /**
169 | * @param string $name
170 | * @param string $instanceName
171 | *
172 | * @test
173 | * @dataProvider itDetectsMultiInstanceInstanceNamesCorrectlyDataProvider
174 | */
175 | public function itDetectsMultiInstanceInstanceNamesCorrectly(string $name, ?string $instanceName): void
176 | {
177 | $commandDispatcher = $this->prophesize(CommandDispatcherInterface::class);
178 | $unit = new UnitStub($name, $commandDispatcher->reveal());
179 |
180 | self::assertSame($instanceName, $unit->getInstanceName());
181 | }
182 |
183 | /**
184 | * @return array
185 | */
186 | public function itDetectsMultiInstanceInstanceNamesCorrectlyDataProvider(): array
187 | {
188 | return [
189 | [
190 | 'name' => 'test1@1',
191 | 'instanceName' => '1',
192 | ],
193 | [
194 | 'name' => 'test1@123.service',
195 | 'instanceName' => '123',
196 | ],
197 | [
198 | 'name' => 'test1@foo',
199 | 'instanceName' => 'foo',
200 | ],
201 | [
202 | 'name' => 'test1@foo.mount',
203 | 'instanceName' => 'foo',
204 | ],
205 | [
206 | 'name' => 'test1.service',
207 | 'instanceName' => null,
208 | ],
209 | [
210 | 'name' => 'test1.timer',
211 | 'instanceName' => null,
212 | ],
213 | [
214 | 'name' => 'test1.socket',
215 | 'instanceName' => null,
216 | ],
217 | [
218 | 'name' => 'test1.scope',
219 | 'instanceName' => null,
220 | ],
221 | [
222 | 'name' => 'test1.slice',
223 | 'instanceName' => null,
224 | ],
225 | [
226 | 'name' => 'test1.swap',
227 | 'instanceName' => null,
228 | ],
229 | [
230 | 'name' => 'test1.target',
231 | 'instanceName' => null,
232 | ],
233 | [
234 | 'name' => 'test1.mount',
235 | 'instanceName' => null,
236 | ],
237 | ];
238 | }
239 |
240 | /**
241 | * @test
242 | */
243 | public function itShouldReturnTrueIfServiceEnabledCommandRanSuccessfully(): void
244 | {
245 | $command = $this->prophesize(CommandInterface::class);
246 | $command->getOutput()->willReturn('enabled');
247 | $commandDispatcher = $this->prophesize(CommandDispatcherInterface::class);
248 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
249 |
250 | $unit = new UnitStub(static::UNIT_NAME, $commandDispatcher->reveal());
251 |
252 | self::assertTrue($unit->isEnabled());
253 | }
254 |
255 | /**
256 | * @test
257 | */
258 | public function itShouldRaiseAnExceptionIfServiceEnabledCommandFailed(): void
259 | {
260 | $commandDispatcher = $this->prophesize(CommandDispatcherInterface::class);
261 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
262 |
263 | $unit = new UnitStub(static::UNIT_NAME, $commandDispatcher->reveal());
264 | $this->expectException(CommandFailedException::class);
265 |
266 | $unit->isEnabled();
267 | }
268 |
269 | /**
270 | * @param bool $commandSuccessful
271 | * @param string $commandOutput
272 | *
273 | * @test
274 | * @dataProvider itShouldReturnFalseIfServiceEnabledCommandOutputDoesNotEqualEnabledDataProvider
275 | */
276 | public function itShouldReturnFalseIfServiceEnabledCommandOutputDoesNotEqualEnabled(
277 | $commandSuccessful,
278 | $commandOutput
279 | ): void {
280 | $command = $this->prophesize(CommandInterface::class);
281 | $command->isSuccessful()->willReturn($commandSuccessful);
282 | $command->getOutput()->willReturn($commandOutput);
283 |
284 | $commandDispatcher = $this->prophesize(CommandDispatcherInterface::class);
285 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
286 |
287 | $unit = new UnitStub(static::UNIT_NAME, $commandDispatcher->reveal());
288 |
289 | self::assertFalse($unit->isEnabled());
290 | }
291 |
292 | /**
293 | * @return array
294 | */
295 | public function itShouldReturnFalseIfServiceEnabledCommandOutputDoesNotEqualEnabledDataProvider(): array
296 | {
297 | return [
298 | [
299 | 'commandSuccessful' => true,
300 | 'commandOutput' => 'static',
301 | ],
302 | [
303 | 'commandSuccessful' => false,
304 | 'commandOutput' => 'static',
305 | ],
306 | [
307 | 'commandSuccessful' => true,
308 | 'commandOutput' => 'enable',
309 | ],
310 | ];
311 | }
312 |
313 | /**
314 | * @test
315 | */
316 | public function itShouldReturnTrueIfServiceActiveCommandRanSuccessfully(): void
317 | {
318 | $command = $this->prophesize(CommandInterface::class);
319 | $command->getOutput()->willReturn('active');
320 |
321 | $commandDispatcher = $this->prophesize(CommandDispatcherInterface::class);
322 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
323 |
324 | $unit = new UnitStub(static::UNIT_NAME, $commandDispatcher->reveal());
325 |
326 | self::assertTrue($unit->isRunning());
327 | }
328 |
329 | /**
330 | * @test
331 | */
332 | public function itShouldRaiseExceptionIfServiceActiveCommandFailed(): void
333 | {
334 | $commandDispatcher = $this->prophesize(CommandDispatcherInterface::class);
335 | $commandDispatcher->dispatch(Argument::cetera())->willThrow(CommandFailedException::class);
336 |
337 | $unit = new UnitStub(static::UNIT_NAME, $commandDispatcher->reveal());
338 |
339 | $this->expectException(CommandFailedException::class);
340 | $unit->isRunning();
341 | }
342 |
343 | /**
344 | * @param bool $commandSuccessful
345 | * @param string $commandOutput
346 | *
347 | * @test
348 | * @dataProvider itShouldReturnFalseIfServiceActiveCommandOutputDoesNotEqualActiveDataProvider
349 | */
350 | public function itShouldReturnFalseIfServiceActiveCommandOutputDoesNotEqualActive(
351 | $commandSuccessful,
352 | $commandOutput
353 | ): void {
354 | $command = $this->prophesize(CommandInterface::class);
355 | $command->isSuccessful()->willReturn($commandSuccessful);
356 | $command->getOutput()->willReturn($commandOutput);
357 |
358 | $commandDispatcher = $this->prophesize(CommandDispatcherInterface::class);
359 | $commandDispatcher->dispatch(Argument::cetera())->willReturn($command);
360 |
361 | $unit = new UnitStub(static::UNIT_NAME, $commandDispatcher->reveal());
362 |
363 | self::assertFalse($unit->isRunning());
364 | }
365 |
366 | /**
367 | * @return array
368 | */
369 | public function itShouldReturnFalseIfServiceActiveCommandOutputDoesNotEqualActiveDataProvider(): array
370 | {
371 | return [
372 | [
373 | 'commandSuccessful' => true,
374 | 'commandOutput' => 'static',
375 | ],
376 | [
377 | 'commandSuccessful' => false,
378 | 'commandOutput' => 'static',
379 | ],
380 | [
381 | 'commandSuccessful' => true,
382 | 'commandOutput' => 'enable',
383 | ],
384 | ];
385 | }
386 |
387 | /**
388 | * @test
389 | */
390 | public function testIfExecuteAppendsTheUnitNameAndSuffix(): void
391 | {
392 | $commandStub = $this->prophesize(CommandInterface::class);
393 | $commandStub->isSuccessful()->willReturn(true);
394 |
395 | $commandDispatcherStub = $this->prophesize(CommandDispatcherInterface::class);
396 | $commandDispatcherStub
397 | ->dispatch(...['start', self::UNIT_NAME . '.' . 'stub'])
398 | ->willReturn($commandStub)
399 | ->shouldBeCalled();
400 |
401 | $unitStub = new UnitStub(self::UNIT_NAME, $commandDispatcherStub->reveal());
402 | $unitStub->start();
403 | }
404 | }
405 |
--------------------------------------------------------------------------------
/test/Unit/Unit/UnitStub.php:
--------------------------------------------------------------------------------
1 | $output,
71 | 'suffix' => 'service',
72 | 'amount' => 5,
73 | ],
74 | [
75 | 'output' => $output,
76 | 'suffix' => 'timer',
77 | 'amount' => 2,
78 | ],
79 | [
80 | 'output' => $output,
81 | 'suffix' => 'socket',
82 | 'amount' => 2,
83 | ],
84 | [
85 | 'output' => $output,
86 | 'suffix' => 'device',
87 | 'amount' => 2,
88 | ],
89 | [
90 | 'output' => $output,
91 | 'suffix' => 'scope',
92 | 'amount' => 2,
93 | ],
94 | [
95 | 'output' => $output,
96 | 'suffix' => 'slice',
97 | 'amount' => 2,
98 | ],
99 | [
100 | 'output' => $output,
101 | 'suffix' => 'swap',
102 | 'amount' => 2,
103 | ],
104 | [
105 | 'output' => $output,
106 | 'suffix' => 'target',
107 | 'amount' => 2,
108 | ],
109 | [
110 | 'output' => $output,
111 | 'suffix' => 'mount',
112 | 'amount' => 4,
113 | ],
114 | [
115 | 'output' => $output,
116 | 'suffix' => 'automount',
117 | 'amount' => 2,
118 | ],
119 | [
120 | 'output' => $output,
121 | 'suffix' => 'notThere',
122 | 'amount' => 0,
123 | ],
124 | ];
125 | }
126 |
127 | /**
128 | * @param string $output
129 | * @param string $suffix
130 | * @param array $expectedUnitNames
131 | *
132 | * @test
133 | * @dataProvider itOnlyExtractsTheUnitNamesDataProvider
134 | */
135 | public function itOnlyExtractsTheUnitNames(string $output, string $suffix, array $expectedUnitNames): void
136 | {
137 | $units = OutputFetcher::fetchUnitNames($suffix, $output);
138 | self::assertSame($expectedUnitNames, $units);
139 | }
140 |
141 | /**
142 | * @return array
143 | */
144 | public function itOnlyExtractsTheUnitNamesDataProvider(): array
145 | {
146 | $output = <<