2 # Copyright 2016 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
13 from unittest import mock
18 class UnitTest(unittest.TestCase):
19 def test_ToGNString(self):
21 (42, '42', '42'), ('foo', '"foo"', '"foo"'), (True, 'true', 'true'),
22 (False, 'false', 'false'), ('', '""', '""'),
23 ('\\$"$\\', '"\\\\\\$\\"\\$\\\\"', '"\\\\\\$\\"\\$\\\\"'),
24 (' \t\r\n', '" $0x09$0x0D$0x0A"', '" $0x09$0x0D$0x0A"'),
25 (u'\u2713', '"$0xE2$0x9C$0x93"', '"$0xE2$0x9C$0x93"'),
26 ([], '[ ]', '[]'), ([1], '[ 1 ]', '[\n 1\n]\n'),
27 ([3, 1, 4, 1], '[ 3, 1, 4, 1 ]', '[\n 3,\n 1,\n 4,\n 1\n]\n'),
28 (['a', True, 2], '[ "a", true, 2 ]', '[\n "a",\n true,\n 2\n]\n'),
31 }, 'single = "item"\n', 'single = "item"\n'),
34 '_42A_Zaz_': [False, True]
35 }, '_42A_Zaz_ = [ false, true ]\nkEy = 137\n',
36 '_42A_Zaz_ = [\n false,\n true\n]\nkEy = 137\n'),
38 ['"thr,.$\\', True, False, [],
39 u'(\u2713)']], '[ 1, "two", [ "\\"thr,.\\$\\\\", true, false, ' +
40 '[ ], "($0xE2$0x9C$0x93)" ] ]', '''[
57 }, 'a = [ 3, "x" ]\nb = true\nn = 42\ns = "foo"\n',
58 'a = [\n 3,\n "x"\n]\nb = true\nn = 42\ns = "foo"\n'),
61 '[ [ [ ], [ [ ] ] ], [ ] ]',
62 '[\n [\n [],\n [\n []\n ]\n ],\n []\n]\n',
72 '[ { a = 1\nb = [ ]\nc = { z = 8 } } ]\n',
73 '[\n {\n a = 1\n b = []\n c = {\n' +
74 ' z = 8\n }\n }\n]\n',
77 for obj, exp_ugly, exp_pretty in test_cases:
78 out_ugly = gn_helpers.ToGNString(obj)
79 self.assertEqual(exp_ugly, out_ugly)
80 out_pretty = gn_helpers.ToGNString(obj, pretty=True)
81 self.assertEqual(exp_pretty, out_pretty)
83 def test_UnescapeGNString(self):
84 # Backslash followed by a \, $, or " means the folling character without
85 # the special meaning. Backslash followed by everything else is a literal.
87 gn_helpers.UnescapeGNString('\\as\\$\\\\asd\\"'),
90 def test_FromGNString(self):
92 gn_helpers.FromGNString('[1, -20, true, false,["as\\"", []]]'),
93 [ 1, -20, True, False, [ 'as"', [] ] ])
95 with self.assertRaises(gn_helpers.GNError):
96 parser = gn_helpers.GNValueParser('123 456')
99 def test_ParseBool(self):
100 parser = gn_helpers.GNValueParser('true')
101 self.assertEqual(parser.Parse(), True)
103 parser = gn_helpers.GNValueParser('false')
104 self.assertEqual(parser.Parse(), False)
106 def test_ParseNumber(self):
107 parser = gn_helpers.GNValueParser('123')
108 self.assertEqual(parser.ParseNumber(), 123)
110 with self.assertRaises(gn_helpers.GNError):
111 parser = gn_helpers.GNValueParser('')
113 with self.assertRaises(gn_helpers.GNError):
114 parser = gn_helpers.GNValueParser('a123')
117 def test_ParseString(self):
118 parser = gn_helpers.GNValueParser('"asdf"')
119 self.assertEqual(parser.ParseString(), 'asdf')
121 with self.assertRaises(gn_helpers.GNError):
122 parser = gn_helpers.GNValueParser('') # Empty.
124 with self.assertRaises(gn_helpers.GNError):
125 parser = gn_helpers.GNValueParser('asdf') # Unquoted.
127 with self.assertRaises(gn_helpers.GNError):
128 parser = gn_helpers.GNValueParser('"trailing') # Unterminated.
131 def test_ParseList(self):
132 parser = gn_helpers.GNValueParser('[1,]') # Optional end comma OK.
133 self.assertEqual(parser.ParseList(), [ 1 ])
135 with self.assertRaises(gn_helpers.GNError):
136 parser = gn_helpers.GNValueParser('') # Empty.
138 with self.assertRaises(gn_helpers.GNError):
139 parser = gn_helpers.GNValueParser('asdf') # No [].
141 with self.assertRaises(gn_helpers.GNError):
142 parser = gn_helpers.GNValueParser('[1, 2') # Unterminated
144 with self.assertRaises(gn_helpers.GNError):
145 parser = gn_helpers.GNValueParser('[1 2]') # No separating comma.
148 def test_ParseScope(self):
149 parser = gn_helpers.GNValueParser('{a = 1}')
150 self.assertEqual(parser.ParseScope(), {'a': 1})
152 with self.assertRaises(gn_helpers.GNError):
153 parser = gn_helpers.GNValueParser('') # Empty.
155 with self.assertRaises(gn_helpers.GNError):
156 parser = gn_helpers.GNValueParser('asdf') # No {}.
158 with self.assertRaises(gn_helpers.GNError):
159 parser = gn_helpers.GNValueParser('{a = 1') # Unterminated.
161 with self.assertRaises(gn_helpers.GNError):
162 parser = gn_helpers.GNValueParser('{"a" = 1}') # Not identifier.
164 with self.assertRaises(gn_helpers.GNError):
165 parser = gn_helpers.GNValueParser('{a = }') # No value.
168 def test_FromGNArgs(self):
169 # Booleans and numbers should work; whitespace is allowed works.
170 self.assertEqual(gn_helpers.FromGNArgs('foo = true\nbar = 1\n'),
171 {'foo': True, 'bar': 1})
173 # Whitespace is not required; strings should also work.
174 self.assertEqual(gn_helpers.FromGNArgs('foo="bar baz"'),
177 # Comments should work (and be ignored).
179 '# Top-level comment.',
181 'bar = 1 # In-line comment followed by whitespace.',
185 self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)), {
192 self.assertEqual(gn_helpers.FromGNArgs('foo=[1, 2, 3]'),
195 # Empty strings should return an empty dict.
196 self.assertEqual(gn_helpers.FromGNArgs(''), {})
197 self.assertEqual(gn_helpers.FromGNArgs(' \n '), {})
199 # Comments should work everywhere (and be ignored).
201 '# Top-level comment.',
203 '# Variable comment.',
206 ' # Value comment in list.',
211 'baz # Comment anywhere, really',
215 self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)), {
221 # Scope should be parsed, even empty ones.
233 self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)),
244 # Non-identifiers should raise an exception.
245 with self.assertRaises(gn_helpers.GNError):
246 gn_helpers.FromGNArgs('123 = true')
248 # References to other variables should raise an exception.
249 with self.assertRaises(gn_helpers.GNError):
250 gn_helpers.FromGNArgs('foo = bar')
252 # References to functions should raise an exception.
253 with self.assertRaises(gn_helpers.GNError):
254 gn_helpers.FromGNArgs('foo = exec_script("//build/baz.py")')
256 # Underscores in identifiers should work.
257 self.assertEqual(gn_helpers.FromGNArgs('_foo = true'),
259 self.assertEqual(gn_helpers.FromGNArgs('foo_bar = true'),
261 self.assertEqual(gn_helpers.FromGNArgs('foo_=true'),
264 def test_ReplaceImports(self):
265 # Should be a no-op on args inputs without any imports.
266 parser = gn_helpers.GNValueParser(
271 parser.ReplaceImports()
279 # A single "import(...)" line should be replaced with the contents of the
280 # file being imported.
281 parser = gn_helpers.GNValueParser(
284 import("//some/args/file.gni")
287 fake_import = 'some_imported_arg = "imported_val"'
288 builtin_var = '__builtin__' if sys.version_info.major < 3 else 'builtins'
289 open_fun = '{}.open'.format(builtin_var)
290 with mock.patch(open_fun, mock.mock_open(read_data=fake_import)):
291 parser.ReplaceImports()
296 some_imported_arg = "imported_val"
300 # No trailing parenthesis should raise an exception.
301 with self.assertRaises(gn_helpers.GNError):
302 parser = gn_helpers.GNValueParser(
303 textwrap.dedent('import("//some/args/file.gni"'))
304 parser.ReplaceImports()
306 # No double quotes should raise an exception.
307 with self.assertRaises(gn_helpers.GNError):
308 parser = gn_helpers.GNValueParser(
309 textwrap.dedent('import(//some/args/file.gni)'))
310 parser.ReplaceImports()
312 # A path that's not source absolute should raise an exception.
313 with self.assertRaises(gn_helpers.GNError):
314 parser = gn_helpers.GNValueParser(
315 textwrap.dedent('import("some/relative/args/file.gni")'))
316 parser.ReplaceImports()
318 def test_CreateBuildCommand(self):
319 with tempfile.TemporaryDirectory() as temp_dir:
320 suffix = '.bat' if sys.platform.startswith('win32') else ''
321 self.assertEqual(f'autoninja{suffix}',
322 gn_helpers.CreateBuildCommand(temp_dir)[0])
324 siso_deps = pathlib.Path(temp_dir) / '.siso_deps'
326 self.assertEqual(f'autosiso{suffix}',
327 gn_helpers.CreateBuildCommand(temp_dir)[0])
329 with mock.patch('shutil.which', lambda x: None):
330 cmd = gn_helpers.CreateBuildCommand(temp_dir)
331 self.assertIn('third_party', cmd[0])
332 self.assertIn(f'{os.sep}siso', cmd[0])
333 self.assertEqual(['ninja', '-C', temp_dir], cmd[1:])
335 ninja_deps = pathlib.Path(temp_dir) / '.ninja_deps'
338 with self.assertRaisesRegex(Exception, 'Found both'):
339 gn_helpers.CreateBuildCommand(temp_dir)
342 self.assertEqual(f'autoninja{suffix}',
343 gn_helpers.CreateBuildCommand(temp_dir)[0])
345 with mock.patch('shutil.which', lambda x: None):
346 cmd = gn_helpers.CreateBuildCommand(temp_dir)
347 self.assertIn('third_party', cmd[0])
348 self.assertIn(f'{os.sep}ninja', cmd[0])
349 self.assertEqual(['-C', temp_dir], cmd[1:])
352 if __name__ == '__main__':