Permalink
Browse files

Added BindingChange class to track changes to object's attributes du…

…ring test generation.
  • Loading branch information...
1 parent b2be5d0 commit c0bdcda6d0e2878845200475b9b5cd9851fa64d1 @mkwiatkowski committed with Apr 14, 2011
@@ -335,14 +335,24 @@ def remove_duplicates_and_bare_method_contexts(events):
# :: ([Event], [Event]) -> [Event]
def include_requirements(test_events, execution_events):
ignored_side_effects = side_effects_of(explicit_calls(test_events))
+ ignored_objects = []
new_events = []
for event in test_events:
for new_event in objects_required_for(event, event.timestamp, execution_events):
# If a call appears explicitly in the test body we should
# ignore all side effects caused by it.
if new_event not in ignored_side_effects:
new_events.append(new_event)
- return new_events + test_events
+ elif isinstance(new_event, AttributeRebind) and not isinstance(new_event.value, ImmutableObject):
+ change = BindingChange(ObjectAttributeReference(new_event.obj, new_event.name, new_event.timestamp),
+ new_event.value,
+ new_event.timestamp)
+ new_events.append(change)
+ # We can ignore the object that already has an assigned name,
+ # unless it was needed earlier.
+ if new_event.value not in new_events:
+ ignored_objects.append(new_event.value)
+ return filter(lambda e: e not in ignored_objects, new_events + test_events)
# :: (Event, [Event], int) -> bool
def used_later_than(event, timeline, timestamp):
@@ -367,6 +377,7 @@ def fix_tests_using_call_outputs(timeline):
isinstance(event.actual, (MethodCallContext, Call)) and \
event.actual.output.timestamp > event.actual.timestamp and \
used_later_than(event.actual.output, timeline, event.timestamp):
+ # TODO use unique names
yield Assign('result', event.actual, event.actual.timestamp-0.0001)
event.actual = 'result'
yield event
@@ -131,6 +131,10 @@ def generate_test_contents(events, template):
for event in events:
if isinstance(event, Assign):
line = variable_assignment_line(event.name, event.obj, already_assigned_names)
+ elif isinstance(event, BindingChange):
+ if event.name.obj in already_assigned_names.keys():
+ already_assigned_names[event.obj] = code_string_from_object_attribute_reference(event.name, already_assigned_names)
+ continue # This is not a real test line, so just go directly to the next line.
elif isinstance(event, EqualAssertionLine):
expected = constructor_as_string(event.expected, already_assigned_names)
if isinstance(event.actual, (Call, MethodCallContext)):
@@ -12,7 +12,7 @@ def remove_objects_unworthy_of_naming(events):
new_events = list(events)
side_effects = all_of_type(events, SideEffect)
affected_objects = objects_affected_by_side_effects(side_effects)
- invoked_objects = objects_with_method_calls(events)
+ invoked_objects = objects_with_method_calls(events) + objects_with_attribute_references(events)
for obj, usage_count in object_usage_counts(events):
# ImmutableObjects don't need to be named, as their identity is
# always unambiguous.
@@ -49,6 +49,21 @@ def objects_from_methods(event):
return None
return compact(map(objects_from_methods, events))
+# :: [Event] -> [SerializedObject]
+def objects_with_attribute_references(events):
+ def objects_from_references(event):
+ if isinstance(event, ObjectAttributeReference):
+ return event.obj
+ elif isinstance(event, EqualAssertionLine):
+ return objects_from_references(event.actual)
+ elif isinstance(event, RaisesAssertionLine):
+ return objects_from_references(event.call)
+ elif isinstance(event, GeneratorAssertionLine):
+ return objects_from_references(event.generator_call)
+ else:
+ return None
+ return compact(map(objects_from_references, events))
+
# :: [Event] -> {SerializedObject: int}
def object_usage_counts(timeline):
return counted(resolve_dependencies(timeline))
@@ -124,8 +124,11 @@ def get_contained_objects(obj):
return []
elif isinstance(obj, ObjectAttributeReference):
return get_those_and_contained_objects([obj.obj])
- elif isinstance(obj, (ImmutableObject, UnknownObject, CallToC, CommentLine,
- SkipTestLine, EqualAssertionStubLine, ModuleVariableReference)):
+ elif isinstance(obj, BindingChange):
+ return get_those_and_contained_objects([obj.obj, obj.name])
+ elif isinstance(obj, (ImmutableObject, UnknownObject, CallToC,
+ CommentLine, SkipTestLine, EqualAssertionStubLine,
+ ModuleVariableReference)):
return []
else:
raise TypeError("Wrong argument to get_contained_objects: %s." % repr(obj))
@@ -4,7 +4,8 @@
__all__ = ['EqualAssertionLine', 'EqualAssertionStubLine',
'GeneratorAssertionLine', 'RaisesAssertionLine',
'CommentLine', 'SkipTestLine',
- 'ModuleVariableReference', 'ObjectAttributeReference', 'Assign']
+ 'ModuleVariableReference', 'ObjectAttributeReference',
+ 'BindingChange', 'Assign']
class Line(Event):
def __init__(self, timestamp):
@@ -57,11 +58,21 @@ def __init__(self, obj, name, timestamp):
self.obj = obj
self.name = name
-class Assign(Line):
+ def __repr__(self):
+ return "ObjectAttributeReference(obj=%r, name=%r)" % (self.obj, self.name)
+
+# This is a virtual line, not necessarily appearing in a test case. It is used
+# to notify builder of a change in name bidning that happened under-the-hood,
+# e.g. during a function call.
+class BindingChange(Line):
def __init__(self, name, obj, timestamp):
Line.__init__(self, timestamp)
self.name = name
self.obj = obj
def __repr__(self):
- return "Assign(name=%r, obj=%r)" % (self.name, self.obj)
+ return "%s(name=%r, obj=%r)" % (self.__class__.__name__, self.name, self.obj)
+
+# Assignment statement.
+class Assign(BindingChange):
+ pass
@@ -5,7 +5,6 @@ def setx(self, x):
class NewStyle(object):
def __init__(self, x):
self.x = x
-
def incrx(self):
self.x += 1
@@ -14,14 +13,26 @@ def create(self):
other = NewStyle(13)
other.incrx()
return other
-
def process(self, ns):
ns.incrx()
+class UsingOtherInternally(object):
+ def __init__(self):
+ self.internal = NewStyle(100)
+ def use(self):
+ self.internal.x += 111
+ self.internal._y = 'private'
+
def main():
o = OldStyle()
o.setx(42)
+
n = NewStyle(3)
n.incrx()
+
uo = UsingOther()
uo.process(uo.create())
+
+ uoi = UsingOtherInternally()
+ uoi.use()
+
@@ -2,6 +2,7 @@
import unittest
from module import NewStyle
from module import UsingOther
+from module import UsingOtherInternally
from module import main
class TestOldStyle(unittest.TestCase):
@@ -11,6 +12,10 @@ def test_setx_returns_None_for_42(self):
self.assertEqual(42, old_style.x)
class TestNewStyle(unittest.TestCase):
+ def test_creation_with_100(self):
+ new_style = NewStyle(100)
+ # Make sure it doesn't raise any exceptions.
+
def test_incrx_2_times_after_creation_with_13(self):
new_style = NewStyle(13)
self.assertEqual(None, new_style.incrx())
@@ -33,6 +38,13 @@ def test_create_and_process(self):
self.assertEqual(new_style, result)
self.assertEqual(None, using_other.process(result))
+class TestUsingOtherInternally(unittest.TestCase):
+ def test_use_returns_None(self):
+ using_other_internally = UsingOtherInternally()
+ self.assertEqual(None, using_other_internally.use())
+ self.assertEqual(211, using_other_internally.internal.x)
+ self.assertEqual('private', using_other_internally.internal._y)
+
class TestMain(unittest.TestCase):
def test_main_returns_None(self):
self.assertEqual(None, main())
@@ -9,13 +9,16 @@
class TestFacade(unittest.TestCase):
def test_just_do_it_returns_None_after_creation_with_system_instance(self):
alist = [Object('one'), Object('two'), Object('three')]
- facade = Facade(System(Composite(alist)))
+ composite = Composite(alist)
+ system = System(composite)
+ facade = Facade(system)
self.assertEqual(None, facade.just_do_it())
class TestSystem(unittest.TestCase):
def test_do_that_and_do_this_after_creation_with_composite_instance(self):
alist = [Object('one'), Object('two'), Object('three')]
- system = System(Composite(alist))
+ composite = Composite(alist)
+ system = System(composite)
self.assertEqual(None, system.do_this())
self.assertEqual(None, system.do_that())
@@ -45,7 +48,8 @@ def test_that_and_this_after_creation_with_two(self):
class TestDoSomethingSimpleWithSystem(unittest.TestCase):
def test_do_something_simple_with_system_returns_None_for_system_instance(self):
alist = [Object('one'), Object('two'), Object('three')]
- self.assertEqual(None, do_something_simple_with_system(System(Composite(alist))))
+ composite = Composite(alist)
+ self.assertEqual(None, do_something_simple_with_system(System(composite)))
class TestMain(unittest.TestCase):
def test_main_returns_None(self):

0 comments on commit c0bdcda

Please sign in to comment.