class CountMinSketchQualityCommonTest(unittest.TestCase):
    def __init__(self, methodName='runTest', log_counting=None):
        self.log_counting = log_counting
        super(CountMinSketchQualityCommonTest, self).__init__(methodName=methodName)

    """
    Functional tests for CountMinSketch.quality method, which returns quality rating of the structure
    """

    def setUp(self):
        self.cms = CountMinSketch(1, log_counting=self.log_counting)

    def test_quality_default(self):
        """
        Uses the default structure
        """
        self.assertEqual(self.cms.quality(), 0)

        three_quarters = int((self.cms.width * 3) / 4)
        for i in range(three_quarters):
            self.cms.increment(str(i), 1 + (i % 13))

        self.assertGreaterEqual(self.cms.quality(), 0.5)
        self.assertLessEqual(self.cms.quality(), 1.0)

        for i in range(three_quarters * 7):
            self.cms.increment(str(i), 1 + (i % 13))

        self.assertGreaterEqual(self.cms.quality(), 4.0)
        self.assertLessEqual(self.cms.quality(), 6.0)
class CountMinSketchSanityCommonTest(unittest.TestCase):
    """
    Functional tests for setting and retrieving values of the counter
    """
    def __init__(self, methodName='runTest', log_counting=None, delta=0.0):
        self.log_counting = log_counting
        self.delta = delta
        super(CountMinSketchSanityCommonTest,
              self).__init__(methodName=methodName)

    def setUp(self):
        self.cms = CountMinSketch(1, log_counting=self.log_counting)

    def test_unknown_is_zero(self):
        self.assertEqual(self.cms['foo'], 0)

    def test_increment_default(self):
        self.cms.increment('foo')
        self.cms.increment('bar')
        self.cms.increment('foo')
        self.cms.increment('foo')

        self.assertEqual(self.cms['foo'], 3)
        self.assertEqual(self.cms['bar'], 1)

    def test_increment_bytes(self):
        self.cms.increment('foo')
        self.cms.increment('bar')
        self.cms.increment(b'foo')
        self.cms.increment('foo')

        self.assertEqual(self.cms['foo'], 3)
        self.assertEqual(self.cms[b'foo'], 3)

    def test_total(self):
        self.assertEqual(self.cms.total(), 0)

        self.cms.increment('foo')
        self.cms.increment('bar')
        self.cms.increment('foo')
        self.cms.increment('foo')
        self.assertEqual(self.cms.total(), 4)

        self.cms.increment('goo', 3)
        self.assertEqual(self.cms.total(), 7)

    def test_cardinality(self):
        self.assertEqual(self.cms.cardinality(), 0)

        self.cms.increment('foo')
        self.cms.increment('bar')
        self.cms.increment('foo')
        self.cms.increment('foo')
        self.assertEqual(self.cms.cardinality(), 2)

        self.cms.increment('goo', 3)
        self.assertEqual(self.cms.cardinality(), 3)

    def test_increment_by_value(self):
        foo_value = 42
        bar_value = 53

        self.cms.increment('foo', foo_value)
        self.cms.increment('bar', bar_value)

        self.assertAlmostEqual(self.cms['foo'],
                               foo_value,
                               delta=self.delta * foo_value)
        self.assertAlmostEqual(self.cms['bar'],
                               bar_value,
                               delta=self.delta * bar_value)

    def test_repeat_increment(self):
        """
        Test that a set successfully replaces existing value of the counter
        """

        self.cms.increment('foo', 5)
        self.cms.increment('foo', 10)

        self.assertEqual(self.cms['foo'], 15)

    def test_increment_int_key(self):
        """
        Negative test: integer keys are not supported and yield TypeError
        """
        with self.assertRaises(TypeError):
            self.cms.increment(1)

    def test_get_increment_object_key(self):
        """
        Negative test: object keys are not supported and yield TypeError
        """
        o = MyClass()

        with self.assertRaises(TypeError):
            self.cms.increment(o)

    def test_get_increment_empty_string(self):
        self.cms.increment('foo', 42)
        self.cms.increment('bar', 53)

        self.assertEqual(self.cms[''], 0)
        self.cms.increment('', 3)
        self.assertEqual(self.cms[''], 3)
        self.cms.increment('')
        self.assertEqual(self.cms[''], 4)

    def test_get_increment_long_string(self):
        long_string = 'l' + ('o' * 100) + 'ng'
        longer_string = 'l' + ('o' * 120) + 'ng'
        self.cms.increment(long_string, 2)
        self.cms.increment(longer_string, 3)

        self.assertEqual(self.cms[long_string], 2)
        self.assertEqual(self.cms[longer_string], 3)

    def test_get_increment_non_ascii_string(self):
        non_ascii_string = "Non-ascii dôverivá Čučoriedka 9#8\\%7 平仮名\n☃\t+☀\t=\t☹ "
        # the second line contains a different symbol
        similar_string = "Non-ascii dôverivá Čučoriedka 9#8\\%7 平仮名\n☃\t+☀\t=\t☺ "

        self.cms.increment(non_ascii_string, 2)
        self.cms.increment(similar_string, 3)

        self.assertEqual(self.cms[non_ascii_string], 2)
        self.assertEqual(self.cms[similar_string], 3)

    def test_get_increment_non_ascii_unicode(self):
        non_ascii_unicode = u"Non-ascii dôverivá Čučoriedka 9#8\\%7 平仮名\n☃\t+☀\t=\t☹ "
        # the second line contains a different symbol
        similar_unicode = u"Non-ascii dôverivá Čučoriedka 9#8\\%7 平仮名\n☃\t+☀\t=\t☺ "

        self.cms.increment(non_ascii_unicode, 2)
        self.cms.increment(similar_unicode, 3)

        self.assertEqual(self.cms[non_ascii_unicode], 2)
        self.assertEqual(self.cms[similar_unicode], 3)

    def test_increment_string_value(self):
        """
        Negative test: string values are not supported and yield TypeError
        """
        with self.assertRaises(TypeError):
            self.cms.increment('foo', 'bar')

    def test_set_object_value(self):
        """
        Negative test: object values are not supported and yield TypeError
        """
        class MyClass(object):
            pass

        with self.assertRaises(TypeError):
            self.cms.increment('foo', MyClass())

    def test_increment_big_number(self):
        big_number = 127451
        self.cms.increment('big number', big_number)
        self.assertAlmostEqual(self.cms['big number'],
                               big_number,
                               delta=self.delta * big_number)

    def test_increment_negative(self):
        """
        Negative test, raises ValueError on negative values
        """
        # new value
        with self.assertRaises(ValueError):
            self.cms.increment('foo', -4)

        self.assertEqual(self.cms['foo'], 0, "value should remain unaffected")

        self.cms.increment('foo', 3)
        # existing value
        with self.assertRaises(ValueError):
            self.cms.increment('foo', -2)

        self.assertEqual(self.cms['foo'], 3, "value should remain unaffected")

    def test_increment_zero(self):
        """
        Setting the zero value
        """
        self.cms.increment('foo', 0)
        self.assertEqual(self.cms['foo'], 0)

        self.cms.increment('foo')
        self.cms.increment('foo', 0)
        self.assertEqual(self.cms['foo'], 1)
Example #3
0
class CountMinSketchPickleCommonTest(unittest.TestCase):
    """
    Functional tests for determining size (cardinality) of hashtable and iterations.
    """

    def __init__(self, methodName='runTest', log_counting=None):
        self.log_counting = log_counting
        super(CountMinSketchPickleCommonTest, self).__init__(methodName=methodName)

    def setUp(self):
        self.cms = CountMinSketch(2, log_counting=self.log_counting)

    def tearDown(self):
        if os.path.isfile(filename):
            os.remove(filename)

    def store_and_load(self):
        with open(filename, 'wb') as outfile:
            pickle.dump(self.cms, outfile)

        with open(filename, 'rb') as outfile:
            reloaded = pickle.load(outfile)

        return reloaded

    def check_cms(self, cms, data):
        self.assertAlmostEqual(cms.cardinality(), len(data))
        self.assertEqual(cms.total(), sum(data.values()))

        result_set = set()
        for key, expected_value in data.items():
            result_set.add((key, cms[key]))

        self.assertEqual(result_set, set(data.items()))

    def test_pickle_empty(self):
        reloaded = self.store_and_load()
        self.check_cms(reloaded, {})

    def test_pickle_simple(self):
        expected = Counter()
        for structure in [self.cms, expected]:
            structure.update("pickling")
            structure.update("lorem ipsum dolor amet")
            structure.update("122333444455555666666")

        self.check_cms(self.cms, expected)

        reloaded = self.store_and_load()
        self.check_cms(reloaded, expected)

    def test_pickle_increment_after_reload(self):
        expected = Counter()
        for structure in [self.cms, expected]:
            structure.update("pickling")
        self.cms.increment('1')
        self.cms.increment('2', 2)
        expected['1'] += 1
        expected['2'] += 2

        self.check_cms(self.cms, expected)

        reloaded = self.store_and_load()

        for structure in [reloaded, expected]:
            structure.update("pickling")
        reloaded.increment('1', 1)
        reloaded.increment('3', 3)
        expected['1'] += 1
        expected['3'] += 3
        self.check_cms(reloaded, expected)