1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-22 21:22:40 +00:00

Start on tests for Feed

- Makes use of PHP's internal Web server to deliver expected responses from a real server
- Windows batch file can be used to run tests (Linux and Mac test runners to come later)
- Added PHPUnit to dev dependencies
This commit is contained in:
J. King 2017-05-21 17:16:32 -04:00
parent 88c0f4986c
commit 590abaf0ef
10 changed files with 1232 additions and 20 deletions

View file

@ -29,7 +29,8 @@
}, },
"require-dev": { "require-dev": {
"mikey179/vfsStream": "^1.6.4", "mikey179/vfsStream": "^1.6.4",
"phake/phake": "^2.3.2" "phake/phake": "^2.3.2",
"phpunit/phpunit": "^6.0.5"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

1049
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -95,6 +95,6 @@ abstract class AbstractStatement implements Statement {
$time = (int) $date; $time = (int) $date;
} }
// ISO 8601 with space in the middle instead of T. // ISO 8601 with space in the middle instead of T.
return date($this->dateFormat($part), $time); return gmdate($this->dateFormat($part), $time);
} }
} }

View file

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace JKingWeb\Arsse\Misc;
trait DateFormatter {
protected function dateTransform($date, string $format = "iso8601", bool $local = false) {
$date = $this->dateNormalize($date);
$format = strtolower($format);
if($format=="unix") return $date;
switch ($format) {
case 'http': $f = "D, d M Y H:i:s \G\M\T"; break;
case 'iso8601': $f = \DateTime::ATOM; break;
case 'sql': $f = "Y-m-d H:i:s"; break;
case 'date': $f = "Y-m-d"; break;
case 'time': $f = "H:i:s"; break;
default: $f = \DateTime::ATOM; break;
}
if($local) {
return date($f, $date);
} else {
return gmdate($f, $date);
}
}
protected function dateNormalize($date) {
// convert input to a Unix timestamp
if($date instanceof \DateTimeInterface) {
$time = $date->getTimestamp();
} else if(is_numeric($date)) {
$time = (int) $date;
} else if($date===null) {
return null;
} else if(is_string($date)) {
$time = strtotime($date);
if($time===false) return null;
} else if (is_bool($date)) {
return null;
} else {
$time = (int) $date;
}
return $time;
}
}

63
tests/Feed/TestFeed.php Normal file
View file

@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace JKingWeb\Arsse;
Use Phake;
class TestFeed extends \PHPUnit\Framework\TestCase {
use Test\Tools;
protected $base = "http://localhost:8000/Feed/";
function time(string $t): string {
return gmdate("D, d M Y H:i:s \G\M\T", strtotime($t));
}
function setUp() {
$this->clearData();
Data::$conf = new Conf();
}
function testComputeNextFetchFrom304() {
// if less than half an hour, check in 15 minutes
$exp = strtotime("now + 15 minutes");
$t = strtotime("now");
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
$this->assertTime($exp, $f->nextFetch);
$t = strtotime("now - 29 minutes");
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
$this->assertTime($exp, $f->nextFetch);
// if less than an hour, check in 30 minutes
$exp = strtotime("now + 30 minutes");
$t = strtotime("now - 30 minutes");
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
$this->assertTime($exp, $f->nextFetch);
$t = strtotime("now - 59 minutes");
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
$this->assertTime($exp, $f->nextFetch);
// if less than three hours, check in an hour
$exp = strtotime("now + 1 hour");
$t = strtotime("now - 1 hour");
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
$this->assertTime($exp, $f->nextFetch);
$t = strtotime("now - 2 hours 59 minutes");
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
$this->assertTime($exp, $f->nextFetch);
// if more than 36 hours, check in 24 hours
$exp = strtotime("now + 1 day");
$t = strtotime("now - 36 hours");
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
$this->assertTime($exp, $f->nextFetch);
$t = strtotime("now - 2 years");
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
$this->assertTime($exp, $f->nextFetch);
// otherwise check in three hours
$exp = strtotime("now + 3 hours");
$t = strtotime("now - 6 hours");
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
$this->assertTime($exp, $f->nextFetch);
$t = strtotime("now - 35 hours");
$f = new Feed(null, $this->base."NextFetch/NotModified?t=$t", $this->dateTransform($t, "http"));
$this->assertTime($exp, $f->nextFetch);
}
}

View file

@ -0,0 +1,4 @@
<?php return [
'code' => 304,
'lastMod' => (int) $_GET['t'],
];

View file

@ -5,6 +5,8 @@ use JKingWeb\Arsse\Exception;
use JKingWeb\Arsse\Data; use JKingWeb\Arsse\Data;
trait Tools { trait Tools {
use \JKingWeb\Arsse\Misc\DateFormatter;
function assertException(string $msg, string $prefix = "", string $type = "Exception") { function assertException(string $msg, string $prefix = "", string $type = "Exception") {
$class = \JKingWeb\Arsse\NS_BASE . ($prefix !== "" ? str_replace("/", "\\", $prefix) . "\\" : "") . $type; $class = \JKingWeb\Arsse\NS_BASE . ($prefix !== "" ? str_replace("/", "\\", $prefix) . "\\" : "") . $type;
$msgID = ($prefix !== "" ? $prefix . "/" : "") . $type. ".$msg"; $msgID = ($prefix !== "" ? $prefix . "/" : "") . $type. ".$msg";
@ -17,6 +19,12 @@ trait Tools {
$this->expectExceptionCode($code); $this->expectExceptionCode($code);
} }
function assertTime($exp, $test) {
$exp = $this->dateTransform($exp);
$test = $this->dateTransform($test);
$this->assertSame($exp, $test);
}
function clearData(bool $loadLang = true): bool { function clearData(bool $loadLang = true): bool {
$r = new \ReflectionClass(\JKingWeb\Arsse\Data::class); $r = new \ReflectionClass(\JKingWeb\Arsse\Data::class);
$props = array_keys($r->getStaticProperties()); $props = array_keys($r->getStaticProperties());

View file

@ -43,5 +43,8 @@
<file>REST/NextCloudNews/TestNCNVersionDiscovery.php</file> <file>REST/NextCloudNews/TestNCNVersionDiscovery.php</file>
<file>REST/NextCloudNews/TestNCNV1_2.php</file> <file>REST/NextCloudNews/TestNCNV1_2.php</file>
</testsuite> </testsuite>
<testsuite name="Feed parser">
<file>Feed/TestFeed.php</file>
</testsuite>
</testsuites> </testsuites>
</phpunit> </phpunit>

66
tests/server.php Normal file
View file

@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace JKingWeb\Arsse;
require_once __DIR__."/../bootstrap.php";
/*
This is a so-called router for the the internal PHP Web server:
<http://php.net/manual/en/features.commandline.webserver.php>
It is used to test feed parsing in a controlled environment,
answering specific requests used in tests with the data required
to pass the test.
The parameters of the responses are kept in separate files,
which include the following data:
- Response content
- Response code
- Content type
- Whether to send cache headers
- Last modified
- Any other headers
*/
$defaults = [ // default values for response
'code' => 200,
'content' => "",
'mime' => "application/octet-stream",
'lastMod' => time(),
'cache' => true,
'fields' => [],
];
$url = explode("?",$_SERVER['REQUEST_URI'])[0];
$base = BASE."tests".\DIRECTORY_SEPARATOR."docroot";
$test = $base.str_replace("/",\DIRECTORY_SEPARATOR,$url).".php";
if(!file_exists($test)) {
$response = [
'code' => 499,
'content' => "Test '$test' missing.",
'mime' => "application/octet-stream",
'lastMod' => time(),
'cache' => true,
'fields' => [],
];
} else {
$response = array_merge($defaults, (include $test));
}
// set the response code
http_response_code((int) $response['code']);
// if the response has a body, set the content type and (possibly) the ETag.
if(strlen($response['content'])) {
header("Content-Type: ".$response['mime']);
if($response['cache']) header("ETag: ".md5($response['content']));
}
// if caching is enabled, set the last-modified date
if($response['cache']) header("Last-Modified: ".gmdate("D, d M Y H:i:s \G\M\T", $response['lastMod']));
// set any other specified fields verbatim
foreach($response['fields'] as $h) {
header($h);
}
// send the content
echo $response['content'];

10
tests/test.bat Normal file
View file

@ -0,0 +1,10 @@
@echo off
setlocal
set base=%~dp0
start php -S localhost:8000 "%base%\server.php"
php "%base%\..\vendor\phpunit\phpunit\phpunit" -c "%base%\phpunit.xml"
timeout /nobreak /t 1 >nul
for /f "usebackq tokens=5" %%a in (`netstat -aon ^| find "LISTENING" ^| find ":8000"`) do (
taskkill /pid %%a >nul
goto :eof
)