Skip to content

Commit

Permalink
Add unique_text_generator
Browse files Browse the repository at this point in the history
This provides a way to make it easier to test with
unicode text strings. The value returned will be like
TestCase.getUniqueString but the value is six.text_type
and contains unicode.

Change-Id: I3f144e1294a801b23793f7a2520465e15f3a5222
  • Loading branch information
Brant Knudson authored and rbtcollins committed Feb 28, 2016
1 parent 11e50d3 commit 4d4bc6f
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 1 deletion.
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Improvements
report the lack of result as a test error. This ought to make weird
concurrency interaction bugs easier to understand. (Jonathan Lange)

* Introduce the unique_text_generator generator function. Similar to the
getUniqueString() method, except it creates unique unicode text strings.
(Brant Knudson)

* Previously, when gathering details caused by a setUp() failure,
a traceback occurred if the fixture used the newer _setUp().
This had the side effect of not clearing up fixtures nor gathering details
Expand Down
4 changes: 4 additions & 0 deletions doc/for-test-authors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,10 @@ details of certain variables don't actually matter.
See pages 419-423 of `xUnit Test Patterns`_ by Gerard Meszaros for a detailed
discussion of creation methods.

``testcase.generate_unique_text()`` is similar to ``getUniqueString``, except
it generates text that contains unicode characters. The value will be a
``unicode`` object in Python 2 and a ``str`` object in Python 3.

Test attributes
---------------

Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ pyrsistent
python-mimeparse
unittest2>=1.0.0
traceback2
six>=1.4.0
2 changes: 2 additions & 0 deletions testtools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
'TimestampingStreamResult',
'try_import',
'try_imports',
'unique_text_generator',
]

# Compat - removal announced in 0.9.25.
Expand Down Expand Up @@ -77,6 +78,7 @@
skip,
skipIf,
skipUnless,
unique_text_generator,
)
from testtools.testresult import (
CopyStreamResult,
Expand Down
40 changes: 40 additions & 0 deletions testtools/testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
'skipIf',
'skipUnless',
'TestCase',
'unique_text_generator',
]

import copy
Expand All @@ -29,6 +30,7 @@
fixtures = try_import('fixtures')
# To let setup.py work, make this a conditional import.
unittest = try_imports(['unittest2', 'unittest'])
import six

from testtools import (
content,
Expand Down Expand Up @@ -182,6 +184,44 @@ def gather_details(source_dict, target_dict):
target_dict[name] = _copy_content(content_object)


def _mods(i, mod):
(q, r) = divmod(i, mod)
while True:
yield r
if not q:
break
(q, r) = divmod(q, mod)


def _unique_text(base_cp, cp_range, index):
s = six.text_type('')
for m in _mods(index, cp_range):
s += six.unichr(base_cp + m)
return s


def unique_text_generator(prefix):
"""Generates unique text values.
Generates text values that are unique. Use this when you need arbitrary
text in your test, or as a helper for custom anonymous factory methods.
:param prefix: The prefix for text.
:return: text that looks like '<prefix>-<text_with_unicode>'.
:rtype: six.text_type
"""
# 0x1e00 is the start of a range of glyphs that are easy to see are
# unicode since they've got circles and dots and other diacriticals.
# 0x1eff is the end of the range of these diacritical chars.
BASE_CP = 0x1e00
CP_RANGE = 0x1f00 - BASE_CP
index = 0
while True:
unique_text = _unique_text(BASE_CP, CP_RANGE, index)
yield six.text_type('%s-%s') % (prefix, unique_text)
index = index + 1


class TestCase(unittest.TestCase):
"""Extensions to the basic TestCase.
Expand Down
49 changes: 48 additions & 1 deletion testtools/tests/test_testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import sys
import unittest

import six

from testtools import (
DecorateTestCaseResult,
ErrorHolder,
Expand Down Expand Up @@ -1117,7 +1119,7 @@ def test_passes_unexpectedly(self):


class TestUniqueFactories(TestCase):
"""Tests for getUniqueString and getUniqueInteger."""
"""Tests for getUniqueString, getUniqueInteger, unique_text_generator."""

run_test_with = FullStackRunTest

Expand Down Expand Up @@ -1145,6 +1147,51 @@ def test_getUniqueString_prefix(self):
name_two = self.getUniqueString('bar')
self.assertThat(name_two, Equals('bar-2'))

def test_unique_text_generator(self):
# unique_text_generator yields the prefix's id followed by unique
# unicode string.
prefix = self.getUniqueString()
unique_text_generator = testcase.unique_text_generator(prefix)
first_result = next(unique_text_generator)
self.assertEqual(six.text_type('%s-%s') % (prefix, _u('\u1e00')),
first_result)
# The next value yielded by unique_text_generator is different.
second_result = next(unique_text_generator)
self.assertEqual(six.text_type('%s-%s') % (prefix, _u('\u1e01')),
second_result)

def test_mods(self):
# given a number and max, generate a list that's the mods.
# The list should contain no numbers >= mod
self.assertEqual([0], list(testcase._mods(0, 5)))
self.assertEqual([1], list(testcase._mods(1, 5)))
self.assertEqual([2], list(testcase._mods(2, 5)))
self.assertEqual([3], list(testcase._mods(3, 5)))
self.assertEqual([4], list(testcase._mods(4, 5)))
self.assertEqual([0, 1], list(testcase._mods(5, 5)))
self.assertEqual([1, 1], list(testcase._mods(6, 5)))
self.assertEqual([2, 1], list(testcase._mods(7, 5)))
self.assertEqual([0, 2], list(testcase._mods(10, 5)))
self.assertEqual([0, 0, 1], list(testcase._mods(25, 5)))
self.assertEqual([1, 0, 1], list(testcase._mods(26, 5)))
self.assertEqual([1], list(testcase._mods(1, 100)))
self.assertEqual([0, 1], list(testcase._mods(100, 100)))
self.assertEqual([0, 10], list(testcase._mods(1000, 100)))

def test_unique_text(self):
self.assertEqual(
u'\u1e00',
testcase._unique_text(base_cp=0x1e00, cp_range=5, index=0))
self.assertEqual(
u'\u1e01',
testcase._unique_text(base_cp=0x1e00, cp_range=5, index=1))
self.assertEqual(
u'\u1e00\u1e01',
testcase._unique_text(base_cp=0x1e00, cp_range=5, index=5))
self.assertEqual(
u'\u1e03\u1e02\u1e01',
testcase._unique_text(base_cp=0x1e00, cp_range=5, index=38))


class TestCloneTestWithNewId(TestCase):
"""Tests for clone_test_with_new_id."""
Expand Down

0 comments on commit 4d4bc6f

Please sign in to comment.