A lax Web news feed parser
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

209 lines
7.2 KiB

<?php
/** @license MIT
* Copyright 2018 J. King
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Lax\TestCase\JSON;
4 years ago
/* Test format is as follows:
Each test is a YAML map with the following keys:
4 years ago
- `input`: The test input, as a string or directly as a JSON Feed structure
- `output`: The result of the parsing upon success; described in more detail below
- `exception`: The exception ID thrown upon failure
- `type`: An HTTP Content-Type (with or without parameters) for the document
- `doc_url`: A fictitious URL where a newsfeed might be located, used for relative URL resolution
4 years ago
The 'input' key along with either 'output' or 'exception' are required for all tests.
4 years ago
The test output is necessarily mangled due to the limits of YAML:
4 years ago
- Any field which should be an absolute URL should be written as a string,
which will be transformed accordingly. Relative URLs should be represented
as a sequence with the relative part first, followed by the base that should
be applied to it
- Any collections should be represented as sequences of maps, which will
4 years ago
all be transformed accordingly
- Rich text can either be supplied as a string (which will yield a Text object
with plain-text content) or as a map with any of the properties of the
4 years ago
Text class listed
The transformations as performed by the `makeFeed` and `makeEntry` methods
4 years ago
of the abstract test case.
*/
4 years ago
use JKingWeb\Lax\Date;
use JKingWeb\Lax\Feed;
use JKingWeb\Lax\Entry;
use JKingWeb\Lax\Text;
use JKingWeb\Lax\Url;
use JKingWeb\Lax\Parser\Exception;
use JKingWeb\Lax\Parser\JSON\Feed as Parser;
use JKingWeb\Lax\Person\Person;
use JKingWeb\Lax\Category\Category;
4 years ago
use JKingWeb\Lax\Enclosure\Enclosure;
use JKingWeb\Lax\Person\Collection as PersonCollection;
use JKingWeb\Lax\Category\Collection as CategoryCollection;
4 years ago
use JKingWeb\Lax\Enclosure\Collection as EnclosureCollection;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Parser as YamlParser;
/**
* @covers JKingWeb\Lax\Parser\Construct<extended>
4 years ago
* @covers JKingWeb\Lax\Parser\JSON\Feed<extended>
* @covers JKingWeb\Lax\Parser\JSON\Entry<extended>
*/
class JSONTest extends \PHPUnit\Framework\TestCase {
/** @dataProvider provideJSONFeedVersion1 */
public function testJSONFeedVersion1($input, string $type, ?string $url, $output): void {
if (is_object($input)) {
$input = json_encode($input);
} elseif (!is_string($input)) {
throw new \Exception("Test input is invalid");
}
$p = new Parser($input, $type, $url);
if ($output instanceof \Exception) {
$this->expectExceptionObject($output);
$p->parse(new Feed);
} else {
$act = $p->parse(new Feed);
$exp = $this->makeFeed($output);
$this->assertEquals($exp, $act);
}
}
public function provideJSONFeedVersion1(): iterable {
foreach (new \GlobIterator(__DIR__."/*.yaml", \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::KEY_AS_FILENAME) as $file => $path) {
foreach ((new YamlParser)->parseFile($path, Yaml::PARSE_OBJECT_FOR_MAP) as $description => $test) {
if (isset($test->exception)) {
$test->output = new Exception((string) $test->exception);
}
yield "$file: {$description}" => [
$test->input,
$test->type ?? "",
$test->doc_url ?? null,
$test->output,
];
}
}
}
protected function makeFeed(\stdClass $output): Feed {
$f = new Feed;
foreach ($output as $k => $v) {
if (in_array($k, ["title", "summary"])) {
$f->$k = $this->makeText($v);
} elseif ($k === "people") {
$c = new PersonCollection;
foreach ($v as $m) {
4 years ago
$c[] = $this->makePerson($m);
}
$f->$k = $c;
} elseif ($k === "entries") {
$c = [];
foreach ($v as $m) {
$c[] = $this->makeEntry($m);
}
$f->$k = $c;
4 years ago
} elseif (in_array($k, ["meta", "sched"])) {
foreach ($v as $kk => $vv) {
if ($kk === "url") {
$f->$k->$kk = $this->makeUrl($vv);
} else {
$f->$k->$kk = $vv;
}
4 years ago
}
} elseif (in_array($k, ["url", "link", "icon", "image"])) {
$f->$k = $this->makeUrl($v);
} else {
$f->$k = $v;
}
}
return $f;
}
protected function makeEntry(\stdClass $entry): Entry {
$e = new Entry;
foreach ($entry as $k => $v) {
4 years ago
if (in_array($k, ["link", "relatedLink", "banner"])) {
$e->$k = $this->makeUrl($v);
4 years ago
} elseif (in_array($k, ["dateCreated", "dateModified"])) {
$e->$k = new Date($v, new \DateTimeZone("UTC"));
4 years ago
} elseif (in_array($k, ["title", "summary", "content"])) {
$e->$k = $this->makeText($v);
4 years ago
} elseif ($k === "people") {
$c = new PersonCollection;
foreach ($v as $m) {
$c[] = $this->makePerson($m);
}
$e->$k = $c;
4 years ago
} elseif ($k === "enclosures") {
$c = new EnclosureCollection;
foreach ($v as $m) {
$c[] = $this->makeEnclosure($m);
}
$e->$k = $c;
} elseif ($k === "categories") {
$c = new CategoryCollection;
foreach ($v as $m) {
$o = new Category;
foreach ($m as $kk => $vv) {
$o->$kk = $vv;
}
$c[] = $o;
}
$e->$k = $c;
} else {
$e->$k = $v;
}
}
return $e;
}
protected function makeText($data): Text {
if (is_string($data)) {
return new Text($data);
}
$out = new Text;
4 years ago
foreach ($data as $k => $v) {
$out->$k = $v;
}
return $out;
}
4 years ago
protected function makePerson(\stdClass $person): Person {
$p = new Person;
foreach ($person as $k => $v) {
if (in_array($k, ["url", "avatar"])) {
$p->$k = $this->makeUrl($v);
4 years ago
} else {
$p->$k = $v;
}
}
return $p;
}
4 years ago
protected function makeEnclosure(\stdClass $enclosure): Enclosure {
$e = new Enclosure;
foreach ($enclosure as $k => $v) {
if ($k === "urli") {
$e->$k = $this->makeUrl($v);
4 years ago
} else {
$e->$k = $v;
}
}
return $e;
}
protected function makeUrl($url): ?Url {
if (is_array($url)) {
return new Url($url[0] ?? "", ($url[1] ?? null) ? new Url($url[1]) : null);
} else {
return new Url($url);
}
}
}