def test_ensure_unique_error_on_list(self): config = { 'all': { 'type': 'cartesian', 'lists': ['one', 'two'] }, 'bad': { 'type': 'cartesian', 'generator': True, 'lists': ['one', 'one'] }, 'one': { 'type': 'words', 'words': ['one', 'one'] }, 'two': { 'type': 'words', 'words': ['two', 'two'] } } RandomGenerator(config) # this is fine config['all']['ensure_unique'] = True with self.assertRaisesRegex( ConfigurationError, r'Invalid config: Impossible to generate with ensure_unique'): RandomGenerator(config)
def test_generate_by_pattern(self): generator = RandomGenerator({ 'all': { 'type': 'cartesian', 'lists': ['size', 'color', 'fruit'], }, 'justcolor': { 'generator': True, 'type': 'cartesian', 'lists': ['color', 'fruit'], }, 'size': { 'type': 'words', 'words': ['small', 'large'] }, 'color': { 'type': 'words', 'words': ['green', 'yellow'] }, 'fruit': { 'type': 'words', 'words': ['apple', 'banana'] }, }) with patch.object(generator, '_randrange', return_value=0): self.assertEqual(generator.generate_slug(), 'small-green-apple') self.assertEqual(generator.generate_slug('justcolor'), 'green-apple')
def test_mix_phrases_and_words_in_nested_list(self): config = { 'all': { 'type': 'cartesian', 'lists': ['a', 'nested'] }, 'a': { 'type': 'const', 'value': 'a' }, 'nested': { 'type': 'nested', 'lists': ['words', 'phrases'] }, 'words': { 'type': 'words', 'words': ['one', 'two'] }, 'phrases': { 'type': 'phrases', 'phrases': [ 'three four', # Can be space-separated string ['five', 'six'] # or a list/tuple ] } } generator = RandomGenerator(config) random.seed(0) values = set(generator.generate_slug() for i in range(28)) self.assertEqual(values, set(['a-one', 'a-two', 'a-three-four', 'a-five-six']))
def test_dump_generator(self): config = {'all': {'type': 'words', 'words': ['one', 'two', 'three']}} generator = RandomGenerator(config) stream = io.StringIO() generator._dump(stream) self.assertEqual(stream.getvalue(), "WordList(['one', 'two', 'three'], len=3)\n")
def test_ensure_unique(self): # Test without ensure_unique - should yield repeats config = { 'all': { 'type': 'cartesian', 'lists': ['adjective', 'of', 'noun'], }, 'adjective': { 'type': 'words', 'words': ['one', 'two'] }, 'of': { 'type': 'const', 'value': 'of' }, 'noun': { 'type': 'words', 'words': ['one', 'two'] } } generator = RandomGenerator(config) with patch.object(generator, '_randrange', side_effect=partial(next, cycle(iter([0, 1, 2, 3])))): self.assertEqual(generator.generate_slug(), 'one-of-one') self.assertEqual(generator.generate_slug(), 'one-of-two') self.assertEqual(generator.generate_slug(), 'two-of-one') self.assertEqual(generator.generate_slug(), 'two-of-two') self.assertEqual(generator.generate_slug(), 'one-of-one') # Invalid ensure_unique config['all']['ensure_unique'] = 'qwe' with self.assertRaisesRegex( ConfigurationError, "Invalid config: Invalid ensure_unique value: expected boolean, got 'qwe'" ): RandomGenerator(config) # Test with ensure_unique config['all']['ensure_unique'] = True with warnings.catch_warnings(record=True) as w: generator = RandomGenerator(config) if len(w) > 0: assert len(w) == 1 assert str( w[0].message ) == 'coolname.generate() may be slow because a significant fraction of combinations contain repeating words and ensure_unique is set' with patch.object(generator, '_randrange', side_effect=partial(next, cycle(iter([0, 1, 2, 3])))): self.assertEqual(generator.generate_slug(), 'one-of-two') self.assertEqual(generator.generate_slug(), 'two-of-one') self.assertEqual(generator.generate_slug(), 'one-of-two') self.assertEqual(generator.generate_slug(), 'two-of-one')
def test_create_from_file(self, load_config_mock, *args): load_config_mock.return_value = { 'all': { 'type': 'cartesian', 'lists': ['number', 'number'] }, 'number': { 'type': 'words', 'words': [str(x) for x in range(0, 10)] } } generator = RandomGenerator(load_config('dummy')) with patch.object(generator, '_randrange', return_value=35): self.assertEqual(generator.generate_slug(), '3-5')
def test_random_default(self): # NOTE: four slugs in this test must be updated every time you change word lists # 1. Re-seed default generator random.seed(123) assert random.random() == 0.052363598850944326 if six.PY2: # randrange returns different results in Python 2 self.assertEqual(coolname.generate_slug(), six.u('smooth-tuscan-limpet-of-opportunity')) else: self.assertEqual( coolname.generate_slug(), 'accelerated-salamander-of-enjoyable-abracadabra') # 2. Replace default generator rand = random.Random() rand.seed(456) assert rand.random() == 0.7482025358782363 coolname.replace_random(rand) if six.PY2: self.assertEqual(coolname.generate_slug(), six.u('resilient-ingenious-skunk-from-pluto')) else: self.assertEqual(coolname.generate_slug(), 'glorious-rose-mouflon-of-opportunity') # 3. Custom generator with custom Random config = { 'all': { 'type': 'cartesian', 'lists': ['digits', 'digits'] }, 'digits': { 'type': 'words', 'words': list(str(x) for x in range(10)) } } generator = RandomGenerator(config) generator.random.seed(12) if six.PY2: self.assertEqual(generator.generate_slug(), '4-7') else: self.assertEqual(generator.generate_slug(), '6-0') generator.random = FakeRandom(33) if six.PY2: generator.generate_slug() == '3-3' else: generator.generate_slug() == '3-3'
def test_ensure_unique_prefix(self): config = { 'all': { 'type': 'cartesian', 'lists': ['w1', 'w2'], }, 'w1': { 'type': 'words', 'words': ['brave', 'agile'] }, 'w2': { 'type': 'words', 'words': ['bravery', 'brass', 'agility', 'age'] } } generator = RandomGenerator(config) with patch.object(generator, '_randrange', side_effect=partial(next, cycle(iter(range(8))))): self.assertEqual(generator.generate_slug(), 'brave-bravery') # This sucks # ensure_unique_prefix = 0 is not allowed config['all']['ensure_unique_prefix'] = 0 with self.assertRaisesRegex( ConfigurationError, 'Invalid config: Invalid ensure_unique_prefix value: expected a positive integer, got 0' ): RandomGenerator(config) # Now enable unique prefix config['all']['ensure_unique_prefix'] = 4 generator = RandomGenerator(config) with patch.object(generator, '_randrange', side_effect=partial(next, cycle(iter(range(8))))): self.assertEqual(generator.generate_slug(), 'brave-brass') self.assertEqual(generator.generate_slug(), 'brave-agility') self.assertEqual(generator.generate_slug(), 'brave-age') self.assertEqual(generator.generate_slug(), 'agile-bravery') self.assertEqual(generator.generate_slug(), 'agile-brass') self.assertEqual(generator.generate_slug(), 'agile-age') self.assertEqual(generator.generate_slug(), 'brave-brass')
def test_max_slug_length_invalid(self): with self.assertRaisesRegex( InitializationError, r'Invalid config: Invalid max_slug_length value'): RandomGenerator({ 'all': { 'type': 'words', 'max_slug_length': 'invalid', 'words': ['one', 'two'] }, })
def test_unicode_config(self): generator = RandomGenerator({ u('all'): { u('type'): u('cartesian'), u('lists'): [u('прилагательное'), u('существительное')] }, u('прилагательное'): { u('type'): u('words'), u('words'): [u('белый'), u('черный')] }, u('существительное'): { u('type'): u('words'), u('words'): [u('круг'), u('квадрат')] } }) with patch.object(generator, '_randrange', side_effect=partial(next, cycle(iter(range(4))))): self.assertEqual(generator.generate_slug(), u('белый-круг')) self.assertEqual(generator.generate_slug(), u('белый-квадрат')) self.assertEqual(generator.generate_slug(), u('черный-круг')) self.assertEqual(generator.generate(), [u('черный'), u('квадрат')])
def test_phrase_list_squash_optimization(self): """PhraseLists should be squashed just like WordLists.""" config = { 'all': { 'type': 'cartesian', 'lists': ['nested1', 'nested2'], 'ensure_unique': True }, 'nested1': { 'type': 'nested', 'lists': ['phrases1', 'phrases2'] }, 'nested2': { 'type': 'nested', 'lists': ['phrases1', 'phrases2'] }, 'phrases1': { 'type': 'phrases', 'phrases': [['alpha', 'one'], ['beta', 'two']] }, 'phrases2': { 'type': 'phrases', 'phrases': [['gamma', 'three'], ['delta', 'four'], ['epsilon']] } } generator = RandomGenerator(config) # Make sure NestedLists are squashed and transformed into PhraseLists all_list = generator._lists[None] assert isinstance(all_list._lists[0], PhraseList) assert isinstance(all_list._lists[1], PhraseList) assert len(all_list._lists[0]) == 5 assert all_list._lists[0] == all_list._lists[1] tuples = [('alpha', 'one'), ('beta', 'two'), ('gamma', 'three'), ('delta', 'four'), ('epsilon', )] assert all_list._lists[0] == sorted(tuples) assert 3 <= len(generator.generate()) <= 4
def test_max_slug_length(self): with warnings.catch_warnings(record=True) as w: generator = RandomGenerator({ 'all': { 'type': 'cartesian', 'max_slug_length': 9, 'lists': ['one', 'two'] }, 'one': { 'type': 'words', 'words': ['big', 'small'] }, 'two': { 'type': 'words', 'words': ['cat', 'tiger'] }, }) if len(w) > 0: assert len(w) == 1 assert str( w[0].message ) == 'coolname.generate() may be slow because a significant fraction of combinations exceed max_slug_length=9' self.assertEqual(set(generator.generate_slug() for i in range(0, 100)), set(['big-cat', 'big-tiger', 'small-cat']))
def test_max_length(self): with self.assertRaisesRegex( InitializationError, "Config at key u?'one' has invalid word u?'tiger' " "\(longer than 4 characters\)"): RandomGenerator({ 'all': { 'type': 'nested', 'lists': ['one'] }, 'one': { 'type': 'words', 'max_length': 4, 'words': ['cat', 'lion', 'tiger'] } })
def test_configuration_error_too_deep(self): config = { 'all': { 'type': 'nested', 'lists': ['list0'] }, 'list100': { 'type': 'words', 'words': ['too', 'deep', 'for', 'you'], } } for i in range(100): config['list{}'.format(i)] = { 'type': 'nested', 'lists': ['list{}'.format(i + 1)] } with self.assertRaisesRegex(InitializationError, "Invalid config: Rule 'all' is too deep"): RandomGenerator(config)
def test_max_slug_length_warning(self, warn_mock): RandomGenerator({ 'all': { 'type': 'cartesian', 'max_slug_length': 3, 'lists': ['one', 'two'] }, 'one': { 'type': 'words', 'words': ['a'] * 70 + ['bb'] * 30 }, 'two': { 'type': 'words', 'words': ['c'] * 70 + ['dd'] * 30 }, }) warn_mock.assert_called_with( 'coolname.generate() may be slow because a significant ' 'fraction of combinations exceed max_slug_length=3')
def test_create_from_directory_conflict(self, load_data_mock, *args): load_data_mock.return_value = ({ 'all': { 'type': 'cartesian', 'lists': ['mywords'] }, 'mywords': { 'type': 'words', 'words': ['this', 'is', 'a', 'conflict'] } }, { 'mywords': ['a', 'b'] }) with self.assertRaisesRegex( InitializationError, r"^Conflict: list 'mywords' is defined both in config " "and in \*\.txt file. If it's a 'words' list, " "you should remove it from config\.$"): RandomGenerator(load_config('dummy'))
def test_max_slug_length_too_small(self): badlist = [str(i) for i in range(10, 100)] with self.assertRaisesRegex( InitializationError, r'Invalid config: Impossible to generate ' r'with max_slug_length=3'): RandomGenerator({ 'all': { 'type': 'cartesian', 'max_slug_length': 3, 'lists': ['one', 'two'] }, 'one': { 'type': 'words', 'words': badlist }, 'two': { 'type': 'words', 'words': badlist }, })
def test_configuration_error_cartesian_inside_cartesian(self, mock): config = { 'all': { 'type': 'cartesian', 'lists': ['word_list', 'cart_list'] }, 'word_list': { 'type': 'words', 'words': ['word1', 'word2'], }, 'cart_list': { 'type': 'cartesian', 'lists': ['word_list', 'word_list'], }, } with self.assertRaisesRegex( InitializationError, r"Invalid config: Cartesian list 'all' contains " r"another Cartesian list 'cart_list'\. Nested Cartesian lists " r"are not allowed\."): RandomGenerator(config)
def test_initialization(self): generator = RandomGenerator(config) assert generator.generate_slug()
def test_configuration_error(self): with self.assertRaisesRegex( InitializationError, "Invalid config: Value at key 'all' is not a dict"): RandomGenerator({'all': ['wrong']}) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has no 'type'"): RandomGenerator({'all': {'typ': 'wrong'}}) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has invalid 'type'"): RandomGenerator({'all': {'type': 'wrong'}}) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has no 'lists'"): RandomGenerator({'all': {'type': 'nested'}}) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has invalid 'lists'"): RandomGenerator({'all': {'type': 'nested', 'lists': 'wrong'}}) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has no 'value'"): RandomGenerator({'all': {'type': 'const'}}) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has invalid 'value'"): RandomGenerator({'all': {'type': 'const', 'value': 123}}) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has no 'words'"): RandomGenerator({'all': {'type': 'words'}}) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has invalid 'words'"): RandomGenerator({'all': {'type': 'words', 'words': []}}) with self.assertRaisesRegex( InitializationError, "Invalid config: Lists are referenced but not defined: one, two" ): RandomGenerator( {'all': { 'type': 'nested', 'lists': ['one', 'two'] }}) with self.assertRaisesRegex( InitializationError, "Invalid config: Rule 'all' is recursive: \['all', 'one'\]"): RandomGenerator({ 'all': { 'type': 'nested', 'lists': ['one'] }, 'one': { 'type': 'nested', 'lists': ['all'] } })
def test_configuration_error_phrases(self): with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has no 'phrases'"): RandomGenerator({'all': {'type': 'phrases', 'words': []}}) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has invalid 'phrases'"): RandomGenerator({'all': {'type': 'phrases', 'phrases': []}}) generator = RandomGenerator( {'all': { 'type': 'phrases', 'phrases': ['str is allowed'] }}) assert generator.generate_slug() == 'str-is-allowed' with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has invalid 'phrases': must be all string/tuple/list" ): RandomGenerator({ 'all': { 'type': 'phrases', 'phrases': [[['too many square brackets']]] } }) # Number of words RandomGenerator({ 'all': { 'type': 'phrases', 'number_of_words': 2, 'phrases': [['one', 'two'], ['three', 'four']] } }) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has invalid phrase 'five' \(1 word\(s\) but number_of_words=2\)" ): RandomGenerator({ 'all': { 'type': 'phrases', 'number_of_words': 2, 'phrases': [['one', 'two'], ['three', 'four'], ['five']] } }) # Max length RandomGenerator({ 'all': { 'type': 'phrases', 'max_length': 10, 'phrases': [['black', 'goose'], ['white', 'hare']] } }) with self.assertRaisesRegex( InitializationError, "Invalid config: Config at key 'all' has invalid phrase 'white rabbit' \(longer than 10 characters\)" ): RandomGenerator({ 'all': { 'type': 'phrases', 'max_length': 10, 'phrases': [['black', 'goose'], ['white', 'rabbit']] } })
def test_create_from_file_not_found(self, *args): with self.assertRaisesRegex(InitializationError, r'File or directory not found: .*dummy'): RandomGenerator(load_config('dummy'))