BridgeImplementationTest.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <?php
  2. use PHPUnit\Framework\TestCase;
  3. use PHPUnit\Framework\TestResult;
  4. use PHPUnit\Framework\AssertionFailedError;
  5. require_once(__DIR__ . '/../lib/RssBridge.php');
  6. Bridge::setDir(__DIR__ . '/../bridges/');
  7. /**
  8. * This class checks bridges for implementation details:
  9. *
  10. * - A bridge must not implement public functions other than the ones specified
  11. * by the bridge interfaces. Custom functions must be defined in private or
  12. * protected scope.
  13. * - getName() must return a valid string (non-empty)
  14. * - getURI() must return a valid URI
  15. * - A bridge must define constants for NAME, URI, DESCRIPTION and MAINTAINER,
  16. * CACHE_TIMEOUT and PARAMETERS are optional
  17. */
  18. final class BridgeImplementationTest extends TestCase {
  19. private function CheckBridgePublicFunctions($bridgeName){
  20. $parent_methods = array();
  21. if(in_array('BridgeInterface', class_parents($bridgeName))) {
  22. $parent_methods = array_merge($parent_methods, get_class_methods('BridgeInterface'));
  23. }
  24. if(in_array('BridgeAbstract', class_parents($bridgeName))) {
  25. $parent_methods = array_merge($parent_methods, get_class_methods('BridgeAbstract'));
  26. }
  27. if(in_array('FeedExpander', class_parents($bridgeName))) {
  28. $parent_methods = array_merge($parent_methods, get_class_methods('FeedExpander'));
  29. }
  30. // Receive all non abstract methods
  31. $methods = array_diff(get_class_methods($bridgeName), $parent_methods);
  32. $method_names = implode(', ', $methods);
  33. $errmsg = $bridgeName
  34. . ' implements additional public method(s): '
  35. . $method_names
  36. . '! Custom functions must be defined in private or protected scope!';
  37. $this->assertEmpty($method_names, $errmsg);
  38. }
  39. private function CheckBridgeGetNameDefaultValue($bridgeName){
  40. if(in_array('BridgeAbstract', class_parents($bridgeName))) { // Is bridge
  41. if(!$this->isFunctionMemberOf($bridgeName, 'getName'))
  42. return;
  43. $bridge = new $bridgeName();
  44. $abstract = new BridgeAbstractTest();
  45. $message = $bridgeName . ': \'getName\' must return a valid name!';
  46. $this->assertNotEmpty(trim($bridge->getName()), $message);
  47. }
  48. }
  49. // Checks whether the getURI function returns empty or default values
  50. private function CheckBridgeGetURIDefaultValue($bridgeName){
  51. if(in_array('BridgeAbstract', class_parents($bridgeName))) { // Is bridge
  52. if(!$this->isFunctionMemberOf($bridgeName, 'getURI'))
  53. return;
  54. $bridge = new $bridgeName();
  55. $abstract = new BridgeAbstractTest();
  56. $message = $bridgeName . ': \'getURI\' must return a valid URI!';
  57. $this->assertNotEmpty(trim($bridge->getURI()), $message);
  58. }
  59. }
  60. private function CheckBridgePublicConstants($bridgeName){
  61. // Assertion only works for BridgeAbstract!
  62. if(in_array('BridgeAbstract', class_parents($bridgeName))) {
  63. $ref = new ReflectionClass($bridgeName);
  64. $constants = $ref->getConstants();
  65. $ref = new ReflectionClass('BridgeAbstract');
  66. $parent_constants = $ref->getConstants();
  67. foreach($parent_constants as $key => $value) {
  68. $this->assertArrayHasKey($key, $constants, 'Constant ' . $key . ' missing in ' . $bridgeName);
  69. // Skip optional constants
  70. if($key !== 'PARAMETERS' && $key !== 'CACHE_TIMEOUT') {
  71. $this->assertNotEquals($value, $constants[$key], 'Constant ' . $key . ' missing in ' . $bridgeName);
  72. }
  73. }
  74. }
  75. }
  76. private function isFunctionMemberOf($bridgeName, $functionName){
  77. $bridgeReflector = new ReflectionClass($bridgeName);
  78. $bridgeMethods = $bridgeReflector->GetMethods();
  79. $bridgeHasMethod = false;
  80. foreach($bridgeMethods as $method) {
  81. if($method->name === $functionName && $method->class === $bridgeReflector->name) {
  82. return true;
  83. }
  84. }
  85. return false;
  86. }
  87. public function testBridgeImplementation($bridgeName){
  88. require_once('bridges/' . $bridgeName . '.php');
  89. $this->CheckBridgePublicFunctions($bridgeName);
  90. $this->CheckBridgePublicConstants($bridgeName);
  91. $this->CheckBridgeGetNameDefaultValue($bridgeName);
  92. $this->CheckBridgeGetURIDefaultValue($bridgeName);
  93. }
  94. public function count() {
  95. return count(Bridge::listBridges());
  96. }
  97. public function run(TestResult $result = null) {
  98. if ($result === null) {
  99. $result = new TestResult;
  100. }
  101. foreach (Bridge::listBridges() as $bridge) {
  102. $bridge .= 'Bridge';
  103. $result->startTest($this);
  104. PHP_Timer::start();
  105. $stopTime = null;
  106. try {
  107. $this->testBridgeImplementation($bridge);
  108. } catch (AssertionFailedError $e) {
  109. $stopTime = PHP_Timer::stop();
  110. $result->addFailure($this, $e, $stopTime);
  111. } catch (Exception $e) {
  112. $stopTime = PHP_Timer::stop();
  113. $result->addError($this, $e, $stopTime);
  114. }
  115. if ($stopTime === null) {
  116. $stopTime = PHP_Timer::stop();
  117. }
  118. $result->endTest($this, $stopTime);
  119. }
  120. return $result;
  121. }
  122. }
  123. class BridgeAbstractTest extends BridgeAbstract {
  124. public function collectData(){}
  125. }