| # 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 os |
| import unittest |
| |
| from mozunit import main |
| |
| from mozbuild.frontend.context import ( |
| AbsolutePath, |
| Context, |
| ContextDerivedTypedHierarchicalStringList, |
| ContextDerivedTypedList, |
| ContextDerivedTypedListWithItems, |
| ContextDerivedTypedRecord, |
| Files, |
| FUNCTIONS, |
| ObjDirPath, |
| Path, |
| SourcePath, |
| SPECIAL_VARIABLES, |
| SUBCONTEXTS, |
| VARIABLES, |
| ) |
| |
| from mozbuild.util import StrictOrderingOnAppendListWithFlagsFactory |
| from mozpack import path as mozpath |
| |
| |
| class TestContext(unittest.TestCase): |
| def test_defaults(self): |
| test = Context({ |
| 'foo': (int, int, '', None), |
| 'bar': (bool, bool, '', None), |
| 'baz': (dict, dict, '', None), |
| }) |
| |
| self.assertEqual(test.keys(), []) |
| |
| self.assertEqual(test['foo'], 0) |
| |
| self.assertEqual(set(test.keys()), { 'foo' }) |
| |
| self.assertEqual(test['bar'], False) |
| |
| self.assertEqual(set(test.keys()), { 'foo', 'bar' }) |
| |
| self.assertEqual(test['baz'], {}) |
| |
| self.assertEqual(set(test.keys()), { 'foo', 'bar', 'baz' }) |
| |
| with self.assertRaises(KeyError): |
| test['qux'] |
| |
| self.assertEqual(set(test.keys()), { 'foo', 'bar', 'baz' }) |
| |
| def test_type_check(self): |
| test = Context({ |
| 'foo': (int, int, '', None), |
| 'baz': (dict, list, '', None), |
| }) |
| |
| test['foo'] = 5 |
| |
| self.assertEqual(test['foo'], 5) |
| |
| with self.assertRaises(ValueError): |
| test['foo'] = {} |
| |
| self.assertEqual(test['foo'], 5) |
| |
| with self.assertRaises(KeyError): |
| test['bar'] = True |
| |
| test['baz'] = [('a', 1), ('b', 2)] |
| |
| self.assertEqual(test['baz'], { 'a': 1, 'b': 2 }) |
| |
| def test_update(self): |
| test = Context({ |
| 'foo': (int, int, '', None), |
| 'bar': (bool, bool, '', None), |
| 'baz': (dict, list, '', None), |
| }) |
| |
| self.assertEqual(test.keys(), []) |
| |
| with self.assertRaises(ValueError): |
| test.update(bar=True, foo={}) |
| |
| self.assertEqual(test.keys(), []) |
| |
| test.update(bar=True, foo=1) |
| |
| self.assertEqual(set(test.keys()), { 'foo', 'bar' }) |
| self.assertEqual(test['foo'], 1) |
| self.assertEqual(test['bar'], True) |
| |
| test.update([('bar', False), ('foo', 2)]) |
| self.assertEqual(test['foo'], 2) |
| self.assertEqual(test['bar'], False) |
| |
| test.update([('foo', 0), ('baz', { 'a': 1, 'b': 2 })]) |
| self.assertEqual(test['foo'], 0) |
| self.assertEqual(test['baz'], { 'a': 1, 'b': 2 }) |
| |
| test.update([('foo', 42), ('baz', [('c', 3), ('d', 4)])]) |
| self.assertEqual(test['foo'], 42) |
| self.assertEqual(test['baz'], { 'c': 3, 'd': 4 }) |
| |
| def test_context_paths(self): |
| test = Context() |
| |
| # Newly created context has no paths. |
| self.assertIsNone(test.main_path) |
| self.assertIsNone(test.current_path) |
| self.assertEqual(test.all_paths, set()) |
| self.assertEqual(test.source_stack, []) |
| |
| foo = os.path.abspath('foo') |
| test.add_source(foo) |
| |
| # Adding the first source makes it the main and current path. |
| self.assertEqual(test.main_path, foo) |
| self.assertEqual(test.current_path, foo) |
| self.assertEqual(test.all_paths, set([foo])) |
| self.assertEqual(test.source_stack, [foo]) |
| |
| bar = os.path.abspath('bar') |
| test.add_source(bar) |
| |
| # Adding the second source makes leaves main and current paths alone. |
| self.assertEqual(test.main_path, foo) |
| self.assertEqual(test.current_path, foo) |
| self.assertEqual(test.all_paths, set([bar, foo])) |
| self.assertEqual(test.source_stack, [foo]) |
| |
| qux = os.path.abspath('qux') |
| test.push_source(qux) |
| |
| # Pushing a source makes it the current path |
| self.assertEqual(test.main_path, foo) |
| self.assertEqual(test.current_path, qux) |
| self.assertEqual(test.all_paths, set([bar, foo, qux])) |
| self.assertEqual(test.source_stack, [foo, qux]) |
| |
| hoge = os.path.abspath('hoge') |
| test.push_source(hoge) |
| self.assertEqual(test.main_path, foo) |
| self.assertEqual(test.current_path, hoge) |
| self.assertEqual(test.all_paths, set([bar, foo, hoge, qux])) |
| self.assertEqual(test.source_stack, [foo, qux, hoge]) |
| |
| fuga = os.path.abspath('fuga') |
| |
| # Adding a source after pushing doesn't change the source stack |
| test.add_source(fuga) |
| self.assertEqual(test.main_path, foo) |
| self.assertEqual(test.current_path, hoge) |
| self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux])) |
| self.assertEqual(test.source_stack, [foo, qux, hoge]) |
| |
| # Adding a source twice doesn't change anything |
| test.add_source(qux) |
| self.assertEqual(test.main_path, foo) |
| self.assertEqual(test.current_path, hoge) |
| self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux])) |
| self.assertEqual(test.source_stack, [foo, qux, hoge]) |
| |
| last = test.pop_source() |
| |
| # Popping a source returns the last pushed one, not the last added one. |
| self.assertEqual(last, hoge) |
| self.assertEqual(test.main_path, foo) |
| self.assertEqual(test.current_path, qux) |
| self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux])) |
| self.assertEqual(test.source_stack, [foo, qux]) |
| |
| last = test.pop_source() |
| self.assertEqual(last, qux) |
| self.assertEqual(test.main_path, foo) |
| self.assertEqual(test.current_path, foo) |
| self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux])) |
| self.assertEqual(test.source_stack, [foo]) |
| |
| # Popping the main path is allowed. |
| last = test.pop_source() |
| self.assertEqual(last, foo) |
| self.assertEqual(test.main_path, foo) |
| self.assertIsNone(test.current_path) |
| self.assertEqual(test.all_paths, set([bar, foo, fuga, hoge, qux])) |
| self.assertEqual(test.source_stack, []) |
| |
| # Popping past the main path asserts. |
| with self.assertRaises(AssertionError): |
| test.pop_source() |
| |
| # Pushing after the main path was popped asserts. |
| with self.assertRaises(AssertionError): |
| test.push_source(foo) |
| |
| test = Context() |
| test.push_source(foo) |
| test.push_source(bar) |
| |
| # Pushing the same file twice is allowed. |
| test.push_source(bar) |
| test.push_source(foo) |
| self.assertEqual(last, foo) |
| self.assertEqual(test.main_path, foo) |
| self.assertEqual(test.current_path, foo) |
| self.assertEqual(test.all_paths, set([bar, foo])) |
| self.assertEqual(test.source_stack, [foo, bar, bar, foo]) |
| |
| def test_context_dirs(self): |
| class Config(object): pass |
| config = Config() |
| config.topsrcdir = mozpath.abspath(os.curdir) |
| config.topobjdir = mozpath.abspath('obj') |
| test = Context(config=config) |
| foo = mozpath.abspath('foo') |
| test.push_source(foo) |
| |
| self.assertEqual(test.srcdir, config.topsrcdir) |
| self.assertEqual(test.relsrcdir, '') |
| self.assertEqual(test.objdir, config.topobjdir) |
| self.assertEqual(test.relobjdir, '') |
| |
| foobar = os.path.abspath('foo/bar') |
| test.push_source(foobar) |
| self.assertEqual(test.srcdir, mozpath.join(config.topsrcdir, 'foo')) |
| self.assertEqual(test.relsrcdir, 'foo') |
| self.assertEqual(test.objdir, config.topobjdir) |
| self.assertEqual(test.relobjdir, '') |
| |
| |
| class TestSymbols(unittest.TestCase): |
| def _verify_doc(self, doc): |
| # Documentation should be of the format: |
| # """SUMMARY LINE |
| # |
| # EXTRA PARAGRAPHS |
| # """ |
| |
| self.assertNotIn('\r', doc) |
| |
| lines = doc.split('\n') |
| |
| # No trailing whitespace. |
| for line in lines[0:-1]: |
| self.assertEqual(line, line.rstrip()) |
| |
| self.assertGreater(len(lines), 0) |
| self.assertGreater(len(lines[0].strip()), 0) |
| |
| # Last line should be empty. |
| self.assertEqual(lines[-1].strip(), '') |
| |
| def test_documentation_formatting(self): |
| for typ, inp, doc, tier in VARIABLES.values(): |
| self._verify_doc(doc) |
| |
| for attr, args, doc in FUNCTIONS.values(): |
| self._verify_doc(doc) |
| |
| for func, typ, doc in SPECIAL_VARIABLES.values(): |
| self._verify_doc(doc) |
| |
| for name, cls in SUBCONTEXTS.items(): |
| self._verify_doc(cls.__doc__) |
| |
| for name, v in cls.VARIABLES.items(): |
| self._verify_doc(v[2]) |
| |
| |
| class TestPaths(unittest.TestCase): |
| @classmethod |
| def setUpClass(cls): |
| class Config(object): pass |
| cls.config = config = Config() |
| config.topsrcdir = mozpath.abspath(os.curdir) |
| config.topobjdir = mozpath.abspath('obj') |
| config.external_source_dir = None |
| |
| def test_path(self): |
| config = self.config |
| ctxt1 = Context(config=config) |
| ctxt1.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) |
| ctxt2 = Context(config=config) |
| ctxt2.push_source(mozpath.join(config.topsrcdir, 'bar', 'moz.build')) |
| |
| path1 = Path(ctxt1, 'qux') |
| self.assertIsInstance(path1, SourcePath) |
| self.assertEqual(path1, 'qux') |
| self.assertEqual(path1.full_path, |
| mozpath.join(config.topsrcdir, 'foo', 'qux')) |
| |
| path2 = Path(ctxt2, '../foo/qux') |
| self.assertIsInstance(path2, SourcePath) |
| self.assertEqual(path2, '../foo/qux') |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topsrcdir, 'foo', 'qux')) |
| |
| self.assertEqual(path1, path2) |
| |
| self.assertEqual(path1.join('../../bar/qux').full_path, |
| mozpath.join(config.topsrcdir, 'bar', 'qux')) |
| |
| path1 = Path(ctxt1, '/qux/qux') |
| self.assertIsInstance(path1, SourcePath) |
| self.assertEqual(path1, '/qux/qux') |
| self.assertEqual(path1.full_path, |
| mozpath.join(config.topsrcdir, 'qux', 'qux')) |
| |
| path2 = Path(ctxt2, '/qux/qux') |
| self.assertIsInstance(path2, SourcePath) |
| self.assertEqual(path2, '/qux/qux') |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topsrcdir, 'qux', 'qux')) |
| |
| self.assertEqual(path1, path2) |
| |
| path1 = Path(ctxt1, '!qux') |
| self.assertIsInstance(path1, ObjDirPath) |
| self.assertEqual(path1, '!qux') |
| self.assertEqual(path1.full_path, |
| mozpath.join(config.topobjdir, 'foo', 'qux')) |
| |
| path2 = Path(ctxt2, '!../foo/qux') |
| self.assertIsInstance(path2, ObjDirPath) |
| self.assertEqual(path2, '!../foo/qux') |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topobjdir, 'foo', 'qux')) |
| |
| self.assertEqual(path1, path2) |
| |
| path1 = Path(ctxt1, '!/qux/qux') |
| self.assertIsInstance(path1, ObjDirPath) |
| self.assertEqual(path1, '!/qux/qux') |
| self.assertEqual(path1.full_path, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| path2 = Path(ctxt2, '!/qux/qux') |
| self.assertIsInstance(path2, ObjDirPath) |
| self.assertEqual(path2, '!/qux/qux') |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| self.assertEqual(path1, path2) |
| |
| path1 = Path(ctxt1, path1) |
| self.assertIsInstance(path1, ObjDirPath) |
| self.assertEqual(path1, '!/qux/qux') |
| self.assertEqual(path1.full_path, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| path2 = Path(ctxt2, path2) |
| self.assertIsInstance(path2, ObjDirPath) |
| self.assertEqual(path2, '!/qux/qux') |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| self.assertEqual(path1, path2) |
| |
| path1 = Path(path1) |
| self.assertIsInstance(path1, ObjDirPath) |
| self.assertEqual(path1, '!/qux/qux') |
| self.assertEqual(path1.full_path, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| self.assertEqual(path1, path2) |
| |
| path2 = Path(path2) |
| self.assertIsInstance(path2, ObjDirPath) |
| self.assertEqual(path2, '!/qux/qux') |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| self.assertEqual(path1, path2) |
| |
| def test_source_path(self): |
| config = self.config |
| ctxt = Context(config=config) |
| ctxt.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) |
| |
| path = SourcePath(ctxt, 'qux') |
| self.assertEqual(path, 'qux') |
| self.assertEqual(path.full_path, |
| mozpath.join(config.topsrcdir, 'foo', 'qux')) |
| self.assertEqual(path.translated, |
| mozpath.join(config.topobjdir, 'foo', 'qux')) |
| |
| path = SourcePath(ctxt, '../bar/qux') |
| self.assertEqual(path, '../bar/qux') |
| self.assertEqual(path.full_path, |
| mozpath.join(config.topsrcdir, 'bar', 'qux')) |
| self.assertEqual(path.translated, |
| mozpath.join(config.topobjdir, 'bar', 'qux')) |
| |
| path = SourcePath(ctxt, '/qux/qux') |
| self.assertEqual(path, '/qux/qux') |
| self.assertEqual(path.full_path, |
| mozpath.join(config.topsrcdir, 'qux', 'qux')) |
| self.assertEqual(path.translated, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| with self.assertRaises(ValueError): |
| SourcePath(ctxt, '!../bar/qux') |
| |
| with self.assertRaises(ValueError): |
| SourcePath(ctxt, '!/qux/qux') |
| |
| path = SourcePath(path) |
| self.assertIsInstance(path, SourcePath) |
| self.assertEqual(path, '/qux/qux') |
| self.assertEqual(path.full_path, |
| mozpath.join(config.topsrcdir, 'qux', 'qux')) |
| self.assertEqual(path.translated, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| path = Path(path) |
| self.assertIsInstance(path, SourcePath) |
| |
| def test_objdir_path(self): |
| config = self.config |
| ctxt = Context(config=config) |
| ctxt.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) |
| |
| path = ObjDirPath(ctxt, '!qux') |
| self.assertEqual(path, '!qux') |
| self.assertEqual(path.full_path, |
| mozpath.join(config.topobjdir, 'foo', 'qux')) |
| |
| path = ObjDirPath(ctxt, '!../bar/qux') |
| self.assertEqual(path, '!../bar/qux') |
| self.assertEqual(path.full_path, |
| mozpath.join(config.topobjdir, 'bar', 'qux')) |
| |
| path = ObjDirPath(ctxt, '!/qux/qux') |
| self.assertEqual(path, '!/qux/qux') |
| self.assertEqual(path.full_path, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| with self.assertRaises(ValueError): |
| path = ObjDirPath(ctxt, '../bar/qux') |
| |
| with self.assertRaises(ValueError): |
| path = ObjDirPath(ctxt, '/qux/qux') |
| |
| path = ObjDirPath(path) |
| self.assertIsInstance(path, ObjDirPath) |
| self.assertEqual(path, '!/qux/qux') |
| self.assertEqual(path.full_path, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| path = Path(path) |
| self.assertIsInstance(path, ObjDirPath) |
| |
| def test_absolute_path(self): |
| config = self.config |
| ctxt = Context(config=config) |
| ctxt.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) |
| |
| path = AbsolutePath(ctxt, '%/qux') |
| self.assertEqual(path, '%/qux') |
| self.assertEqual(path.full_path, '/qux') |
| |
| with self.assertRaises(ValueError): |
| path = AbsolutePath(ctxt, '%qux') |
| |
| def test_path_with_mixed_contexts(self): |
| config = self.config |
| ctxt1 = Context(config=config) |
| ctxt1.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) |
| ctxt2 = Context(config=config) |
| ctxt2.push_source(mozpath.join(config.topsrcdir, 'bar', 'moz.build')) |
| |
| path1 = Path(ctxt1, 'qux') |
| path2 = Path(ctxt2, path1) |
| self.assertEqual(path2, path1) |
| self.assertEqual(path2, 'qux') |
| self.assertEqual(path2.context, ctxt1) |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topsrcdir, 'foo', 'qux')) |
| |
| path1 = Path(ctxt1, '../bar/qux') |
| path2 = Path(ctxt2, path1) |
| self.assertEqual(path2, path1) |
| self.assertEqual(path2, '../bar/qux') |
| self.assertEqual(path2.context, ctxt1) |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topsrcdir, 'bar', 'qux')) |
| |
| path1 = Path(ctxt1, '/qux/qux') |
| path2 = Path(ctxt2, path1) |
| self.assertEqual(path2, path1) |
| self.assertEqual(path2, '/qux/qux') |
| self.assertEqual(path2.context, ctxt1) |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topsrcdir, 'qux', 'qux')) |
| |
| path1 = Path(ctxt1, '!qux') |
| path2 = Path(ctxt2, path1) |
| self.assertEqual(path2, path1) |
| self.assertEqual(path2, '!qux') |
| self.assertEqual(path2.context, ctxt1) |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topobjdir, 'foo', 'qux')) |
| |
| path1 = Path(ctxt1, '!../bar/qux') |
| path2 = Path(ctxt2, path1) |
| self.assertEqual(path2, path1) |
| self.assertEqual(path2, '!../bar/qux') |
| self.assertEqual(path2.context, ctxt1) |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topobjdir, 'bar', 'qux')) |
| |
| path1 = Path(ctxt1, '!/qux/qux') |
| path2 = Path(ctxt2, path1) |
| self.assertEqual(path2, path1) |
| self.assertEqual(path2, '!/qux/qux') |
| self.assertEqual(path2.context, ctxt1) |
| self.assertEqual(path2.full_path, |
| mozpath.join(config.topobjdir, 'qux', 'qux')) |
| |
| def test_path_typed_list(self): |
| config = self.config |
| ctxt1 = Context(config=config) |
| ctxt1.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) |
| ctxt2 = Context(config=config) |
| ctxt2.push_source(mozpath.join(config.topsrcdir, 'bar', 'moz.build')) |
| |
| paths = [ |
| '!../bar/qux', |
| '!/qux/qux', |
| '!qux', |
| '../bar/qux', |
| '/qux/qux', |
| 'qux', |
| ] |
| |
| MyList = ContextDerivedTypedList(Path) |
| l = MyList(ctxt1) |
| l += paths |
| |
| for p_str, p_path in zip(paths, l): |
| self.assertEqual(p_str, p_path) |
| self.assertEqual(p_path, Path(ctxt1, p_str)) |
| self.assertEqual(p_path.join('foo'), |
| Path(ctxt1, mozpath.join(p_str, 'foo'))) |
| |
| l2 = MyList(ctxt2) |
| l2 += paths |
| |
| for p_str, p_path in zip(paths, l2): |
| self.assertEqual(p_str, p_path) |
| self.assertEqual(p_path, Path(ctxt2, p_str)) |
| |
| # Assigning with Paths from another context doesn't rebase them |
| l2 = MyList(ctxt2) |
| l2 += l |
| |
| for p_str, p_path in zip(paths, l2): |
| self.assertEqual(p_str, p_path) |
| self.assertEqual(p_path, Path(ctxt1, p_str)) |
| |
| MyListWithFlags = ContextDerivedTypedListWithItems( |
| Path, StrictOrderingOnAppendListWithFlagsFactory({ |
| 'foo': bool, |
| })) |
| l = MyListWithFlags(ctxt1) |
| l += paths |
| |
| for p in paths: |
| l[p].foo = True |
| |
| for p_str, p_path in zip(paths, l): |
| self.assertEqual(p_str, p_path) |
| self.assertEqual(p_path, Path(ctxt1, p_str)) |
| self.assertEqual(l[p_str].foo, True) |
| self.assertEqual(l[p_path].foo, True) |
| |
| def test_path_typed_hierarchy_list(self): |
| config = self.config |
| ctxt1 = Context(config=config) |
| ctxt1.push_source(mozpath.join(config.topsrcdir, 'foo', 'moz.build')) |
| ctxt2 = Context(config=config) |
| ctxt2.push_source(mozpath.join(config.topsrcdir, 'bar', 'moz.build')) |
| |
| paths = [ |
| '!../bar/qux', |
| '!/qux/qux', |
| '!qux', |
| '../bar/qux', |
| '/qux/qux', |
| 'qux', |
| ] |
| |
| MyList = ContextDerivedTypedHierarchicalStringList(Path) |
| l = MyList(ctxt1) |
| l += paths |
| l.subdir += paths |
| |
| for _, files in l.walk(): |
| for p_str, p_path in zip(paths, files): |
| self.assertEqual(p_str, p_path) |
| self.assertEqual(p_path, Path(ctxt1, p_str)) |
| self.assertEqual(p_path.join('foo'), |
| Path(ctxt1, mozpath.join(p_str, 'foo'))) |
| |
| l2 = MyList(ctxt2) |
| l2 += paths |
| l2.subdir += paths |
| |
| for _, files in l2.walk(): |
| for p_str, p_path in zip(paths, files): |
| self.assertEqual(p_str, p_path) |
| self.assertEqual(p_path, Path(ctxt2, p_str)) |
| |
| # Assigning with Paths from another context doesn't rebase them |
| l2 = MyList(ctxt2) |
| l2 += l |
| |
| for _, files in l2.walk(): |
| for p_str, p_path in zip(paths, files): |
| self.assertEqual(p_str, p_path) |
| self.assertEqual(p_path, Path(ctxt1, p_str)) |
| |
| |
| class TestTypedRecord(unittest.TestCase): |
| |
| def test_fields(self): |
| T = ContextDerivedTypedRecord(('field1', unicode), |
| ('field2', list)) |
| inst = T(None) |
| self.assertEqual(inst.field1, '') |
| self.assertEqual(inst.field2, []) |
| |
| inst.field1 = 'foo' |
| inst.field2 += ['bar'] |
| |
| self.assertEqual(inst.field1, 'foo') |
| self.assertEqual(inst.field2, ['bar']) |
| |
| with self.assertRaises(AttributeError): |
| inst.field3 = [] |
| |
| def test_coercion(self): |
| T = ContextDerivedTypedRecord(('field1', unicode), |
| ('field2', list)) |
| inst = T(None) |
| inst.field1 = 3 |
| inst.field2 += ('bar',) |
| self.assertEqual(inst.field1, '3') |
| self.assertEqual(inst.field2, ['bar']) |
| |
| with self.assertRaises(TypeError): |
| inst.field2 = object() |
| |
| |
| class TestFiles(unittest.TestCase): |
| def test_aggregate_empty(self): |
| c = Context({}) |
| |
| files = {'moz.build': Files(c, pattern='**')} |
| |
| self.assertEqual(Files.aggregate(files), { |
| 'bug_component_counts': [], |
| 'recommended_bug_component': None, |
| }) |
| |
| def test_single_bug_component(self): |
| c = Context({}) |
| f = Files(c, pattern='**') |
| f['BUG_COMPONENT'] = (u'Product1', u'Component1') |
| |
| files = {'moz.build': f} |
| self.assertEqual(Files.aggregate(files), { |
| 'bug_component_counts': [((u'Product1', u'Component1'), 1)], |
| 'recommended_bug_component': (u'Product1', u'Component1'), |
| }) |
| |
| def test_multiple_bug_components(self): |
| c = Context({}) |
| f1 = Files(c, pattern='**') |
| f1['BUG_COMPONENT'] = (u'Product1', u'Component1') |
| |
| f2 = Files(c, pattern='**') |
| f2['BUG_COMPONENT'] = (u'Product2', u'Component2') |
| |
| files = {'a': f1, 'b': f2, 'c': f1} |
| self.assertEqual(Files.aggregate(files), { |
| 'bug_component_counts': [ |
| ((u'Product1', u'Component1'), 2), |
| ((u'Product2', u'Component2'), 1), |
| ], |
| 'recommended_bug_component': (u'Product1', u'Component1'), |
| }) |
| |
| def test_no_recommended_bug_component(self): |
| """If there is no clear count winner, we don't recommend a bug component.""" |
| c = Context({}) |
| f1 = Files(c, pattern='**') |
| f1['BUG_COMPONENT'] = (u'Product1', u'Component1') |
| |
| f2 = Files(c, pattern='**') |
| f2['BUG_COMPONENT'] = (u'Product2', u'Component2') |
| |
| files = {'a': f1, 'b': f2} |
| self.assertEqual(Files.aggregate(files), { |
| 'bug_component_counts': [ |
| ((u'Product1', u'Component1'), 1), |
| ((u'Product2', u'Component2'), 1), |
| ], |
| 'recommended_bug_component': None, |
| }) |
| |
| |
| if __name__ == '__main__': |
| main() |