dictutil: fix bug in str(ValueOrderedDict), and improve test coverage

It looks like str() was meant to truncate the dict, but a missing i+=1 meant
that it never actually did. I also changed the format to include a clear
"..." in case we truncate it, to avoid confusion with a non-truncated dict of
the same size.

This also improves test coverage in subtract() and
NumDict.item_with_largest_value().

refs ticket:2891
This commit is contained in:
Brian Warner 2017-08-09 18:53:29 -07:00
parent 95ac5494ff
commit 3f2f7dfb05
2 changed files with 48 additions and 5 deletions

View File

@ -1,7 +1,8 @@
def foo(): pass # keep the line number constant def foo(): pass # keep the line number constant
import os, time, sys, yaml import os, time, sys, itertools, random
import yaml
from StringIO import StringIO from StringIO import StringIO
from datetime import timedelta from datetime import timedelta
from twisted.trial import unittest from twisted.trial import unittest
@ -1464,10 +1465,10 @@ class DictUtil(unittest.TestCase):
d3 = dictutil.subtract(d1, d2) d3 = dictutil.subtract(d1, d2)
self.failUnlessEqual(d3, {1: "a"}) self.failUnlessEqual(d3, {1: "a"})
d1 = {1: "a", 2: "b"} d1 = {1: "a", 2: "b", 3: "c"}
d2 = {2: "c"} d2 = {2: "c", 4: "d"}
d3 = dictutil.subtract(d1, d2) d3 = dictutil.subtract(d1, d2)
self.failUnlessEqual(d3, {1: "a"}) self.failUnlessEqual(d3, {1: "a", 3: "c"})
def test_utildict(self): def test_utildict(self):
d = dictutil.UtilDict({1: "a", 2: "b"}) d = dictutil.UtilDict({1: "a", 2: "b"})
@ -1556,6 +1557,35 @@ class DictUtil(unittest.TestCase):
[("c", -1), ("a", 1), ("d", 4), ("b", 6)]) [("c", -1), ("a", 1), ("d", 4), ("b", 6)])
self.failUnlessEqual(d.item_with_largest_value(), ("b", 6)) self.failUnlessEqual(d.item_with_largest_value(), ("b", 6))
# to get full coverage of item_with_largest_value(), we need to
# exercise two situations: the first value (in iteritems() order) is
# larger than the second, and vice versa. Since iteration is not
# deterministic, we need to try a bunch of random dictionaries to
# exercise this
r = random.Random(0) # consistent seed
count = itertools.count()
found = set()
while count.next() < 1000:
a = r.randrange(100)
b = r.randrange(100)
larger = ("a",a) if a > b else ("b",b)
if a == b:
continue
d0 = dictutil.NumDict()
d0.add_num("a", a)
d0.add_num("b", b)
self.failUnlessEqual(d0, {"a": a, "b": b})
items = list(d0.d.iteritems())
if items[0][1] > items[1][1]:
found.add("first-larger")
else:
found.add("first-smaller")
self.failUnlessEqual(d0.item_with_largest_value(), larger)
if found == set(["first-larger", "first-smaller"]):
break
else:
self.fail("unable to exercise all cases of item_with_largest_value")
d = dictutil.NumDict({"a": 1, "b": 2}) d = dictutil.NumDict({"a": 1, "b": 2})
self.failUnlessIn(repr(d), ("{'a': 1, 'b': 2}", self.failUnlessIn(repr(d), ("{'a': 1, 'b': 2}",
"{'b': 2, 'a': 1}")) "{'b': 2, 'a': 1}"))
@ -1627,6 +1657,15 @@ class DictUtil(unittest.TestCase):
self.failUnlessEqual(d.values(), [1, 2, 3]) self.failUnlessEqual(d.values(), [1, 2, 3])
self.failUnlessEqual(d.keys(), ["c", "b", "a"]) self.failUnlessEqual(d.keys(), ["c", "b", "a"])
self.failUnlessEqual(repr(d), "<ValueOrderedDict {c: 1, b: 2, a: 3}>") self.failUnlessEqual(repr(d), "<ValueOrderedDict {c: 1, b: 2, a: 3}>")
self.failUnlessEqual(str(d), "<ValueOrderedDict {c: 1, b: 2, a: 3}>")
# str() is supposed to only show the first 16 entries
large_d = dictutil.ValueOrderedDict()
for i in range(20):
large_d["k%d" % i] = i
large_d_repr = ("<ValueOrderedDict {%s, ...}>" %
", ".join(["k%d: %d" % (i, i) for i in range(16)]))
self.failUnlessEqual(str(large_d), large_d_repr)
def eq(a, b): def eq(a, b):
return a == b return a == b
self.failIf(d == {"a": 4}) self.failIf(d == {"a": 4})

View File

@ -436,8 +436,12 @@ class ValueOrderedDict:
s.append(str(x[0])); s.append(": "); s.append(str(x[1])) s.append(str(x[0])); s.append(": "); s.append(str(x[1]))
i = 1 i = 1
while (n is None) or (i < n): while (n is None) or (i < n):
i += 1
x = iter.next() x = iter.next()
s.append(", "); s.append(str(x[0])); s.append(": "); s.append(str(x[1])) s.append(", ");
s.append(str(x[0])); s.append(": "); s.append(str(x[1]))
# if we get here, we're truncating the repr, so make that clear
s.append(", ...")
except StopIteration: except StopIteration:
pass pass
s.append("}") s.append("}")