Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use PHP 8.1 features #11

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
27 changes: 5 additions & 22 deletions src/AnomalyDetectionResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,11 @@
*/
public const DIRECTION_DOWN = 'down';

/** @var float The standard deviation */
private float $std_dev;

/** @var float The mean value */
private float $mean;

/** @var int The sensitivity */
private int $sensitivity;

/** @var float The low bound */
private float $low;
private readonly float $low;

/** @var float The high bound */
private float $high;

/** @var float The latest value */
private float $latest;
private readonly float $high;

/** @var string The direction of the anomaly */
private string $direction = self::DIRECTION_NONE;
Expand All @@ -80,27 +68,22 @@
* @param float $latest The latest value
* @param int $sensitivity The sensitivity (see `SlidingWindowCounter::detectAnomaly()`)
*/
public function __construct(float $std_dev, float $mean, float $latest, int $sensitivity)
public function __construct(private readonly float $std_dev, private readonly float $mean, private readonly float $latest, private readonly int $sensitivity)
{
$this->std_dev = $std_dev;
$this->mean = $mean;
$this->latest = $latest;
$this->sensitivity = $sensitivity;

$this->high = ceil($this->mean + ($sensitivity * $this->std_dev));
$this->low = floor($this->mean - ($sensitivity * $this->std_dev));
$this->high = ceil($this->mean + ($this->sensitivity * $this->std_dev));

Check warning on line 73 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "RoundingFamily": --- Original +++ New @@ @@ */ public function __construct(private readonly float $std_dev, private readonly float $mean, private readonly float $latest, private readonly int $sensitivity) { - $this->high = ceil($this->mean + $this->sensitivity * $this->std_dev); + $this->high = round($this->mean + $this->sensitivity * $this->std_dev); $this->low = floor($this->mean - $this->sensitivity * $this->std_dev); if ($this->latest >= $this->low && $this->latest <= $this->high) { return;
$this->low = floor($this->mean - ($this->sensitivity * $this->std_dev));

Check warning on line 74 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "RoundingFamily": --- Original +++ New @@ @@ public function __construct(private readonly float $std_dev, private readonly float $mean, private readonly float $latest, private readonly int $sensitivity) { $this->high = ceil($this->mean + $this->sensitivity * $this->std_dev); - $this->low = floor($this->mean - $this->sensitivity * $this->std_dev); + $this->low = round($this->mean - $this->sensitivity * $this->std_dev); if ($this->latest >= $this->low && $this->latest <= $this->high) { return; }

if ($this->latest >= $this->low && $this->latest <= $this->high) {

Check warning on line 76 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "GreaterThanOrEqualTo": --- Original +++ New @@ @@ { $this->high = ceil($this->mean + $this->sensitivity * $this->std_dev); $this->low = floor($this->mean - $this->sensitivity * $this->std_dev); - if ($this->latest >= $this->low && $this->latest <= $this->high) { + if ($this->latest > $this->low && $this->latest <= $this->high) { return; } if ($this->std_dev > 0.0) {
return;
}

if ($this->std_dev > 0.0) {

Check warning on line 80 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "GreaterThan": --- Original +++ New @@ @@ if ($this->latest >= $this->low && $this->latest <= $this->high) { return; } - if ($this->std_dev > 0.0) { + if ($this->std_dev >= 0.0) { $this->hops = abs($this->mean - $this->latest) / $this->std_dev; } if ($this->latest < $this->low) {
$this->hops = abs($this->mean - $this->latest) / $this->std_dev;

Check warning on line 81 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "Division": --- Original +++ New @@ @@ return; } if ($this->std_dev > 0.0) { - $this->hops = abs($this->mean - $this->latest) / $this->std_dev; + $this->hops = abs($this->mean - $this->latest) * $this->std_dev; } if ($this->latest < $this->low) { $this->direction = self::DIRECTION_DOWN;
}

if ($this->latest < $this->low) {

Check warning on line 84 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "LessThan": --- Original +++ New @@ @@ if ($this->std_dev > 0.0) { $this->hops = abs($this->mean - $this->latest) / $this->std_dev; } - if ($this->latest < $this->low) { + if ($this->latest <= $this->low) { $this->direction = self::DIRECTION_DOWN; } elseif ($this->latest > $this->high) { $this->direction = self::DIRECTION_UP;
$this->direction = self::DIRECTION_DOWN;
} elseif ($this->latest > $this->high) {

Check warning on line 86 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "GreaterThan": --- Original +++ New @@ @@ } if ($this->latest < $this->low) { $this->direction = self::DIRECTION_DOWN; - } elseif ($this->latest > $this->high) { + } elseif ($this->latest >= $this->high) { $this->direction = self::DIRECTION_UP; } }
$this->direction = self::DIRECTION_UP;
}
}
Expand All @@ -118,7 +101,7 @@
*
* @param int $precision the number of decimal digits to round to
*/
public function toArray(int $precision = 2): array

Check warning on line 104 in src/AnomalyDetectionResult.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "DecrementInteger": --- Original +++ New @@ @@ * * @param int $precision the number of decimal digits to round to */ - public function toArray(int $precision = 2) : array + public function toArray(int $precision = 1) : array { return array_map(fn($val) => is_float($val) ? round($val, $precision) : $val, get_object_vars($this)); }
{
return array_map(
fn ($val) => is_float($val) ? round($val, $precision) : $val,
Expand Down
5 changes: 1 addition & 4 deletions src/Cache/MemcachedAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,8 @@
*/
class MemcachedAdapter implements CounterCache
{
private Memcached $cache;

public function __construct(Memcached $memcached)
public function __construct(private readonly Memcached $cache)
{
$this->cache = $memcached;
}

public function increment(string $cache_name, string $cache_key, int $ttl, int $step)
Expand Down
14 changes: 6 additions & 8 deletions src/Cache/WPCacheAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,12 @@
*/
class WPCacheAdapter implements CounterCache
{
/**
* The WordPress object cache instance.
*/
private WP_Object_Cache $cache;

public function __construct(WP_Object_Cache $cache)
{
$this->cache = $cache;
public function __construct(
/**
* The WordPress object cache instance.
*/
private readonly WP_Object_Cache $cache
) {
}

public function increment(string $cache_name, string $cache_key, int $ttl, int $step)
Expand Down
10 changes: 1 addition & 9 deletions src/Helper/Frame.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@
*/
class Frame
{
/** @var int Frame's reference timestamp */
private int $time;

/** @var int The window size */
private int $window_size;

/** @var float The current value */
private ?float $value = null;

Expand All @@ -43,10 +37,8 @@ class Frame
* @param int $time the frame's reference time
* @param int<1, max> $window_size the window size
*/
public function __construct(int $time, int $window_size)
public function __construct(private readonly int $time, private readonly int $window_size)
{
$this->time = $time;
$this->window_size = $window_size;
}

/**
Expand Down
14 changes: 1 addition & 13 deletions src/Helper/FrameBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,27 +30,15 @@
*/
class FrameBuilder
{
/** @var int<1, max> The size of the window in seconds. */
private int $window_size;

/** @var int Maximum number of seconds for the buckets to last in cache. */
private int $observation_period;

/** @var Chorus\TimeKeeper The timekeeper instance. */
private Chorus\TimeKeeper $time_keeper;

/**
* FrameBuilder constructor.
*
* @param int<1, max> $window_size the size of the window in seconds
* @param int $observation_period maximum number of seconds for the buckets to last in cache
* @param Chorus\TimeKeeper $time_keeper the timekeeper instance
*/
public function __construct(int $window_size, int $observation_period, Chorus\TimeKeeper $time_keeper)
public function __construct(private readonly int $window_size, private readonly int $observation_period, private readonly Chorus\TimeKeeper $time_keeper)
{
$this->window_size = $window_size;
$this->observation_period = $observation_period;
$this->time_keeper = $time_keeper;
}

/**
Expand All @@ -58,7 +46,7 @@
*
* @param int $time the frame's reference time
*/
public function newFrame(int $time): Frame

Check warning on line 49 in src/Helper/FrameBuilder.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "PublicVisibility": --- Original +++ New @@ @@ * * @param int $time the frame's reference time */ - public function newFrame(int $time) : Frame + protected function newFrame(int $time) : Frame { return new Frame($time, $this->window_size); }
{
return new Frame($time, $this->window_size);
}
Expand All @@ -73,7 +61,7 @@
*
* @throws InvalidArgumentException If the start time is in the future
*/
public function generateFrames(int $start_time = 0, ?int $end_time = null): iterable

Check warning on line 64 in src/Helper/FrameBuilder.php

View workflow job for this annotation

GitHub Actions / Mutatation testing with PHP 8.3

Escaped Mutant for Mutator "DecrementInteger": --- Original +++ New @@ @@ * * @throws InvalidArgumentException If the start time is in the future */ - public function generateFrames(int $start_time = 0, ?int $end_time = null) : iterable + public function generateFrames(int $start_time = -1, ?int $end_time = null) : iterable { if (null !== $end_time && $end_time < $start_time) { throw new InvalidArgumentException("End time cannot be before start time (start: {$start_time}, end: {$end_time})");
{
if (null !== $end_time && $end_time < $start_time) {
throw new InvalidArgumentException("End time cannot be before start time (start: {$start_time}, end: {$end_time})");
Expand Down
17 changes: 7 additions & 10 deletions src/SlidingWindowCounter.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,19 @@
class SlidingWindowCounter
{
/** @var string Memcached cache name to use for buckets. */
private string $cache_name;
private readonly string $cache_name;

/** @var int The size of the window in seconds. */
private int $window_size;
private readonly int $window_size;

/** @var int Maximum number of seconds for the buckets to last in cache. */
private int $observation_period;

/** @var Cache\CounterCache The counter cache instance */
private Cache\CounterCache $counter_cache;
private readonly int $observation_period;

/** @var Chorus\TimeKeeper The timekeeper instance. */
private Chorus\TimeKeeper $time_keeper;
private readonly Chorus\TimeKeeper $time_keeper;

/** @var Helper\FrameBuilder The frame builder. */
private Helper\FrameBuilder $frame_builder;
private readonly Helper\FrameBuilder $frame_builder;

/**
* Construct a new `SlidingWindowCounter`.
Expand All @@ -68,7 +65,8 @@ public function __construct(
string $cache_name,
int $window_size,
int $observation_period,
Cache\CounterCache $counter_cache,
/** @var Cache\CounterCache The counter cache instance */
private readonly Cache\CounterCache $counter_cache,
Chorus\TimeKeeper $time_keeper = null,
Helper\FrameBuilder $frame_builder = null
) {
Expand All @@ -89,7 +87,6 @@ public function __construct(
$this->cache_name = $cache_name;
$this->window_size = $window_size;
$this->observation_period = $observation_period;
$this->counter_cache = $counter_cache;

// Optional dependencies
$this->time_keeper = $time_keeper ?? new Chorus\TimeKeeper();
Expand Down
8 changes: 4 additions & 4 deletions tests/AnomalyDetectionResultTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
namespace Tests\Automattic\SlidingWindowCounter;

use Automattic\SlidingWindowCounter\AnomalyDetectionResult;
use PHPUnit\Framework\TestCase;

/**
* @covers \Automattic\SlidingWindowCounter\AnomalyDetectionResult
Expand All @@ -40,7 +39,7 @@ public function testHappyPath(): void
$this->assertFalse($result->isAnomaly());
$this->assertSame(AnomalyDetectionResult::DIRECTION_NONE, $result->getDirection());

$this->assertSame([
$this->assertSameSorted([
'std_dev' => 1.0,
'mean' => 10.0,
'sensitivity' => 1,
Expand Down Expand Up @@ -78,7 +77,7 @@ public function testAnomalyDirectionUp(): void

$this->assertTrue($result->isAnomaly());

$this->assertSame([
$this->assertSameSorted([
'std_dev' => 1.0,
'mean' => 10.0,
'sensitivity' => 1,
Expand All @@ -99,7 +98,7 @@ public function testAnomalyDetectionDown(): void

$this->assertTrue($result->isAnomaly());

$this->assertSame([
$this->assertSameSorted([
'std_dev' => 1.0,
'mean' => 10.0,
'sensitivity' => 3,
Expand Down Expand Up @@ -147,4 +146,5 @@ public function testDirections(float $latest, bool $expected_is_anomaly, string
$this->assertSame($expected_is_anomaly, $result->isAnomaly(), 'Unexpected anomaly result');
$this->assertSame($expected_direction, $result->getDirection(), 'Unexpected direction');
}

}
5 changes: 3 additions & 2 deletions tests/SlidingWindowCounterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
use Automattic\SlidingWindowCounter\Helper\Frame;
use Automattic\SlidingWindowCounter\SlidingWindowCounter;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use ReflectionMethod;
use Tests\Automattic\SlidingWindowCounter\Cache\FakeCache;
use Tumblr\Chorus\FakeTimeKeeper;
Expand All @@ -35,6 +34,7 @@
use function Pipeline\take;
use function range;
use function sprintf;
use function ksort;

/**
* @covers \Automattic\SlidingWindowCounter\SlidingWindowCounter
Expand Down Expand Up @@ -247,7 +247,7 @@ public function testVariance(): void

$this->assertSame(5, $counter->getHistoricVariance('test')->getCount());

$this->assertSame([
$this->assertSameSorted([
'std_dev' => 7.24,
'mean' => 6.82,
'sensitivity' => 3,
Expand Down Expand Up @@ -369,4 +369,5 @@ private static function getMaterialValues(
->cast(fn (Frame $frame) => $frame->getValue())
->toArrayPreservingKeys();
}

}
46 changes: 46 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php declare(strict_types=1);
/**
* The Sliding Window Counter, a short-lived time series library.
* Copyright 2023 Automattic, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

namespace Tests\Automattic\SlidingWindowCounter;

use PHPUnit\Framework\TestCase as BaseTestCase;

use function ksort;

/**
* @internal
*/
abstract class TestCase extends BaseTestCase
{
/**
* Asserts that two arrays are equal, ignoring the order of keys.
*
* @param array $expected The expected array
* @param array $actual The actual array
* @param string $message The message to display on failure
* @return void
*/
protected function assertSameSorted(array $expected, array $actual, string $message = ''): void
{
ksort($expected);
ksort($actual);
$this->assertSame($expected, $actual, $message);
}
}
Loading