tempDirectory = $base.DIRECTORY_SEPARATOR.'ls-tool-'.bin2hex(random_bytes(8)); if (! mkdir($this->tempDirectory, 0777, true) && ! is_dir($this->tempDirectory)) { $this->fail('无法创建临时目录:'.$this->tempDirectory); } file_put_contents($this->tempDirectory.DIRECTORY_SEPARATOR.'a.txt', 'a'); file_put_contents($this->tempDirectory.DIRECTORY_SEPARATOR.'b.php', 'tempDirectory.DIRECTORY_SEPARATOR.'.env', 'KEY=VALUE'); mkdir($this->tempDirectory.DIRECTORY_SEPARATOR.'dir', 0777, true); } protected function tearDown(): void { $this->deleteDirectory($this->tempDirectory); parent::tearDown(); } public function test_execute_lists_entries_in_directory(): void { $tool = new LsTool; $result = $tool->execute(['directory' => $this->tempDirectory]); $this->assertSame($this->tempDirectory, $result['directory']); $this->assertSame(['.env', 'a.txt', 'b.php', 'dir'], $result['entries']); } public function test_execute_can_exclude_hidden_entries(): void { $tool = new LsTool; $result = $tool->execute([ 'directory' => $this->tempDirectory, 'include_hidden' => false, ]); $this->assertSame(['a.txt', 'b.php', 'dir'], $result['entries']); } public function test_execute_can_filter_files_only(): void { $tool = new LsTool; $result = $tool->execute([ 'directory' => $this->tempDirectory, 'filter' => 'files', ]); $this->assertSame(['.env', 'a.txt', 'b.php'], $result['entries']); } public function test_execute_can_filter_directories_only(): void { $tool = new LsTool; $result = $tool->execute([ 'directory' => $this->tempDirectory, 'filter' => 'directories', ]); $this->assertSame(['dir'], $result['entries']); } public function test_execute_can_match_glob_pattern(): void { $tool = new LsTool; $result = $tool->execute([ 'directory' => $this->tempDirectory, 'match' => '*.php', ]); $this->assertSame(['b.php'], $result['entries']); } public function test_execute_can_return_details(): void { $tool = new LsTool; $result = $tool->execute([ 'directory' => $this->tempDirectory, 'details' => true, ]); $this->assertCount(4, $result['entries']); $fileEntry = $this->findEntry($result['entries'], 'a.txt'); $this->assertSame('file', $fileEntry['type']); $this->assertIsInt($fileEntry['size']); $this->assertIsInt($fileEntry['modified_at']); $dirEntry = $this->findEntry($result['entries'], 'dir'); $this->assertSame('directory', $dirEntry['type']); $this->assertNull($dirEntry['size']); } public function test_execute_can_sort_by_mtime_desc(): void { $mtimeDirectory = $this->tempDirectory.DIRECTORY_SEPARATOR.'mtime'; mkdir($mtimeDirectory, 0777, true); $older = $mtimeDirectory.DIRECTORY_SEPARATOR.'older.txt'; $newer = $mtimeDirectory.DIRECTORY_SEPARATOR.'newer.txt'; file_put_contents($older, 'old'); file_put_contents($newer, 'new'); $now = time(); touch($older, $now - 10); touch($newer, $now - 5); $tool = new LsTool; $result = $tool->execute([ 'directory' => $mtimeDirectory, 'include_hidden' => false, 'sort' => 'mtime_desc', 'filter' => 'files', 'match' => '*.txt', ]); $this->assertSame(['newer.txt', 'older.txt'], $result['entries']); } public function test_execute_throws_when_directory_is_invalid(): void { $this->expectException(InvalidArgumentException::class); $tool = new LsTool; $tool->execute(['directory' => $this->tempDirectory.DIRECTORY_SEPARATOR.'missing']); } /** * @param array> $entries * @return array */ private function findEntry(array $entries, string $name): array { foreach ($entries as $entry) { if (($entry['name'] ?? null) === $name) { return $entry; } } $this->fail('未找到条目:'.$name); } private function deleteDirectory(string $directory): void { if (! is_dir($directory)) { return; } $items = scandir($directory); if ($items === false) { return; } foreach ($items as $item) { if ($item === '.' || $item === '..') { continue; } $path = $directory.DIRECTORY_SEPARATOR.$item; if (is_dir($path)) { $this->deleteDirectory($path); continue; } @unlink($path); } @rmdir($directory); } }