summaryrefslogtreecommitdiffstats
path: root/python/mozbuild/mozbuild/test/test_util.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/mozbuild/mozbuild/test/test_util.py')
-rw-r--r--python/mozbuild/mozbuild/test/test_util.py889
1 files changed, 889 insertions, 0 deletions
diff --git a/python/mozbuild/mozbuild/test/test_util.py b/python/mozbuild/mozbuild/test/test_util.py
new file mode 100644
index 0000000000..9931b338b9
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/test_util.py
@@ -0,0 +1,889 @@
+# 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/.
+
+import copy
+import hashlib
+import itertools
+import os
+import string
+import sys
+import unittest
+
+import pytest
+import six
+from mozfile.mozfile import NamedTemporaryFile
+from mozunit import main
+
+from mozbuild.util import (
+ EnumString,
+ EnumStringComparisonError,
+ HierarchicalStringList,
+ MozbuildDeletionError,
+ ReadOnlyDict,
+ StrictOrderingOnAppendList,
+ StrictOrderingOnAppendListWithAction,
+ StrictOrderingOnAppendListWithFlagsFactory,
+ TypedList,
+ TypedNamedTuple,
+ UnsortedError,
+ expand_variables,
+ group_unified_files,
+ hash_file,
+ hexdump,
+ memoize,
+ memoized_property,
+ pair,
+ resolve_target_to_make,
+)
+
+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 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"]
+ six.assertCountEqual(self, self.EXPORTS._children, {"foo": True})
+ self.assertEqual(self.EXPORTS.foo._strings, ["foo.h"])
+ self.EXPORTS.bar += ["bar.h"]
+ six.assertCountEqual(self, 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"]
+ six.assertCountEqual(self, self.EXPORTS._children, {"foo": True})
+ six.assertCountEqual(self, self.EXPORTS.foo._children, {"bar": True})
+ six.assertCountEqual(self, 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"
+ six.assertRegex(
+ self,
+ str(ve.exception),
+ "Expected a list of strings, not <(?:type|class) '%s'>" % str_type,
+ )
+
+ def test_invalid_exports_set(self):
+ with self.assertRaises(ValueError) as ve:
+ self.EXPORTS.foo = "foo.h"
+
+ six.assertRegex(
+ self,
+ str(ve.exception),
+ "Expected a list of strings, not <(?:type|class) '%s'>" % str_type,
+ )
+
+ def test_invalid_exports_append_base(self):
+ with self.assertRaises(ValueError) as ve:
+ self.EXPORTS += "foo.h"
+
+ six.assertRegex(
+ self,
+ str(ve.exception),
+ "Expected a list of strings, not <(?:type|class) '%s'>" % str_type,
+ )
+
+ def test_invalid_exports_bool(self):
+ with self.assertRaises(ValueError) as ve:
+ self.EXPORTS += [True]
+
+ six.assertRegex(
+ self,
+ str(ve.exception),
+ "Expected a list of strings, not an element of " "<(?:type|class) 'bool'>",
+ )
+
+ def test_del_exports(self):
+ with self.assertRaises(MozbuildDeletionError):
+ self.EXPORTS.foo += ["bar.h"]
+ del self.EXPORTS.foo
+
+ def test_unsorted(self):
+ with self.assertRaises(UnsortedError):
+ self.EXPORTS += ["foo.h", "bar.h"]
+
+ with self.assertRaises(UnsortedError):
+ self.EXPORTS.foo = ["foo.h", "bar.h"]
+
+ with self.assertRaises(UnsortedError):
+ self.EXPORTS.foo += ["foo.h", "bar.h"]
+
+ def test_reassign(self):
+ self.EXPORTS.foo = ["foo.h"]
+
+ with self.assertRaises(KeyError):
+ 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 TestStrictOrderingOnAppendListWithAction(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 = StrictOrderingOnAppendListWithAction(action=self.action)
+ self.assertEqual(len(l), 0)
+ original = ["a", "b", "c"]
+ l = StrictOrderingOnAppendListWithAction(["a", "b", "c"], action=self.action)
+ expected = [self.action(i) for i in original]
+ self.assertSameList(expected, l)
+
+ with self.assertRaises(ValueError):
+ StrictOrderingOnAppendListWithAction("abc", action=self.action)
+
+ with self.assertRaises(ValueError):
+ StrictOrderingOnAppendListWithAction()
+
+ def test_extend(self):
+ l = StrictOrderingOnAppendListWithAction(action=self.action)
+ original = ["a", "b"]
+ l.extend(original)
+ expected = [self.action(i) for i in original]
+ self.assertSameList(expected, l)
+
+ with self.assertRaises(ValueError):
+ l.extend("ab")
+
+ def test_slicing(self):
+ l = StrictOrderingOnAppendListWithAction(action=self.action)
+ original = ["a", "b"]
+ l[:] = original
+ expected = [self.action(i) for i in original]
+ self.assertSameList(expected, l)
+
+ with self.assertRaises(ValueError):
+ l[:] = "ab"
+
+ def test_add(self):
+ l = StrictOrderingOnAppendListWithAction(action=self.action)
+ original = ["a", "b"]
+ l2 = l + original
+ expected = [self.action(i) for i in original]
+ self.assertSameList(expected, l2)
+
+ with self.assertRaises(ValueError):
+ l + "abc"
+
+ def test_iadd(self):
+ l = StrictOrderingOnAppendListWithAction(action=self.action)
+ original = ["a", "b"]
+ l += original
+ expected = [self.action(i) for i in 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):
+ 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": six.text_type}
+ )
+ 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": six.text_type, "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 __setitem__.
+ 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.__setitem__(l, slice(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(six.text_type):
+ def __new__(cls, other):
+ if not isinstance(other, six.text_type):
+ raise ValueError()
+ return six.text_type.__new__(cls, 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", six.text_type), ("bar", int)])
+
+ t = FooBar(foo="foo", bar=2)
+ self.assertEqual(type(t), FooBar)
+ self.assertEqual(t.foo, "foo")
+ self.assertEqual(t.bar, 2)
+ self.assertEqual(t[0], "foo")
+ self.assertEqual(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.assertEqual(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)
+
+
+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("gcc", "clang", "clang-cl")
+
+ type = CompilerType("gcc")
+ self.assertEqual(type, "gcc")
+ self.assertNotEqual(type, "clang")
+ self.assertNotEqual(type, "clang-cl")
+ self.assertIn(type, ("gcc", "clang-cl"))
+ self.assertNotIn(type, ("clang", "clang-cl"))
+
+ with self.assertRaises(EnumStringComparisonError):
+ self.assertEqual(type, "foo")
+
+ with self.assertRaises(EnumStringComparisonError):
+ self.assertNotEqual(type, "foo")
+
+ with self.assertRaises(EnumStringComparisonError):
+ self.assertIn(type, ("foo", "gcc"))
+
+ with self.assertRaises(ValueError):
+ type = CompilerType("foo")
+
+
+class TestHexDump(unittest.TestCase):
+ @unittest.skipUnless(six.PY3, "requires Python 3")
+ def test_hexdump(self):
+ self.assertEqual(
+ hexdump("abcdef123💩ZYXWVU".encode("utf-8")),
+ [
+ "00 61 62 63 64 65 66 31 32 33 f0 9f 92 a9 5a 59 58 |abcdef123....ZYX|\n",
+ "10 57 56 55 |WVU |\n",
+ ],
+ )
+
+
+def test_read_only_dict():
+ d = ReadOnlyDict(foo="bar")
+ with pytest.raises(Exception):
+ d["foo"] = "baz"
+
+ with pytest.raises(Exception):
+ d.update({"foo": "baz"})
+
+ with pytest.raises(Exception):
+ del d["foo"]
+
+ # ensure copy still works
+ d_copy = d.copy()
+ assert d == d_copy
+ # TODO Returning a dict here feels like a bug, but there are places in-tree
+ # relying on this behaviour.
+ assert isinstance(d_copy, dict)
+
+ d_copy = copy.copy(d)
+ assert d == d_copy
+ assert isinstance(d_copy, ReadOnlyDict)
+
+ d_copy = copy.deepcopy(d)
+ assert d == d_copy
+ assert isinstance(d_copy, ReadOnlyDict)
+
+
+if __name__ == "__main__":
+ main()