summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrant Knudson <bknudson@us.ibm.com>2015-12-09 13:12:01 -0600
committerRobert Collins <robertc@robertcollins.net>2016-02-29 12:28:49 +1300
commit4d4bc6f384a920daf1710101ac2a5dc68c49fc01 (patch)
treedf84d2c5241ff2ec1cf62ed67d9aa4dee946a8a6
parent11e50d3fc2e89b902e1b464f0a60bab2a1620442 (diff)
downloadtesttools-4d4bc6f384a920daf1710101ac2a5dc68c49fc01.tar.gz
Add unique_text_generator
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
-rw-r--r--NEWS4
-rw-r--r--doc/for-test-authors.rst4
-rw-r--r--requirements.txt1
-rw-r--r--testtools/__init__.py2
-rw-r--r--testtools/testcase.py40
-rw-r--r--testtools/tests/test_testcase.py49
6 files changed, 99 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index a2f8a52..013b83b 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/doc/for-test-authors.rst b/doc/for-test-authors.rst
index 06a107d..eec2783 100644
--- a/doc/for-test-authors.rst
+++ b/doc/for-test-authors.rst
@@ -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
---------------
diff --git a/requirements.txt b/requirements.txt
index 100d8e4..73152ec 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,3 +7,4 @@ pyrsistent
python-mimeparse
unittest2>=1.0.0
traceback2
+six>=1.4.0
diff --git a/testtools/__init__.py b/testtools/__init__.py
index 336df24..d6c15d2 100644
--- a/testtools/__init__.py
+++ b/testtools/__init__.py
@@ -42,6 +42,7 @@ __all__ = [
'TimestampingStreamResult',
'try_import',
'try_imports',
+ 'unique_text_generator',
]
# Compat - removal announced in 0.9.25.
@@ -77,6 +78,7 @@ else:
skip,
skipIf,
skipUnless,
+ unique_text_generator,
)
from testtools.testresult import (
CopyStreamResult,
diff --git a/testtools/testcase.py b/testtools/testcase.py
index 97160e0..1e131ee 100644
--- a/testtools/testcase.py
+++ b/testtools/testcase.py
@@ -13,6 +13,7 @@ __all__ = [
'skipIf',
'skipUnless',
'TestCase',
+ 'unique_text_generator',
]
import copy
@@ -29,6 +30,7 @@ from extras import (
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,
@@ -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.
diff --git a/testtools/tests/test_testcase.py b/testtools/tests/test_testcase.py
index 9153680..f0aa881 100644
--- a/testtools/tests/test_testcase.py
+++ b/testtools/tests/test_testcase.py
@@ -7,6 +7,8 @@ from pprint import pformat
import sys
import unittest
+import six
+
from testtools import (
DecorateTestCaseResult,
ErrorHolder,
@@ -1117,7 +1119,7 @@ class TestExpectedFailure(TestWithDetails):
class TestUniqueFactories(TestCase):
- """Tests for getUniqueString and getUniqueInteger."""
+ """Tests for getUniqueString, getUniqueInteger, unique_text_generator."""
run_test_with = FullStackRunTest
@@ -1145,6 +1147,51 @@ class TestUniqueFactories(TestCase):
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."""