Files
XC_VM/tests/Unit/XmlStringStreamerTest.php
T
Divarion_D d3555ebb20 test(core): cover SettingsManager, DomainResolver, RequestManager, XML streamer
Second coverage batch — config, request state and the XML parser:

- SettingsManagerTest: set/getAll roundtrip, update, and the typed getters.
  Locks the deliberate split on a present-null value (get()/getArray() fall back
  via ?? while getBool/getInt/getString cast), and getBool()'s PHP truthiness.
- DomainResolverTest: the non-proxied resolve() path — forced vs kept protocol,
  https on :443, Host-header port stripping, and the domain_name / server_ip
  fallbacks. Captures a latent bug: an https:// prefix in domain_name yields
  'https:host' because '/' is stripped before 'https://' in the str_replace
  order (documented in the test so a fix updates it deliberately).
- RequestManagerTest: same store shape as SettingsManager, with has() on isset
  semantics (present-null reads as absent).
- XmlStringStreamerTest: end-to-end StringWalker (depth-2 children) and
  UniqueNode (named element) parsing over an in-memory stream, empty document,
  the File "missing path" and UniqueNode "missing option" error paths.

+26 tests. Suite: 634 tests, 0 errors.
2026-09-13 14:39:43 +03:00

86 lines
2.8 KiB
PHP

<?php
use XcVm\Core\Parsing\File;
use XcVm\Core\Parsing\StringWalker;
use XcVm\Core\Parsing\UniqueNode;
use XcVm\Core\Parsing\XmlStringStreamer;
use PHPUnit\Framework\TestCase;
/**
* XmlStringStreamer — the vendored xml-string-streamer used to parse large EPG /
* playlist XML one node at a time. These drive the real StringWalker and
* UniqueNode parsers end to end over an in-memory stream, plus the File stream
* source and the parsers' error paths.
*/
final class XmlStringStreamerTest extends TestCase {
/** Build a seekable in-memory stream handle from a string. */
private function streamFrom(string $xml)
{
$handle = fopen('php://memory', 'r+');
fwrite($handle, $xml);
rewind($handle);
return $handle;
}
/** Drain every node the streamer yields. */
private function collect(XmlStringStreamer $streamer): array {
$nodes = [];
while (($node = $streamer->getNode()) !== false) {
$nodes[] = $node;
}
return $nodes;
}
public function testStringWalkerYieldsEachRootChildNode(): void {
$xml = '<?xml version="1.0"?><tv>'
. '<programme id="1">Alpha</programme>'
. '<programme id="2">Beta</programme>'
. '</tv>';
$nodes = $this->collect(XmlStringStreamer::createStringWalkerParser($this->streamFrom($xml)));
$this->assertCount(2, $nodes, 'one node per depth-2 child');
$this->assertStringContainsString('id="1"', $nodes[0]);
$this->assertStringContainsString('Alpha', $nodes[0]);
$this->assertStringContainsString('id="2"', $nodes[1]);
$this->assertStringContainsString('Beta', $nodes[1]);
}
public function testUniqueNodeParserExtractsNamedElements(): void {
$xml = '<tv><channel id="c1"><name>One</name></channel>'
. '<channel id="c2"><name>Two</name></channel></tv>';
$streamer = XmlStringStreamer::createUniqueNodeParser(
$this->streamFrom($xml),
['uniqueNode' => 'channel']
);
$nodes = $this->collect($streamer);
$this->assertCount(2, $nodes);
$this->assertStringContainsString('id="c1"', $nodes[0]);
$this->assertStringContainsString('<name>One</name>', $nodes[0]);
$this->assertStringContainsString('id="c2"', $nodes[1]);
}
public function testEmptyDocumentYieldsNoNodes(): void {
$nodes = $this->collect(XmlStringStreamer::createStringWalkerParser($this->streamFrom('<tv></tv>')));
$this->assertSame([], $nodes);
}
public function testUniqueNodeRequiresTheUniqueNodeOption(): void {
$this->expectException(\Exception::class);
new UniqueNode([]);
}
public function testFileThrowsWhenPathDoesNotExist(): void {
$this->expectException(\Exception::class);
new File('/no/such/file/xc_vm_test_missing.xml');
}
public function testFileAcceptsAStreamResource(): void {
$file = new File($this->streamFrom('<r><a/></r>'));
$this->assertIsString($file->getChunk(), 'reads a chunk from the handle');
}
}