Browse Source

[2136] revised the internal method _accum()

- Other conditions are considered. One case is that two args are string type,
  another case is that either of them is None type.

- _accum() was moved to outside of the Stats class for testing it individually.

- revised the FIXME description in update_statistics_data() according to the
  above changes
Naoki Kambe 12 years ago
parent
commit
003d0276c8
2 changed files with 89 additions and 34 deletions
  1. 51 34
      src/bin/stats/stats.py.in
  2. 38 0
      src/bin/stats/tests/b10-stats_test.py

+ 51 - 34
src/bin/stats/stats.py.in

@@ -98,6 +98,45 @@ def get_spec_defaults(spec):
             return spec.get("item_default", None)
     return dict([ (s['item_name'], _get_spec_defaults(s)) for s in spec ])
 
+def _accum(a, b):
+    """If the first arg is dict or list type, two values
+    would be merged and accumlated. This is for internal use."""
+
+    # If both of args are dict or list type, two
+    # values are merged.
+    if type(a) is dict and type(b) is dict:
+        return dict([ (k, _accum(v, b[k])) \
+                          if k in b else (k, v) \
+                          for (k, v) in a.items() ] \
+                        + [ (k, v) \
+                                for (k, v) in b.items() \
+                                if k not in a ])
+    elif type(a) is list and type(b) is list:
+        return [ _accum(a[i], b[i]) \
+                     if len(b) > i else a[i] \
+                     for i in range(len(a)) ] \
+                     + [ b[i] \
+                             for i in range(len(b)) \
+                             if len(a) <= i ]
+    # If both of args are integer or float type, two
+    # values are added.
+    elif (type(a) is int and type(b) is int) \
+            or (type(a) is float or type(b) is float):
+        return a + b
+
+    # If both of args are string type,
+    # values are compared and bigger one is returned.
+    elif type(a) is str and type(b) is str:
+        if a < b: return b
+        return a
+
+    # If the first arg is None type, the second value is returned.
+    elif a is None:
+        return b
+
+    # Nothing matches above, the first arg is returned
+    return a
+
 class Callback():
     """
     A Callback handler class
@@ -392,40 +431,18 @@ class Stats:
         # interfaces aren't changed in this fix.
 
         def _accum_bymodule(statistics_data_bymid):
-            # This is an internal function for the superordinate
-            # function. It accumulates statistics data of each module id by
-            # module. It returns the accumulation result.
-            def _accum(a, b):
-                # If the first arg is dict or list type, two values
-                # would be merged and accumlated.
-                if type(a) is dict:
-                    return dict([ (k, _accum(v, b[k])) \
-                                      if k in b else (k, v) \
-                                      for (k, v) in a.items() ] \
-                                    + [ (k, v) \
-                                            for (k, v) in b.items() \
-                                            if k not in a ])
-                elif type(a) is list:
-                    return [ _accum(a[i], b[i]) \
-                                 if len(b) > i else a[i] \
-                                 for i in range(len(a)) ] \
-                                 + [ b[i] \
-                                         for i in range(len(b)) \
-                                         if len(a) <= i ]
-                # If the first arg is integer or float type, two
-                # values are just added.
-                # FIXME: A issue might happen when consolidating
-                # statistics of the multiple instances. If they have
-                # different statistics data which are not for adding
-                # each other, this might happen: If these are integer
-                # or float, these are added each other, otherwise
-                # these are overwritten into one of them.
-                elif type(a) is int or type(a) is float:
-                    return a + b
-                # If the first arg is str or other types than above,
-                # then it just returns the first arg which is assumed
-                # to be the newer value.
-                return a
+            """This is an internal method for the superordinate
+            method. It accumulates statistics data of each module id
+            by module. It returns a accumulated result."""
+            # FIXME: A issue might happen when consolidating
+            # statistics of the multiple instances. If they have
+            # different statistics data which are not for adding each
+            # other, this might happen: If these are integer or float,
+            # these are added each other. If these are string , these
+            # are compared and consolidated into bigger one.  If one
+            # of them is None type , these might be consolidated
+            # into not None-type one. Otherwise these are overwritten
+            # into one of them.
             ret = {}
             for data in statistics_data_bymid.values():
                 ret.update(_accum(data, ret))

+ 38 - 0
src/bin/stats/tests/b10-stats_test.py

@@ -100,6 +100,44 @@ class TestUtilties(unittest.TestCase):
         self.assertNotEqual(stats.get_datetime(
                 (2011, 6, 22, 8, 23, 40, 2, 173, 0)), self.const_datetime)
 
+    def test__accum(self):
+        self.assertEqual(stats._accum(None, None), None)
+        self.assertEqual(stats._accum(None, "b"), "b")
+        self.assertEqual(stats._accum("a", None), "a")
+        self.assertEqual(stats._accum(1, 2), 3)
+        self.assertEqual(stats._accum(0.5, 0.3), 0.8)
+        self.assertEqual(stats._accum('aa','bb'), 'bb')
+        self.assertEqual(stats._accum('1970-01-01T09:00:00Z','2012-08-09T09:33:31Z'),
+                         '2012-08-09T09:33:31Z')
+        self.assertEqual(stats._accum(
+                [1, 2, 3], [4, 5]), [5, 7, 3])
+        self.assertEqual(stats._accum(
+                [4, 5], [1, 2, 3]), [5, 7, 3])
+        self.assertEqual(stats._accum(
+                [1, 2, 3], [None, 5, 6]), [1, 7, 9])
+        self.assertEqual(stats._accum(
+                [None, 5, 6], [1, 2, 3]), [1, 7, 9])
+        self.assertEqual(stats._accum(
+                [1, 2, 3], [None, None, None, None]), [1,2,3,None])
+        self.assertEqual(stats._accum(
+                [[1,2],3],[[],5,6]), [[1,2],8,6])
+        self.assertEqual(stats._accum(
+                {'one': 1, 'two': 2, 'three': 3},
+                {'one': 4, 'two': 5}),
+                         {'one': 5, 'two': 7, 'three': 3})
+        self.assertEqual(stats._accum(
+                {'one': 1, 'two': 2, 'three': 3},
+                {'four': 4, 'five': 5}),
+                         {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5})
+        self.assertEqual(stats._accum(
+                {'one': [1, 2], 'two': [3, None, 5], 'three': [None, 3, None]},
+                {'one': [2], 'two': [4, 5], 'three': [None, None, None], 'four': 'FOUR'}),
+                         {'one':[3,2], 'two':[7,5,5], 'three':[None,3,None], 'four': 'FOUR'})
+        self.assertEqual(stats._accum(
+                [ {'one': 1, 'two': 2, 'three': 3}, {'four': 4, 'five': 5, 'six': 6} ],
+                [ {}, {'four': 1, 'five': 2, 'six': 3} ]),
+                [ {'one': 1, 'two': 2, 'three': 3}, {'four': 5, 'five': 7, 'six': 9} ])
+
 class TestCallback(unittest.TestCase):
     def setUp(self):
         self.dummy_func = lambda *x, **y : (x, y)