Initial
This commit is contained in:
150
tests/Algorithms/ExternalGroupByTest.php
Normal file
150
tests/Algorithms/ExternalGroupByTest.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SqrtSpace\SpaceTime\Tests\Algorithms;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use SqrtSpace\SpaceTime\Algorithms\ExternalGroupBy;
|
||||
use SqrtSpace\SpaceTime\SpaceTimeConfig;
|
||||
|
||||
class ExternalGroupByTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
SpaceTimeConfig::configure([
|
||||
'external_storage_path' => sys_get_temp_dir() . '/spacetime_test',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$path = sys_get_temp_dir() . '/spacetime_test';
|
||||
if (is_dir($path)) {
|
||||
array_map('unlink', glob("$path/*"));
|
||||
rmdir($path);
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testBasicGroupBy(): void
|
||||
{
|
||||
$data = [
|
||||
['category' => 'A', 'value' => 1],
|
||||
['category' => 'B', 'value' => 2],
|
||||
['category' => 'A', 'value' => 3],
|
||||
['category' => 'B', 'value' => 4],
|
||||
['category' => 'C', 'value' => 5],
|
||||
];
|
||||
|
||||
$grouped = ExternalGroupBy::groupBy($data, fn($item) => $item['category']);
|
||||
|
||||
$this->assertCount(3, $grouped);
|
||||
$this->assertCount(2, $grouped['A']);
|
||||
$this->assertCount(2, $grouped['B']);
|
||||
$this->assertCount(1, $grouped['C']);
|
||||
|
||||
$this->assertEquals(1, $grouped['A'][0]['value']);
|
||||
$this->assertEquals(3, $grouped['A'][1]['value']);
|
||||
}
|
||||
|
||||
public function testGroupByCount(): void
|
||||
{
|
||||
$data = [
|
||||
['type' => 'foo'],
|
||||
['type' => 'bar'],
|
||||
['type' => 'foo'],
|
||||
['type' => 'baz'],
|
||||
['type' => 'foo'],
|
||||
];
|
||||
|
||||
$counts = ExternalGroupBy::groupByCount($data, fn($item) => $item['type']);
|
||||
|
||||
$this->assertEquals(3, $counts['foo']);
|
||||
$this->assertEquals(1, $counts['bar']);
|
||||
$this->assertEquals(1, $counts['baz']);
|
||||
}
|
||||
|
||||
public function testGroupBySum(): void
|
||||
{
|
||||
$data = [
|
||||
['group' => 'A', 'amount' => 10],
|
||||
['group' => 'B', 'amount' => 20],
|
||||
['group' => 'A', 'amount' => 15],
|
||||
['group' => 'B', 'amount' => 25],
|
||||
];
|
||||
|
||||
$sums = ExternalGroupBy::groupBySum(
|
||||
$data,
|
||||
fn($item) => $item['group'],
|
||||
fn($item) => $item['amount']
|
||||
);
|
||||
|
||||
$this->assertEquals(25, $sums['A']);
|
||||
$this->assertEquals(45, $sums['B']);
|
||||
}
|
||||
|
||||
public function testGroupByAggregate(): void
|
||||
{
|
||||
$data = [
|
||||
['user' => 'john', 'score' => 80],
|
||||
['user' => 'jane', 'score' => 90],
|
||||
['user' => 'john', 'score' => 85],
|
||||
['user' => 'jane', 'score' => 95],
|
||||
];
|
||||
|
||||
$maxScores = ExternalGroupBy::groupByAggregate(
|
||||
$data,
|
||||
fn($item) => $item['user'],
|
||||
fn($max, $item) => max($max ?? 0, $item['score']),
|
||||
0
|
||||
);
|
||||
|
||||
$this->assertEquals(85, $maxScores['john']);
|
||||
$this->assertEquals(95, $maxScores['jane']);
|
||||
}
|
||||
|
||||
public function testGroupByStreaming(): void
|
||||
{
|
||||
$data = [];
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$data[] = [
|
||||
'group' => chr(65 + ($i % 5)), // A-E
|
||||
'value' => $i,
|
||||
];
|
||||
}
|
||||
|
||||
$groups = [];
|
||||
foreach (ExternalGroupBy::groupByStreaming($data, fn($item) => $item['group']) as $key => $items) {
|
||||
$groups[$key] = count($items);
|
||||
}
|
||||
|
||||
$this->assertCount(5, $groups);
|
||||
$this->assertEquals(20, $groups['A']);
|
||||
$this->assertEquals(20, $groups['B']);
|
||||
}
|
||||
|
||||
public function testGroupByWithLimit(): void
|
||||
{
|
||||
$data = [];
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
$data[] = ['key' => "group_$i", 'value' => $i];
|
||||
}
|
||||
|
||||
$grouped = ExternalGroupBy::groupByWithLimit(
|
||||
$data,
|
||||
fn($item) => $item['key'],
|
||||
5 // Small limit to force external storage
|
||||
);
|
||||
|
||||
$this->assertCount(50, $grouped);
|
||||
|
||||
foreach ($grouped as $key => $items) {
|
||||
$this->assertCount(1, $items);
|
||||
$this->assertEquals($key, $items[0]['key']);
|
||||
}
|
||||
}
|
||||
}
|
||||
111
tests/Algorithms/ExternalSortTest.php
Normal file
111
tests/Algorithms/ExternalSortTest.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SqrtSpace\SpaceTime\Tests\Algorithms;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use SqrtSpace\SpaceTime\Algorithms\ExternalSort;
|
||||
use SqrtSpace\SpaceTime\SpaceTimeConfig;
|
||||
|
||||
class ExternalSortTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
SpaceTimeConfig::configure([
|
||||
'external_storage_path' => sys_get_temp_dir() . '/spacetime_test',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$path = sys_get_temp_dir() . '/spacetime_test';
|
||||
if (is_dir($path)) {
|
||||
array_map('unlink', glob("$path/*"));
|
||||
rmdir($path);
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testBasicSort(): void
|
||||
{
|
||||
$data = [5, 2, 8, 1, 9, 3, 7, 4, 6];
|
||||
$sorted = ExternalSort::sort($data);
|
||||
|
||||
$this->assertEquals([1, 2, 3, 4, 5, 6, 7, 8, 9], $sorted);
|
||||
}
|
||||
|
||||
public function testSortWithCustomComparator(): void
|
||||
{
|
||||
$data = [5, 2, 8, 1, 9, 3, 7, 4, 6];
|
||||
$sorted = ExternalSort::sort($data, fn($a, $b) => $b <=> $a);
|
||||
|
||||
$this->assertEquals([9, 8, 7, 6, 5, 4, 3, 2, 1], $sorted);
|
||||
}
|
||||
|
||||
public function testSortBy(): void
|
||||
{
|
||||
$data = [
|
||||
['name' => 'John', 'age' => 25],
|
||||
['name' => 'Jane', 'age' => 30],
|
||||
['name' => 'Bob', 'age' => 20],
|
||||
];
|
||||
|
||||
$sorted = ExternalSort::sortBy($data, fn($item) => $item['age']);
|
||||
|
||||
$this->assertEquals('Bob', $sorted[0]['name']);
|
||||
$this->assertEquals('John', $sorted[1]['name']);
|
||||
$this->assertEquals('Jane', $sorted[2]['name']);
|
||||
}
|
||||
|
||||
public function testLargeDataSet(): void
|
||||
{
|
||||
// Generate large dataset
|
||||
$data = [];
|
||||
for ($i = 0; $i < 20000; $i++) {
|
||||
$data[] = mt_rand(1, 100000);
|
||||
}
|
||||
|
||||
$sorted = ExternalSort::sort($data);
|
||||
|
||||
// Verify it's sorted
|
||||
for ($i = 1; $i < count($sorted); $i++) {
|
||||
$this->assertGreaterThanOrEqual($sorted[$i - 1], $sorted[$i]);
|
||||
}
|
||||
|
||||
// Verify same elements
|
||||
$this->assertEquals(count($data), count($sorted));
|
||||
sort($data);
|
||||
$this->assertEquals($data, $sorted);
|
||||
}
|
||||
|
||||
public function testSortObjects(): void
|
||||
{
|
||||
$objects = [
|
||||
(object)['id' => 3, 'value' => 'c'],
|
||||
(object)['id' => 1, 'value' => 'a'],
|
||||
(object)['id' => 2, 'value' => 'b'],
|
||||
];
|
||||
|
||||
$sorted = ExternalSort::sortBy($objects, fn($obj) => $obj->id);
|
||||
|
||||
$this->assertEquals(1, $sorted[0]->id);
|
||||
$this->assertEquals(2, $sorted[1]->id);
|
||||
$this->assertEquals(3, $sorted[2]->id);
|
||||
}
|
||||
|
||||
public function testStreamingSort(): void
|
||||
{
|
||||
$data = range(10, 1);
|
||||
$result = [];
|
||||
|
||||
foreach (ExternalSort::sortStreaming($data) as $item) {
|
||||
$result[] = $item;
|
||||
}
|
||||
|
||||
$this->assertEquals(range(1, 10), $result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user