- 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
300 lines
9.8 KiB
PHP
300 lines
9.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace ProPresenter\Parser\Tests;
|
|
|
|
use PHPUnit\Framework\Attributes\Test;
|
|
use PHPUnit\Framework\TestCase;
|
|
use ProPresenter\Parser\ProFileReader;
|
|
use RuntimeException;
|
|
|
|
class MassValidationTest extends TestCase
|
|
{
|
|
private const EMPTY_FILE = 'Du machst alles neu_ver2025-05-11-4.pro';
|
|
|
|
private static function refPath(): string
|
|
{
|
|
return dirname(__DIR__) . '/doc/reference_samples/';
|
|
}
|
|
|
|
/**
|
|
* Collect all .pro files from doc/reference_samples/all-songs/ plus doc/reference_samples/Test.pro.
|
|
*
|
|
* @return string[]
|
|
*/
|
|
private static function collectAllProFiles(): array
|
|
{
|
|
$dir = self::refPath() . 'all-songs/';
|
|
$files = glob($dir . '*.pro');
|
|
self::assertNotFalse($files);
|
|
|
|
$testPro = self::refPath() . 'Test.pro';
|
|
self::assertFileExists($testPro);
|
|
$files[] = $testPro;
|
|
|
|
sort($files);
|
|
|
|
return $files;
|
|
}
|
|
|
|
#[Test]
|
|
public function emptyFileThrowsRuntimeException(): void
|
|
{
|
|
$emptyFile = self::refPath() . 'all-songs/' . self::EMPTY_FILE;
|
|
$this->assertFileExists($emptyFile);
|
|
$this->assertSame(0, filesize($emptyFile));
|
|
|
|
$this->expectException(RuntimeException::class);
|
|
ProFileReader::read($emptyFile);
|
|
}
|
|
|
|
#[Test]
|
|
public function allNonEmptyProFilesParseSuccessfully(): void
|
|
{
|
|
$allFiles = self::collectAllProFiles();
|
|
|
|
$totalFiles = 0;
|
|
$totalGroups = 0;
|
|
$totalSlides = 0;
|
|
$totalTranslations = 0;
|
|
$totalArrangements = 0;
|
|
$skipped = 0;
|
|
|
|
foreach ($allFiles as $file) {
|
|
$basename = basename($file);
|
|
|
|
// Skip the known empty file
|
|
if ($basename === self::EMPTY_FILE) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
$song = ProFileReader::read($file);
|
|
$totalFiles++;
|
|
|
|
// Song name must be non-empty
|
|
$this->assertNotSame('', $song->getName(), sprintf(
|
|
'Song name should not be empty for %s',
|
|
$basename,
|
|
));
|
|
|
|
// Groups must be an array
|
|
$groups = $song->getGroups();
|
|
$this->assertIsArray($groups, sprintf(
|
|
'getGroups() should return array for %s',
|
|
$basename,
|
|
));
|
|
|
|
// Slides must be an array
|
|
$slides = $song->getSlides();
|
|
$this->assertIsArray($slides, sprintf(
|
|
'getSlides() should return array for %s',
|
|
$basename,
|
|
));
|
|
|
|
// Validate each group — name may be empty in non-song files
|
|
foreach ($groups as $group) {
|
|
$this->assertIsString($group->getName(), sprintf(
|
|
'Group getName() should return a string in %s',
|
|
$basename,
|
|
));
|
|
$totalGroups++;
|
|
}
|
|
|
|
// Validate each slide — text elements may exist with empty text (placeholder shapes)
|
|
foreach ($slides as $slide) {
|
|
$plainText = $slide->getPlainText();
|
|
|
|
// getPlainText() must return a string (may be empty for non-content slides)
|
|
$this->assertIsString($plainText, sprintf(
|
|
'getPlainText() should return a string for slide in %s',
|
|
$basename,
|
|
));
|
|
|
|
if ($slide->hasTranslation()) {
|
|
$translation = $slide->getTranslation();
|
|
$this->assertNotNull($translation, sprintf(
|
|
'Slide reporting hasTranslation() should return non-null getTranslation() in %s',
|
|
$basename,
|
|
));
|
|
$totalTranslations++;
|
|
}
|
|
|
|
$totalSlides++;
|
|
}
|
|
|
|
$totalArrangements += count($song->getArrangements());
|
|
}
|
|
|
|
$this->assertSame(1, $skipped, 'Expected exactly 1 empty file to be skipped');
|
|
|
|
// 168 non-empty files from all-songs + 1 Test.pro = at least 169
|
|
// But we skip 1, so 168 parseable from all-songs + 1 Test.pro = 169 parsed
|
|
$this->assertGreaterThanOrEqual(169, $totalFiles, 'Expected at least 169 non-empty files parsed');
|
|
|
|
fwrite(STDERR, sprintf(
|
|
"\n[MassValidation] %d files parsed, %d skipped | %d groups, %d slides, %d translations, %d arrangements\n",
|
|
$totalFiles,
|
|
$skipped,
|
|
$totalGroups,
|
|
$totalSlides,
|
|
$totalTranslations,
|
|
$totalArrangements,
|
|
));
|
|
}
|
|
|
|
#[Test]
|
|
public function songsWithoutArrangementsAreValid(): void
|
|
{
|
|
$allFiles = self::collectAllProFiles();
|
|
$noArrangementCount = 0;
|
|
|
|
foreach ($allFiles as $file) {
|
|
if (basename($file) === self::EMPTY_FILE) {
|
|
continue;
|
|
}
|
|
|
|
$song = ProFileReader::read($file);
|
|
|
|
if (count($song->getArrangements()) === 0) {
|
|
$noArrangementCount++;
|
|
// Still valid: name and groups must work
|
|
$this->assertNotSame('', $song->getName(), sprintf(
|
|
'Song without arrangements should still have a name: %s',
|
|
basename($file),
|
|
));
|
|
$this->assertIsArray($song->getGroups());
|
|
$this->assertIsArray($song->getSlides());
|
|
}
|
|
}
|
|
|
|
// Some files are expected to have no arrangements
|
|
$this->assertGreaterThan(0, $noArrangementCount, 'Expected at least some files without arrangements');
|
|
|
|
fwrite(STDERR, sprintf(
|
|
"\n[MassValidation] %d files have zero arrangements\n",
|
|
$noArrangementCount,
|
|
));
|
|
}
|
|
|
|
#[Test]
|
|
public function nonSongFilesParseWithGroups(): void
|
|
{
|
|
$refDir = self::refPath() . 'all-songs/';
|
|
|
|
// These are non-song files (announcements, moderation, thema)
|
|
$patterns = [
|
|
'MODERATION' => $refDir . '-- MODERATION --.pro',
|
|
];
|
|
|
|
// Use glob for UTF-8 filenames
|
|
$ankMatches = glob($refDir . '-- ANK*DIGUNGEN --.pro');
|
|
$this->assertNotFalse($ankMatches);
|
|
$this->assertNotEmpty($ankMatches, 'ANKÜNDIGUNGEN file should exist');
|
|
$patterns['ANKUENDIGUNGEN'] = $ankMatches[0];
|
|
|
|
$themaMatches = glob($refDir . 'THEMA*');
|
|
$this->assertNotFalse($themaMatches);
|
|
$this->assertNotEmpty($themaMatches, 'THEMA files should exist');
|
|
$patterns['THEMA'] = $themaMatches[0];
|
|
|
|
foreach ($patterns as $label => $file) {
|
|
$this->assertFileExists($file, sprintf('%s file should exist', $label));
|
|
$song = ProFileReader::read($file);
|
|
|
|
$this->assertNotSame('', $song->getName(), sprintf(
|
|
'%s should have a name',
|
|
$label,
|
|
));
|
|
|
|
// Non-song files have groups but may have no text in slides
|
|
$this->assertIsArray($song->getGroups(), sprintf(
|
|
'%s should return groups array',
|
|
$label,
|
|
));
|
|
}
|
|
}
|
|
|
|
#[Test]
|
|
public function transFilesHaveTranslations(): void
|
|
{
|
|
$refDir = self::refPath() . 'all-songs/';
|
|
$transFiles = glob($refDir . '*[TRANS]*.pro');
|
|
$this->assertNotFalse($transFiles);
|
|
$this->assertNotEmpty($transFiles, 'Expected [TRANS] files to exist');
|
|
|
|
$filesWithTranslations = 0;
|
|
|
|
foreach ($transFiles as $file) {
|
|
$song = ProFileReader::read($file);
|
|
$slides = $song->getSlides();
|
|
$hasAnyTranslation = false;
|
|
|
|
foreach ($slides as $slide) {
|
|
if ($slide->hasTranslation()) {
|
|
$hasAnyTranslation = true;
|
|
$translation = $slide->getTranslation();
|
|
$this->assertNotNull($translation, sprintf(
|
|
'Translation should be non-null when hasTranslation() is true in %s',
|
|
basename($file),
|
|
));
|
|
}
|
|
}
|
|
|
|
if ($hasAnyTranslation) {
|
|
$filesWithTranslations++;
|
|
}
|
|
}
|
|
|
|
$this->assertGreaterThan(0, $filesWithTranslations, 'Expected at least some [TRANS] files to have translations');
|
|
|
|
fwrite(STDERR, sprintf(
|
|
"\n[MassValidation] %d of %d [TRANS] files have slides with translations\n",
|
|
$filesWithTranslations,
|
|
count($transFiles),
|
|
));
|
|
}
|
|
|
|
#[Test]
|
|
public function everyGroupHasConsistentSlideUuids(): void
|
|
{
|
|
$allFiles = self::collectAllProFiles();
|
|
$totalGroupsChecked = 0;
|
|
|
|
foreach ($allFiles as $file) {
|
|
if (basename($file) === self::EMPTY_FILE) {
|
|
continue;
|
|
}
|
|
|
|
$song = ProFileReader::read($file);
|
|
|
|
foreach ($song->getGroups() as $group) {
|
|
$slideUuids = $group->getSlideUuids();
|
|
$this->assertIsArray($slideUuids, sprintf(
|
|
'Group "%s" in %s should return slide UUIDs array',
|
|
$group->getName(),
|
|
basename($file),
|
|
));
|
|
|
|
// Each UUID referenced by the group should resolve to a slide
|
|
$resolvedSlides = $song->getSlidesForGroup($group);
|
|
$this->assertIsArray($resolvedSlides);
|
|
$this->assertCount(count($slideUuids), $resolvedSlides, sprintf(
|
|
'Group "%s" in %s: all %d slide UUIDs should resolve',
|
|
$group->getName(),
|
|
basename($file),
|
|
count($slideUuids),
|
|
));
|
|
|
|
$totalGroupsChecked++;
|
|
}
|
|
}
|
|
|
|
fwrite(STDERR, sprintf(
|
|
"\n[MassValidation] %d groups verified with consistent slide UUID resolution\n",
|
|
$totalGroupsChecked,
|
|
));
|
|
}
|
|
}
|