FileTreeBuilderTest.php•8.95 kB
<?php
declare(strict_types=1);
namespace Tests\Unit\Lib\TreeBuilder;
use Butschster\ContextGenerator\Lib\TreeBuilder\FileTreeBuilder;
use Butschster\ContextGenerator\Lib\TreeBuilder\TreeRendererInterface;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;
#[CoversClass(FileTreeBuilder::class)]
final class FileTreeBuilderTest extends TestCase
{
private string $fixturesDir;
private string $tempDir;
#[Test]
public function it_should_build_tree_with_default_renderer(): void
{
// Since we can't mock the final AsciiTreeRenderer class, we'll test with real files
$builder = new FileTreeBuilder();
$files = [
$this->tempDir . '/file1.txt',
$this->tempDir . '/dir1',
$this->tempDir . '/dir1/file2.txt',
];
$result = $builder->buildTree($files, $this->tempDir);
// Use the heredoc syntax for clear assertion of the expected tree structure
$expectedTree = <<<TREE
└── dir1/
├── file2.txt
└── file1.txt
TREE;
$this->assertSame($expectedTree . "\n", $result);
}
#[Test]
public function it_should_use_custom_renderer(): void
{
$renderer = $this->createMockRenderer('custom-tree-output');
$builder = new FileTreeBuilder($renderer);
$files = [
$this->tempDir . '/file1.txt',
$this->tempDir . '/dir1',
];
$result = $builder->buildTree($files, $this->tempDir);
$this->assertSame('custom-tree-output', $result);
}
#[Test]
public function it_should_pass_options_to_renderer(): void
{
$options = [
'showSize' => true,
'showLastModified' => true,
'includeFiles' => false,
];
$renderer = $this->createMock(TreeRendererInterface::class);
$renderer
->expects($this->once())
->method('render')
->with(
$this->anything(),
$this->equalTo($options),
)
->willReturn('tree-with-options');
$builder = new FileTreeBuilder($renderer);
$files = [
$this->tempDir . '/file1.txt',
$this->tempDir . '/dir1',
];
$result = $builder->buildTree($files, $this->tempDir, $options);
$this->assertSame('tree-with-options', $result);
}
#[Test]
public function it_should_handle_empty_file_list(): void
{
$builder = new FileTreeBuilder();
$result = $builder->buildTree([], $this->tempDir);
// Empty tree should be an empty string
$this->assertSame('', $result);
}
#[Test]
public function it_should_normalize_windows_paths(): void
{
$builder = new FileTreeBuilder();
// Create simple test files in fixed locations for consistent testing
\mkdir($this->tempDir . '/windows/path', 0777, true);
\file_put_contents($this->tempDir . '/windows/path/file.txt', 'content');
$windowsPaths = [
\str_replace('/', '\\', $this->tempDir . '/windows/path/file.txt'),
\str_replace('/', '\\', $this->tempDir . '/windows/path'),
];
$result = $builder->buildTree($windowsPaths, \str_replace('/', '\\', $this->tempDir));
$expectedTree = <<<TREE
└── windows/
└── path/
└── file.txt
TREE;
$this->assertSame($expectedTree . "\n", $result);
}
#[Test]
public function it_should_handle_mixed_path_separators(): void
{
$builder = new FileTreeBuilder();
// Create files and directories for testing
\mkdir($this->tempDir . '/mixed/unix/path', 0777, true);
\mkdir($this->tempDir . '/mixed/windows/path', 0777, true);
\file_put_contents($this->tempDir . '/mixed/unix/path/unix-file.txt', 'content');
\file_put_contents($this->tempDir . '/mixed/windows/path/windows-file.txt', 'content');
// Create a mix of Windows and Unix paths
$mixedPaths = [
$this->tempDir . '/mixed/unix/path/unix-file.txt',
\str_replace('/', '\\', $this->tempDir . '/mixed/windows/path/windows-file.txt'),
$this->tempDir . '/mixed/unix/path',
\str_replace('/', '\\', $this->tempDir . '/mixed/windows/path'),
];
$result = $builder->buildTree($mixedPaths, $this->tempDir);
$expectedTree = <<<TREE
└── mixed/
└── unix/
├── path/
│ └── unix-file.txt
└── windows/
└── path/
└── windows-file.txt
TREE;
$this->assertSame($expectedTree . "\n", $result);
}
#[Test]
public function it_should_detect_directories_correctly(): void
{
$builder = new FileTreeBuilder();
$files = [
$this->tempDir . '/dir1', // Directory
$this->tempDir . '/file1.txt', // File
];
$result = $builder->buildTree($files, $this->tempDir);
$expectedTree = <<<TREE
└── dir1/
└── file1.txt
TREE;
$this->assertSame($expectedTree . "\n", $result);
}
#[Test]
public function it_should_handle_iterator_input(): void
{
$builder = new FileTreeBuilder();
$files = [
$this->tempDir . '/file1.txt',
$this->tempDir . '/dir1',
];
$result = $builder->buildTree($files, $this->tempDir);
$expectedTree = <<<TREE
└── dir1/
└── file1.txt
TREE;
$this->assertSame($expectedTree . "\n", $result);
}
#[Test]
public function it_should_maintain_correct_relative_paths(): void
{
$builder = new FileTreeBuilder();
$basePath = $this->tempDir . '/dir2';
$files = [
$this->tempDir . '/dir2/file3.txt',
$this->tempDir . '/dir2/subdir/file4.txt',
];
$result = $builder->buildTree($files, $basePath);
$expectedTree = <<<TREE
└── file3.txt
└── subdir/
└── file4.txt
TREE;
$this->assertSame($expectedTree . "\n", $result);
}
#[Test]
public function it_should_handle_options_for_tree_rendering(): void
{
$builder = new FileTreeBuilder();
$files = [
$this->tempDir . '/dir1',
$this->tempDir . '/dir1/file2.txt',
$this->tempDir . '/file1.txt',
];
// Test with includeFiles=false option
$result = $builder->buildTree($files, $this->tempDir, ['includeFiles' => false]);
$expectedTree = <<<TREE
└── dir1/
TREE;
$this->assertSame($expectedTree . "\n", $result);
}
protected function setUp(): void
{
$this->fixturesDir = \dirname(__DIR__, 3) . '/fixtures/TreeBuilder';
// Create fixtures directory if it doesn't exist
if (!\is_dir($this->fixturesDir)) {
\mkdir($this->fixturesDir, 0777, true);
}
// Create temp directory for file operations
$this->tempDir = \sys_get_temp_dir() . '/test-tree-builder-' . \uniqid();
\mkdir($this->tempDir, 0777, true);
// Create some test files and directories
\mkdir($this->tempDir . '/dir1', 0777, true);
\mkdir($this->tempDir . '/dir2/subdir', 0777, true);
\file_put_contents($this->tempDir . '/file1.txt', 'content');
\file_put_contents($this->tempDir . '/dir1/file2.txt', 'content');
\file_put_contents($this->tempDir . '/dir2/file3.txt', 'content');
\file_put_contents($this->tempDir . '/dir2/subdir/file4.txt', 'content');
}
#[\Override]
protected function tearDown(): void
{
$this->removeDirectory($this->tempDir);
}
private function removeDirectory(string $dir): void
{
if (!\is_dir($dir)) {
return;
}
$files = \array_diff(\scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = "$dir/$file";
if (\is_dir($path)) {
$this->removeDirectory($path);
} else {
\unlink($path);
}
}
\rmdir($dir);
}
/**
* Create a mock TreeRenderer that returns a predictable output
*/
private function createMockRenderer(string $returnValue = 'mock-tree-output'): TreeRendererInterface
{
$renderer = $this->createMock(TreeRendererInterface::class);
$renderer->method('render')->willReturn($returnValue);
return $renderer;
}
}