BridgeImplementationTest.php 4.8 KB

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