6bceb2b2db
Adds unit test for bridge implementations: - Custom functions must be in protected or private scope - getName() must return a valid string (non-empty) - getURI() must return a valid URI - Each bridge must define constants for NAME, URI, DESCRIPTION and MAINTAINER. CACHE_TIMEOUT and PARAMETERS are optional. The unit test is written for PHPUnit 6.x and will automatically be tested by Travis-CI for PHP 7.0 (see .travis.yml). Remarks: Unit tests for bridge data were scrapped in #378 for complexity reasons (tests would have to be maintained for each bridge). This unit test, however, is written for testing all bridges without taking specific implementation details into account.
193 lines
4.8 KiB
PHP
193 lines
4.8 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use PHPUnit\Framework\TestResult;
|
|
use PHPUnit\Framework\AssertionFailedError;
|
|
|
|
require_once('Bridge.php');
|
|
require_once('BridgeAbstract.php');
|
|
require_once('FeedExpander.php');
|
|
|
|
Bridge::setDir(__DIR__ . '/../bridges/');
|
|
|
|
/**
|
|
* This class checks bridges for implementation details:
|
|
*
|
|
* - A bridge must not implement public functions other than the ones specified
|
|
* by the bridge interfaces. Custom functions must be defined in private or
|
|
* protected scope.
|
|
* - getName() must return a valid string (non-empty)
|
|
* - getURI() must return a valid URI
|
|
* - A bridge must define constants for NAME, URI, DESCRIPTION and MAINTAINER,
|
|
* CACHE_TIMEOUT and PARAMETERS are optional
|
|
*/
|
|
final class BridgeImplementationTest extends TestCase {
|
|
|
|
private function CheckBridgePublicFunctions($bridgeName){
|
|
|
|
$parent_methods = array();
|
|
|
|
if(in_array('BridgeInterface', class_parents($bridgeName))) {
|
|
$parent_methods = array_merge($parent_methods, get_class_methods('BridgeInterface'));
|
|
}
|
|
|
|
if(in_array('BridgeAbstract', class_parents($bridgeName))) {
|
|
$parent_methods = array_merge($parent_methods, get_class_methods('BridgeAbstract'));
|
|
}
|
|
|
|
if(in_array('FeedExpander', class_parents($bridgeName))) {
|
|
$parent_methods = array_merge($parent_methods, get_class_methods('FeedExpander'));
|
|
}
|
|
|
|
// Receive all non abstract methods
|
|
$methods = array_diff(get_class_methods($bridgeName), $parent_methods);
|
|
$method_names = implode(', ', $methods);
|
|
|
|
$errmsg = $bridgeName
|
|
. ' implements additional public method(s): '
|
|
. $method_names
|
|
. '! Custom functions must be defined in private or protected scope!';
|
|
|
|
$this->assertEmpty($method_names, $errmsg);
|
|
|
|
}
|
|
|
|
private function CheckBridgeGetNameDefaultValue($bridgeName){
|
|
|
|
if(in_array('BridgeAbstract', class_parents($bridgeName))) { // Is bridge
|
|
|
|
if(!$this->isFunctionMemberOf($bridgeName, 'getName'))
|
|
return;
|
|
|
|
$bridge = new $bridgeName();
|
|
$abstract = new BridgeAbstractTest();
|
|
|
|
$message = $bridgeName . ': \'getName\' must return a valid name!';
|
|
|
|
$this->assertNotEmpty(trim($bridge->getName()), $message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Checks whether the getURI function returns empty or default values
|
|
private function CheckBridgeGetURIDefaultValue($bridgeName){
|
|
|
|
if(in_array('BridgeAbstract', class_parents($bridgeName))) { // Is bridge
|
|
|
|
if(!$this->isFunctionMemberOf($bridgeName, 'getURI'))
|
|
return;
|
|
|
|
$bridge = new $bridgeName();
|
|
$abstract = new BridgeAbstractTest();
|
|
|
|
$message = $bridgeName . ': \'getURI\' must return a valid URI!';
|
|
|
|
$this->assertNotEmpty(trim($bridge->getURI()), $message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private function CheckBridgePublicConstants($bridgeName){
|
|
|
|
// Assertion only works for BridgeAbstract!
|
|
if(in_array('BridgeAbstract', class_parents($bridgeName))) {
|
|
|
|
$ref = new ReflectionClass($bridgeName);
|
|
$constants = $ref->getConstants();
|
|
|
|
$ref = new ReflectionClass('BridgeAbstract');
|
|
$parent_constants = $ref->getConstants();
|
|
|
|
foreach($parent_constants as $key => $value) {
|
|
|
|
$this->assertArrayHasKey($key, $constants, 'Constant ' . $key . ' missing in ' . $bridgeName);
|
|
|
|
// Skip optional constants
|
|
if($key !== 'PARAMETERS' && $key !== 'CACHE_TIMEOUT') {
|
|
$this->assertNotEquals($value, $constants[$key], 'Constant ' . $key . ' missing in ' . $bridgeName);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private function isFunctionMemberOf($bridgeName, $functionName){
|
|
|
|
$bridgeReflector = new ReflectionClass($bridgeName);
|
|
$bridgeMethods = $bridgeReflector->GetMethods();
|
|
$bridgeHasMethod = false;
|
|
|
|
foreach($bridgeMethods as $method) {
|
|
|
|
if($method->name === $functionName && $method->class === $bridgeReflector->name) {
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
public function testBridgeImplementation($bridgeName){
|
|
|
|
require_once('bridges/' . $bridgeName . '.php');
|
|
|
|
$this->CheckBridgePublicFunctions($bridgeName);
|
|
$this->CheckBridgePublicConstants($bridgeName);
|
|
$this->CheckBridgeGetNameDefaultValue($bridgeName);
|
|
$this->CheckBridgeGetURIDefaultValue($bridgeName);
|
|
|
|
}
|
|
|
|
public function count() {
|
|
return count(Bridge::listBridges());
|
|
}
|
|
|
|
public function run(TestResult $result = null) {
|
|
|
|
if ($result === null) {
|
|
$result = new TestResult;
|
|
}
|
|
|
|
foreach (Bridge::listBridges() as $bridge) {
|
|
|
|
$bridge .= 'Bridge';
|
|
|
|
$result->startTest($this);
|
|
PHP_Timer::start();
|
|
$stopTime = null;
|
|
|
|
try {
|
|
$this->testBridgeImplementation($bridge);
|
|
} catch (AssertionFailedError $e) {
|
|
|
|
$stopTime = PHP_Timer::stop();
|
|
$result->addFailure($this, $e, $stopTime);
|
|
|
|
} catch (Exception $e) {
|
|
|
|
$stopTime = PHP_Timer::stop();
|
|
$result->addError($this, $e, $stopTime);
|
|
|
|
}
|
|
|
|
if ($stopTime === null) {
|
|
$stopTime = PHP_Timer::stop();
|
|
}
|
|
|
|
$result->endTest($this, $stopTime);
|
|
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
class BridgeAbstractTest extends BridgeAbstract {
|
|
public function collectData(){}
|
|
}
|