Nikita Putikhin | 9aa3bc6 | 2023-05-19 20:39:51 +0000 | [diff] [blame] | 1 | # |
| 2 | # Copyright (C) 2023 The Android Open Source Project |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | # you may not use this file except in compliance with the License. |
| 6 | # You may obtain a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. |
| 15 | # |
| 16 | |
Nikita Putikhin | 9aa3bc6 | 2023-05-19 20:39:51 +0000 | [diff] [blame] | 17 | import copy |
| 18 | import json |
Nikita Putikhin | 7da179e | 2023-07-03 07:37:58 +0000 | [diff] [blame] | 19 | import textwrap |
| 20 | import unittest |
Nikita Putikhin | 9aa3bc6 | 2023-05-19 20:39:51 +0000 | [diff] [blame] | 21 | from typing import Any |
| 22 | |
Nikita Putikhin | 7da179e | 2023-07-03 07:37:58 +0000 | [diff] [blame] | 23 | import gdbclient |
| 24 | |
| 25 | |
Nikita Putikhin | 9aa3bc6 | 2023-05-19 20:39:51 +0000 | [diff] [blame] | 26 | class LaunchConfigMergeTest(unittest.TestCase): |
| 27 | def merge_compare(self, base: dict[str, Any], to_add: dict[str, Any] | None, expected: dict[str, Any]) -> None: |
| 28 | actual = copy.deepcopy(base) |
| 29 | gdbclient.merge_launch_dict(actual, to_add) |
| 30 | self.assertEqual(actual, expected, f'base={base}, to_add={to_add}') |
| 31 | |
| 32 | def test_add_none(self) -> None: |
| 33 | base = { 'foo' : 1 } |
| 34 | to_add = None |
| 35 | expected = { 'foo' : 1 } |
| 36 | self.merge_compare(base, to_add, expected) |
| 37 | |
| 38 | def test_add_val(self) -> None: |
| 39 | base = { 'foo' : 1 } |
| 40 | to_add = { 'bar' : 2} |
| 41 | expected = { 'foo' : 1, 'bar' : 2 } |
| 42 | self.merge_compare(base, to_add, expected) |
| 43 | |
| 44 | def test_overwrite_val(self) -> None: |
| 45 | base = { 'foo' : 1 } |
| 46 | to_add = { 'foo' : 2} |
| 47 | expected = { 'foo' : 2 } |
| 48 | self.merge_compare(base, to_add, expected) |
| 49 | |
| 50 | def test_lists_get_appended(self) -> None: |
| 51 | base = { 'foo' : [1, 2] } |
| 52 | to_add = { 'foo' : [3, 4]} |
| 53 | expected = { 'foo' : [1, 2, 3, 4] } |
| 54 | self.merge_compare(base, to_add, expected) |
| 55 | |
| 56 | def test_add_elem_to_dict(self) -> None: |
| 57 | base = { 'foo' : { 'bar' : 1 } } |
| 58 | to_add = { 'foo' : { 'baz' : 2 } } |
| 59 | expected = { 'foo' : { 'bar' : 1, 'baz' : 2 } } |
| 60 | self.merge_compare(base, to_add, expected) |
| 61 | |
| 62 | def test_overwrite_elem_in_dict(self) -> None: |
| 63 | base = { 'foo' : { 'bar' : 1 } } |
| 64 | to_add = { 'foo' : { 'bar' : 2 } } |
| 65 | expected = { 'foo' : { 'bar' : 2 } } |
| 66 | self.merge_compare(base, to_add, expected) |
| 67 | |
| 68 | def test_merging_dict_and_value_raises(self) -> None: |
| 69 | base = { 'foo' : { 'bar' : 1 } } |
| 70 | to_add = { 'foo' : 2 } |
| 71 | with self.assertRaises(ValueError): |
| 72 | gdbclient.merge_launch_dict(base, to_add) |
| 73 | |
| 74 | def test_merging_value_and_dict_raises(self) -> None: |
| 75 | base = { 'foo' : 2 } |
| 76 | to_add = { 'foo' : { 'bar' : 1 } } |
| 77 | with self.assertRaises(ValueError): |
| 78 | gdbclient.merge_launch_dict(base, to_add) |
| 79 | |
| 80 | def test_merging_dict_and_list_raises(self) -> None: |
| 81 | base = { 'foo' : { 'bar' : 1 } } |
| 82 | to_add = { 'foo' : [1] } |
| 83 | with self.assertRaises(ValueError): |
| 84 | gdbclient.merge_launch_dict(base, to_add) |
| 85 | |
| 86 | def test_merging_list_and_dict_raises(self) -> None: |
| 87 | base = { 'foo' : [1] } |
| 88 | to_add = { 'foo' : { 'bar' : 1 } } |
| 89 | with self.assertRaises(ValueError): |
| 90 | gdbclient.merge_launch_dict(base, to_add) |
| 91 | |
| 92 | def test_adding_elem_to_list_raises(self) -> None: |
| 93 | base = { 'foo' : [1] } |
| 94 | to_add = { 'foo' : 2} |
| 95 | with self.assertRaises(ValueError): |
| 96 | gdbclient.merge_launch_dict(base, to_add) |
| 97 | |
| 98 | def test_adding_list_to_elem_raises(self) -> None: |
| 99 | base = { 'foo' : 1 } |
| 100 | to_add = { 'foo' : [2]} |
| 101 | with self.assertRaises(ValueError): |
| 102 | gdbclient.merge_launch_dict(base, to_add) |
| 103 | |
| 104 | |
| 105 | class VsCodeLaunchGeneratorTest(unittest.TestCase): |
| 106 | def setUp(self) -> None: |
| 107 | # These tests can generate long diffs, so we remove the limit |
| 108 | self.maxDiff = None |
| 109 | |
| 110 | def test_generate_script(self) -> None: |
| 111 | self.assertEqual(json.loads(gdbclient.generate_vscode_lldb_script(root='/root', |
| 112 | sysroot='/sysroot', |
| 113 | binary_name='test', |
| 114 | port=123, |
| 115 | solib_search_path=['/path1', |
| 116 | '/path2'], |
| 117 | extra_props=None)), |
| 118 | { |
| 119 | 'name': '(lldbclient.py) Attach test (port: 123)', |
| 120 | 'type': 'lldb', |
| 121 | 'request': 'custom', |
| 122 | 'relativePathBase': '/root', |
| 123 | 'sourceMap': { '/b/f/w' : '/root', '': '/root', '.': '/root' }, |
| 124 | 'initCommands': ['settings append target.exec-search-paths /path1 /path2'], |
| 125 | 'targetCreateCommands': ['target create test', |
| 126 | 'target modules search-paths add / /sysroot/'], |
| 127 | 'processCreateCommands': ['gdb-remote 123'] |
| 128 | }) |
| 129 | |
| 130 | def test_generate_script_with_extra_props(self) -> None: |
| 131 | extra_props = { |
| 132 | 'initCommands' : ['settings append target.exec-search-paths /path3'], |
| 133 | 'processCreateCommands' : ['break main', 'continue'], |
| 134 | 'sourceMap' : { '/test/' : '/root/test'}, |
| 135 | 'preLaunchTask' : 'Build' |
| 136 | } |
| 137 | self.assertEqual(json.loads(gdbclient.generate_vscode_lldb_script(root='/root', |
| 138 | sysroot='/sysroot', |
| 139 | binary_name='test', |
| 140 | port=123, |
| 141 | solib_search_path=['/path1', |
| 142 | '/path2'], |
| 143 | extra_props=extra_props)), |
| 144 | { |
| 145 | 'name': '(lldbclient.py) Attach test (port: 123)', |
| 146 | 'type': 'lldb', |
| 147 | 'request': 'custom', |
| 148 | 'relativePathBase': '/root', |
| 149 | 'sourceMap': { '/b/f/w' : '/root', |
| 150 | '': '/root', |
| 151 | '.': '/root', |
| 152 | '/test/' : '/root/test' }, |
| 153 | 'initCommands': [ |
| 154 | 'settings append target.exec-search-paths /path1 /path2', |
| 155 | 'settings append target.exec-search-paths /path3', |
| 156 | ], |
| 157 | 'targetCreateCommands': ['target create test', |
| 158 | 'target modules search-paths add / /sysroot/'], |
| 159 | 'processCreateCommands': ['gdb-remote 123', |
| 160 | 'break main', |
| 161 | 'continue'], |
| 162 | 'preLaunchTask' : 'Build' |
| 163 | }) |
| 164 | |
| 165 | |
Nikita Putikhin | 7da179e | 2023-07-03 07:37:58 +0000 | [diff] [blame] | 166 | class LaunchConfigInsertTest(unittest.TestCase): |
| 167 | def setUp(self) -> None: |
| 168 | # These tests can generate long diffs, so we remove the limit |
| 169 | self.maxDiff = None |
| 170 | |
| 171 | def test_insert_config(self) -> None: |
| 172 | dst = textwrap.dedent("""\ |
| 173 | // #lldbclient-generated-begin |
| 174 | // #lldbclient-generated-end""") |
| 175 | to_insert = textwrap.dedent("""\ |
| 176 | foo |
| 177 | bar""") |
| 178 | self.assertEqual(gdbclient.insert_commands_into_vscode_config(dst, |
| 179 | to_insert), |
| 180 | textwrap.dedent("""\ |
| 181 | // #lldbclient-generated-begin |
| 182 | foo |
| 183 | bar |
| 184 | // #lldbclient-generated-end""")) |
| 185 | |
| 186 | def test_insert_into_start(self) -> None: |
| 187 | dst = textwrap.dedent("""\ |
| 188 | // #lldbclient-generated-begin |
| 189 | // #lldbclient-generated-end |
| 190 | more content""") |
| 191 | to_insert = textwrap.dedent("""\ |
| 192 | foo |
| 193 | bar""") |
| 194 | self.assertEqual(gdbclient.insert_commands_into_vscode_config(dst, |
| 195 | to_insert), |
| 196 | textwrap.dedent("""\ |
| 197 | // #lldbclient-generated-begin |
| 198 | foo |
| 199 | bar |
| 200 | // #lldbclient-generated-end |
| 201 | more content""")) |
| 202 | |
| 203 | def test_insert_into_mid(self) -> None: |
| 204 | dst = textwrap.dedent("""\ |
| 205 | start content |
| 206 | // #lldbclient-generated-begin |
| 207 | // #lldbclient-generated-end |
| 208 | more content""") |
| 209 | to_insert = textwrap.dedent("""\ |
| 210 | foo |
| 211 | bar""") |
| 212 | self.assertEqual(gdbclient.insert_commands_into_vscode_config(dst, |
| 213 | to_insert), |
| 214 | textwrap.dedent("""\ |
| 215 | start content |
| 216 | // #lldbclient-generated-begin |
| 217 | foo |
| 218 | bar |
| 219 | // #lldbclient-generated-end |
| 220 | more content""")) |
| 221 | |
| 222 | def test_insert_into_end(self) -> None: |
| 223 | dst = textwrap.dedent("""\ |
| 224 | start content |
| 225 | // #lldbclient-generated-begin |
| 226 | // #lldbclient-generated-end""") |
| 227 | to_insert = textwrap.dedent("""\ |
| 228 | foo |
| 229 | bar""") |
| 230 | self.assertEqual(gdbclient.insert_commands_into_vscode_config(dst, |
| 231 | to_insert), |
| 232 | textwrap.dedent("""\ |
| 233 | start content |
| 234 | // #lldbclient-generated-begin |
| 235 | foo |
| 236 | bar |
| 237 | // #lldbclient-generated-end""")) |
| 238 | |
| 239 | def test_insert_twice(self) -> None: |
| 240 | dst = textwrap.dedent("""\ |
| 241 | // #lldbclient-generated-begin |
| 242 | // #lldbclient-generated-end |
| 243 | // #lldbclient-generated-begin |
| 244 | // #lldbclient-generated-end |
| 245 | """) |
| 246 | to_insert = 'foo' |
| 247 | self.assertEqual(gdbclient.insert_commands_into_vscode_config(dst, |
| 248 | to_insert), |
| 249 | textwrap.dedent("""\ |
| 250 | // #lldbclient-generated-begin |
| 251 | foo |
| 252 | // #lldbclient-generated-end |
| 253 | // #lldbclient-generated-begin |
| 254 | foo |
| 255 | // #lldbclient-generated-end |
| 256 | """)) |
| 257 | |
| 258 | def test_preserve_space_indent(self) -> None: |
| 259 | dst = textwrap.dedent("""\ |
| 260 | { |
| 261 | "version": "0.2.0", |
| 262 | "configurations": [ |
| 263 | // #lldbclient-generated-begin |
| 264 | // #lldbclient-generated-end |
| 265 | ] |
| 266 | } |
| 267 | """) |
| 268 | to_insert = textwrap.dedent("""\ |
| 269 | { |
| 270 | "name": "(lldbclient.py) Attach test", |
| 271 | "type": "lldb", |
| 272 | "processCreateCommands": [ |
| 273 | "gdb-remote 123", |
| 274 | "test" |
| 275 | ] |
| 276 | }""") |
| 277 | self.assertEqual(gdbclient.insert_commands_into_vscode_config(dst, |
| 278 | to_insert), |
| 279 | textwrap.dedent("""\ |
| 280 | { |
| 281 | "version": "0.2.0", |
| 282 | "configurations": [ |
| 283 | // #lldbclient-generated-begin |
| 284 | { |
| 285 | "name": "(lldbclient.py) Attach test", |
| 286 | "type": "lldb", |
| 287 | "processCreateCommands": [ |
| 288 | "gdb-remote 123", |
| 289 | "test" |
| 290 | ] |
| 291 | } |
| 292 | // #lldbclient-generated-end |
| 293 | ] |
| 294 | } |
| 295 | """)) |
| 296 | |
| 297 | def test_preserve_tab_indent(self) -> None: |
| 298 | dst = textwrap.dedent("""\ |
| 299 | { |
| 300 | \t"version": "0.2.0", |
| 301 | \t"configurations": [ |
| 302 | \t\t// #lldbclient-generated-begin |
| 303 | \t\t// #lldbclient-generated-end |
| 304 | \t] |
| 305 | } |
| 306 | """) |
| 307 | to_insert = textwrap.dedent("""\ |
| 308 | { |
| 309 | \t"name": "(lldbclient.py) Attach test", |
| 310 | \t"type": "lldb", |
| 311 | \t"processCreateCommands": [ |
| 312 | \t\t"gdb-remote 123", |
| 313 | \t\t"test" |
| 314 | \t] |
| 315 | }""") |
| 316 | self.assertEqual(gdbclient.insert_commands_into_vscode_config(dst, |
| 317 | to_insert), |
| 318 | textwrap.dedent("""\ |
| 319 | { |
| 320 | \t"version": "0.2.0", |
| 321 | \t"configurations": [ |
| 322 | \t\t// #lldbclient-generated-begin |
| 323 | \t\t{ |
| 324 | \t\t\t"name": "(lldbclient.py) Attach test", |
| 325 | \t\t\t"type": "lldb", |
| 326 | \t\t\t"processCreateCommands": [ |
| 327 | \t\t\t\t"gdb-remote 123", |
| 328 | \t\t\t\t"test" |
| 329 | \t\t\t] |
| 330 | \t\t} |
| 331 | \t\t// #lldbclient-generated-end |
| 332 | \t] |
| 333 | } |
| 334 | """)) |
| 335 | |
| 336 | def test_preserve_trailing_whitespace(self) -> None: |
| 337 | dst = textwrap.dedent("""\ |
| 338 | // #lldbclient-generated-begin \t |
| 339 | // #lldbclient-generated-end\t """) |
| 340 | to_insert = 'foo' |
| 341 | self.assertEqual(gdbclient.insert_commands_into_vscode_config(dst, |
| 342 | to_insert), |
| 343 | textwrap.dedent("""\ |
| 344 | // #lldbclient-generated-begin \t |
| 345 | foo |
| 346 | // #lldbclient-generated-end\t """)) |
| 347 | |
| 348 | def test_fail_if_no_begin(self) -> None: |
| 349 | dst = textwrap.dedent("""\ |
| 350 | // #lldbclient-generated-end""") |
| 351 | with self.assertRaisesRegex(ValueError, 'Did not find begin marker line'): |
| 352 | gdbclient.insert_commands_into_vscode_config(dst, 'foo') |
| 353 | |
| 354 | def test_fail_if_no_end(self) -> None: |
| 355 | dst = textwrap.dedent("""\ |
| 356 | // #lldbclient-generated-begin""") |
| 357 | with self.assertRaisesRegex(ValueError, 'Unterminated begin marker at line 1'): |
| 358 | gdbclient.insert_commands_into_vscode_config(dst, 'foo') |
| 359 | |
| 360 | def test_fail_if_begin_has_extra_text(self) -> None: |
| 361 | dst = textwrap.dedent("""\ |
| 362 | // #lldbclient-generated-begin text |
| 363 | // #lldbclient-generated-end""") |
| 364 | with self.assertRaisesRegex(ValueError, 'Did not find begin marker line'): |
| 365 | gdbclient.insert_commands_into_vscode_config(dst, 'foo') |
| 366 | |
| 367 | def test_fail_if_end_has_extra_text(self) -> None: |
| 368 | dst = textwrap.dedent("""\ |
| 369 | // #lldbclient-generated-begin |
| 370 | // #lldbclient-generated-end text""") |
| 371 | with self.assertRaisesRegex(ValueError, 'Unterminated begin marker at line 1'): |
| 372 | gdbclient.insert_commands_into_vscode_config(dst, 'foo') |
| 373 | |
| 374 | def test_fail_if_begin_end_swapped(self) -> None: |
| 375 | dst = textwrap.dedent("""\ |
| 376 | // #lldbclient-generated-end |
| 377 | // #lldbclient-generated-begin""") |
| 378 | with self.assertRaisesRegex(ValueError, 'Unterminated begin marker at line 2'): |
| 379 | gdbclient.insert_commands_into_vscode_config(dst, 'foo') |
| 380 | |
| 381 | |
Nikita Putikhin | 9aa3bc6 | 2023-05-19 20:39:51 +0000 | [diff] [blame] | 382 | if __name__ == '__main__': |
| 383 | unittest.main(verbosity=2) |