def test_get_arraytypes(self):
# __doc__ (as of 2008-06-25) for pygame.sndarray.get_arraytypes:
# pygame.sndarray.get_arraytypes (): return tuple
#
# Gets the array system types currently supported.
#
# Checks, which array system types are available and returns them as a
# tuple of strings. The values of the tuple can be used directly in
# the use_arraytype () method.
#
# If no supported array system could be found, None will be returned.
self.assert_(test_not_implemented())
test_not_implemented() will fail if any test suite is run with a "(-i|--incomplete)" command line option.
As mentioned in previous posts, I developed a unittest stub generator that will output stubs for any untested units. It is supported by a naming scheme for the tests. The stubber will inspect the xxxx_test.py modules and based upon the names of the unittest.TestCase's and their children test_xxxx methods will determine what is already tested.
For each public callable there is a corresponding test named test_$callable_name. Comments or descriptions will be appended to this separated by a double underscore.
test_quit__returns_None_if_not_already_init
What if there is a module.quit and a module.class.quit ? Each class has it's own TestCase (and thus namespace) named $classTypeTest. This is typically the case anyway with setUp()'s specific to the class tested.
def get_callables(obj, if_of = None, check_where_defined=False):
publics = (getattr(obj, x) for x in dir(obj) if is_public(x))
callables = (x for x in publics if callable(x) or isgetsetdescriptor(x))
if check_where_defined:
callables = (c for c in callables if ( 'pygame' in c.__module__ or
('__builtin__' == c.__module__ and isclass(c)) )
and REAL_HOMES.get(c, 0) in (0, obj))
if if_of:
callables = (x for x in callables if if_of(x)) # isclass, ismethod etc
return set(callables)
The script uses inspection to find all testables in pygame but there were a few complications, for example getter/setter properties and the fact that some objects need to be instantiated before inspection reveals their innards. Also, filtering out non-pygame callables and after that callables that appeared in more than one module.
eg pygame.rect.Rect led a double life as pygame.sprite.Rect. Just check the __module__ attribute ?
In [4]: pygame.sprite.Rect.__module__
Out[4]: 'pygame'
The workaround was to make a mapping of object to the place where it was defined. There were only 9 of these.
REAL_HOMES = {
pygame.rect.Rect : pygame.rect,
pygame.mask.from_surface : pygame.mask,
pygame.time.get_ticks : pygame.time,
.....
On some of the classes the __module__ attribute was __builtin__ so I needed put an exception for them in the filtering out of non pygame callables.
In [7]: pygame.cdrom.CDType.__module__
Out[7]: '__builtin__'
def module_stubs(module):
stubs = {}
all_callables = get_callables(module, check_where_defined = True) - IGNORES
classes = set (
c for c in all_callables if isclass(c) or c in MUST_INSTANTIATE
)
for class_ in classes:
base_type = class_
if class_ in MUST_INSTANTIATE:
class_ = get_instance(class_)
stubs.update (
make_stubs(get_callables(class_) - IGNORES, module, base_type)
)
stubs.update(make_stubs(all_callables - classes, module))
return stubs
The stubber finds all modules in the pygame package. For each module it uses inspection to create a set of all the callables minus those set in the IGNORE setting. This is here for any exceptions to the filtering and also for tests that have been grouped under one test name. These objects will not be stubbed.
IGNORES = set([
pygame.rect.Rect.h, pygame.rect.Rect.w,
pygame.rect.Rect.x, pygame.rect.Rect.y,
pygame.color.Color.a, pygame.color.Color.b,
pygame.color.Color.g, pygame.color.Color.r,
......
From that it creates a subset of "classes", the criteria being that for each element "inspect.isclass(element)" or that the element is in the manually set MUST_INSTANTIATE dict. This is a mapping of class to helper function, and instantiation args required to return an instance.
MUST_INSTANTIATE = {
# BaseType / Helper # (Instantiator / Args) / Callable
pygame.cdrom.CDType : (pygame.cdrom.CD, (0,)),
pygame.mixer.ChannelType : (pygame.mixer.Channel, (0,)),
pygame.time.Clock : (pygame.time.Clock, ()),
..
}
Inspecting the xxxxType would reveal no methods, and they needed to be instantiated, but then the object returned contained no other attributes; one example being __name__ needed later for determing the test name. Therefore the xxxxType was sent to the stub generation function as the "parent class" for each callable that was gathered by inspecting the instantiation.
Any callables not in the "classes" set are assumed module level functions and a stub is created for each.
The test stubber is used from the command line:
$ gen_stubs.py --help
Usage:
$ gen_stubs.py ROOT
eg.
$ gen_stubs.py sprite.Sprite
def test_add(self):
# Doc string for pygame.sprite.Sprite:
...
Options:
-h, --help show this help message and exit
-l, --list list callable names not stubs
-t, --test_names list test names not stubs
$ gen_stubs.py pygame -l
pygame.base.error.args,
pygame.bufferproxy.BufferProxy.length,
pygame.bufferproxy.BufferProxy.raw,
pygame.event.Event,
pygame.image.tostring,
pygame.joystick.Joystick,
pygame.key.get_repeat,
pygame.mask.Mask,
pygame.mixer.Channel,
pygame.movie.Movie,
pygame.overlay.overlay.display,
pygame.overlay.overlay.get_hardware,
pygame.overlay.overlay.set_location,
pygame.pixelarray.PixelArray.surface,
pygame.sprite.AbstractGroup.add,
pygame.sprite.AbstractGroup.add_internal,
pygame.sprite.AbstractGroup.clear,
pygame.sprite.AbstractGroup.copy,
pygame.sprite.AbstractGroup.draw,
pygame.sprite.AbstractGroup.empty,
pygame.sprite.AbstractGroup.has_internal,
pygame.sprite.AbstractGroup.remove,
pygame.sprite.AbstractGroup.remove_internal,
pygame.sprite.AbstractGroup.sprites,
pygame.sprite.AbstractGroup.update,
pygame.sprite.collide_rect,
Commas are appended for easy copy/paste into IGNORE list.
gen_stubs.py is an integral part of the plan to make it extremely easy for people to contribute to unittests. One man can only do so much.
No comments:
Post a Comment