gn_helpers_unittest.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. # Copyright 2016 The Chromium Authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. import mock
  5. import sys
  6. import textwrap
  7. import unittest
  8. import gn_helpers
  9. class UnitTest(unittest.TestCase):
  10. def test_ToGNString(self):
  11. test_cases = [
  12. (42, '42', '42'), ('foo', '"foo"', '"foo"'), (True, 'true', 'true'),
  13. (False, 'false', 'false'), ('', '""', '""'),
  14. ('\\$"$\\', '"\\\\\\$\\"\\$\\\\"', '"\\\\\\$\\"\\$\\\\"'),
  15. (' \t\r\n', '" $0x09$0x0D$0x0A"', '" $0x09$0x0D$0x0A"'),
  16. (u'\u2713', '"$0xE2$0x9C$0x93"', '"$0xE2$0x9C$0x93"'),
  17. ([], '[ ]', '[]'), ([1], '[ 1 ]', '[\n 1\n]\n'),
  18. ([3, 1, 4, 1], '[ 3, 1, 4, 1 ]', '[\n 3,\n 1,\n 4,\n 1\n]\n'),
  19. (['a', True, 2], '[ "a", true, 2 ]', '[\n "a",\n true,\n 2\n]\n'),
  20. ({
  21. 'single': 'item'
  22. }, 'single = "item"\n', 'single = "item"\n'),
  23. ({
  24. 'kEy': 137,
  25. '_42A_Zaz_': [False, True]
  26. }, '_42A_Zaz_ = [ false, true ]\nkEy = 137\n',
  27. '_42A_Zaz_ = [\n false,\n true\n]\nkEy = 137\n'),
  28. ([1, 'two',
  29. ['"thr,.$\\', True, False, [],
  30. u'(\u2713)']], '[ 1, "two", [ "\\"thr,.\\$\\\\", true, false, ' +
  31. '[ ], "($0xE2$0x9C$0x93)" ] ]', '''[
  32. 1,
  33. "two",
  34. [
  35. "\\"thr,.\\$\\\\",
  36. true,
  37. false,
  38. [],
  39. "($0xE2$0x9C$0x93)"
  40. ]
  41. ]
  42. '''),
  43. ({
  44. 's': 'foo',
  45. 'n': 42,
  46. 'b': True,
  47. 'a': [3, 'x']
  48. }, 'a = [ 3, "x" ]\nb = true\nn = 42\ns = "foo"\n',
  49. 'a = [\n 3,\n "x"\n]\nb = true\nn = 42\ns = "foo"\n'),
  50. (
  51. [[[], [[]]], []],
  52. '[ [ [ ], [ [ ] ] ], [ ] ]',
  53. '[\n [\n [],\n [\n []\n ]\n ],\n []\n]\n',
  54. ),
  55. (
  56. [{
  57. 'a': 1,
  58. 'c': {
  59. 'z': 8
  60. },
  61. 'b': []
  62. }],
  63. '[ { a = 1\nb = [ ]\nc = { z = 8 } } ]\n',
  64. '[\n {\n a = 1\n b = []\n c = {\n' +
  65. ' z = 8\n }\n }\n]\n',
  66. )
  67. ]
  68. for obj, exp_ugly, exp_pretty in test_cases:
  69. out_ugly = gn_helpers.ToGNString(obj)
  70. self.assertEqual(exp_ugly, out_ugly)
  71. out_pretty = gn_helpers.ToGNString(obj, pretty=True)
  72. self.assertEqual(exp_pretty, out_pretty)
  73. def test_UnescapeGNString(self):
  74. # Backslash followed by a \, $, or " means the folling character without
  75. # the special meaning. Backslash followed by everything else is a literal.
  76. self.assertEqual(
  77. gn_helpers.UnescapeGNString('\\as\\$\\\\asd\\"'),
  78. '\\as$\\asd"')
  79. def test_FromGNString(self):
  80. self.assertEqual(
  81. gn_helpers.FromGNString('[1, -20, true, false,["as\\"", []]]'),
  82. [ 1, -20, True, False, [ 'as"', [] ] ])
  83. with self.assertRaises(gn_helpers.GNError):
  84. parser = gn_helpers.GNValueParser('123 456')
  85. parser.Parse()
  86. def test_ParseBool(self):
  87. parser = gn_helpers.GNValueParser('true')
  88. self.assertEqual(parser.Parse(), True)
  89. parser = gn_helpers.GNValueParser('false')
  90. self.assertEqual(parser.Parse(), False)
  91. def test_ParseNumber(self):
  92. parser = gn_helpers.GNValueParser('123')
  93. self.assertEqual(parser.ParseNumber(), 123)
  94. with self.assertRaises(gn_helpers.GNError):
  95. parser = gn_helpers.GNValueParser('')
  96. parser.ParseNumber()
  97. with self.assertRaises(gn_helpers.GNError):
  98. parser = gn_helpers.GNValueParser('a123')
  99. parser.ParseNumber()
  100. def test_ParseString(self):
  101. parser = gn_helpers.GNValueParser('"asdf"')
  102. self.assertEqual(parser.ParseString(), 'asdf')
  103. with self.assertRaises(gn_helpers.GNError):
  104. parser = gn_helpers.GNValueParser('') # Empty.
  105. parser.ParseString()
  106. with self.assertRaises(gn_helpers.GNError):
  107. parser = gn_helpers.GNValueParser('asdf') # Unquoted.
  108. parser.ParseString()
  109. with self.assertRaises(gn_helpers.GNError):
  110. parser = gn_helpers.GNValueParser('"trailing') # Unterminated.
  111. parser.ParseString()
  112. def test_ParseList(self):
  113. parser = gn_helpers.GNValueParser('[1,]') # Optional end comma OK.
  114. self.assertEqual(parser.ParseList(), [ 1 ])
  115. with self.assertRaises(gn_helpers.GNError):
  116. parser = gn_helpers.GNValueParser('') # Empty.
  117. parser.ParseList()
  118. with self.assertRaises(gn_helpers.GNError):
  119. parser = gn_helpers.GNValueParser('asdf') # No [].
  120. parser.ParseList()
  121. with self.assertRaises(gn_helpers.GNError):
  122. parser = gn_helpers.GNValueParser('[1, 2') # Unterminated
  123. parser.ParseList()
  124. with self.assertRaises(gn_helpers.GNError):
  125. parser = gn_helpers.GNValueParser('[1 2]') # No separating comma.
  126. parser.ParseList()
  127. def test_ParseScope(self):
  128. parser = gn_helpers.GNValueParser('{a = 1}')
  129. self.assertEqual(parser.ParseScope(), {'a': 1})
  130. with self.assertRaises(gn_helpers.GNError):
  131. parser = gn_helpers.GNValueParser('') # Empty.
  132. parser.ParseScope()
  133. with self.assertRaises(gn_helpers.GNError):
  134. parser = gn_helpers.GNValueParser('asdf') # No {}.
  135. parser.ParseScope()
  136. with self.assertRaises(gn_helpers.GNError):
  137. parser = gn_helpers.GNValueParser('{a = 1') # Unterminated.
  138. parser.ParseScope()
  139. with self.assertRaises(gn_helpers.GNError):
  140. parser = gn_helpers.GNValueParser('{"a" = 1}') # Not identifier.
  141. parser.ParseScope()
  142. with self.assertRaises(gn_helpers.GNError):
  143. parser = gn_helpers.GNValueParser('{a = }') # No value.
  144. parser.ParseScope()
  145. def test_FromGNArgs(self):
  146. # Booleans and numbers should work; whitespace is allowed works.
  147. self.assertEqual(gn_helpers.FromGNArgs('foo = true\nbar = 1\n'),
  148. {'foo': True, 'bar': 1})
  149. # Whitespace is not required; strings should also work.
  150. self.assertEqual(gn_helpers.FromGNArgs('foo="bar baz"'),
  151. {'foo': 'bar baz'})
  152. # Comments should work (and be ignored).
  153. gn_args_lines = [
  154. '# Top-level comment.',
  155. 'foo = true',
  156. 'bar = 1 # In-line comment followed by whitespace.',
  157. ' ',
  158. 'baz = false',
  159. ]
  160. self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)), {
  161. 'foo': True,
  162. 'bar': 1,
  163. 'baz': False
  164. })
  165. # Lists should work.
  166. self.assertEqual(gn_helpers.FromGNArgs('foo=[1, 2, 3]'),
  167. {'foo': [1, 2, 3]})
  168. # Empty strings should return an empty dict.
  169. self.assertEqual(gn_helpers.FromGNArgs(''), {})
  170. self.assertEqual(gn_helpers.FromGNArgs(' \n '), {})
  171. # Comments should work everywhere (and be ignored).
  172. gn_args_lines = [
  173. '# Top-level comment.',
  174. '',
  175. '# Variable comment.',
  176. 'foo = true',
  177. 'bar = [',
  178. ' # Value comment in list.',
  179. ' 1,',
  180. ' 2,',
  181. ']',
  182. '',
  183. 'baz # Comment anywhere, really',
  184. ' = # also here',
  185. ' 4',
  186. ]
  187. self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)), {
  188. 'foo': True,
  189. 'bar': [1, 2],
  190. 'baz': 4
  191. })
  192. # Scope should be parsed, even empty ones.
  193. gn_args_lines = [
  194. 'foo = {',
  195. ' a = 1',
  196. ' b = [',
  197. ' { },',
  198. ' {',
  199. ' c = 1',
  200. ' },',
  201. ' ]',
  202. '}',
  203. ]
  204. self.assertEqual(gn_helpers.FromGNArgs('\n'.join(gn_args_lines)),
  205. {'foo': {
  206. 'a': 1,
  207. 'b': [
  208. {},
  209. {
  210. 'c': 1,
  211. },
  212. ]
  213. }})
  214. # Non-identifiers should raise an exception.
  215. with self.assertRaises(gn_helpers.GNError):
  216. gn_helpers.FromGNArgs('123 = true')
  217. # References to other variables should raise an exception.
  218. with self.assertRaises(gn_helpers.GNError):
  219. gn_helpers.FromGNArgs('foo = bar')
  220. # References to functions should raise an exception.
  221. with self.assertRaises(gn_helpers.GNError):
  222. gn_helpers.FromGNArgs('foo = exec_script("//build/baz.py")')
  223. # Underscores in identifiers should work.
  224. self.assertEqual(gn_helpers.FromGNArgs('_foo = true'),
  225. {'_foo': True})
  226. self.assertEqual(gn_helpers.FromGNArgs('foo_bar = true'),
  227. {'foo_bar': True})
  228. self.assertEqual(gn_helpers.FromGNArgs('foo_=true'),
  229. {'foo_': True})
  230. def test_ReplaceImports(self):
  231. # Should be a no-op on args inputs without any imports.
  232. parser = gn_helpers.GNValueParser(
  233. textwrap.dedent("""
  234. some_arg1 = "val1"
  235. some_arg2 = "val2"
  236. """))
  237. parser.ReplaceImports()
  238. self.assertEqual(
  239. parser.input,
  240. textwrap.dedent("""
  241. some_arg1 = "val1"
  242. some_arg2 = "val2"
  243. """))
  244. # A single "import(...)" line should be replaced with the contents of the
  245. # file being imported.
  246. parser = gn_helpers.GNValueParser(
  247. textwrap.dedent("""
  248. some_arg1 = "val1"
  249. import("//some/args/file.gni")
  250. some_arg2 = "val2"
  251. """))
  252. fake_import = 'some_imported_arg = "imported_val"'
  253. builtin_var = '__builtin__' if sys.version_info.major < 3 else 'builtins'
  254. open_fun = '{}.open'.format(builtin_var)
  255. with mock.patch(open_fun, mock.mock_open(read_data=fake_import)):
  256. parser.ReplaceImports()
  257. self.assertEqual(
  258. parser.input,
  259. textwrap.dedent("""
  260. some_arg1 = "val1"
  261. some_imported_arg = "imported_val"
  262. some_arg2 = "val2"
  263. """))
  264. # No trailing parenthesis should raise an exception.
  265. with self.assertRaises(gn_helpers.GNError):
  266. parser = gn_helpers.GNValueParser(
  267. textwrap.dedent('import("//some/args/file.gni"'))
  268. parser.ReplaceImports()
  269. # No double quotes should raise an exception.
  270. with self.assertRaises(gn_helpers.GNError):
  271. parser = gn_helpers.GNValueParser(
  272. textwrap.dedent('import(//some/args/file.gni)'))
  273. parser.ReplaceImports()
  274. # A path that's not source absolute should raise an exception.
  275. with self.assertRaises(gn_helpers.GNError):
  276. parser = gn_helpers.GNValueParser(
  277. textwrap.dedent('import("some/relative/args/file.gni")'))
  278. parser.ReplaceImports()
  279. if __name__ == '__main__':
  280. unittest.main()