Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F80431163
PhutilLibraryTestCase.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sat, Aug 31, 16:51
Size
5 KB
Mime Type
text/x-php
Expires
Mon, Sep 2, 16:51 (2 d)
Engine
blob
Format
Raw Data
Handle
20369100
Attached To
rPHU libphutil
PhutilLibraryTestCase.php
View Options
<?php
/**
* @concrete-extensible
*/
class
PhutilLibraryTestCase
extends
PhutilTestCase
{
/**
* This is more of an acceptance test case instead of a unit test. It verifies
* that all symbols can be loaded correctly. It can catch problems like
* missing methods in descendants of abstract base classes.
*/
public
function
testEverythingImplemented
()
{
id
(
new
PhutilSymbolLoader
())
->
setLibrary
(
$this
->
getLibraryName
())
->
selectAndLoadSymbols
();
$this
->
assertTrue
(
true
);
}
/**
* This is more of an acceptance test case instead of a unit test. It verifies
* that all the library map is up-to-date.
*/
public
function
testLibraryMap
()
{
$root
=
$this
->
getLibraryRoot
();
$library
=
phutil_get_library_name_for_root
(
$root
);
$new_library_map
=
id
(
new
PhutilLibraryMapBuilder
(
$root
))
->
buildMap
();
$bootloader
=
PhutilBootloader
::
getInstance
();
$old_library_map
=
$bootloader
->
getLibraryMapWithoutExtensions
(
$library
);
unset
(
$old_library_map
[
PhutilLibraryMapBuilder
::
LIBRARY_MAP_VERSION_KEY
]);
$identical
=
(
$new_library_map
===
$old_library_map
);
if
(!
$identical
)
{
$differences
=
$this
->
getMapDifferences
(
$old_library_map
,
$new_library_map
);
sort
(
$differences
);
}
else
{
$differences
=
array
();
}
$this
->
assertTrue
(
$identical
,
pht
(
"The library map is out of date. Rebuild it with `%s`.
\n
"
.
"These entries differ: %s."
,
'arc liberate'
,
implode
(
', '
,
$differences
)));
}
private
function
getMapDifferences
(
$old
,
$new
)
{
$changed
=
array
();
$all
=
$old
+
$new
;
foreach
(
$all
as
$key
=>
$value
)
{
$old_exists
=
array_key_exists
(
$key
,
$old
);
$new_exists
=
array_key_exists
(
$key
,
$new
);
// One map has it and the other does not, so mark it as changed.
if
(
$old_exists
!=
$new_exists
)
{
$changed
[]
=
$key
;
continue
;
}
$oldv
=
idx
(
$old
,
$key
);
$newv
=
idx
(
$new
,
$key
);
if
(
$oldv
===
$newv
)
{
continue
;
}
if
(
is_array
(
$oldv
)
&&
is_array
(
$newv
))
{
$child_changed
=
$this
->
getMapDifferences
(
$oldv
,
$newv
);
foreach
(
$child_changed
as
$child
)
{
$changed
[]
=
$key
.
'.'
.
$child
;
}
}
else
{
$changed
[]
=
$key
;
}
}
return
$changed
;
}
/**
* This is more of an acceptance test case instead of a unit test. It verifies
* that methods in subclasses have the same visibility as the method in the
* parent class.
*/
public
function
testMethodVisibility
()
{
$symbols
=
id
(
new
PhutilSymbolLoader
())
->
setLibrary
(
$this
->
getLibraryName
())
->
selectSymbolsWithoutLoading
();
$classes
=
array
();
foreach
(
$symbols
as
$symbol
)
{
if
(
$symbol
[
'type'
]
==
'class'
)
{
$classes
[
$symbol
[
'name'
]]
=
new
ReflectionClass
(
$symbol
[
'name'
]);
}
}
$failures
=
array
();
foreach
(
$classes
as
$class_name
=>
$class
)
{
$parents
=
array
();
$parent
=
$class
;
while
(
$parent
=
$parent
->
getParentClass
())
{
$parents
[]
=
$parent
;
}
$interfaces
=
$class
->
getInterfaces
();
foreach
(
$class
->
getMethods
()
as
$method
)
{
$method_name
=
$method
->
getName
();
foreach
(
array_merge
(
$parents
,
$interfaces
)
as
$extends
)
{
if
(
$extends
->
hasMethod
(
$method_name
))
{
$xmethod
=
$extends
->
getMethod
(
$method_name
);
if
(!
$this
->
compareVisibility
(
$xmethod
,
$method
))
{
$failures
[]
=
pht
(
'Class "%s" implements method "%s" with the wrong visibility. '
.
'The method has visibility "%s", but it is defined in parent '
.
'"%s" with visibility "%s". In Phabricator, a method which '
.
'overrides another must always have the same visibility.'
,
$class_name
,
$method_name
,
$this
->
getVisibility
(
$method
),
$extends
->
getName
(),
$this
->
getVisibility
(
$xmethod
));
}
// We found a declaration somewhere, so stop looking.
break
;
}
}
}
}
$this
->
assertTrue
(
empty
(
$failures
),
"
\n\n
"
.
implode
(
"
\n\n
"
,
$failures
));
}
/**
* Get the name of the library currently being tested.
*/
protected
function
getLibraryName
()
{
return
phutil_get_library_name_for_root
(
$this
->
getLibraryRoot
());
}
/**
* Get the root directory for the library currently being tested.
*/
protected
function
getLibraryRoot
()
{
$caller
=
id
(
new
ReflectionClass
(
$this
))->
getFileName
();
return
phutil_get_library_root_for_path
(
$caller
);
}
private
function
compareVisibility
(
ReflectionMethod
$parent_method
,
ReflectionMethod
$method
)
{
static
$bitmask
;
if
(
$bitmask
===
null
)
{
$bitmask
=
ReflectionMethod
::
IS_PUBLIC
;
$bitmask
+=
ReflectionMethod
::
IS_PROTECTED
;
$bitmask
+=
ReflectionMethod
::
IS_PRIVATE
;
}
$parent_modifiers
=
$parent_method
->
getModifiers
();
$modifiers
=
$method
->
getModifiers
();
return
!((
$parent_modifiers
^
$modifiers
)
&
$bitmask
);
}
private
function
getVisibility
(
ReflectionMethod
$method
)
{
if
(
$method
->
isPrivate
())
{
return
'private'
;
}
else
if
(
$method
->
isProtected
())
{
return
'protected'
;
}
else
{
return
'public'
;
}
}
}
Event Timeline
Log In to Comment