forked from ILIAS-eLearning/ILIAS
-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Infrastructure to use crawler in factory tests #9
Merged
Merged
Changes from 12 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
080e7c6
crawler exception handler
572cc62
Crawler wrapper to use crawler in abstract factory tests
4b6d43c
crawler wrapper tests
4057275
docstrings-indents
2a090c3
Merge branch 'trunk_gui' of https://github.com/klees/ILIAS into trunk…
8096268
moved the factory crashtest to test folder and stripped it from its n…
86dfa72
Merge branch 'trunk_gui' of https://github.com/klees/ILIAS into trunk…
a7b0c56
Merge branch 'trunk_gui' of https://github.com/klees/ILIAS into trunk…
302e821
abstract test for factory interfaces
8728a61
sample test for glyph facoty
3e2f7a1
Merge branch 'trunk_gui' of https://github.com/klees/ILIAS into trunk…
9da1598
finished Abstract factory test: all methods are final, documentation …
6e0df80
included a rule for factories interface testing
43fb08d
corrected jf-proposal test rule
7c8fec2
finished the glyph factory test
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
15 changes: 15 additions & 0 deletions
15
src/UI/Implementation/Crawler/Exception/CrawlerExceptionHandler.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
namespace ILIAS\UI\Implementation\Crawler\Exception; | ||
/** | ||
* Handle Crawler exceptions. | ||
*/ | ||
interface CrawlerExceptionHandler { | ||
|
||
/** | ||
* Handle an exception request. | ||
* | ||
* @param int $exception_code | ||
* @param string $exception_info | ||
*/ | ||
public function handleException(CrawlerException $ex); | ||
} |
27 changes: 27 additions & 0 deletions
27
src/UI/Implementation/Crawler/Exception/CrawlerExceptionLogger.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
namespace ILIAS\UI\Implementation\Crawler\Exception | ||
/** | ||
* Sometimes we would like to store exception, instead of throwing them on spot, | ||
* e.g. for the purpose of testing. | ||
*/ | ||
class CrawlerExceptionLogger implements CrawlerExceptionHandler { | ||
protected $exceptions = array(); | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function handleException(CrawlerException $ex) { | ||
$this->exceptions[] = $ex; | ||
} | ||
|
||
/** | ||
* Get all exception thrown sofar and reset the logger. | ||
* | ||
* @return CrawlerException[] $return | ||
*/ | ||
public function exceptions() { | ||
$return = $this->exceptions; | ||
$this->exceptions = array() | ||
return $return; | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
src/UI/Implementation/Crawler/Exception/CrawlerExceptionThrower.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
namespace ILIAS\UI\Implementation\Crawler\Exception; | ||
/** | ||
* A wrapper around the excepiton to simply throw them. | ||
*/ | ||
class CrawlerExceptionThrower implements CrawlerExceptionHandler { | ||
protected $exceptions = array(); | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function handleException(CrawlerException $ex) { | ||
throw $ex; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
<?php | ||
|
||
require_once("libs/composer/vendor/autoload.php"); | ||
use ILIAS\UI\Implementation\Crawler\Exception as CrawlerException; | ||
use ILIAS\UI\Implementation\Crawler as Crawler; | ||
|
||
/** | ||
* Defines tests every UI-factory MUST pass. | ||
*/ | ||
abstract class AbstractFactoryTest extends PHPUnit_Framework_TestCase { | ||
|
||
const IS_COMPONENT = 1; | ||
const IS_FACTORY = 2; | ||
|
||
public static $factoryReflection; | ||
/** | ||
* kitchensink info test configuration: | ||
* true = should be there, check | ||
* false = may be there, don't check | ||
* Notice, some properties (MUST/MUST NOT) will allways be checked. | ||
*/ | ||
protected $kitchensink_info_settings_default | ||
= array('description' | ||
=> true | ||
,'background' | ||
=> false | ||
,'context' | ||
=> true | ||
,'featurewiki' | ||
=> false | ||
,'javascript' | ||
=> false | ||
,'rules' | ||
=> true); | ||
|
||
protected $description_categories = | ||
array('purpose','composition','effect','rival'); | ||
protected $rules_categories = | ||
array('usage','interaction','wording','style','ordering','responsiveness','composition','accessibility'); | ||
public function setUp() { | ||
$this->yaml_parser = new Crawler\EntriesYamlParser(); | ||
} | ||
|
||
final public function test_proper_namespace() { | ||
$this->assertRegExp("#^ILIAS\\\\UI\\\\Component.#", self::$factoryReflection->getNamespaceName()); | ||
} | ||
|
||
final public function test_proper_name() { | ||
$this->assertTrue($this->isFactoryName( self::$factoryReflection->getName())); | ||
} | ||
|
||
final protected function setupMethodTestcase(ReflectionMethod $method_reflection) { | ||
try { | ||
$docstring_data = $this->yaml_parser->parseArrayFromString($method_reflection->getDocComment())[0]; | ||
} catch (CrawlerException\CrawlerException $ex) { | ||
$this->assertFalse($ex->getMessage(),$method_reflection->getName().": parse error kitchensink YAML string."); | ||
} | ||
$method_name = $method_reflection->getName(); | ||
if($this->returnsFactory($docstring_data)) { | ||
$type = self::IS_FACTORY; | ||
} elseif($this->returnsComponent($docstring_data, $method_name, $method_reflection)) { | ||
$type = self::IS_COMPONENT; | ||
} else { | ||
$this->assertFalse( "Method ".$method_name." seems to return neither factory nor component." | ||
."Please check the @return docstring of your interface." | ||
."If this method is supposed to return a component, please ensure " | ||
."the existense of the corresponding interface."); | ||
} | ||
return array( | ||
"method_reflection" => $method_reflection | ||
,"method_name" => $method_name | ||
,"docstring_data" => $docstring_data | ||
,"type" => $type); | ||
} | ||
|
||
/** | ||
* Tests, wether the YAML Kitchen Sink info may be parsed. | ||
* | ||
* @dataProvider methods | ||
*/ | ||
final public function test_check_yaml_extraction($method_reflection) { | ||
try { | ||
$docstring_data = $this->yaml_parser->parseArrayFromString($method_reflection->getDocComment())[0]; | ||
$this->assertTrue(true); | ||
} catch (CrawlerException\CrawlerException $ex) { | ||
$this->assertFalse($ex->getMessage(),$method_reflection->getName().": parse error kitchensink YAML string."); | ||
} | ||
} | ||
|
||
/** | ||
* Tests the method name. Does it match the return doctring? | ||
* | ||
* @dataProvider methods | ||
*/ | ||
final public function test_factory_method_name_compatible_docstring($method_reflection) { | ||
$param = $this->setupMethodTestcase($method_reflection); | ||
|
||
if($param["type"] === self::IS_FACTORY ) { | ||
$this->checkFactoryMethodNameCompatibleDocstring($param["docstring_data"],$param["method_name"]); | ||
} | ||
if($param["type"] === self::IS_COMPONENT ) { | ||
$this->checkComponentMethodNameCompatibleDocstring($param["docstring_data"],$param["method_name"]); | ||
} | ||
} | ||
|
||
/** | ||
* Tests the method parameters. Methodfs returning factories must not have parameters. | ||
* | ||
* @dataProvider methods | ||
*/ | ||
final public function test_method_params($method_reflection) { | ||
$param = $this->setupMethodTestcase($method_reflection); | ||
if($param["type"] === self::IS_FACTORY ) { | ||
$this->assertCount(0,$param["method_reflection"]->getNumberOfParameters() | ||
,$emthod_reflection->getName().": method representing an abstract node must not have parameters."); | ||
} | ||
if($param["type"] === self::IS_COMPONENT ) { | ||
$this->assertTrue(true); | ||
} | ||
} | ||
|
||
/** | ||
* Tests the content of the YAML Kithcen Sink information. | ||
* | ||
* @dataProvider methods | ||
*/ | ||
final public function test_kitchensink_info($method_reflection) { | ||
$param = $this->setupMethodTestcase($method_reflection); | ||
|
||
$kitchensink_info_settings = | ||
array_merge($this->kitchensink_info_settings_default | ||
,isset($this->kitchensink_info_settings[$param["method_name"]]) ? | ||
$this->kitchensink_info_settings[$param["method_name"]] : | ||
array()); | ||
|
||
if($param["type"] === self::IS_FACTORY ) { | ||
$this->checkFactoryDocstringData($param["docstring_data"] | ||
,$kitchensink_info_settings | ||
,$param["method_name"]); | ||
} | ||
if($param["type"] === self::IS_COMPONENT ) { | ||
$this->checkComponentDocstringData($param["docstring_data"] | ||
,$kitchensink_info_settings | ||
,$param["method_name"]); | ||
} | ||
} | ||
|
||
final public function methods() { | ||
if(!self::$factoryReflection) { | ||
self::$factoryReflection = new ReflectionClass(static::$factory_title); | ||
} | ||
return array_map(function($element) {return array($element);} | ||
,self::$factoryReflection->getMethods()); | ||
} | ||
|
||
final protected function checkFactoryDocstringData(array $docstring_data,array $kitchensink_info_settings,$method_name) { | ||
if(isset($docstring_data['context'])) { | ||
$this->assertFalse($method_name.": factories must not have context"); | ||
} | ||
$this->checkCommonDocstringData($docstring_data,$kitchensink_info_settings,$method_name); | ||
|
||
} | ||
|
||
final protected function checkComponentDocstringData(array $docstring_data,array $kitchensink_info_settings,$method_name) { | ||
if($kitchensink_info_settings['context']) { | ||
$this->assertArrayHasKey('context',$docstring_data,$method_name); | ||
} | ||
$this->checkCommonDocstringData($docstring_data,$kitchensink_info_settings,$method_name); | ||
} | ||
|
||
final protected function checkCommonDocstringData(array $docstring_data,array $kitchensink_info_settings,$method_name) { | ||
if($kitchensink_info_settings['description']) { | ||
$this->assertArrayHasKey('description',$docstring_data,$method_name); | ||
$this->assertGreaterThanOrEqual(1, | ||
count(array_intersect( | ||
array_keys($docstring_data['description']), $this->description_categories)) | ||
,$method_name.": description should contain at least one of the following " | ||
.implode(", ",$this->description_categories)."."); | ||
} | ||
if($kitchensink_info_settings['background']) { | ||
$this->assertArrayHasKey('background',$docstring_data,$method_name); | ||
} | ||
if($kitchensink_info_settings['featurewiki']) { | ||
$this->assertArrayHasKey('featurewiki',$docstring_data,$method_name); | ||
} | ||
if($kitchensink_info_settings['javascript']) { | ||
$this->assertArrayHasKey('javascript',$docstring_data,$method_name); | ||
} | ||
if($kitchensink_info_settings['rules']) { | ||
$this->assertArrayHasKey('rules',$docstring_data,$method_name); | ||
$this->assertGreaterThanOrEqual(1, | ||
count(array_intersect( | ||
array_keys($docstring_data['rules']), $this->rules_categories)) | ||
,$method_name.": description should contain at least one of the following" | ||
.implode(", ",$this->rules_categories)."."); | ||
$rule_indices = array(); | ||
foreach ($docstring_data['rules'] as $rule_category => $rules) { | ||
foreach ($rules as $rule_index => $rule) { | ||
$this->assertTrue(is_numeric($rule_index), $method_name.": rule indices must be numeric"); | ||
$rule_indices[] = (int)$rule_index; | ||
} | ||
} | ||
$num_rules = count($rule_indices); | ||
$cnt_start = min($rule_indices); | ||
$cnt = 1; | ||
while($cnt < $num_rules) { | ||
$this->assertTrue(in_array($cnt_start + $cnt, $rule_indices), $method_name.": rule indices must be successive"); | ||
$cnt++; | ||
} | ||
} | ||
} | ||
|
||
final public function checkFactoryMethodNameCompatibleDocstring($docstring_data,$method_name) { | ||
$return_doc = $docstring_data["namespace"]; | ||
$method_name_uppercase = ucwords($method_name); | ||
$this->assertRegExp("#^(\\\\?)" | ||
.str_replace("\\", "\\\\", self::$factoryReflection->getNamespaceName()) | ||
."\\\\".$method_name_uppercase."\\\\Factory$#", $return_doc | ||
, $method_name.": it seems the the @return docstring does not match the method name"); | ||
} | ||
|
||
final public function checkComponentMethodNameCompatibleDocstring($docstring_data,$method_name) { | ||
$return_doc = $docstring_data["namespace"]; | ||
$this->assertRegExp("#^(\\\\?)".str_replace("\\", "\\\\", self::$factoryReflection->getNamespaceName())."\\\\*#", $return_doc | ||
, $method_name.": it seems the the @return docstring does not match the method name"); | ||
} | ||
|
||
final protected function returnsFactory($docstring_data) { | ||
return $this->isFactoryName($docstring_data["namespace"]); | ||
} | ||
|
||
final protected function returnsComponent($docstring_data) { | ||
$reflection = new ReflectionClass($docstring_data["namespace"]); | ||
return in_array("ILIAS\\UI\\Component\\Component", $reflection->getInterfaceNames()); | ||
} | ||
|
||
final protected function isFactoryName($name) { | ||
return preg_match("#^ILIAS\\\\UI\\\\Component\\\\([a-zA-Z]+\\\\)*Factory$#", $name) === 1; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<?php | ||
require_once("libs/composer/vendor/autoload.php"); | ||
require_once("tests/UI/Crawler/FactoriesCrawlerTest.php"); | ||
use ILIAS\UI\Implementation\Crawler\Exception as Ex; | ||
use ILIAS\UI\Implementation\Crawler as Crawler; | ||
|
||
class FactoriesCrawlerCrashtestdummyTest extends FactoriesCrawlerTest { | ||
protected function setUp(){ | ||
$this->crawler = new Crawler\FactoriesCrawlerCrashtestdummy(new Ex\CrawlerExceptionThrower()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
use ILIAS\UI\Implementation\Crawler\Exception as Ex; | ||
use ILIAS\UI\Implementation\Crawler\Entry as En; | ||
use ILIAS\UI\Implementation\Crawler as Crawler; | ||
|
||
/** | ||
* This is a wrapper around the actual FactoriesCrawler created in order | ||
* to make it more suitable for abstract factory testing. Instead of simply | ||
* dying with an exception at an invalid factory branch, it will be skipped, | ||
* after logging the exception. Note that this may also be tested using Crawler | ||
* tests by including an appropriate exception handler. | ||
*/ | ||
class FactoriesCrawlerCrashtestdummy extends Crawler\FactoriesCrawler { | ||
protected $exception_handler; | ||
public function __construct(Ex\CrawlerExceptionHandler $exception_handler) { | ||
$this->exception_handler = $exception_handler; | ||
parent::__construct(); | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function crawlFactory($factoryPath, En\ComponentEntry $parent = null,$depth=0) { | ||
try{ | ||
return parent::crawlFactory($factoryPath,$parent,$depth); | ||
} catch (Ex\CrawlerException $ex) { | ||
$this->exception_handler->handleException($ex); | ||
return new En\ComponentEntries(); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
emthod_reflection -> method_reflection