# coding: utf-8 # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import unicode_literals import itertools import hashlib import os import unittest import shutil import string import sys import tempfile import textwrap from mozfile.mozfile import NamedTemporaryFile from mozunit import ( main, MockedOpen, ) from mozbuild.util import ( expand_variables, FileAvoidWrite, group_unified_files, hash_file, indented_repr, memoize, memoized_property, pair, resolve_target_to_make, MozbuildDeletionError, HierarchicalStringList, EnumString, EnumStringComparisonError, ListWithAction, StrictOrderingOnAppendList, StrictOrderingOnAppendListWithFlagsFactory, TypedList, TypedNamedTuple, UnsortedError, ) if sys.version_info[0] == 3: str_type = 'str' else: str_type = 'unicode' data_path = os.path.abspath(os.path.dirname(__file__)) data_path = os.path.join(data_path, 'data') class TestHashing(unittest.TestCase): def test_hash_file_known_hash(self): """Ensure a known hash value is recreated.""" data = b'The quick brown fox jumps over the lazy cog' expected = 'de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3' temp = NamedTemporaryFile() temp.write(data) temp.flush() actual = hash_file(temp.name) self.assertEqual(actual, expected) def test_hash_file_large(self): """Ensure that hash_file seems to work with a large file.""" data = b'x' * 1048576 hasher = hashlib.sha1() hasher.update(data) expected = hasher.hexdigest() temp = NamedTemporaryFile() temp.write(data) temp.flush() actual = hash_file(temp.name) self.assertEqual(actual, expected) class TestFileAvoidWrite(unittest.TestCase): def test_file_avoid_write(self): with MockedOpen({'file': 'content'}): # Overwriting an existing file replaces its content faw = FileAvoidWrite('file') faw.write('bazqux') self.assertEqual(faw.close(), (True, True)) self.assertEqual(open('file', 'r').read(), 'bazqux') # Creating a new file (obviously) stores its content faw = FileAvoidWrite('file2') faw.write('content') self.assertEqual(faw.close(), (False, True)) self.assertEqual(open('file2').read(), 'content') with MockedOpen({'file': 'content'}): with FileAvoidWrite('file') as file: file.write('foobar') self.assertEqual(open('file', 'r').read(), 'foobar') class MyMockedOpen(MockedOpen): '''MockedOpen extension to raise an exception if something attempts to write in an opened file. ''' def __call__(self, name, mode): if 'w' in mode: raise Exception, 'Unexpected open with write mode' return MockedOpen.__call__(self, name, mode) with MyMockedOpen({'file': 'content'}): # Validate that MyMockedOpen works as intended file = FileAvoidWrite('file') file.write('foobar') self.assertRaises(Exception, file.close) # Check that no write actually happens when writing the # same content as what already is in the file faw = FileAvoidWrite('file') faw.write('content') self.assertEqual(faw.close(), (True, False)) def test_diff_not_default(self): """Diffs are not produced by default.""" with MockedOpen({'file': 'old'}): faw = FileAvoidWrite('file') faw.write('dummy') faw.close() self.assertIsNone(faw.diff) def test_diff_update(self): """Diffs are produced on file update.""" with MockedOpen({'file': 'old'}): faw = FileAvoidWrite('file', capture_diff=True) faw.write('new') faw.close() diff = '\n'.join(faw.diff) self.assertIn('-old', diff) self.assertIn('+new', diff) def test_diff_create(self): """Diffs are produced when files are created.""" tmpdir = tempfile.mkdtemp() try: path = os.path.join(tmpdir, 'file') faw = FileAvoidWrite(path, capture_diff=True) faw.write('new') faw.close() diff = '\n'.join(faw.diff) self.assertIn('+new', diff) finally: shutil.rmtree(tmpdir) class TestResolveTargetToMake(unittest.TestCase): def setUp(self): self.topobjdir = data_path def assertResolve(self, path, expected): # Handle Windows path separators. (reldir, target) = resolve_target_to_make(self.topobjdir, path) if reldir is not None: reldir = reldir.replace(os.sep, '/') if target is not None: target = target.replace(os.sep, '/') self.assertEqual((reldir, target), expected) def test_root_path(self): self.assertResolve('/test-dir', ('test-dir', None)) self.assertResolve('/test-dir/with', ('test-dir/with', None)) self.assertResolve('/test-dir/without', ('test-dir', None)) self.assertResolve('/test-dir/without/with', ('test-dir/without/with', None)) def test_dir(self): self.assertResolve('test-dir', ('test-dir', None)) self.assertResolve('test-dir/with', ('test-dir/with', None)) self.assertResolve('test-dir/with', ('test-dir/with', None)) self.assertResolve('test-dir/without', ('test-dir', None)) self.assertResolve('test-dir/without/with', ('test-dir/without/with', None)) def test_top_level(self): self.assertResolve('package', (None, 'package')) # Makefile handling shouldn't affect top-level targets. self.assertResolve('Makefile', (None, 'Makefile')) def test_regular_file(self): self.assertResolve('test-dir/with/file', ('test-dir/with', 'file')) self.assertResolve('test-dir/with/without/file', ('test-dir/with', 'without/file')) self.assertResolve('test-dir/with/without/with/file', ('test-dir/with/without/with', 'file')) self.assertResolve('test-dir/without/file', ('test-dir', 'without/file')) self.assertResolve('test-dir/without/with/file', ('test-dir/without/with', 'file')) self.assertResolve('test-dir/without/with/without/file', ('test-dir/without/with', 'without/file')) def test_Makefile(self): self.assertResolve('test-dir/with/Makefile', ('test-dir', 'with/Makefile')) self.assertResolve('test-dir/with/without/Makefile', ('test-dir/with', 'without/Makefile')) self.assertResolve('test-dir/with/without/with/Makefile', ('test-dir/with', 'without/with/Makefile')) self.assertResolve('test-dir/without/Makefile', ('test-dir', 'without/Makefile')) self.assertResolve('test-dir/without/with/Makefile', ('test-dir', 'without/with/Makefile')) self.assertResolve('test-dir/without/with/without/Makefile', ('test-dir/without/with', 'without/Makefile')) class TestHierarchicalStringList(unittest.TestCase): def setUp(self): self.EXPORTS = HierarchicalStringList() def test_exports_append(self): self.assertEqual(self.EXPORTS._strings, []) self.EXPORTS += ["foo.h"] self.assertEqual(self.EXPORTS._strings, ["foo.h"]) self.EXPORTS += ["bar.h"] self.assertEqual(self.EXPORTS._strings, ["foo.h", "bar.h"]) def test_exports_subdir(self): self.assertEqual(self.EXPORTS._children, {}) self.EXPORTS.foo += ["foo.h"] self.assertItemsEqual(self.EXPORTS._children, {"foo" : True}) self.assertEqual(self.EXPORTS.foo._strings, ["foo.h"]) self.EXPORTS.bar += ["bar.h"] self.assertItemsEqual(self.EXPORTS._children, {"foo" : True, "bar" : True}) self.assertEqual(self.EXPORTS.foo._strings, ["foo.h"]) self.assertEqual(self.EXPORTS.bar._strings, ["bar.h"]) def test_exports_multiple_subdir(self): self.EXPORTS.foo.bar = ["foobar.h"] self.assertItemsEqual(self.EXPORTS._children, {"foo" : True}) self.assertItemsEqual(self.EXPORTS.foo._children, {"bar" : True}) self.assertItemsEqual(self.EXPORTS.foo.bar._children, {}) self.assertEqual(self.EXPORTS._strings, []) self.assertEqual(self.EXPORTS.foo._strings, []) self.assertEqual(self.EXPORTS.foo.bar._strings, ["foobar.h"]) def test_invalid_exports_append(self): with self.assertRaises(ValueError) as ve: self.EXPORTS += "foo.h" self.assertEqual(str(ve.exception), "Expected a list of strings, not " % str_type) def test_invalid_exports_set(self): with self.assertRaises(ValueError) as ve: self.EXPORTS.foo = "foo.h" self.assertEqual(str(ve.exception), "Expected a list of strings, not " % str_type) def test_invalid_exports_append_base(self): with self.assertRaises(ValueError) as ve: self.EXPORTS += "foo.h" self.assertEqual(str(ve.exception), "Expected a list of strings, not " % str_type) def test_invalid_exports_bool(self): with self.assertRaises(ValueError) as ve: self.EXPORTS += [True] self.assertEqual(str(ve.exception), "Expected a list of strings, not an element of " "") def test_del_exports(self): with self.assertRaises(MozbuildDeletionError) as mde: self.EXPORTS.foo += ['bar.h'] del self.EXPORTS.foo def test_unsorted(self): with self.assertRaises(UnsortedError) as ee: self.EXPORTS += ['foo.h', 'bar.h'] with self.assertRaises(UnsortedError) as ee: self.EXPORTS.foo = ['foo.h', 'bar.h'] with self.assertRaises(UnsortedError) as ee: self.EXPORTS.foo += ['foo.h', 'bar.h'] def test_reassign(self): self.EXPORTS.foo = ['foo.h'] with self.assertRaises(KeyError) as ee: self.EXPORTS.foo = ['bar.h'] def test_walk(self): l = HierarchicalStringList() l += ['root1', 'root2', 'root3'] l.child1 += ['child11', 'child12', 'child13'] l.child1.grandchild1 += ['grandchild111', 'grandchild112'] l.child1.grandchild2 += ['grandchild121', 'grandchild122'] l.child2.grandchild1 += ['grandchild211', 'grandchild212'] l.child2.grandchild1 += ['grandchild213', 'grandchild214'] els = list((path, list(seq)) for path, seq in l.walk()) self.assertEqual(els, [ ('', ['root1', 'root2', 'root3']), ('child1', ['child11', 'child12', 'child13']), ('child1/grandchild1', ['grandchild111', 'grandchild112']), ('child1/grandchild2', ['grandchild121', 'grandchild122']), ('child2/grandchild1', ['grandchild211', 'grandchild212', 'grandchild213', 'grandchild214']), ]) def test_merge(self): l1 = HierarchicalStringList() l1 += ['root1', 'root2', 'root3'] l1.child1 += ['child11', 'child12', 'child13'] l1.child1.grandchild1 += ['grandchild111', 'grandchild112'] l1.child1.grandchild2 += ['grandchild121', 'grandchild122'] l1.child2.grandchild1 += ['grandchild211', 'grandchild212'] l1.child2.grandchild1 += ['grandchild213', 'grandchild214'] l2 = HierarchicalStringList() l2.child1 += ['child14', 'child15'] l2.child1.grandchild2 += ['grandchild123'] l2.child3 += ['child31', 'child32'] l1 += l2 els = list((path, list(seq)) for path, seq in l1.walk()) self.assertEqual(els, [ ('', ['root1', 'root2', 'root3']), ('child1', ['child11', 'child12', 'child13', 'child14', 'child15']), ('child1/grandchild1', ['grandchild111', 'grandchild112']), ('child1/grandchild2', ['grandchild121', 'grandchild122', 'grandchild123']), ('child2/grandchild1', ['grandchild211', 'grandchild212', 'grandchild213', 'grandchild214']), ('child3', ['child31', 'child32']), ]) class TestStrictOrderingOnAppendList(unittest.TestCase): def test_init(self): l = StrictOrderingOnAppendList() self.assertEqual(len(l), 0) l = StrictOrderingOnAppendList(['a', 'b', 'c']) self.assertEqual(len(l), 3) with self.assertRaises(UnsortedError): StrictOrderingOnAppendList(['c', 'b', 'a']) self.assertEqual(len(l), 3) def test_extend(self): l = StrictOrderingOnAppendList() l.extend(['a', 'b']) self.assertEqual(len(l), 2) self.assertIsInstance(l, StrictOrderingOnAppendList) with self.assertRaises(UnsortedError): l.extend(['d', 'c']) self.assertEqual(len(l), 2) def test_slicing(self): l = StrictOrderingOnAppendList() l[:] = ['a', 'b'] self.assertEqual(len(l), 2) self.assertIsInstance(l, StrictOrderingOnAppendList) with self.assertRaises(UnsortedError): l[:] = ['b', 'a'] self.assertEqual(len(l), 2) def test_add(self): l = StrictOrderingOnAppendList() l2 = l + ['a', 'b'] self.assertEqual(len(l), 0) self.assertEqual(len(l2), 2) self.assertIsInstance(l2, StrictOrderingOnAppendList) with self.assertRaises(UnsortedError): l2 = l + ['b', 'a'] self.assertEqual(len(l), 0) def test_iadd(self): l = StrictOrderingOnAppendList() l += ['a', 'b'] self.assertEqual(len(l), 2) self.assertIsInstance(l, StrictOrderingOnAppendList) with self.assertRaises(UnsortedError): l += ['b', 'a'] self.assertEqual(len(l), 2) def test_add_after_iadd(self): l = StrictOrderingOnAppendList(['b']) l += ['a'] l2 = l + ['c', 'd'] self.assertEqual(len(l), 2) self.assertEqual(len(l2), 4) self.assertIsInstance(l2, StrictOrderingOnAppendList) with self.assertRaises(UnsortedError): l2 = l + ['d', 'c'] self.assertEqual(len(l), 2) def test_add_StrictOrderingOnAppendList(self): l = StrictOrderingOnAppendList() l += ['c', 'd'] l += ['a', 'b'] l2 = StrictOrderingOnAppendList() with self.assertRaises(UnsortedError): l2 += list(l) # Adding a StrictOrderingOnAppendList to another shouldn't throw l2 += l class TestListWithAction(unittest.TestCase): def setUp(self): self.action = lambda a: (a, id(a)) def assertSameList(self, expected, actual): self.assertEqual(len(expected), len(actual)) for idx, item in enumerate(actual): self.assertEqual(item, expected[idx]) def test_init(self): l = ListWithAction(action=self.action) self.assertEqual(len(l), 0) original = ['a', 'b', 'c'] l = ListWithAction(['a', 'b', 'c'], action=self.action) expected = map(self.action, original) self.assertSameList(expected, l) with self.assertRaises(ValueError): ListWithAction('abc', action=self.action) with self.assertRaises(ValueError): ListWithAction() def test_extend(self): l = ListWithAction(action=self.action) original = ['a', 'b'] l.extend(original) expected = map(self.action, original) self.assertSameList(expected, l) with self.assertRaises(ValueError): l.extend('ab') def test_slicing(self): l = ListWithAction(action=self.action) original = ['a', 'b'] l[:] = original expected = map(self.action, original) self.assertSameList(expected, l) with self.assertRaises(ValueError): l[:] = 'ab' def test_add(self): l = ListWithAction(action=self.action) original = ['a', 'b'] l2 = l + original expected = map(self.action, original) self.assertSameList(expected, l2) with self.assertRaises(ValueError): l + 'abc' def test_iadd(self): l = ListWithAction(action=self.action) original = ['a', 'b'] l += original expected = map(self.action, original) self.assertSameList(expected, l) with self.assertRaises(ValueError): l += 'abc' class TestStrictOrderingOnAppendListWithFlagsFactory(unittest.TestCase): def test_strict_ordering_on_append_list_with_flags_factory(self): cls = StrictOrderingOnAppendListWithFlagsFactory({ 'foo': bool, 'bar': int, }) l = cls() l += ['a', 'b'] with self.assertRaises(Exception): l['a'] = 'foo' with self.assertRaises(Exception): c = l['c'] self.assertEqual(l['a'].foo, False) l['a'].foo = True self.assertEqual(l['a'].foo, True) with self.assertRaises(TypeError): l['a'].bar = 'bar' self.assertEqual(l['a'].bar, 0) l['a'].bar = 42 self.assertEqual(l['a'].bar, 42) l['b'].foo = True self.assertEqual(l['b'].foo, True) with self.assertRaises(AttributeError): l['b'].baz = False l['b'].update(foo=False, bar=12) self.assertEqual(l['b'].foo, False) self.assertEqual(l['b'].bar, 12) with self.assertRaises(AttributeError): l['b'].update(xyz=1) def test_strict_ordering_on_append_list_with_flags_factory_extend(self): FooList = StrictOrderingOnAppendListWithFlagsFactory({ 'foo': bool, 'bar': unicode }) foo = FooList(['a', 'b', 'c']) foo['a'].foo = True foo['b'].bar = 'bar' # Don't allow extending lists with different flag definitions. BarList = StrictOrderingOnAppendListWithFlagsFactory({ 'foo': unicode, 'baz': bool }) bar = BarList(['d', 'e', 'f']) bar['d'].foo = 'foo' bar['e'].baz = True with self.assertRaises(ValueError): foo + bar with self.assertRaises(ValueError): bar + foo # It's not obvious what to do with duplicate list items with possibly # different flag values, so don't allow that case. with self.assertRaises(ValueError): foo + foo def assertExtended(l): self.assertEqual(len(l), 6) self.assertEqual(l['a'].foo, True) self.assertEqual(l['b'].bar, 'bar') self.assertTrue('c' in l) self.assertEqual(l['d'].foo, True) self.assertEqual(l['e'].bar, 'bar') self.assertTrue('f' in l) # Test extend. zot = FooList(['d', 'e', 'f']) zot['d'].foo = True zot['e'].bar = 'bar' zot.extend(foo) assertExtended(zot) # Test __add__. zot = FooList(['d', 'e', 'f']) zot['d'].foo = True zot['e'].bar = 'bar' assertExtended(foo + zot) assertExtended(zot + foo) # Test __iadd__. foo += zot assertExtended(foo) # Test __setslice__. foo[3:] = [] self.assertEqual(len(foo), 3) foo[3:] = zot assertExtended(foo) class TestMemoize(unittest.TestCase): def test_memoize(self): self._count = 0 @memoize def wrapped(a, b): self._count += 1 return a + b self.assertEqual(self._count, 0) self.assertEqual(wrapped(1, 1), 2) self.assertEqual(self._count, 1) self.assertEqual(wrapped(1, 1), 2) self.assertEqual(self._count, 1) self.assertEqual(wrapped(2, 1), 3) self.assertEqual(self._count, 2) self.assertEqual(wrapped(1, 2), 3) self.assertEqual(self._count, 3) self.assertEqual(wrapped(1, 2), 3) self.assertEqual(self._count, 3) self.assertEqual(wrapped(1, 1), 2) self.assertEqual(self._count, 3) def test_memoize_method(self): class foo(object): def __init__(self): self._count = 0 @memoize def wrapped(self, a, b): self._count += 1 return a + b instance = foo() refcount = sys.getrefcount(instance) self.assertEqual(instance._count, 0) self.assertEqual(instance.wrapped(1, 1), 2) self.assertEqual(instance._count, 1) self.assertEqual(instance.wrapped(1, 1), 2) self.assertEqual(instance._count, 1) self.assertEqual(instance.wrapped(2, 1), 3) self.assertEqual(instance._count, 2) self.assertEqual(instance.wrapped(1, 2), 3) self.assertEqual(instance._count, 3) self.assertEqual(instance.wrapped(1, 2), 3) self.assertEqual(instance._count, 3) self.assertEqual(instance.wrapped(1, 1), 2) self.assertEqual(instance._count, 3) # Memoization of methods is expected to not keep references to # instances, so the refcount shouldn't have changed after executing the # memoized method. self.assertEqual(refcount, sys.getrefcount(instance)) def test_memoized_property(self): class foo(object): def __init__(self): self._count = 0 @memoized_property def wrapped(self): self._count += 1 return 42 instance = foo() self.assertEqual(instance._count, 0) self.assertEqual(instance.wrapped, 42) self.assertEqual(instance._count, 1) self.assertEqual(instance.wrapped, 42) self.assertEqual(instance._count, 1) class TestTypedList(unittest.TestCase): def test_init(self): cls = TypedList(int) l = cls() self.assertEqual(len(l), 0) l = cls([1, 2, 3]) self.assertEqual(len(l), 3) with self.assertRaises(ValueError): cls([1, 2, 'c']) def test_extend(self): cls = TypedList(int) l = cls() l.extend([1, 2]) self.assertEqual(len(l), 2) self.assertIsInstance(l, cls) with self.assertRaises(ValueError): l.extend([3, 'c']) self.assertEqual(len(l), 2) def test_slicing(self): cls = TypedList(int) l = cls() l[:] = [1, 2] self.assertEqual(len(l), 2) self.assertIsInstance(l, cls) with self.assertRaises(ValueError): l[:] = [3, 'c'] self.assertEqual(len(l), 2) def test_add(self): cls = TypedList(int) l = cls() l2 = l + [1, 2] self.assertEqual(len(l), 0) self.assertEqual(len(l2), 2) self.assertIsInstance(l2, cls) with self.assertRaises(ValueError): l2 = l + [3, 'c'] self.assertEqual(len(l), 0) def test_iadd(self): cls = TypedList(int) l = cls() l += [1, 2] self.assertEqual(len(l), 2) self.assertIsInstance(l, cls) with self.assertRaises(ValueError): l += [3, 'c'] self.assertEqual(len(l), 2) def test_add_coercion(self): objs = [] class Foo(object): def __init__(self, obj): objs.append(obj) cls = TypedList(Foo) l = cls() l += [1, 2] self.assertEqual(len(objs), 2) self.assertEqual(type(l[0]), Foo) self.assertEqual(type(l[1]), Foo) # Adding a TypedList to a TypedList shouldn't trigger coercion again l2 = cls() l2 += l self.assertEqual(len(objs), 2) self.assertEqual(type(l2[0]), Foo) self.assertEqual(type(l2[1]), Foo) # Adding a TypedList to a TypedList shouldn't even trigger the code # that does coercion at all. l2 = cls() list.__setslice__(l, 0, -1, [1, 2]) l2 += l self.assertEqual(len(objs), 2) self.assertEqual(type(l2[0]), int) self.assertEqual(type(l2[1]), int) def test_memoized(self): cls = TypedList(int) cls2 = TypedList(str) self.assertEqual(TypedList(int), cls) self.assertNotEqual(cls, cls2) class TypedTestStrictOrderingOnAppendList(unittest.TestCase): def test_init(self): class Unicode(unicode): def __init__(self, other): if not isinstance(other, unicode): raise ValueError() super(Unicode, self).__init__(other) cls = TypedList(Unicode, StrictOrderingOnAppendList) l = cls() self.assertEqual(len(l), 0) l = cls(['a', 'b', 'c']) self.assertEqual(len(l), 3) with self.assertRaises(UnsortedError): cls(['c', 'b', 'a']) with self.assertRaises(ValueError): cls(['a', 'b', 3]) self.assertEqual(len(l), 3) class TestTypedNamedTuple(unittest.TestCase): def test_simple(self): FooBar = TypedNamedTuple('FooBar', [('foo', unicode), ('bar', int)]) t = FooBar(foo='foo', bar=2) self.assertEquals(type(t), FooBar) self.assertEquals(t.foo, 'foo') self.assertEquals(t.bar, 2) self.assertEquals(t[0], 'foo') self.assertEquals(t[1], 2) FooBar('foo', 2) with self.assertRaises(TypeError): FooBar('foo', 'not integer') with self.assertRaises(TypeError): FooBar(2, 4) # Passing a tuple as the first argument is the same as passing multiple # arguments. t1 = ('foo', 3) t2 = FooBar(t1) self.assertEquals(type(t2), FooBar) self.assertEqual(FooBar(t1), FooBar('foo', 3)) class TestGroupUnifiedFiles(unittest.TestCase): FILES = ['%s.cpp' % letter for letter in string.ascii_lowercase] def test_multiple_files(self): mapping = list(group_unified_files(self.FILES, 'Unified', 'cpp', 5)) def check_mapping(index, expected_num_source_files): (unified_file, source_files) = mapping[index] self.assertEqual(unified_file, 'Unified%d.cpp' % index) self.assertEqual(len(source_files), expected_num_source_files) all_files = list(itertools.chain(*[files for (_, files) in mapping])) self.assertEqual(len(all_files), len(self.FILES)) self.assertEqual(set(all_files), set(self.FILES)) expected_amounts = [5, 5, 5, 5, 5, 1] for i, amount in enumerate(expected_amounts): check_mapping(i, amount) def test_unsorted_files(self): unsorted_files = ['a%d.cpp' % i for i in range(11)] sorted_files = sorted(unsorted_files) mapping = list(group_unified_files(unsorted_files, 'Unified', 'cpp', 5)) self.assertEqual(mapping[0][1], sorted_files[0:5]) self.assertEqual(mapping[1][1], sorted_files[5:10]) self.assertEqual(mapping[2][1], sorted_files[10:]) class TestMisc(unittest.TestCase): def test_pair(self): self.assertEqual( list(pair([1, 2, 3, 4, 5, 6])), [(1, 2), (3, 4), (5, 6)] ) self.assertEqual( list(pair([1, 2, 3, 4, 5, 6, 7])), [(1, 2), (3, 4), (5, 6), (7, None)] ) def test_expand_variables(self): self.assertEqual( expand_variables('$(var)', {'var': 'value'}), 'value' ) self.assertEqual( expand_variables('$(a) and $(b)', {'a': '1', 'b': '2'}), '1 and 2' ) self.assertEqual( expand_variables('$(a) and $(undefined)', {'a': '1', 'b': '2'}), '1 and ' ) self.assertEqual( expand_variables('before $(string) between $(list) after', { 'string': 'abc', 'list': ['a', 'b', 'c'] }), 'before abc between a b c after' ) class TestEnumString(unittest.TestCase): def test_string(self): CompilerType = EnumString.subclass('msvc', 'gcc', 'clang', 'clang-cl') type = CompilerType('msvc') self.assertEquals(type, 'msvc') self.assertNotEquals(type, 'gcc') self.assertNotEquals(type, 'clang') self.assertNotEquals(type, 'clang-cl') self.assertIn(type, ('msvc', 'clang-cl')) self.assertNotIn(type, ('gcc', 'clang')) with self.assertRaises(EnumStringComparisonError): self.assertEquals(type, 'foo') with self.assertRaises(EnumStringComparisonError): self.assertNotEquals(type, 'foo') with self.assertRaises(EnumStringComparisonError): self.assertIn(type, ('foo', 'gcc')) with self.assertRaises(ValueError): type = CompilerType('foo') class TestIndentedRepr(unittest.TestCase): def test_indented_repr(self): data = textwrap.dedent(r''' { 'a': 1, 'b': b'abc', b'c': 'xyz', 'd': False, 'e': { 'a': 1, 'b': b'2', 'c': '3', }, 'f': [ 1, b'2', '3', ], 'pile_of_bytes': b'\xf0\x9f\x92\xa9', 'pile_of_poo': '💩', 'special_chars': '\\\'"\x08\n\t', 'with_accents': 'éàñ', }''').lstrip() obj = eval(data) self.assertEqual(indented_repr(obj), data) if __name__ == '__main__': main()