6e305d719c15b2f39e1d0dd6695958f0f4948b5d
[platform/framework/web/chromium-efl.git] / build / gn_helpers_unittest.py
1 #!/usr/bin/env python3
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.
5
6 import os
7 import pathlib
8 import shutil
9 import sys
10 import tempfile
11 import textwrap
12 import unittest
13 from unittest import mock
14
15 import gn_helpers
16
17
18 class UnitTest(unittest.TestCase):
19   def test_ToGNString(self):
20     test_cases = [
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'),
29         ({
30             'single': 'item'
31         }, 'single = "item"\n', 'single = "item"\n'),
32         ({
33             'kEy': 137,
34             '_42A_Zaz_': [False, True]
35         }, '_42A_Zaz_ = [ false, true ]\nkEy = 137\n',
36          '_42A_Zaz_ = [\n  false,\n  true\n]\nkEy = 137\n'),
37         ([1, 'two',
38           ['"thr,.$\\', True, False, [],
39            u'(\u2713)']], '[ 1, "two", [ "\\"thr,.\\$\\\\", true, false, ' +
40          '[  ], "($0xE2$0x9C$0x93)" ] ]', '''[
41   1,
42   "two",
43   [
44     "\\"thr,.\\$\\\\",
45     true,
46     false,
47     [],
48     "($0xE2$0x9C$0x93)"
49   ]
50 ]
51 '''),
52         ({
53             's': 'foo',
54             'n': 42,
55             'b': True,
56             'a': [3, 'x']
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'),
59         (
60             [[[], [[]]], []],
61             '[ [ [  ], [ [  ] ] ], [  ] ]',
62             '[\n  [\n    [],\n    [\n      []\n    ]\n  ],\n  []\n]\n',
63         ),
64         (
65             [{
66                 'a': 1,
67                 'c': {
68                     'z': 8
69                 },
70                 'b': []
71             }],
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',
75         )
76     ]
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)
82
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.
86     self.assertEqual(
87         gn_helpers.UnescapeGNString('\\as\\$\\\\asd\\"'),
88         '\\as$\\asd"')
89
90   def test_FromGNString(self):
91     self.assertEqual(
92         gn_helpers.FromGNString('[1, -20, true, false,["as\\"", []]]'),
93         [ 1, -20, True, False, [ 'as"', [] ] ])
94
95     with self.assertRaises(gn_helpers.GNError):
96       parser = gn_helpers.GNValueParser('123 456')
97       parser.Parse()
98
99   def test_ParseBool(self):
100     parser = gn_helpers.GNValueParser('true')
101     self.assertEqual(parser.Parse(), True)
102
103     parser = gn_helpers.GNValueParser('false')
104     self.assertEqual(parser.Parse(), False)
105
106   def test_ParseNumber(self):
107     parser = gn_helpers.GNValueParser('123')
108     self.assertEqual(parser.ParseNumber(), 123)
109
110     with self.assertRaises(gn_helpers.GNError):
111       parser = gn_helpers.GNValueParser('')
112       parser.ParseNumber()
113     with self.assertRaises(gn_helpers.GNError):
114       parser = gn_helpers.GNValueParser('a123')
115       parser.ParseNumber()
116
117   def test_ParseString(self):
118     parser = gn_helpers.GNValueParser('"asdf"')
119     self.assertEqual(parser.ParseString(), 'asdf')
120
121     with self.assertRaises(gn_helpers.GNError):
122       parser = gn_helpers.GNValueParser('')  # Empty.
123       parser.ParseString()
124     with self.assertRaises(gn_helpers.GNError):
125       parser = gn_helpers.GNValueParser('asdf')  # Unquoted.
126       parser.ParseString()
127     with self.assertRaises(gn_helpers.GNError):
128       parser = gn_helpers.GNValueParser('"trailing')  # Unterminated.
129       parser.ParseString()
130
131   def test_ParseList(self):
132     parser = gn_helpers.GNValueParser('[1,]')  # Optional end comma OK.
133     self.assertEqual(parser.ParseList(), [ 1 ])
134
135     with self.assertRaises(gn_helpers.GNError):
136       parser = gn_helpers.GNValueParser('')  # Empty.
137       parser.ParseList()
138     with self.assertRaises(gn_helpers.GNError):
139       parser = gn_helpers.GNValueParser('asdf')  # No [].
140       parser.ParseList()
141     with self.assertRaises(gn_helpers.GNError):
142       parser = gn_helpers.GNValueParser('[1, 2')  # Unterminated
143       parser.ParseList()
144     with self.assertRaises(gn_helpers.GNError):
145       parser = gn_helpers.GNValueParser('[1 2]')  # No separating comma.
146       parser.ParseList()
147
148   def test_ParseScope(self):
149     parser = gn_helpers.GNValueParser('{a = 1}')
150     self.assertEqual(parser.ParseScope(), {'a': 1})
151
152     with self.assertRaises(gn_helpers.GNError):
153       parser = gn_helpers.GNValueParser('')  # Empty.
154       parser.ParseScope()
155     with self.assertRaises(gn_helpers.GNError):
156       parser = gn_helpers.GNValueParser('asdf')  # No {}.
157       parser.ParseScope()
158     with self.assertRaises(gn_helpers.GNError):
159       parser = gn_helpers.GNValueParser('{a = 1')  # Unterminated.
160       parser.ParseScope()
161     with self.assertRaises(gn_helpers.GNError):
162       parser = gn_helpers.GNValueParser('{"a" = 1}')  # Not identifier.
163       parser.ParseScope()
164     with self.assertRaises(gn_helpers.GNError):
165       parser = gn_helpers.GNValueParser('{a = }')  # No value.
166       parser.ParseScope()
167
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})
172
173     # Whitespace is not required; strings should also work.
174     self.assertEqual(gn_helpers.FromGNArgs('foo="bar baz"'),
175                      {'foo': 'bar baz'})
176
177     # Comments should work (and be ignored).
178     gn_args_lines = [
179         '# Top-level comment.',
180         'foo = true',
181         'bar = 1  # In-line comment followed by whitespace.',
182         ' ',
183         'baz = false',
184     ]
185     self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)), {
186         'foo': True,
187         'bar': 1,
188         'baz': False
189     })
190
191     # Lists should work.
192     self.assertEqual(gn_helpers.FromGNArgs('foo=[1, 2, 3]'),
193                      {'foo': [1, 2, 3]})
194
195     # Empty strings should return an empty dict.
196     self.assertEqual(gn_helpers.FromGNArgs(''), {})
197     self.assertEqual(gn_helpers.FromGNArgs(' \n '), {})
198
199     # Comments should work everywhere (and be ignored).
200     gn_args_lines = [
201         '# Top-level comment.',
202         '',
203         '# Variable comment.',
204         'foo = true',
205         'bar = [',
206         '    # Value comment in list.',
207         '    1,',
208         '    2,',
209         ']',
210         '',
211         'baz # Comment anywhere, really',
212         '  = # also here',
213         '    4',
214     ]
215     self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)), {
216         'foo': True,
217         'bar': [1, 2],
218         'baz': 4
219     })
220
221     # Scope should be parsed, even empty ones.
222     gn_args_lines = [
223         'foo = {',
224         '  a = 1',
225         '  b = [',
226         '    { },',
227         '    {',
228         '      c = 1',
229         '    },',
230         '  ]',
231         '}',
232     ]
233     self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)),
234                      {'foo': {
235                          'a': 1,
236                          'b': [
237                              {},
238                              {
239                                  'c': 1,
240                              },
241                          ]
242                      }})
243
244     # Non-identifiers should raise an exception.
245     with self.assertRaises(gn_helpers.GNError):
246       gn_helpers.FromGNArgs('123 = true')
247
248     # References to other variables should raise an exception.
249     with self.assertRaises(gn_helpers.GNError):
250       gn_helpers.FromGNArgs('foo = bar')
251
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")')
255
256     # Underscores in identifiers should work.
257     self.assertEqual(gn_helpers.FromGNArgs('_foo = true'),
258                      {'_foo': True})
259     self.assertEqual(gn_helpers.FromGNArgs('foo_bar = true'),
260                      {'foo_bar': True})
261     self.assertEqual(gn_helpers.FromGNArgs('foo_=true'),
262                      {'foo_': True})
263
264   def test_ReplaceImports(self):
265     # Should be a no-op on args inputs without any imports.
266     parser = gn_helpers.GNValueParser(
267         textwrap.dedent("""
268         some_arg1 = "val1"
269         some_arg2 = "val2"
270     """))
271     parser.ReplaceImports()
272     self.assertEqual(
273         parser.input,
274         textwrap.dedent("""
275         some_arg1 = "val1"
276         some_arg2 = "val2"
277     """))
278
279     # A single "import(...)" line should be replaced with the contents of the
280     # file being imported.
281     parser = gn_helpers.GNValueParser(
282         textwrap.dedent("""
283         some_arg1 = "val1"
284         import("//some/args/file.gni")
285         some_arg2 = "val2"
286     """))
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()
292     self.assertEqual(
293         parser.input,
294         textwrap.dedent("""
295         some_arg1 = "val1"
296         some_imported_arg = "imported_val"
297         some_arg2 = "val2"
298     """))
299
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()
305
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()
311
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()
317
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])
323
324       siso_deps = pathlib.Path(temp_dir) / '.siso_deps'
325       siso_deps.touch()
326       self.assertEqual(f'autosiso{suffix}',
327                        gn_helpers.CreateBuildCommand(temp_dir)[0])
328
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:])
334
335       ninja_deps = pathlib.Path(temp_dir) / '.ninja_deps'
336       ninja_deps.touch()
337
338       with self.assertRaisesRegex(Exception, 'Found both'):
339         gn_helpers.CreateBuildCommand(temp_dir)
340
341       siso_deps.unlink()
342       self.assertEqual(f'autoninja{suffix}',
343                        gn_helpers.CreateBuildCommand(temp_dir)[0])
344
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:])
350
351
352 if __name__ == '__main__':
353   unittest.main()