| #!/usr/bin/python | |
| # | |
| # Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| """Unittest for generate_gn.py. | |
| It's tough to test the lower-level GetSourceFiles() and GetObjectFiles() | |
| functions, so this focuses on the higher-level functions assuming those two | |
| functions are working as intended (i.e., producing lists of files). | |
| """ | |
| import generate_gn as gg | |
| from generate_gn import SourceSet, SourceListCondition | |
| import string | |
| import unittest | |
| from os import path | |
| class ModuleUnittest(unittest.TestCase): | |
| def testGetObjectToSourceMapping(self): | |
| srcs = [ | |
| 'a.c', | |
| 'b.asm', | |
| 'c.cc', | |
| ] | |
| expected = { | |
| 'a.o': 'a.c', | |
| 'b.o': 'b.asm', | |
| 'c.o': 'c.cc', | |
| } | |
| self.assertEqual(expected, gg.GetObjectToSourceMapping(srcs)) | |
| def testGetSourceFileSet(self): | |
| objs_to_srcs = { | |
| 'a.o': 'a.c', | |
| 'b.o': 'b.asm', | |
| 'c.o': 'c.cc', | |
| } | |
| objs = [ | |
| 'a.o', | |
| 'c.o', | |
| ] | |
| expected = set(['a.c', 'c.cc']) | |
| self.assertEqual(expected, gg.GetSourceFileSet(objs_to_srcs, objs)) | |
| def testGetSourceFileSet_NotFound(self): | |
| objs_to_srcs = { | |
| 'a.o': 'a.c', | |
| 'b.o': 'b.asm', | |
| 'c.o': 'c.cc', | |
| } | |
| objs = [ | |
| 'd.o', | |
| ] | |
| self.assertRaises(KeyError, gg.GetSourceFileSet, objs_to_srcs, objs) | |
| class SourceSetUnittest(unittest.TestCase): | |
| def testEquals(self): | |
| a = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '2', '3')])) | |
| b = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '2', '3')])) | |
| c = SourceSet(set(['c', 'd']), set([SourceListCondition('1', '2', '3')])) | |
| d = SourceSet(set(['a', 'b']), set([SourceListCondition('0', '2', '3')])) | |
| e = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '0', '3')])) | |
| f = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '2', '0')])) | |
| self.assertEqual(a, b) | |
| self.assertNotEqual(a, c) | |
| self.assertNotEqual(a, d) | |
| self.assertNotEqual(a, e) | |
| self.assertNotEqual(a, f) | |
| def testIntersect_Exact(self): | |
| a = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '2', '3')])) | |
| b = SourceSet(set(['a', 'b']), set([SourceListCondition('3', '4', '6')])) | |
| c = a.Intersect(b) | |
| self.assertEqual(c.sources, set(['a', 'b'])) | |
| self.assertEqual( | |
| c.conditions, | |
| set([ | |
| SourceListCondition('1', '2', '3'), | |
| SourceListCondition('3', '4', '6') | |
| ])) | |
| self.assertFalse(c.IsEmpty()) | |
| def testIntersect_Disjoint(self): | |
| a = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '2', '3')])) | |
| b = SourceSet(set(['c', 'd']), set([SourceListCondition('3', '4', '6')])) | |
| c = a.Intersect(b) | |
| self.assertEqual(c.sources, set()) | |
| self.assertEqual( | |
| c.conditions, | |
| set([ | |
| SourceListCondition('1', '2', '3'), | |
| SourceListCondition('3', '4', '6') | |
| ])) | |
| self.assertTrue(c.IsEmpty()) | |
| def testIntersect_Overlap(self): | |
| a = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '2', '3')])) | |
| b = SourceSet(set(['b', 'c']), set([SourceListCondition('3', '4', '6')])) | |
| c = a.Intersect(b) | |
| self.assertEqual(c.sources, set(['b'])) | |
| self.assertEqual( | |
| c.conditions, | |
| set([ | |
| SourceListCondition('1', '2', '3'), | |
| SourceListCondition('3', '4', '6') | |
| ])) | |
| self.assertFalse(c.IsEmpty()) | |
| def testDifference_Exact(self): | |
| a = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '2', '3')])) | |
| b = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '2', '3')])) | |
| c = a.Difference(b) | |
| self.assertEqual(c.sources, set()) | |
| self.assertEqual(c.conditions, set([SourceListCondition('1', '2', '3')])) | |
| self.assertTrue(c.IsEmpty()) | |
| def testDifference_Disjoint(self): | |
| a = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '2', '3')])) | |
| b = SourceSet(set(['c', 'd']), set([SourceListCondition('3', '4', '6')])) | |
| c = a.Difference(b) | |
| self.assertEqual(c.sources, set(['a', 'b'])) | |
| self.assertEqual(c.conditions, set()) | |
| self.assertTrue(c.IsEmpty()) | |
| def testDifference_Overlap(self): | |
| a = SourceSet(set(['a', 'b']), set([SourceListCondition('1', '2', '5')])) | |
| b = SourceSet( | |
| set(['b', 'c', 'd']), | |
| set([ | |
| SourceListCondition('1', '2', '5'), | |
| SourceListCondition('3', '4', '6') | |
| ])) | |
| c = a.Difference(b) | |
| self.assertEqual(c.sources, set(['a'])) | |
| self.assertEqual(c.conditions, set([SourceListCondition('1', '2', '5')])) | |
| self.assertFalse(c.IsEmpty()) | |
| def testGenerateGnStanza(self): | |
| # ia32 should be x86. Win should appear as an OS restriction. | |
| a = SourceSet( | |
| set(['a', 'b']), set([SourceListCondition('ia32', 'Chromium', 'win')])) | |
| a_stanza = a.GenerateGnStanza() | |
| string.index(a_stanza, 'current_cpu == "x86"') | |
| string.index(a_stanza, 'is_win') | |
| # x64 should just be x64. Linux should appear as an OS restriction. | |
| b = SourceSet( | |
| set(['a', 'b']), set([SourceListCondition('x64', 'Chromium', 'linux')])) | |
| b_stanza = b.GenerateGnStanza() | |
| string.index(b_stanza, 'current_cpu == "x64"') | |
| string.index(b_stanza, 'use_linux_config') | |
| # arm should just be arm. | |
| c = SourceSet( | |
| set(['a', 'b']), set([SourceListCondition('arm', 'Chromium', 'linux')])) | |
| c_stanza = c.GenerateGnStanza() | |
| string.index(c_stanza, 'current_cpu == "arm"') | |
| # arm-neon should be arm and flip the arm_neon switch. | |
| d = SourceSet( | |
| set(['a', 'b']), | |
| set([SourceListCondition('arm-neon', 'Chromium', 'linux')])) | |
| d_stanza = d.GenerateGnStanza() | |
| string.index(d_stanza, 'current_cpu == "arm" && arm_use_neon') | |
| # Multiple conditions | |
| e = SourceSet( | |
| set(['a', 'b']), | |
| set([ | |
| SourceListCondition('arm', 'Chrome', 'win'), | |
| SourceListCondition('x64', 'Chromium', 'linux') | |
| ])) | |
| e_stanza = e.GenerateGnStanza() | |
| string.index(e_stanza, ('is_win && current_cpu == "arm"' | |
| ' && ffmpeg_branding == "Chrome"')) | |
| string.index(e_stanza, ('use_linux_config && current_cpu == "x64"' | |
| ' && ffmpeg_branding == "Chromium"')) | |
| def testComplexSourceListConditions(self): | |
| # Create 2 sets with intersecting source 'a', but setup such that 'a' | |
| # is only valid for combinations (x86 && windows) || (x64 && linux). The | |
| # generated gn stanza should then not allow for inclusion of the 'a' file | |
| # for combinations like x86 && linux. | |
| a = SourceSet(set(['a']), set([SourceListCondition('x86', 'c', 'win')])) | |
| b = SourceSet(set(['a']), set([SourceListCondition('x64', 'c', 'linux')])) | |
| disjoint_sets = gg.CreatePairwiseDisjointSets([a, b]) | |
| # This condition is bad because x86 && linux would pass. Admittedly a very | |
| # fragile way to test this, but evaulating gn stanzas is hard, and it at | |
| # least serves to document the motivation for the associated changes to | |
| # our generate_gn.py | |
| bad_condition = ('(current_cpu == "x86" || current_cpu == "x64")' | |
| ' && (ffmpeg_branding == "c")' | |
| ' && (is_win || is_linux || is_chromeos)') | |
| # Expect only a single set since the two original sets have the same source | |
| # list. | |
| self.assertEqual(1, len(disjoint_sets)) | |
| stanza = disjoint_sets[0].GenerateGnStanza() | |
| self.assertEqual(string.find(stanza, bad_condition), -1) | |
| def assertEqualSourceSets(self, expected, actual): | |
| assert all(isinstance(a, SourceSet) for a in expected) | |
| assert all(isinstance(a, SourceSet) for a in actual) | |
| def SourceSetToString(source_set): | |
| sources = [str(e) for e in source_set.sources] | |
| conditions = [str(e) for e in source_set.conditions] | |
| sources_str = ','.join(sources) | |
| conditions_str = '\n\t'.join(conditions) | |
| return ' sources:%s\n cs:\t%s' % (sources_str, conditions_str) | |
| missing_elements = expected.difference(actual) | |
| extra_elements = actual.difference(expected) | |
| msg = '' | |
| if len(missing_elements): | |
| msg += 'Missing expected elements:\n' | |
| for e in missing_elements: | |
| msg += SourceSetToString(e) + '\n' | |
| if len(extra_elements): | |
| msg += 'Found extra elements:\n' | |
| for e in extra_elements: | |
| msg += SourceSetToString(e) + '\n' | |
| self.assertTrue(expected == actual, msg=msg) | |
| def testCreatePairwiseDisjointSets_Pair(self): | |
| a = SourceSet( | |
| set(['common', 'intel']), | |
| set([SourceListCondition('ia32', 'Chromium', 'win')])) | |
| b = SourceSet( | |
| set(['common', 'intel', 'chrome']), | |
| set([SourceListCondition('ia32', 'Chrome', 'win')])) | |
| expected = set() | |
| expected.add( | |
| SourceSet( | |
| set(['common', 'intel']), | |
| set([ | |
| SourceListCondition('ia32', 'Chromium', 'win'), | |
| SourceListCondition('ia32', 'Chrome', 'win') | |
| ]))) | |
| expected.add( | |
| SourceSet( | |
| set(['chrome']), set([SourceListCondition('ia32', 'Chrome', | |
| 'win')]))) | |
| source_sets = gg.CreatePairwiseDisjointSets([a, b]) | |
| self.assertEqualSourceSets(expected, set(source_sets)) | |
| def testCreatePairwiseDisjointSets_Triplet(self): | |
| a = SourceSet( | |
| set(['common', 'intel']), | |
| set([SourceListCondition('ia32', 'Chromium', 'win')])) | |
| b = SourceSet( | |
| set(['common', 'intel', 'chrome']), | |
| set([SourceListCondition('x64', 'Chrome', 'win')])) | |
| c = SourceSet( | |
| set(['common', 'arm']), | |
| set([SourceListCondition('arm', 'Chromium', 'win')])) | |
| expected = set() | |
| expected.add( | |
| SourceSet( | |
| set(['common']), | |
| set([ | |
| SourceListCondition('ia32', 'Chromium', 'win'), | |
| SourceListCondition('x64', 'Chrome', 'win'), | |
| SourceListCondition('arm', 'Chromium', 'win') | |
| ]))) | |
| expected.add( | |
| SourceSet( | |
| set(['intel']), | |
| set([ | |
| SourceListCondition('ia32', 'Chromium', 'win'), | |
| SourceListCondition('x64', 'Chrome', 'win') | |
| ]))) | |
| expected.add( | |
| SourceSet( | |
| set(['chrome']), set([SourceListCondition('x64', 'Chrome', | |
| 'win')]))) | |
| expected.add( | |
| SourceSet( | |
| set(['arm']), set([SourceListCondition('arm', 'Chromium', 'win')]))) | |
| source_sets = gg.CreatePairwiseDisjointSets([a, b, c]) | |
| self.assertEqualSourceSets(expected, set(source_sets)) | |
| def testCreatePairwiseDisjointSets_Multiple(self): | |
| a = SourceSet( | |
| set(['common', 'intel']), | |
| set([SourceListCondition('ia32', 'Chromium', 'linux')])) | |
| b = SourceSet( | |
| set(['common', 'intel', 'chrome']), | |
| set([SourceListCondition('ia32', 'Chrome', 'linux')])) | |
| c = SourceSet( | |
| set(['common', 'intel']), | |
| set([SourceListCondition('x64', 'Chromium', 'linux')])) | |
| d = SourceSet( | |
| set(['common', 'intel', 'chrome']), | |
| set([SourceListCondition('x64', 'Chrome', 'linux')])) | |
| e = SourceSet( | |
| set(['common', 'arm']), | |
| set([SourceListCondition('arm', 'Chromium', 'linux')])) | |
| f = SourceSet( | |
| set(['common', 'arm-neon', 'chrome', 'chromeos']), | |
| set([SourceListCondition('arm-neon', 'ChromeOS', 'linux')])) | |
| expected = set() | |
| expected.add( | |
| SourceSet( | |
| set(['common']), | |
| set([ | |
| SourceListCondition('ia32', 'Chromium', 'linux'), | |
| SourceListCondition('ia32', 'Chrome', 'linux'), | |
| SourceListCondition('x64', 'Chromium', 'linux'), | |
| SourceListCondition('x64', 'Chrome', 'linux'), | |
| SourceListCondition('arm', 'Chromium', 'linux'), | |
| SourceListCondition('arm-neon', 'ChromeOS', 'linux') | |
| ]))) | |
| expected.add( | |
| SourceSet( | |
| set(['intel']), | |
| set([ | |
| SourceListCondition('ia32', 'Chromium', 'linux'), | |
| SourceListCondition('ia32', 'Chrome', 'linux'), | |
| SourceListCondition('x64', 'Chromium', 'linux'), | |
| SourceListCondition('x64', 'Chrome', 'linux') | |
| ]))) | |
| expected.add( | |
| SourceSet( | |
| set(['arm']), set([SourceListCondition('arm', 'Chromium', | |
| 'linux')]))) | |
| expected.add( | |
| SourceSet( | |
| set(['chrome']), | |
| set([ | |
| SourceListCondition('ia32', 'Chrome', 'linux'), | |
| SourceListCondition('x64', 'Chrome', 'linux'), | |
| SourceListCondition('arm-neon', 'ChromeOS', 'linux') | |
| ]))) | |
| expected.add( | |
| SourceSet( | |
| set(['arm-neon', 'chromeos']), | |
| set([SourceListCondition('arm-neon', 'ChromeOS', 'linux')]))) | |
| source_sets = gg.CreatePairwiseDisjointSets([a, b, c, d, e, f]) | |
| self.assertEqualSourceSets(expected, set(source_sets)) | |
| def testReduceConditions(self): | |
| # Set conditions span all of the supported architectures for linux. | |
| a = SourceSet( | |
| set(['foo.c']), | |
| set([ | |
| SourceListCondition('ia32', 'Chromium', 'linux'), | |
| SourceListCondition('x64', 'Chromium', 'linux'), | |
| SourceListCondition('arm', 'Chromium', 'linux'), | |
| SourceListCondition('arm64', 'Chromium', 'linux'), | |
| SourceListCondition('arm-neon', 'Chromium', 'linux'), | |
| SourceListCondition('mipsel', 'Chromium', 'linux'), | |
| SourceListCondition('mips64el', 'Chromium', 'linux'), | |
| ])) | |
| gg.ReduceConditionalLogic(a) | |
| # Conditions should reduce to a single condition with wild-card for arch. | |
| expected = set([SourceListCondition('*', 'Chromium', 'linux')]) | |
| self.assertEqual(expected, a.conditions) | |
| # Set conditions span all of the supported architectures for windows. | |
| b = SourceSet( | |
| set(['foo.c']), | |
| set([ | |
| SourceListCondition('ia32', 'Chromium', 'win'), | |
| SourceListCondition('x64', 'Chromium', 'win'), | |
| SourceListCondition('arm64', 'Chromium', 'win'), | |
| ])) | |
| gg.ReduceConditionalLogic(b) | |
| # Conditions should reduce to a single condition with wild-card for | |
| expected = set([SourceListCondition('*', 'Chromium', 'win')]) | |
| self.assertEqual(expected, b.conditions) | |
| # Set conditions span all supported architectures and brandings for windows. | |
| b = SourceSet( | |
| set(['foo.c']), | |
| set([ | |
| SourceListCondition('ia32', 'Chromium', 'win'), | |
| SourceListCondition('x64', 'Chromium', 'win'), | |
| SourceListCondition('arm64', 'Chromium', 'win'), | |
| SourceListCondition('ia32', 'Chrome', 'win'), | |
| SourceListCondition('x64', 'Chrome', 'win'), | |
| SourceListCondition('arm64', 'Chrome', 'win'), | |
| ])) | |
| gg.ReduceConditionalLogic(b) | |
| expected = set([SourceListCondition('*', '*', 'win')]) | |
| self.assertEqual(expected, b.conditions) | |
| # Set conditions span all supported platforms. | |
| c = SourceSet( | |
| set(['foo.c']), | |
| set([ | |
| SourceListCondition('x64', 'Chromium', 'win'), | |
| SourceListCondition('x64', 'Chromium', 'mac'), | |
| SourceListCondition('x64', 'Chromium', 'linux'), | |
| SourceListCondition('x64', 'Chromium', 'android'), | |
| ])) | |
| gg.ReduceConditionalLogic(c) | |
| expected = set([SourceListCondition('x64', 'Chromium', '*')]) | |
| self.assertEqual(expected, c.conditions) | |
| # Spans all architectures for Chromium, but also all targets for ia32 & win. | |
| d = SourceSet( | |
| set(['foo.c']), | |
| set([ | |
| SourceListCondition('arm64', 'Chromium', 'win'), | |
| SourceListCondition('x64', 'Chromium', 'win'), | |
| SourceListCondition('ia32', 'Chromium', 'win'), | |
| SourceListCondition('ia32', 'Chrome', 'win'), | |
| ])) | |
| gg.ReduceConditionalLogic(d) | |
| expected = set([ | |
| SourceListCondition('*', 'Chromium', 'win'), | |
| SourceListCondition('ia32', '*', 'win'), | |
| ]) | |
| self.assertEqual(expected, d.conditions) | |
| def testReduceConditions_fullSpan(self): | |
| # Build SourceSet with conditions spanning every combination of attributes. | |
| ss = SourceSet(set(['foo.c']), set()) | |
| for arch in gg.SUPPORT_MATRIX[gg.Attr.ARCHITECTURE]: | |
| for target in gg.SUPPORT_MATRIX[gg.Attr.TARGET]: | |
| for platform in gg.SUPPORT_MATRIX[gg.Attr.PLATFORM]: | |
| ss.conditions.add(SourceListCondition(arch, target, platform)) | |
| gg.ReduceConditionalLogic(ss) | |
| expected = set([SourceListCondition('*', '*', '*')]) | |
| self.assertEqual(expected, ss.conditions) | |
| def testGenerateStanzaWildCard(self): | |
| a = SourceSet( | |
| set(['foo.c']), set([SourceListCondition('x64', 'Chromium', '*')])) | |
| stanza = a.GenerateGnStanza() | |
| string.index(stanza, '== "x64"') | |
| string.index(stanza, 'ffmpeg_branding == "Chromium"') | |
| # OS is wild-card, so it should not be mentioned in the stanza. | |
| self.assertEqual(-1, string.find(stanza, 'OS ==')) | |
| def testFixObjectBasenameCollisions(self): | |
| # Use callback to capture executed renames. | |
| observed_renames = set() | |
| def do_rename_cb(old_path, new_path, content): | |
| observed_renames.add((old_path, new_path)) | |
| # Verify basic rename case - same basename in different directories. | |
| a = SourceSet(set(['foo.c']), set([SourceListCondition('*', '*', '*')])) | |
| b = SourceSet( | |
| set([path.join('a','foo.c'), path.join('b', 'foo.c')]), | |
| set([SourceListCondition('*', '*', '*')])) | |
| expected_renames = set([(path.join('a', 'foo.c'), | |
| path.join('a', 'autorename_a_foo.c')), | |
| (path.join('b', 'foo.c'), | |
| path.join('b', 'autorename_b_foo.c'))]) | |
| gg.FixObjectBasenameCollisions([a, b], [], do_rename_cb, log_renames=False) | |
| self.assertEqual(expected_renames, observed_renames) | |
| # Verify renames file extensions in same and different directory. | |
| observed_renames = set() | |
| a = SourceSet(set(['foo.c']), set([SourceListCondition('*', '*', '*')])) | |
| b = SourceSet(set(['foo.asm']), set([SourceListCondition('*', '*', '*')])) | |
| c = SourceSet(set([path.join('a', 'foo.S'), path.join('b', 'foo.asm')]), | |
| set([SourceListCondition('*', '*', '*')])) | |
| expected_renames = set([('foo.asm', 'autorename_foo.asm'), | |
| (path.join('a', 'foo.S'), | |
| path.join('a', 'autorename_a_foo.S')), | |
| (path.join('b', 'foo.asm'), | |
| path.join('b', 'autorename_b_foo.asm'))]) | |
| gg.FixObjectBasenameCollisions( | |
| [a, b, c], [], do_rename_cb, log_renames=False) | |
| self.assertEqual(expected_renames, observed_renames) | |
| if __name__ == '__main__': | |
| unittest.main() |