FSPathWindowsTest.php•7.07 kB
<?php
declare(strict_types=1);
namespace Tests\Unit\Application;
use Butschster\ContextGenerator\Application\FSPath;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
#[CoversClass(FSPath::class)]
final class FSPathWindowsTest extends TestCase
{
private const string WINDOWS_DIRECTORY_SEPARATOR = '\\';
private bool $isSeparatorOverridden = false;
public static function provideRelativePathTests(): \Generator
{
yield 'same path' => [
'C:\\Users\\test',
'C:\\Users\\test',
'.',
];
yield 'child directory' => [
'C:\\Users',
'C:\\Users\\test',
'test',
];
yield 'sibling directory' => [
'C:\\Users\\admin',
'C:\\Users\\test',
'..\\test',
];
yield 'different drives' => [
'D:\\Work',
'C:\\Users',
'C:\\Users', // Should return absolute path when drives differ
];
yield 'deeper path' => [
'C:\\Users',
'C:\\Users\\test\\documents\\file.txt',
'test\\documents\\file.txt',
];
}
#[Test]
public function it_should_create_path_from_string(): void
{
$path = FSPath::create('C:\\Users\\test');
$this->assertSame('C:\\Users\\test', $path->toString());
}
#[Test]
public function it_should_join_paths_correctly(): void
{
$base = FSPath::create('C:\\Users');
$joined = $base->join('test', 'documents');
$this->assertSame('C:\\Users\\test\\documents', $joined->toString());
$joined = $base->join('');
$this->assertSame('C:\\Users', $joined->toString());
$joined = $base->join('test', 'documents', '..\\..\\Current');
$this->assertSame('C:\\Users\\Current', $joined->toString());
}
#[Test]
public function it_should_handle_name_operations(): void
{
$path = FSPath::create('C:\\Users\\test\\document.txt');
$newPath = $path->withName('new.txt');
$this->assertSame('C:\\Users\\test\\new.txt', $newPath->toString());
$extension = $path->extension();
$this->assertSame('txt', $extension);
}
#[Test]
public function it_should_handle_extension_operations(): void
{
$path = FSPath::create('C:\\Users\\test\\document.txt');
$newPath = $path->withExt('md');
$this->assertSame('C:\\Users\\test\\document.md', $newPath->toString());
$newPath = $path->withExt('.md');
$this->assertSame('C:\\Users\\test\\document.md', $newPath->toString());
$newPath = $path->withStem('report');
$this->assertSame('C:\\Users\\test\\report.txt', $newPath->toString());
}
#[Test]
public function it_should_get_parent_directory(): void
{
$path = FSPath::create('C:\\Users\\test\\document.txt');
$parent = $path->parent();
$this->assertSame('C:\\Users\\test', $parent->toString());
$grandParent = $parent->parent();
$this->assertSame('C:\\Users', $grandParent->toString());
}
#[Test]
public function it_should_handle_root_paths(): void
{
$root = FSPath::create('C:\\');
// Root parent should be itself
$this->assertSame('C:\\', $root->parent()->toString());
// These operations should not change a root path
$this->assertSame('C:\\', $root->withName('test')->toString());
$this->assertSame('C:\\', $root->withExt('txt')->toString());
$this->assertSame('C:\\', $root->withStem('test')->toString());
}
#[Test]
public function it_should_check_path_types(): void
{
$absolutePath = FSPath::create('C:\\Users\\test');
$this->assertTrue($absolutePath->isAbsolute());
$this->assertFalse($absolutePath->isRelative());
$relativePath = FSPath::create('Users\\test');
$this->assertFalse($relativePath->isAbsolute());
$this->assertTrue($relativePath->isRelative());
}
#[Test]
#[DataProvider('provideRelativePathTests')]
public function it_should_handle_relative_paths(string $from, string $to, string $expected): void
{
$fromPath = FSPath::create($from);
$toPath = FSPath::create($to);
$relativePath = $toPath->relativeTo($fromPath);
$this->assertSame($expected, $relativePath->toString());
}
#[Test]
public function it_should_normalize_paths(): void
{
// Mixed separators
$path = FSPath::create('C:/Users\\test/documents');
$this->assertSame('C:\\Users\\test\\documents', $path->toString());
// Double separators
$path = FSPath::create('C:\\\\Users\\\\\\test');
$this->assertSame('C:\\Users\\test', $path->toString());
// Dot segments
$path = FSPath::create('C:\\Users\\.\\test\\..\\admin');
$this->assertSame('C:\\Users\\admin', $path->toString());
}
#[Test]
public function it_should_split_path_into_parts(): void
{
$path = FSPath::create('C:\\Users\\test\\documents\\file.txt');
$parts = $path->parts();
$this->assertSame(['C:', 'Users', 'test', 'documents', 'file.txt'], $parts);
}
#[Test]
public function it_should_handle_stringable_interface(): void
{
$path = FSPath::create('C:\\Users\\test');
$this->assertSame('C:\\Users\\test', (string) $path);
$this->assertSame($path->toString(), (string) $path);
}
#[Test]
public function it_should_handle_empty_paths(): void
{
$path = FSPath::create('');
$this->assertSame('.', $path->toString());
$this->assertFalse($path->isAbsolute());
$this->assertTrue($path->isRelative());
}
#[Test]
public function it_should_properly_add_extension_prefix_when_changing_stem(): void
{
// Test specifically for the bug where withStem doesn't add the period before extension
$path = FSPath::create('C:\\Users\\test\\document.txt');
$newPath = $path->withStem('report');
$this->assertSame('C:\\Users\\test\\report.txt', $newPath->toString());
// Also test with no extension
$path = FSPath::create('C:\\Users\\test\\document');
$newPath = $path->withStem('report');
$this->assertSame('C:\\Users\\test\\report', $newPath->toString());
}
protected function setUp(): void
{
// Override directory separator for testing on non-Windows systems
if (DIRECTORY_SEPARATOR !== self::WINDOWS_DIRECTORY_SEPARATOR) {
FSPath::setDirectorySeparator(self::WINDOWS_DIRECTORY_SEPARATOR);
$this->isSeparatorOverridden = true;
}
}
#[\Override]
protected function tearDown(): void
{
// Reset directory separator if it was overridden
if ($this->isSeparatorOverridden) {
FSPath::setDirectorySeparator(null);
}
}
}