propresenter-php/tests/ProPlaylistReaderTest.php
Thorsten Bus 22ba4aff7d refactor: make repo Composer-compatible by moving php/ to root and ref/ to doc/reference_samples
- Move src/, tests/, bin/, generated/, proto/, composer.json, composer.lock, phpunit.xml from php/ to repo root
- Move ref/ to doc/reference_samples/ for better organization
- Remove vendor/ from git tracking (now properly gitignored)
- Update all test file paths (dirname adjustments and ref/ -> doc/reference_samples/)
- Update all documentation paths (AGENTS.md, doc/*.md)
- Remove php.bak/ directory
- All 252 tests pass
2026-03-30 13:26:29 +02:00

193 lines
6.3 KiB
PHP

<?php
declare(strict_types=1);
namespace ProPresenter\Parser\Tests;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use ProPresenter\Parser\PlaylistArchive;
use ProPresenter\Parser\ProPlaylistReader;
use RuntimeException;
class ProPlaylistReaderTest extends TestCase
{
private function repoRoot(): string
{
return dirname(__DIR__);
}
#[Test]
public function readThrowsOnMissingFile(): void
{
$filePath = $this->repoRoot() . '/doc/reference_samples/does-not-exist.proplaylist';
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage(sprintf('Playlist file not found: %s', $filePath));
ProPlaylistReader::read($filePath);
}
#[Test]
public function readThrowsOnEmptyFile(): void
{
$filePath = tempnam(sys_get_temp_dir(), 'proplaylist-empty-');
if ($filePath === false) {
self::fail('Unable to create temporary test file.');
}
try {
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage(sprintf('Playlist file is empty: %s', $filePath));
ProPlaylistReader::read($filePath);
} finally {
@unlink($filePath);
}
}
#[Test]
public function readThrowsOnInvalidZipFormat(): void
{
$filePath = tempnam(sys_get_temp_dir(), 'proplaylist-invalid-');
if ($filePath === false) {
self::fail('Unable to create temporary test file.');
}
try {
file_put_contents($filePath, 'not-a-zip-archive');
$this->expectException(RuntimeException::class);
ProPlaylistReader::read($filePath);
} finally {
@unlink($filePath);
}
}
#[Test]
public function readReturnsPlaylistArchiveForTestPlaylist(): void
{
$archive = ProPlaylistReader::read($this->repoRoot() . '/doc/reference_samples/TestPlaylist.proplaylist');
$this->assertInstanceOf(PlaylistArchive::class, $archive);
$this->assertSame('TestPlaylist', $archive->getName());
$this->assertGreaterThan(0, $archive->getEntryCount());
}
#[Test]
public function readExtractsEmbeddedFilesFromTestPlaylist(): void
{
$archive = ProPlaylistReader::read($this->repoRoot() . '/doc/reference_samples/TestPlaylist.proplaylist');
$embeddedFiles = $archive->getEmbeddedFiles();
$this->assertNotEmpty($embeddedFiles);
$this->assertArrayNotHasKey('data', $embeddedFiles);
$this->assertGreaterThanOrEqual(2, count($archive->getEmbeddedProFiles()));
$this->assertGreaterThanOrEqual(1, count($archive->getEmbeddedMediaFiles()));
}
#[Test]
public function readParsesEmbeddedSongsLazilyFromTestPlaylist(): void
{
$archive = ProPlaylistReader::read($this->repoRoot() . '/doc/reference_samples/TestPlaylist.proplaylist');
$embeddedProFiles = $archive->getEmbeddedProFiles();
$this->assertNotEmpty($embeddedProFiles);
$firstProFilename = array_key_first($embeddedProFiles);
$this->assertNotNull($firstProFilename);
$song = $archive->getEmbeddedSong((string) $firstProFilename);
$this->assertNotNull($song);
$this->assertNotSame('', $song->getName());
}
#[Test]
public function readHandlesGottesdienstPlaylist(): void
{
$archive = ProPlaylistReader::read($this->repoRoot() . '/doc/reference_samples/ExamplePlaylists/Gottesdienst.proplaylist');
$this->assertNotSame('', $archive->getName());
$this->assertGreaterThan(0, $archive->getEntryCount());
$this->assertNotEmpty($archive->getEmbeddedFiles());
}
#[Test]
public function readHandlesGottesdienst2Playlist(): void
{
$archive = ProPlaylistReader::read($this->repoRoot() . '/doc/reference_samples/ExamplePlaylists/Gottesdienst 2.proplaylist');
$this->assertNotSame('', $archive->getName());
$this->assertGreaterThan(0, $archive->getEntryCount());
$this->assertNotEmpty($archive->getEmbeddedFiles());
}
#[Test]
public function readHandlesGottesdienst3Playlist(): void
{
$archive = ProPlaylistReader::read($this->repoRoot() . '/doc/reference_samples/ExamplePlaylists/Gottesdienst 3.proplaylist');
$this->assertNotSame('', $archive->getName());
$this->assertGreaterThan(0, $archive->getEntryCount());
$this->assertNotEmpty($archive->getEmbeddedFiles());
}
#[Test]
public function readCleansUpTempFileWhenZipOpenFails(): void
{
$filePath = tempnam(sys_get_temp_dir(), 'proplaylist-badzip-');
if ($filePath === false) {
self::fail('Unable to create temporary test file.');
}
$before = glob(sys_get_temp_dir() . '/proplaylist-*');
if ($before === false) {
$before = [];
}
try {
file_put_contents($filePath, str_repeat('x', 128));
try {
ProPlaylistReader::read($filePath);
self::fail('Expected RuntimeException was not thrown.');
} catch (RuntimeException) {
}
$after = glob(sys_get_temp_dir() . '/proplaylist-*');
if ($after === false) {
$after = [];
}
sort($before);
sort($after);
$this->assertSame($before, $after);
} finally {
@unlink($filePath);
}
}
#[Test]
public function readThrowsWhenDataEntryIsMissing(): void
{
$filePath = tempnam(sys_get_temp_dir(), 'proplaylist-nodata-');
if ($filePath === false) {
self::fail('Unable to create temporary test file.');
}
$zip = new \ZipArchive();
try {
$openResult = $zip->open($filePath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$this->assertTrue($openResult === true, sprintf('Unable to open zip for test, code: %s', (string) $openResult));
$zip->addFromString('song1.pro', 'dummy-song');
$zip->close();
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage(sprintf('Missing data entry in playlist archive: %s', $filePath));
ProPlaylistReader::read($filePath);
} finally {
@unlink($filePath);
}
}
}