From c96a7c5121ba830c4d24b4f77f031dc5461f6223 Mon Sep 17 00:00:00 2001 From: Eser DENIZ <srwiez@gmail.com> Date: Wed, 18 Dec 2024 19:25:08 +0100 Subject: [PATCH] Fixes and improvements to powerMonitor (#445) * fix: powerMonitor couldn't pass arguments with get methods * feat: additional events * fix: PowerMonitor Facade docblock * feat: added fake class for tests --- src/Client/Client.php | 4 +- src/Contracts/PowerMonitor.php | 17 +++ src/Events/PowerMonitor/ScreenLocked.php | 23 ++++ src/Events/PowerMonitor/ScreenUnlocked.php | 23 ++++ src/Events/PowerMonitor/Shutdown.php | 23 ++++ .../PowerMonitor/UserDidBecomeActive.php | 23 ++++ .../PowerMonitor/UserDidResignActive.php | 23 ++++ src/Facades/PowerMonitor.php | 15 ++- src/Fakes/PowerMonitorFake.php | 93 +++++++++++++ src/NativeServiceProvider.php | 6 + src/PowerMonitor.php | 3 +- tests/Fakes/FakePowerMonitorTest.php | 123 ++++++++++++++++++ 12 files changed, 370 insertions(+), 6 deletions(-) create mode 100644 src/Contracts/PowerMonitor.php create mode 100644 src/Events/PowerMonitor/ScreenLocked.php create mode 100644 src/Events/PowerMonitor/ScreenUnlocked.php create mode 100644 src/Events/PowerMonitor/Shutdown.php create mode 100644 src/Events/PowerMonitor/UserDidBecomeActive.php create mode 100644 src/Events/PowerMonitor/UserDidResignActive.php create mode 100644 src/Fakes/PowerMonitorFake.php create mode 100644 tests/Fakes/FakePowerMonitorTest.php diff --git a/src/Client/Client.php b/src/Client/Client.php index e444ea4..9a8ec81 100644 --- a/src/Client/Client.php +++ b/src/Client/Client.php @@ -21,9 +21,9 @@ public function __construct() ->asJson(); } - public function get(string $endpoint): Response + public function get(string $endpoint, array|string|null $query = null): Response { - return $this->client->get($endpoint); + return $this->client->get($endpoint, $query); } public function post(string $endpoint, array $data = []): Response diff --git a/src/Contracts/PowerMonitor.php b/src/Contracts/PowerMonitor.php new file mode 100644 index 0000000..e8ec3d5 --- /dev/null +++ b/src/Contracts/PowerMonitor.php @@ -0,0 +1,17 @@ +<?php + +namespace Native\Laravel\Contracts; + +use Native\Laravel\Enums\SystemIdleStatesEnum; +use Native\Laravel\Enums\ThermalStatesEnum; + +interface PowerMonitor +{ + public function getSystemIdleState(int $threshold): SystemIdleStatesEnum; + + public function getSystemIdleTime(): int; + + public function getCurrentThermalState(): ThermalStatesEnum; + + public function isOnBatteryPower(): bool; +} diff --git a/src/Events/PowerMonitor/ScreenLocked.php b/src/Events/PowerMonitor/ScreenLocked.php new file mode 100644 index 0000000..dbaca58 --- /dev/null +++ b/src/Events/PowerMonitor/ScreenLocked.php @@ -0,0 +1,23 @@ +<?php + +namespace Native\Laravel\Events\PowerMonitor; + +use Illuminate\Broadcasting\Channel; +use Illuminate\Broadcasting\InteractsWithSockets; +use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Queue\SerializesModels; + +class ScreenLocked implements ShouldBroadcastNow +{ + use Dispatchable, InteractsWithSockets, SerializesModels; + + public function __construct() {} + + public function broadcastOn() + { + return [ + new Channel('nativephp'), + ]; + } +} diff --git a/src/Events/PowerMonitor/ScreenUnlocked.php b/src/Events/PowerMonitor/ScreenUnlocked.php new file mode 100644 index 0000000..b4d1524 --- /dev/null +++ b/src/Events/PowerMonitor/ScreenUnlocked.php @@ -0,0 +1,23 @@ +<?php + +namespace Native\Laravel\Events\PowerMonitor; + +use Illuminate\Broadcasting\Channel; +use Illuminate\Broadcasting\InteractsWithSockets; +use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Queue\SerializesModels; + +class ScreenUnlocked implements ShouldBroadcastNow +{ + use Dispatchable, InteractsWithSockets, SerializesModels; + + public function __construct() {} + + public function broadcastOn() + { + return [ + new Channel('nativephp'), + ]; + } +} diff --git a/src/Events/PowerMonitor/Shutdown.php b/src/Events/PowerMonitor/Shutdown.php new file mode 100644 index 0000000..19e563c --- /dev/null +++ b/src/Events/PowerMonitor/Shutdown.php @@ -0,0 +1,23 @@ +<?php + +namespace Native\Laravel\Events\PowerMonitor; + +use Illuminate\Broadcasting\Channel; +use Illuminate\Broadcasting\InteractsWithSockets; +use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Queue\SerializesModels; + +class Shutdown implements ShouldBroadcastNow +{ + use Dispatchable, InteractsWithSockets, SerializesModels; + + public function __construct() {} + + public function broadcastOn() + { + return [ + new Channel('nativephp'), + ]; + } +} diff --git a/src/Events/PowerMonitor/UserDidBecomeActive.php b/src/Events/PowerMonitor/UserDidBecomeActive.php new file mode 100644 index 0000000..e077a52 --- /dev/null +++ b/src/Events/PowerMonitor/UserDidBecomeActive.php @@ -0,0 +1,23 @@ +<?php + +namespace Native\Laravel\Events\PowerMonitor; + +use Illuminate\Broadcasting\Channel; +use Illuminate\Broadcasting\InteractsWithSockets; +use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Queue\SerializesModels; + +class UserDidBecomeActive implements ShouldBroadcastNow +{ + use Dispatchable, InteractsWithSockets, SerializesModels; + + public function __construct() {} + + public function broadcastOn() + { + return [ + new Channel('nativephp'), + ]; + } +} diff --git a/src/Events/PowerMonitor/UserDidResignActive.php b/src/Events/PowerMonitor/UserDidResignActive.php new file mode 100644 index 0000000..5608102 --- /dev/null +++ b/src/Events/PowerMonitor/UserDidResignActive.php @@ -0,0 +1,23 @@ +<?php + +namespace Native\Laravel\Events\PowerMonitor; + +use Illuminate\Broadcasting\Channel; +use Illuminate\Broadcasting\InteractsWithSockets; +use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Queue\SerializesModels; + +class UserDidResignActive implements ShouldBroadcastNow +{ + use Dispatchable, InteractsWithSockets, SerializesModels; + + public function __construct() {} + + public function broadcastOn() + { + return [ + new Channel('nativephp'), + ]; + } +} diff --git a/src/Facades/PowerMonitor.php b/src/Facades/PowerMonitor.php index 5233856..d72d9d7 100644 --- a/src/Facades/PowerMonitor.php +++ b/src/Facades/PowerMonitor.php @@ -3,17 +3,26 @@ namespace Native\Laravel\Facades; use Illuminate\Support\Facades\Facade; +use Native\Laravel\Contracts\PowerMonitor as PowerMonitorContract; +use Native\Laravel\Fakes\PowerMonitorFake; /** - * @method static \Native\Laravel\Enums\SystemIdelStatesEnum getSystemIdleState(int $threshold) + * @method static \Native\Laravel\Enums\SystemIdleStatesEnum getSystemIdleState(int $threshold) * @method static int getSystemIdleTime() * @method static \Native\Laravel\Enums\ThermalStatesEnum getCurrentThermalState() * @method static bool isOnBatteryPower() */ class PowerMonitor extends Facade { - protected static function getFacadeAccessor() + public static function fake() { - return \Native\Laravel\PowerMonitor::class; + return tap(static::getFacadeApplication()->make(PowerMonitorFake::class), function ($fake) { + static::swap($fake); + }); + } + + protected static function getFacadeAccessor(): string + { + return PowerMonitorContract::class; } } diff --git a/src/Fakes/PowerMonitorFake.php b/src/Fakes/PowerMonitorFake.php new file mode 100644 index 0000000..2af9916 --- /dev/null +++ b/src/Fakes/PowerMonitorFake.php @@ -0,0 +1,93 @@ +<?php + +namespace Native\Laravel\Fakes; + +use Closure; +use Native\Laravel\Contracts\PowerMonitor as PowerMonitorContract; +use Native\Laravel\Enums\SystemIdleStatesEnum; +use Native\Laravel\Enums\ThermalStatesEnum; +use PHPUnit\Framework\Assert as PHPUnit; + +class PowerMonitorFake implements PowerMonitorContract +{ + public array $getSystemIdleStateCalls = []; + + public int $getSystemIdleStateCount = 0; + + public int $getSystemIdleTimeCount = 0; + + public int $getCurrentThermalStateCount = 0; + + public int $isOnBatteryPowerCount = 0; + + public function getSystemIdleState(int $threshold): SystemIdleStatesEnum + { + $this->getSystemIdleStateCount++; + + $this->getSystemIdleStateCalls[] = $threshold; + + return SystemIdleStatesEnum::UNKNOWN; + } + + public function getSystemIdleTime(): int + { + $this->getSystemIdleTimeCount++; + + return 0; + } + + public function getCurrentThermalState(): ThermalStatesEnum + { + $this->getCurrentThermalStateCount++; + + return ThermalStatesEnum::UNKNOWN; + } + + public function isOnBatteryPower(): bool + { + $this->isOnBatteryPowerCount++; + + return false; + } + + /** + * @param int|Closure(int): bool $key + */ + public function assertGetSystemIdleState(int|Closure $key): void + { + if (is_callable($key) === false) { + PHPUnit::assertContains($key, $this->getSystemIdleStateCalls); + + return; + } + + $hit = empty( + array_filter( + $this->getSystemIdleStateCalls, + fn (string $keyIteration) => $key($keyIteration) === true + ) + ) === false; + + PHPUnit::assertTrue($hit); + } + + public function assertGetSystemIdleStateCount(int $count): void + { + PHPUnit::assertSame($count, $this->getSystemIdleStateCount); + } + + public function assertGetSystemIdleTimeCount(int $count): void + { + PHPUnit::assertSame($count, $this->getSystemIdleTimeCount); + } + + public function assertGetCurrentThermalStateCount(int $count): void + { + PHPUnit::assertSame($count, $this->getCurrentThermalStateCount); + } + + public function assertIsOnBatteryPowerCount(int $count): void + { + PHPUnit::assertSame($count, $this->isOnBatteryPowerCount); + } +} diff --git a/src/NativeServiceProvider.php b/src/NativeServiceProvider.php index 89c0740..079bb0a 100644 --- a/src/NativeServiceProvider.php +++ b/src/NativeServiceProvider.php @@ -16,11 +16,13 @@ use Native\Laravel\Commands\SeedDatabaseCommand; use Native\Laravel\Contracts\ChildProcess as ChildProcessContract; use Native\Laravel\Contracts\GlobalShortcut as GlobalShortcutContract; +use Native\Laravel\Contracts\PowerMonitor as PowerMonitorContract; use Native\Laravel\Contracts\WindowManager as WindowManagerContract; use Native\Laravel\Events\EventWatcher; use Native\Laravel\Exceptions\Handler; use Native\Laravel\GlobalShortcut as GlobalShortcutImplementation; use Native\Laravel\Logging\LogWatcher; +use Native\Laravel\PowerMonitor as PowerMonitorImplementation; use Native\Laravel\Windows\WindowManager as WindowManagerImplementation; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -66,6 +68,10 @@ public function packageRegistered() return $app->make(GlobalShortcutImplementation::class); }); + $this->app->bind(PowerMonitorContract::class, function (Foundation $app) { + return $app->make(PowerMonitorImplementation::class); + }); + if (config('nativephp-internal.running')) { $this->app->singleton( \Illuminate\Contracts\Debug\ExceptionHandler::class, diff --git a/src/PowerMonitor.php b/src/PowerMonitor.php index ea0d587..c0307b1 100644 --- a/src/PowerMonitor.php +++ b/src/PowerMonitor.php @@ -3,10 +3,11 @@ namespace Native\Laravel; use Native\Laravel\Client\Client; +use Native\Laravel\Contracts\PowerMonitor as PowerMonitorContract; use Native\Laravel\Enums\SystemIdleStatesEnum; use Native\Laravel\Enums\ThermalStatesEnum; -class PowerMonitor +class PowerMonitor implements PowerMonitorContract { public function __construct(protected Client $client) {} diff --git a/tests/Fakes/FakePowerMonitorTest.php b/tests/Fakes/FakePowerMonitorTest.php new file mode 100644 index 0000000..4761877 --- /dev/null +++ b/tests/Fakes/FakePowerMonitorTest.php @@ -0,0 +1,123 @@ +<?php + +use Native\Laravel\Contracts\PowerMonitor as PowerMonitorContract; +use Native\Laravel\Facades\PowerMonitor; +use Native\Laravel\Fakes\PowerMonitorFake; +use PHPUnit\Framework\AssertionFailedError; + +use function Pest\Laravel\swap; + +it('swaps implementations using facade', function () { + PowerMonitor::fake(); + + expect(app(PowerMonitorContract::class)) + ->toBeInstanceOf(PowerMonitorFake::class); +}); + +it('asserts getSystemIdleState using int', function () { + swap(PowerMonitorContract::class, $fake = app(PowerMonitorFake::class)); + + $fake->getSystemIdleState(10); + $fake->getSystemIdleState(60); + + $fake->assertGetSystemIdleState(10); + $fake->assertGetSystemIdleState(60); + + try { + $fake->assertGetSystemIdleState(20); + } catch (AssertionFailedError) { + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts getSystemIdleState using callable', function () { + swap(PowerMonitorContract::class, $fake = app(PowerMonitorFake::class)); + + $fake->getSystemIdleState(10); + $fake->getSystemIdleState(60); + + $fake->assertGetSystemIdleState(fn (int $key) => $key === 10); + $fake->assertGetSystemIdleState(fn (int $key) => $key === 60); + + try { + $fake->assertGetSystemIdleState(fn (int $key) => $key === 20); + } catch (AssertionFailedError) { + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts getSystemIdleState count', function () { + swap(PowerMonitorContract::class, $fake = app(PowerMonitorFake::class)); + + $fake->getSystemIdleState(10); + $fake->getSystemIdleState(20); + $fake->getSystemIdleState(60); + + $fake->assertGetSystemIdleStateCount(3); + + try { + $fake->assertGetSystemIdleStateCount(2); + } catch (AssertionFailedError) { + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts getSystemIdleTime count', function () { + swap(PowerMonitorContract::class, $fake = app(PowerMonitorFake::class)); + + $fake->getSystemIdleTime(); + $fake->getSystemIdleTime(); + $fake->getSystemIdleTime(); + + $fake->assertGetSystemIdleTimeCount(3); + + try { + $fake->assertGetSystemIdleTimeCount(2); + } catch (AssertionFailedError) { + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts getCurrentThermalState count', function () { + swap(PowerMonitorContract::class, $fake = app(PowerMonitorFake::class)); + + $fake->getCurrentThermalState(); + $fake->getCurrentThermalState(); + $fake->getCurrentThermalState(); + + $fake->assertGetCurrentThermalStateCount(3); + + try { + $fake->assertGetCurrentThermalStateCount(2); + } catch (AssertionFailedError) { + return; + } + + $this->fail('Expected assertion to fail'); +}); + +it('asserts isOnBatteryPower count', function () { + swap(PowerMonitorContract::class, $fake = app(PowerMonitorFake::class)); + + $fake->isOnBatteryPower(); + $fake->isOnBatteryPower(); + $fake->isOnBatteryPower(); + + $fake->assertIsOnBatteryPowerCount(3); + + try { + $fake->assertIsOnBatteryPowerCount(2); + } catch (AssertionFailedError) { + return; + } + + $this->fail('Expected assertion to fail'); +});