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