6
class PhutilLibraryTestCase extends PhutilTestCase {
9
* This is more of an acceptance test case instead of a unit test. It verifies
10
* that all symbols can be loaded correctly. It can catch problems like
11
* missing methods in descendants of abstract base classes.
13
public function testEverythingImplemented() {
14
id(new PhutilSymbolLoader())
15
->setLibrary($this->getLibraryName())
16
->selectAndLoadSymbols();
17
$this->assertTrue(true);
21
* This is more of an acceptance test case instead of a unit test. It verifies
22
* that all the library map is up-to-date.
24
public function testLibraryMap() {
25
$root = $this->getLibraryRoot();
26
$library = phutil_get_library_name_for_root($root);
28
$new_library_map = id(new PhutilLibraryMapBuilder($root))
31
$bootloader = PhutilBootloader::getInstance();
32
$old_library_map = $bootloader->getLibraryMapWithoutExtensions($library);
33
unset($old_library_map[PhutilLibraryMapBuilder::LIBRARY_MAP_VERSION_KEY]);
39
'The library map does not appear to be up-to-date. Try '.
40
'rebuilding the map with `%s`.',
45
* This is more of an acceptance test case instead of a unit test. It verifies
46
* that methods in subclasses have the same visibility as the method in the
49
public function testMethodVisibility() {
50
$symbols = id(new PhutilSymbolLoader())
51
->setLibrary($this->getLibraryName())
52
->selectSymbolsWithoutLoading();
55
foreach ($symbols as $symbol) {
56
if ($symbol['type'] == 'class') {
57
$classes[$symbol['name']] = new ReflectionClass($symbol['name']);
63
foreach ($classes as $class_name => $class) {
66
while ($parent = $parent->getParentClass()) {
70
$interfaces = $class->getInterfaces();
72
foreach ($class->getMethods() as $method) {
73
$visibility = $this->getVisibility($method);
74
$method_name = $method->getName();
76
foreach (array_merge($parents, $interfaces) as $extends) {
77
if ($extends->hasMethod($method_name)) {
78
$xmethod = $extends->getMethod($method_name);
79
$xvisibility = $this->getVisibility($xmethod);
81
if ($xvisibility != $visibility) {
83
'Class "%s" implements method "%s" with the wrong visibility. '.
84
'The method has visibility "%s", but it is defined in parent '.
85
'"%s" with visibility "%s". In Phabricator, a method which '.
86
'overrides another must always have the same visibility.',
94
// We found a declaration somewhere, so stop looking.
103
"\n\n".implode("\n\n", $failures));
107
* Get the name of the library currently being tested.
109
protected function getLibraryName() {
110
return phutil_get_library_name_for_root($this->getLibraryRoot());
114
* Get the root directory for the library currently being tested.
116
protected function getLibraryRoot() {
117
$caller = id(new ReflectionClass($this))->getFileName();
118
return phutil_get_library_root_for_path($caller);
121
private function getVisibility(ReflectionMethod $method) {
122
if ($method->isPrivate()) {
124
} else if ($method->isProtected()) {