Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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

…ring test generation.
  • Loading branch information...
commit c0bdcda6d0e2878845200475b9b5cd9851fa64d1 1 parent b2be5d0
Michal Kwiatkowski authored mkwiatkowski committed
13  pythoscope/generator/assertions.py
@@ -335,6 +335,7 @@ def remove_duplicates_and_bare_method_contexts(events):
335 335
 # :: ([Event], [Event]) -> [Event]
336 336
 def include_requirements(test_events, execution_events):
337 337
     ignored_side_effects = side_effects_of(explicit_calls(test_events))
  338
+    ignored_objects = []
338 339
     new_events = []
339 340
     for event in test_events:
340 341
         for new_event in objects_required_for(event, event.timestamp, execution_events):
@@ -342,7 +343,16 @@ def include_requirements(test_events, execution_events):
342 343
             # ignore all side effects caused by it.
343 344
             if new_event not in ignored_side_effects:
344 345
                 new_events.append(new_event)
345  
-    return new_events + test_events
  346
+            elif isinstance(new_event, AttributeRebind) and not isinstance(new_event.value, ImmutableObject):
  347
+                change = BindingChange(ObjectAttributeReference(new_event.obj, new_event.name, new_event.timestamp),
  348
+                                       new_event.value,
  349
+                                       new_event.timestamp)
  350
+                new_events.append(change)
  351
+                # We can ignore the object that already has an assigned name,
  352
+                # unless it was needed earlier.
  353
+                if new_event.value not in new_events:
  354
+                    ignored_objects.append(new_event.value)
  355
+    return filter(lambda e: e not in ignored_objects, new_events + test_events)
346 356
 
347 357
 # :: (Event, [Event], int) -> bool
348 358
 def used_later_than(event, timeline, timestamp):
@@ -367,6 +377,7 @@ def fix_tests_using_call_outputs(timeline):
367 377
                 isinstance(event.actual, (MethodCallContext, Call)) and \
368 378
                 event.actual.output.timestamp > event.actual.timestamp and \
369 379
                 used_later_than(event.actual.output, timeline, event.timestamp):
  380
+            # TODO use unique names
370 381
             yield Assign('result', event.actual, event.actual.timestamp-0.0001)
371 382
             event.actual = 'result'
372 383
             yield event
4  pythoscope/generator/builder.py
@@ -131,6 +131,10 @@ def generate_test_contents(events, template):
131 131
     for event in events:
132 132
         if isinstance(event, Assign):
133 133
             line = variable_assignment_line(event.name, event.obj, already_assigned_names)
  134
+        elif isinstance(event, BindingChange):
  135
+            if event.name.obj in already_assigned_names.keys():
  136
+                already_assigned_names[event.obj] = code_string_from_object_attribute_reference(event.name, already_assigned_names)
  137
+            continue # This is not a real test line, so just go directly to the next line.
134 138
         elif isinstance(event, EqualAssertionLine):
135 139
             expected = constructor_as_string(event.expected, already_assigned_names)
136 140
             if isinstance(event.actual, (Call, MethodCallContext)):
17  pythoscope/generator/cleaner.py
@@ -12,7 +12,7 @@ def remove_objects_unworthy_of_naming(events):
12 12
     new_events = list(events)
13 13
     side_effects = all_of_type(events, SideEffect)
14 14
     affected_objects = objects_affected_by_side_effects(side_effects)
15  
-    invoked_objects = objects_with_method_calls(events)
  15
+    invoked_objects = objects_with_method_calls(events) + objects_with_attribute_references(events)
16 16
     for obj, usage_count in object_usage_counts(events):
17 17
         # ImmutableObjects don't need to be named, as their identity is
18 18
         # always unambiguous.
@@ -49,6 +49,21 @@ def objects_from_methods(event):
49 49
             return None
50 50
     return compact(map(objects_from_methods, events))
51 51
 
  52
+# :: [Event] -> [SerializedObject]
  53
+def objects_with_attribute_references(events):
  54
+    def objects_from_references(event):
  55
+        if isinstance(event, ObjectAttributeReference):
  56
+            return event.obj
  57
+        elif isinstance(event, EqualAssertionLine):
  58
+            return objects_from_references(event.actual)
  59
+        elif isinstance(event, RaisesAssertionLine):
  60
+            return objects_from_references(event.call)
  61
+        elif isinstance(event, GeneratorAssertionLine):
  62
+            return objects_from_references(event.generator_call)
  63
+        else:
  64
+            return None
  65
+    return compact(map(objects_from_references, events))
  66
+
52 67
 # :: [Event] -> {SerializedObject: int}
53 68
 def object_usage_counts(timeline):
54 69
     return counted(resolve_dependencies(timeline))
7  pythoscope/generator/dependencies.py
@@ -124,8 +124,11 @@ def get_contained_objects(obj):
124 124
             return []
125 125
         elif isinstance(obj, ObjectAttributeReference):
126 126
             return get_those_and_contained_objects([obj.obj])
127  
-        elif isinstance(obj, (ImmutableObject, UnknownObject, CallToC, CommentLine,
128  
-                              SkipTestLine, EqualAssertionStubLine, ModuleVariableReference)):
  127
+        elif isinstance(obj, BindingChange):
  128
+            return get_those_and_contained_objects([obj.obj, obj.name])
  129
+        elif isinstance(obj, (ImmutableObject, UnknownObject, CallToC,
  130
+                              CommentLine, SkipTestLine, EqualAssertionStubLine,
  131
+                              ModuleVariableReference)):
129 132
             return []
130 133
         else:
131 134
             raise TypeError("Wrong argument to get_contained_objects: %s." % repr(obj))
17  pythoscope/generator/lines.py
@@ -4,7 +4,8 @@
4 4
 __all__ = ['EqualAssertionLine', 'EqualAssertionStubLine',
5 5
            'GeneratorAssertionLine', 'RaisesAssertionLine',
6 6
            'CommentLine', 'SkipTestLine',
7  
-           'ModuleVariableReference', 'ObjectAttributeReference', 'Assign']
  7
+           'ModuleVariableReference', 'ObjectAttributeReference',
  8
+           'BindingChange', 'Assign']
8 9
 
9 10
 class Line(Event):
10 11
     def __init__(self, timestamp):
@@ -57,11 +58,21 @@ def __init__(self, obj, name, timestamp):
57 58
         self.obj = obj
58 59
         self.name = name
59 60
 
60  
-class Assign(Line):
  61
+    def __repr__(self):
  62
+        return "ObjectAttributeReference(obj=%r, name=%r)" % (self.obj, self.name)
  63
+
  64
+# This is a virtual line, not necessarily appearing in a test case. It is used
  65
+# to notify builder of a change in name bidning that happened under-the-hood,
  66
+# e.g. during a function call.
  67
+class BindingChange(Line):
61 68
     def __init__(self, name, obj, timestamp):
62 69
         Line.__init__(self, timestamp)
63 70
         self.name = name
64 71
         self.obj = obj
65 72
 
66 73
     def __repr__(self):
67  
-        return "Assign(name=%r, obj=%r)" % (self.name, self.obj)
  74
+        return "%s(name=%r, obj=%r)" % (self.__class__.__name__, self.name, self.obj)
  75
+
  76
+# Assignment statement.
  77
+class Assign(BindingChange):
  78
+    pass
15  test/data/attributes_rebind_module.py
@@ -5,7 +5,6 @@ def setx(self, x):
5 5
 class NewStyle(object):
6 6
     def __init__(self, x):
7 7
         self.x = x
8  
-
9 8
     def incrx(self):
10 9
         self.x += 1
11 10
 
@@ -14,14 +13,26 @@ def create(self):
14 13
         other = NewStyle(13)
15 14
         other.incrx()
16 15
         return other
17  
-
18 16
     def process(self, ns):
19 17
         ns.incrx()
20 18
 
  19
+class UsingOtherInternally(object):
  20
+    def __init__(self):
  21
+        self.internal = NewStyle(100)
  22
+    def use(self):
  23
+        self.internal.x += 111
  24
+        self.internal._y = 'private'
  25
+
21 26
 def main():
22 27
     o = OldStyle()
23 28
     o.setx(42)
  29
+
24 30
     n = NewStyle(3)
25 31
     n.incrx()
  32
+
26 33
     uo = UsingOther()
27 34
     uo.process(uo.create())
  35
+
  36
+    uoi = UsingOtherInternally()
  37
+    uoi.use()
  38
+
12  test/data/attributes_rebind_output.py
@@ -2,6 +2,7 @@
2 2
 import unittest
3 3
 from module import NewStyle
4 4
 from module import UsingOther
  5
+from module import UsingOtherInternally
5 6
 from module import main
6 7
 
7 8
 class TestOldStyle(unittest.TestCase):
@@ -11,6 +12,10 @@ def test_setx_returns_None_for_42(self):
11 12
         self.assertEqual(42, old_style.x)
12 13
 
13 14
 class TestNewStyle(unittest.TestCase):
  15
+    def test_creation_with_100(self):
  16
+        new_style = NewStyle(100)
  17
+        # Make sure it doesn't raise any exceptions.
  18
+
14 19
     def test_incrx_2_times_after_creation_with_13(self):
15 20
         new_style = NewStyle(13)
16 21
         self.assertEqual(None, new_style.incrx())
@@ -33,6 +38,13 @@ def test_create_and_process(self):
33 38
         self.assertEqual(new_style, result)
34 39
         self.assertEqual(None, using_other.process(result))
35 40
 
  41
+class TestUsingOtherInternally(unittest.TestCase):
  42
+    def test_use_returns_None(self):
  43
+        using_other_internally = UsingOtherInternally()
  44
+        self.assertEqual(None, using_other_internally.use())
  45
+        self.assertEqual(211, using_other_internally.internal.x)
  46
+        self.assertEqual('private', using_other_internally.internal._y)
  47
+
36 48
 class TestMain(unittest.TestCase):
37 49
     def test_main_returns_None(self):
38 50
         self.assertEqual(None, main())
10  test/data/objects_identity_output.py
@@ -9,13 +9,16 @@
9 9
 class TestFacade(unittest.TestCase):
10 10
     def test_just_do_it_returns_None_after_creation_with_system_instance(self):
11 11
         alist = [Object('one'), Object('two'), Object('three')]
12  
-        facade = Facade(System(Composite(alist)))
  12
+        composite = Composite(alist)
  13
+        system = System(composite)
  14
+        facade = Facade(system)
13 15
         self.assertEqual(None, facade.just_do_it())
14 16
 
15 17
 class TestSystem(unittest.TestCase):
16 18
     def test_do_that_and_do_this_after_creation_with_composite_instance(self):
17 19
         alist = [Object('one'), Object('two'), Object('three')]
18  
-        system = System(Composite(alist))
  20
+        composite = Composite(alist)
  21
+        system = System(composite)
19 22
         self.assertEqual(None, system.do_this())
20 23
         self.assertEqual(None, system.do_that())
21 24
 
@@ -45,7 +48,8 @@ def test_that_and_this_after_creation_with_two(self):
45 48
 class TestDoSomethingSimpleWithSystem(unittest.TestCase):
46 49
     def test_do_something_simple_with_system_returns_None_for_system_instance(self):
47 50
         alist = [Object('one'), Object('two'), Object('three')]
48  
-        self.assertEqual(None, do_something_simple_with_system(System(Composite(alist))))
  51
+        composite = Composite(alist)
  52
+        self.assertEqual(None, do_something_simple_with_system(System(composite)))
49 53
 
50 54
 class TestMain(unittest.TestCase):
51 55
     def test_main_returns_None(self):

0 notes on commit c0bdcda

Please sign in to comment.
Something went wrong with that request. Please try again.