1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5from __future__ import absolute_import, print_function, unicode_literals 6 7import os 8import six 9import unittest 10 11from mozunit import main 12 13from mozbuild.frontend.context import ( 14 ObjDirPath, 15 Path, 16) 17from mozbuild.frontend.data import ( 18 ComputedFlags, 19 ConfigFileSubstitution, 20 Defines, 21 DirectoryTraversal, 22 Exports, 23 FinalTargetPreprocessedFiles, 24 GeneratedFile, 25 GeneratedSources, 26 HostProgram, 27 HostRustLibrary, 28 HostRustProgram, 29 HostSources, 30 IPDLCollection, 31 JARManifest, 32 LocalInclude, 33 LocalizedFiles, 34 LocalizedPreprocessedFiles, 35 Program, 36 RustLibrary, 37 RustProgram, 38 SharedLibrary, 39 SimpleProgram, 40 Sources, 41 StaticLibrary, 42 TestHarnessFiles, 43 TestManifest, 44 UnifiedSources, 45 VariablePassthru, 46 WasmSources, 47) 48from mozbuild.frontend.emitter import TreeMetadataEmitter 49from mozbuild.frontend.reader import ( 50 BuildReader, 51 BuildReaderError, 52 SandboxValidationError, 53) 54 55from mozbuild.test.common import MockConfig 56 57import mozpack.path as mozpath 58 59 60data_path = mozpath.abspath(mozpath.dirname(__file__)) 61data_path = mozpath.join(data_path, 'data') 62 63 64class TestEmitterBasic(unittest.TestCase): 65 def setUp(self): 66 self._old_env = dict(os.environ) 67 os.environ.pop('MOZ_OBJDIR', None) 68 69 def tearDown(self): 70 os.environ.clear() 71 os.environ.update(self._old_env) 72 73 def reader(self, name, enable_tests=False, extra_substs=None): 74 substs = dict( 75 ENABLE_TESTS='1' if enable_tests else '', 76 BIN_SUFFIX='.prog', 77 HOST_BIN_SUFFIX='.hostprog', 78 OS_TARGET='WINNT', 79 COMPILE_ENVIRONMENT='1', 80 STL_FLAGS=['-I/path/to/topobjdir/dist/stl_wrappers'], 81 VISIBILITY_FLAGS=['-include', 82 '$(topsrcdir)/config/gcc_hidden.h'], 83 OBJ_SUFFIX='obj', 84 WASM_OBJ_SUFFIX='wasm', 85 WASM_CFLAGS=['-foo'], 86 ) 87 if extra_substs: 88 substs.update(extra_substs) 89 config = MockConfig(mozpath.join(data_path, name), extra_substs=substs) 90 91 return BuildReader(config) 92 93 def read_topsrcdir(self, reader, filter_common=True): 94 emitter = TreeMetadataEmitter(reader.config) 95 objs = list(emitter.emit(reader.read_topsrcdir())) 96 self.assertGreater(len(objs), 0) 97 98 filtered = [] 99 for obj in objs: 100 if filter_common and isinstance(obj, DirectoryTraversal): 101 continue 102 103 filtered.append(obj) 104 105 return filtered 106 107 def test_dirs_traversal_simple(self): 108 reader = self.reader('traversal-simple') 109 objs = self.read_topsrcdir(reader, filter_common=False) 110 self.assertEqual(len(objs), 4) 111 112 for o in objs: 113 self.assertIsInstance(o, DirectoryTraversal) 114 self.assertTrue(os.path.isabs(o.context_main_path)) 115 self.assertEqual(len(o.context_all_paths), 1) 116 117 reldirs = [o.relsrcdir for o in objs] 118 self.assertEqual(reldirs, ['', 'foo', 'foo/biz', 'bar']) 119 120 dirs = [[d.full_path for d in o.dirs] for o in objs] 121 self.assertEqual(dirs, [ 122 [ 123 mozpath.join(reader.config.topsrcdir, 'foo'), 124 mozpath.join(reader.config.topsrcdir, 'bar') 125 ], [ 126 mozpath.join(reader.config.topsrcdir, 'foo', 'biz') 127 ], [], []]) 128 129 def test_traversal_all_vars(self): 130 reader = self.reader('traversal-all-vars') 131 objs = self.read_topsrcdir(reader, filter_common=False) 132 self.assertEqual(len(objs), 2) 133 134 for o in objs: 135 self.assertIsInstance(o, DirectoryTraversal) 136 137 reldirs = set([o.relsrcdir for o in objs]) 138 self.assertEqual(reldirs, set(['', 'regular'])) 139 140 for o in objs: 141 reldir = o.relsrcdir 142 143 if reldir == '': 144 self.assertEqual([d.full_path for d in o.dirs], [ 145 mozpath.join(reader.config.topsrcdir, 'regular')]) 146 147 def test_traversal_all_vars_enable_tests(self): 148 reader = self.reader('traversal-all-vars', enable_tests=True) 149 objs = self.read_topsrcdir(reader, filter_common=False) 150 self.assertEqual(len(objs), 3) 151 152 for o in objs: 153 self.assertIsInstance(o, DirectoryTraversal) 154 155 reldirs = set([o.relsrcdir for o in objs]) 156 self.assertEqual(reldirs, set(['', 'regular', 'test'])) 157 158 for o in objs: 159 reldir = o.relsrcdir 160 161 if reldir == '': 162 self.assertEqual([d.full_path for d in o.dirs], [ 163 mozpath.join(reader.config.topsrcdir, 'regular'), 164 mozpath.join(reader.config.topsrcdir, 'test')]) 165 166 def test_config_file_substitution(self): 167 reader = self.reader('config-file-substitution') 168 objs = self.read_topsrcdir(reader) 169 self.assertEqual(len(objs), 2) 170 171 self.assertIsInstance(objs[0], ConfigFileSubstitution) 172 self.assertIsInstance(objs[1], ConfigFileSubstitution) 173 174 topobjdir = mozpath.abspath(reader.config.topobjdir) 175 self.assertEqual(objs[0].relpath, 'foo') 176 self.assertEqual(mozpath.normpath(objs[0].output_path), 177 mozpath.normpath(mozpath.join(topobjdir, 'foo'))) 178 self.assertEqual(mozpath.normpath(objs[1].output_path), 179 mozpath.normpath(mozpath.join(topobjdir, 'bar'))) 180 181 def test_variable_passthru(self): 182 reader = self.reader('variable-passthru') 183 objs = self.read_topsrcdir(reader) 184 185 self.assertEqual(len(objs), 1) 186 self.assertIsInstance(objs[0], VariablePassthru) 187 188 wanted = { 189 'NO_DIST_INSTALL': True, 190 'RCFILE': 'foo.rc', 191 'RESFILE': 'bar.res', 192 'RCINCLUDE': 'bar.rc', 193 'EXTRA_DEPS': [mozpath.join(mozpath.relpath(reader.config.topsrcdir, 194 reader.config.topobjdir), 195 'baz.def')], 196 'WIN32_EXE_LDFLAGS': ['-subsystem:console'], 197 } 198 199 variables = objs[0].variables 200 maxDiff = self.maxDiff 201 self.maxDiff = None 202 self.assertEqual(wanted, variables) 203 self.maxDiff = maxDiff 204 205 def test_compile_flags(self): 206 reader = self.reader('compile-flags', extra_substs={ 207 'WARNINGS_AS_ERRORS': '-Werror', 208 }) 209 sources, ldflags, lib, flags = self.read_topsrcdir(reader) 210 self.assertIsInstance(flags, ComputedFlags) 211 self.assertEqual(flags.flags['STL'], reader.config.substs['STL_FLAGS']) 212 self.assertEqual(flags.flags['VISIBILITY'], reader.config.substs['VISIBILITY_FLAGS']) 213 self.assertEqual(flags.flags['WARNINGS_AS_ERRORS'], ['-Werror']) 214 self.assertEqual(flags.flags['MOZBUILD_CFLAGS'], ['-Wall', '-funroll-loops']) 215 self.assertEqual(flags.flags['MOZBUILD_CXXFLAGS'], ['-funroll-loops', '-Wall']) 216 217 def test_asflags(self): 218 reader = self.reader('asflags', extra_substs={ 219 'ASFLAGS': ['-safeseh'], 220 }) 221 as_sources, sources, ldflags, lib, flags, asflags = self.read_topsrcdir(reader) 222 self.assertIsInstance(asflags, ComputedFlags) 223 self.assertEqual(asflags.flags['OS'], reader.config.substs['ASFLAGS']) 224 self.assertEqual(asflags.flags['MOZBUILD'], ['-no-integrated-as']) 225 226 def test_debug_flags(self): 227 reader = self.reader('compile-flags', extra_substs={ 228 'MOZ_DEBUG_FLAGS': '-g', 229 'MOZ_DEBUG_SYMBOLS': '1', 230 }) 231 sources, ldflags, lib, flags = self.read_topsrcdir(reader) 232 self.assertIsInstance(flags, ComputedFlags) 233 self.assertEqual(flags.flags['DEBUG'], ['-g']) 234 235 def test_disable_debug_flags(self): 236 reader = self.reader('compile-flags', extra_substs={ 237 'MOZ_DEBUG_FLAGS': '-g', 238 'MOZ_DEBUG_SYMBOLS': '', 239 }) 240 sources, ldflags, lib, flags = self.read_topsrcdir(reader) 241 self.assertIsInstance(flags, ComputedFlags) 242 self.assertEqual(flags.flags['DEBUG'], []) 243 244 def test_link_flags(self): 245 reader = self.reader('link-flags', extra_substs={ 246 'OS_LDFLAGS': ['-Wl,rpath-link=/usr/lib'], 247 'MOZ_OPTIMIZE': '', 248 'MOZ_OPTIMIZE_LDFLAGS': ['-Wl,-dead_strip'], 249 'MOZ_DEBUG_LDFLAGS': ['-framework ExceptionHandling'], 250 }) 251 sources, ldflags, lib, compile_flags = self.read_topsrcdir(reader) 252 self.assertIsInstance(ldflags, ComputedFlags) 253 self.assertEqual(ldflags.flags['OS'], reader.config.substs['OS_LDFLAGS']) 254 self.assertEqual(ldflags.flags['MOZBUILD'], ['-Wl,-U_foo', '-framework Foo', '-x']) 255 self.assertEqual(ldflags.flags['OPTIMIZE'], []) 256 257 def test_debug_ldflags(self): 258 reader = self.reader('link-flags', extra_substs={ 259 'MOZ_DEBUG_SYMBOLS': '1', 260 'MOZ_DEBUG_LDFLAGS': ['-framework ExceptionHandling'], 261 }) 262 sources, ldflags, lib, compile_flags = self.read_topsrcdir(reader) 263 self.assertIsInstance(ldflags, ComputedFlags) 264 self.assertEqual(ldflags.flags['OS'], 265 reader.config.substs['MOZ_DEBUG_LDFLAGS']) 266 267 def test_windows_opt_link_flags(self): 268 reader = self.reader('link-flags', extra_substs={ 269 'OS_ARCH': 'WINNT', 270 'GNU_CC': '', 271 'MOZ_OPTIMIZE': '1', 272 'MOZ_DEBUG_LDFLAGS': ['-DEBUG'], 273 'MOZ_DEBUG_SYMBOLS': '1', 274 'MOZ_OPTIMIZE_FLAGS': [], 275 'MOZ_OPTIMIZE_LDFLAGS': [], 276 }) 277 sources, ldflags, lib, compile_flags = self.read_topsrcdir(reader) 278 self.assertIsInstance(ldflags, ComputedFlags) 279 self.assertIn('-DEBUG', ldflags.flags['OS']) 280 self.assertIn('-OPT:REF,ICF', ldflags.flags['OS']) 281 282 def test_windows_dmd_link_flags(self): 283 reader = self.reader('link-flags', extra_substs={ 284 'OS_ARCH': 'WINNT', 285 'GNU_CC': '', 286 'MOZ_DMD': '1', 287 'MOZ_DEBUG_LDFLAGS': ['-DEBUG'], 288 'MOZ_DEBUG_SYMBOLS': '1', 289 'MOZ_OPTIMIZE': '1', 290 'MOZ_OPTIMIZE_FLAGS': [], 291 }) 292 sources, ldflags, lib, compile_flags = self.read_topsrcdir(reader) 293 self.assertIsInstance(ldflags, ComputedFlags) 294 self.assertEqual(ldflags.flags['OS'], 295 ['-DEBUG', '-OPT:REF,ICF']) 296 297 def test_host_compile_flags(self): 298 reader = self.reader('host-compile-flags', extra_substs={ 299 'HOST_CXXFLAGS': ['-Wall', '-Werror'], 300 'HOST_CFLAGS': ['-Werror', '-Wall'], 301 }) 302 sources, ldflags, flags, lib, target_flags = self.read_topsrcdir(reader) 303 self.assertIsInstance(flags, ComputedFlags) 304 self.assertEqual(flags.flags['HOST_CXXFLAGS'], reader.config.substs['HOST_CXXFLAGS']) 305 self.assertEqual(flags.flags['HOST_CFLAGS'], reader.config.substs['HOST_CFLAGS']) 306 self.assertEqual(set(flags.flags['HOST_DEFINES']), 307 set(['-DFOO', '-DBAZ="abcd"', '-UQUX', '-DBAR=7', '-DVALUE=xyz'])) 308 self.assertEqual(flags.flags['MOZBUILD_HOST_CFLAGS'], ['-funroll-loops', '-host-arg']) 309 self.assertEqual(flags.flags['MOZBUILD_HOST_CXXFLAGS'], []) 310 311 def test_host_no_optimize_flags(self): 312 reader = self.reader('host-compile-flags', extra_substs={ 313 'MOZ_OPTIMIZE': '', 314 'MOZ_OPTIMIZE_FLAGS': ['-O2'], 315 }) 316 sources, ldflags, flags, lib, target_flags = self.read_topsrcdir(reader) 317 self.assertIsInstance(flags, ComputedFlags) 318 self.assertEqual(flags.flags['HOST_OPTIMIZE'], []) 319 320 def test_host_optimize_flags(self): 321 reader = self.reader('host-compile-flags', extra_substs={ 322 'MOZ_OPTIMIZE': '1', 323 'MOZ_OPTIMIZE_FLAGS': ['-O2'], 324 }) 325 sources, ldflags, flags, lib, target_flags = self.read_topsrcdir(reader) 326 self.assertIsInstance(flags, ComputedFlags) 327 self.assertEqual(flags.flags['HOST_OPTIMIZE'], ['-O2']) 328 329 def test_cross_optimize_flags(self): 330 reader = self.reader('host-compile-flags', extra_substs={ 331 'MOZ_OPTIMIZE': '1', 332 'MOZ_OPTIMIZE_FLAGS': ['-O2'], 333 'HOST_OPTIMIZE_FLAGS': ['-O3'], 334 'CROSS_COMPILE': '1', 335 }) 336 sources, ldflags, flags, lib, target_flags = self.read_topsrcdir(reader) 337 self.assertIsInstance(flags, ComputedFlags) 338 self.assertEqual(flags.flags['HOST_OPTIMIZE'], ['-O3']) 339 340 def test_host_rtl_flag(self): 341 reader = self.reader('host-compile-flags', extra_substs={ 342 'OS_ARCH': 'WINNT', 343 'MOZ_DEBUG': '1', 344 }) 345 sources, ldflags, flags, lib, target_flags = self.read_topsrcdir(reader) 346 self.assertIsInstance(flags, ComputedFlags) 347 self.assertEqual(flags.flags['RTL'], ['-MDd']) 348 349 def test_compile_flags_validation(self): 350 reader = self.reader('compile-flags-field-validation') 351 352 with six.assertRaisesRegex(self, BuildReaderError, 'Invalid value.'): 353 self.read_topsrcdir(reader) 354 355 reader = self.reader('compile-flags-type-validation') 356 with six.assertRaisesRegex(self, BuildReaderError, 357 'A list of strings must be provided'): 358 self.read_topsrcdir(reader) 359 360 def test_compile_flags_templates(self): 361 reader = self.reader('compile-flags-templates', extra_substs={ 362 'NSPR_CFLAGS': ['-I/nspr/path'], 363 'NSS_CFLAGS': ['-I/nss/path'], 364 'MOZ_JPEG_CFLAGS': ['-I/jpeg/path'], 365 'MOZ_PNG_CFLAGS': ['-I/png/path'], 366 'MOZ_ZLIB_CFLAGS': ['-I/zlib/path'], 367 'MOZ_PIXMAN_CFLAGS': ['-I/pixman/path'], 368 }) 369 sources, ldflags, lib, flags = self.read_topsrcdir(reader) 370 self.assertIsInstance(flags, ComputedFlags) 371 self.assertEqual(flags.flags['STL'], []) 372 self.assertEqual(flags.flags['VISIBILITY'], []) 373 self.assertEqual(flags.flags['OS_INCLUDES'], [ 374 '-I/nspr/path', 375 '-I/nss/path', 376 '-I/jpeg/path', 377 '-I/png/path', 378 '-I/zlib/path', 379 '-I/pixman/path', 380 ]) 381 382 def test_disable_stl_wrapping(self): 383 reader = self.reader('disable-stl-wrapping') 384 sources, ldflags, lib, flags = self.read_topsrcdir(reader) 385 self.assertIsInstance(flags, ComputedFlags) 386 self.assertEqual(flags.flags['STL'], []) 387 388 def test_visibility_flags(self): 389 reader = self.reader('visibility-flags') 390 sources, ldflags, lib, flags = self.read_topsrcdir(reader) 391 self.assertIsInstance(flags, ComputedFlags) 392 self.assertEqual(flags.flags['VISIBILITY'], []) 393 394 def test_defines_in_flags(self): 395 reader = self.reader('compile-defines') 396 defines, sources, ldflags, lib, flags = self.read_topsrcdir(reader) 397 self.assertIsInstance(flags, ComputedFlags) 398 self.assertEqual(flags.flags['LIBRARY_DEFINES'], 399 ['-DMOZ_LIBRARY_DEFINE=MOZ_TEST']) 400 self.assertEqual(flags.flags['DEFINES'], 401 ['-DMOZ_TEST_DEFINE']) 402 403 def test_resolved_flags_error(self): 404 reader = self.reader('resolved-flags-error') 405 with six.assertRaisesRegex(self, BuildReaderError, 406 "`DEFINES` may not be set in COMPILE_FLAGS from moz.build"): 407 self.read_topsrcdir(reader) 408 409 def test_includes_in_flags(self): 410 reader = self.reader('compile-includes') 411 defines, sources, ldflags, lib, flags = self.read_topsrcdir(reader) 412 self.assertIsInstance(flags, ComputedFlags) 413 self.assertEqual(flags.flags['BASE_INCLUDES'], 414 ['-I%s' % reader.config.topsrcdir, 415 '-I%s' % reader.config.topobjdir]) 416 self.assertEqual(flags.flags['EXTRA_INCLUDES'], 417 ['-I%s/dist/include' % reader.config.topobjdir]) 418 self.assertEqual(flags.flags['LOCAL_INCLUDES'], 419 ['-I%s/subdir' % reader.config.topsrcdir]) 420 421 def test_allow_compiler_warnings(self): 422 reader = self.reader('allow-compiler-warnings', extra_substs={ 423 'WARNINGS_AS_ERRORS': '-Werror', 424 }) 425 sources, ldflags, lib, flags = self.read_topsrcdir(reader) 426 self.assertEqual(flags.flags['WARNINGS_AS_ERRORS'], []) 427 428 def test_disable_compiler_warnings(self): 429 reader = self.reader('disable-compiler-warnings', extra_substs={ 430 'WARNINGS_CFLAGS': '-Wall', 431 }) 432 sources, ldflags, lib, flags = self.read_topsrcdir(reader) 433 self.assertEqual(flags.flags['WARNINGS_CFLAGS'], []) 434 435 def test_use_yasm(self): 436 # When yasm is not available, this should raise. 437 reader = self.reader('use-yasm') 438 with six.assertRaisesRegex(self, SandboxValidationError, 439 'yasm is not available'): 440 self.read_topsrcdir(reader) 441 442 # When yasm is available, this should work. 443 reader = self.reader('use-yasm', 444 extra_substs=dict( 445 YASM='yasm', 446 YASM_ASFLAGS='-foo', 447 )) 448 449 sources, passthru, ldflags, lib, flags, asflags = self.read_topsrcdir(reader) 450 451 self.assertIsInstance(passthru, VariablePassthru) 452 self.assertIsInstance(ldflags, ComputedFlags) 453 self.assertIsInstance(flags, ComputedFlags) 454 self.assertIsInstance(asflags, ComputedFlags) 455 456 self.assertEqual(asflags.flags['OS'], reader.config.substs['YASM_ASFLAGS']) 457 458 maxDiff = self.maxDiff 459 self.maxDiff = None 460 self.assertEqual(passthru.variables, 461 {'AS': 'yasm', 462 'AS_DASH_C_FLAG': '', 463 'ASOUTOPTION': '-o '}) 464 self.maxDiff = maxDiff 465 466 def test_generated_files(self): 467 reader = self.reader('generated-files') 468 objs = self.read_topsrcdir(reader) 469 470 self.assertEqual(len(objs), 3) 471 for o in objs: 472 self.assertIsInstance(o, GeneratedFile) 473 self.assertFalse(o.localized) 474 self.assertFalse(o.force) 475 476 expected = ['bar.c', 'foo.c', ('xpidllex.py', 'xpidlyacc.py'), ] 477 for o, f in zip(objs, expected): 478 expected_filename = f if isinstance(f, tuple) else (f,) 479 self.assertEqual(o.outputs, expected_filename) 480 self.assertEqual(o.script, None) 481 self.assertEqual(o.method, None) 482 self.assertEqual(o.inputs, []) 483 484 def test_generated_files_force(self): 485 reader = self.reader('generated-files-force') 486 objs = self.read_topsrcdir(reader) 487 488 self.assertEqual(len(objs), 3) 489 for o in objs: 490 self.assertIsInstance(o, GeneratedFile) 491 self.assertEqual(o.force, 'bar.c' in o.outputs) 492 493 def test_localized_generated_files(self): 494 reader = self.reader('localized-generated-files') 495 objs = self.read_topsrcdir(reader) 496 497 self.assertEqual(len(objs), 2) 498 for o in objs: 499 self.assertIsInstance(o, GeneratedFile) 500 self.assertTrue(o.localized) 501 502 expected = ['abc.ini', ('bar', 'baz'), ] 503 for o, f in zip(objs, expected): 504 expected_filename = f if isinstance(f, tuple) else (f,) 505 self.assertEqual(o.outputs, expected_filename) 506 self.assertEqual(o.script, None) 507 self.assertEqual(o.method, None) 508 self.assertEqual(o.inputs, []) 509 510 def test_localized_generated_files_force(self): 511 reader = self.reader('localized-generated-files-force') 512 objs = self.read_topsrcdir(reader) 513 514 self.assertEqual(len(objs), 2) 515 for o in objs: 516 self.assertIsInstance(o, GeneratedFile) 517 self.assertTrue(o.localized) 518 self.assertEqual(o.force, 'abc.ini' in o.outputs) 519 520 def test_localized_files_from_generated(self): 521 """Test that using LOCALIZED_GENERATED_FILES and then putting the output in 522 LOCALIZED_FILES as an objdir path works. 523 """ 524 reader = self.reader('localized-files-from-generated') 525 objs = self.read_topsrcdir(reader) 526 527 self.assertEqual(len(objs), 2) 528 self.assertIsInstance(objs[0], GeneratedFile) 529 self.assertIsInstance(objs[1], LocalizedFiles) 530 531 def test_localized_files_not_localized_generated(self): 532 """Test that using GENERATED_FILES and then putting the output in 533 LOCALIZED_FILES as an objdir path produces an error. 534 """ 535 reader = self.reader('localized-files-not-localized-generated') 536 with six.assertRaisesRegex( 537 self, 538 SandboxValidationError, 539 'Objdir file listed in LOCALIZED_FILES not in LOCALIZED_GENERATED_FILES:' 540 ): 541 self.read_topsrcdir(reader) 542 543 def test_localized_generated_files_final_target_files(self): 544 """Test that using LOCALIZED_GENERATED_FILES and then putting the output in 545 FINAL_TARGET_FILES as an objdir path produces an error. 546 """ 547 reader = self.reader('localized-generated-files-final-target-files') 548 with six.assertRaisesRegex( 549 self, 550 SandboxValidationError, 551 'Outputs of LOCALIZED_GENERATED_FILES cannot be used in FINAL_TARGET_FILES:' 552 ): 553 self.read_topsrcdir(reader) 554 555 def test_generated_files_method_names(self): 556 reader = self.reader('generated-files-method-names') 557 objs = self.read_topsrcdir(reader) 558 559 self.assertEqual(len(objs), 2) 560 for o in objs: 561 self.assertIsInstance(o, GeneratedFile) 562 563 expected = ['bar.c', 'foo.c'] 564 expected_method_names = ['make_bar', 'main'] 565 for o, expected_filename, expected_method in zip(objs, expected, expected_method_names): 566 self.assertEqual(o.outputs, (expected_filename,)) 567 self.assertEqual(o.method, expected_method) 568 self.assertEqual(o.inputs, []) 569 570 def test_generated_files_absolute_script(self): 571 reader = self.reader('generated-files-absolute-script') 572 objs = self.read_topsrcdir(reader) 573 574 self.assertEqual(len(objs), 1) 575 576 o = objs[0] 577 self.assertIsInstance(o, GeneratedFile) 578 self.assertEqual(o.outputs, ('bar.c',)) 579 self.assertRegexpMatches(o.script, 'script.py$') 580 self.assertEqual(o.method, 'make_bar') 581 self.assertEqual(o.inputs, []) 582 583 def test_generated_files_no_script(self): 584 reader = self.reader('generated-files-no-script') 585 with six.assertRaisesRegex(self, SandboxValidationError, 586 'Script for generating bar.c does not exist'): 587 self.read_topsrcdir(reader) 588 589 def test_generated_files_no_inputs(self): 590 reader = self.reader('generated-files-no-inputs') 591 with six.assertRaisesRegex(self, SandboxValidationError, 592 'Input for generating foo.c does not exist'): 593 self.read_topsrcdir(reader) 594 595 def test_generated_files_no_python_script(self): 596 reader = self.reader('generated-files-no-python-script') 597 with six.assertRaisesRegex(self, SandboxValidationError, 598 'Script for generating bar.c does not end in .py'): 599 self.read_topsrcdir(reader) 600 601 def test_exports(self): 602 reader = self.reader('exports') 603 objs = self.read_topsrcdir(reader) 604 605 self.assertEqual(len(objs), 1) 606 self.assertIsInstance(objs[0], Exports) 607 608 expected = [ 609 ('', ['foo.h', 'bar.h', 'baz.h']), 610 ('mozilla', ['mozilla1.h', 'mozilla2.h']), 611 ('mozilla/dom', ['dom1.h', 'dom2.h', 'dom3.h']), 612 ('mozilla/gfx', ['gfx.h']), 613 ('nspr/private', ['pprio.h', 'pprthred.h']), 614 ('vpx', ['mem.h', 'mem2.h']), 615 ] 616 for (expect_path, expect_headers), (actual_path, actual_headers) in \ 617 zip(expected, [(path, list(seq)) for path, seq in objs[0].files.walk()]): 618 self.assertEqual(expect_path, actual_path) 619 self.assertEqual(expect_headers, actual_headers) 620 621 def test_exports_missing(self): 622 ''' 623 Missing files in EXPORTS is an error. 624 ''' 625 reader = self.reader('exports-missing') 626 with six.assertRaisesRegex(self, SandboxValidationError, 627 'File listed in EXPORTS does not exist:'): 628 self.read_topsrcdir(reader) 629 630 def test_exports_missing_generated(self): 631 ''' 632 An objdir file in EXPORTS that is not in GENERATED_FILES is an error. 633 ''' 634 reader = self.reader('exports-missing-generated') 635 with six.assertRaisesRegex(self, SandboxValidationError, 636 'Objdir file listed in EXPORTS not in GENERATED_FILES:'): 637 self.read_topsrcdir(reader) 638 639 def test_exports_generated(self): 640 reader = self.reader('exports-generated') 641 objs = self.read_topsrcdir(reader) 642 643 self.assertEqual(len(objs), 2) 644 self.assertIsInstance(objs[0], GeneratedFile) 645 self.assertIsInstance(objs[1], Exports) 646 exports = [(path, list(seq)) for path, seq in objs[1].files.walk()] 647 self.assertEqual(exports, 648 [('', ['foo.h']), 649 ('mozilla', ['mozilla1.h', '!mozilla2.h'])]) 650 path, files = exports[1] 651 self.assertIsInstance(files[1], ObjDirPath) 652 653 def test_test_harness_files(self): 654 reader = self.reader('test-harness-files') 655 objs = self.read_topsrcdir(reader) 656 657 self.assertEqual(len(objs), 1) 658 self.assertIsInstance(objs[0], TestHarnessFiles) 659 660 expected = { 661 'mochitest': ['runtests.py', 'utils.py'], 662 'testing/mochitest': ['mochitest.py', 'mochitest.ini'], 663 } 664 665 for path, strings in objs[0].files.walk(): 666 self.assertTrue(path in expected) 667 basenames = sorted(mozpath.basename(s) for s in strings) 668 self.assertEqual(sorted(expected[path]), basenames) 669 670 def test_test_harness_files_root(self): 671 reader = self.reader('test-harness-files-root') 672 with six.assertRaisesRegex(self, SandboxValidationError, 673 'Cannot install files to the root of TEST_HARNESS_FILES'): 674 self.read_topsrcdir(reader) 675 676 def test_program(self): 677 reader = self.reader('program') 678 objs = self.read_topsrcdir(reader) 679 680 self.assertEqual(len(objs), 6) 681 self.assertIsInstance(objs[0], Sources) 682 self.assertIsInstance(objs[1], ComputedFlags) 683 self.assertIsInstance(objs[2], ComputedFlags) 684 self.assertIsInstance(objs[3], Program) 685 self.assertIsInstance(objs[4], SimpleProgram) 686 self.assertIsInstance(objs[5], SimpleProgram) 687 688 self.assertEqual(objs[3].program, 'test_program.prog') 689 self.assertEqual(objs[4].program, 'test_program1.prog') 690 self.assertEqual(objs[5].program, 'test_program2.prog') 691 692 self.assertEqual(objs[3].name, 'test_program.prog') 693 self.assertEqual(objs[4].name, 'test_program1.prog') 694 self.assertEqual(objs[5].name, 'test_program2.prog') 695 696 self.assertEqual(objs[4].objs, 697 [mozpath.join(reader.config.topobjdir, 698 'test_program1.%s' % 699 reader.config.substs['OBJ_SUFFIX'])]) 700 self.assertEqual(objs[5].objs, 701 [mozpath.join(reader.config.topobjdir, 702 'test_program2.%s' % 703 reader.config.substs['OBJ_SUFFIX'])]) 704 705 def test_program_paths(self): 706 """Various moz.build settings that change the destination of PROGRAM should be 707 accurately reflected in Program.output_path.""" 708 reader = self.reader('program-paths') 709 objs = self.read_topsrcdir(reader) 710 prog_paths = [o.output_path for o in objs if isinstance(o, Program)] 711 self.assertEqual(prog_paths, [ 712 '!/dist/bin/dist-bin.prog', 713 '!/dist/bin/foo/dist-subdir.prog', 714 '!/final/target/final-target.prog', 715 '!not-installed.prog', 716 ]) 717 718 def test_host_program_paths(self): 719 """The destination of a HOST_PROGRAM (almost always dist/host/bin) 720 should be accurately reflected in Program.output_path.""" 721 reader = self.reader('host-program-paths') 722 objs = self.read_topsrcdir(reader) 723 prog_paths = [o.output_path for o in objs if isinstance(o, HostProgram)] 724 self.assertEqual(prog_paths, [ 725 '!/dist/host/bin/final-target.hostprog', 726 '!/dist/host/bin/dist-host-bin.hostprog', 727 '!not-installed.hostprog', 728 ]) 729 730 def test_test_manifest_missing_manifest(self): 731 """A missing manifest file should result in an error.""" 732 reader = self.reader('test-manifest-missing-manifest') 733 734 with six.assertRaisesRegex(self, BuildReaderError, 'Missing files'): 735 self.read_topsrcdir(reader) 736 737 def test_empty_test_manifest_rejected(self): 738 """A test manifest without any entries is rejected.""" 739 reader = self.reader('test-manifest-empty') 740 741 with six.assertRaisesRegex(self, SandboxValidationError, 'Empty test manifest'): 742 self.read_topsrcdir(reader) 743 744 def test_test_manifest_just_support_files(self): 745 """A test manifest with no tests but support-files is not supported.""" 746 reader = self.reader('test-manifest-just-support') 747 748 with six.assertRaisesRegex(self, SandboxValidationError, 'Empty test manifest'): 749 self.read_topsrcdir(reader) 750 751 def test_test_manifest_dupe_support_files(self): 752 """A test manifest with dupe support-files in a single test is not 753 supported. 754 """ 755 reader = self.reader('test-manifest-dupes') 756 757 with six.assertRaisesRegex( 758 self, 759 SandboxValidationError, 760 'bar.js appears multiple times ' 761 'in a test manifest under a support-files field, please omit the duplicate entry.' 762 ): 763 self.read_topsrcdir(reader) 764 765 def test_test_manifest_absolute_support_files(self): 766 """Support files starting with '/' are placed relative to the install root""" 767 reader = self.reader('test-manifest-absolute-support') 768 769 objs = self.read_topsrcdir(reader) 770 self.assertEqual(len(objs), 1) 771 o = objs[0] 772 self.assertEqual(len(o.installs), 3) 773 expected = [ 774 mozpath.normpath(mozpath.join(o.install_prefix, "../.well-known/foo.txt")), 775 mozpath.join(o.install_prefix, "absolute-support.ini"), 776 mozpath.join(o.install_prefix, "test_file.js"), 777 ] 778 paths = sorted([v[0] for v in o.installs.values()]) 779 self.assertEqual(paths, expected) 780 781 @unittest.skip('Bug 1304316 - Items in the second set but not the first') 782 def test_test_manifest_shared_support_files(self): 783 """Support files starting with '!' are given separate treatment, so their 784 installation can be resolved when running tests. 785 """ 786 reader = self.reader('test-manifest-shared-support') 787 supported, child = self.read_topsrcdir(reader) 788 789 expected_deferred_installs = { 790 '!/child/test_sub.js', 791 '!/child/another-file.sjs', 792 '!/child/data/**', 793 } 794 795 self.assertEqual(len(supported.installs), 3) 796 self.assertEqual(set(supported.deferred_installs), 797 expected_deferred_installs) 798 self.assertEqual(len(child.installs), 3) 799 self.assertEqual(len(child.pattern_installs), 1) 800 801 def test_test_manifest_deffered_install_missing(self): 802 """A non-existent shared support file reference produces an error.""" 803 reader = self.reader('test-manifest-shared-missing') 804 805 with six.assertRaisesRegex(self, SandboxValidationError, 806 'entry in support-files not present in the srcdir'): 807 self.read_topsrcdir(reader) 808 809 def test_test_manifest_install_includes(self): 810 """Ensure that any [include:foo.ini] are copied to the objdir.""" 811 reader = self.reader('test-manifest-install-includes') 812 813 objs = self.read_topsrcdir(reader) 814 self.assertEqual(len(objs), 1) 815 o = objs[0] 816 self.assertEqual(len(o.installs), 3) 817 self.assertEqual(o.manifest_relpath, "mochitest.ini") 818 self.assertEqual(o.manifest_obj_relpath, "mochitest.ini") 819 expected = [ 820 mozpath.normpath(mozpath.join(o.install_prefix, "common.ini")), 821 mozpath.normpath(mozpath.join(o.install_prefix, "mochitest.ini")), 822 mozpath.normpath(mozpath.join(o.install_prefix, "test_foo.html")), 823 ] 824 paths = sorted([v[0] for v in o.installs.values()]) 825 self.assertEqual(paths, expected) 826 827 def test_test_manifest_includes(self): 828 """Ensure that manifest objects from the emitter list a correct manifest. 829 """ 830 reader = self.reader('test-manifest-emitted-includes') 831 [obj] = self.read_topsrcdir(reader) 832 833 # Expected manifest leafs for our tests. 834 expected_manifests = { 835 'reftest1.html': 'reftest.list', 836 'reftest1-ref.html': 'reftest.list', 837 'reftest2.html': 'included-reftest.list', 838 'reftest2-ref.html': 'included-reftest.list', 839 } 840 841 for t in obj.tests: 842 self.assertTrue(t['manifest'].endswith(expected_manifests[t['name']])) 843 844 def test_test_manifest_keys_extracted(self): 845 """Ensure all metadata from test manifests is extracted.""" 846 reader = self.reader('test-manifest-keys-extracted') 847 848 objs = [o for o in self.read_topsrcdir(reader) 849 if isinstance(o, TestManifest)] 850 851 self.assertEqual(len(objs), 8) 852 853 metadata = { 854 'a11y.ini': { 855 'flavor': 'a11y', 856 'installs': { 857 'a11y.ini': False, 858 'test_a11y.js': True, 859 }, 860 'pattern-installs': 1, 861 }, 862 'browser.ini': { 863 'flavor': 'browser-chrome', 864 'installs': { 865 'browser.ini': False, 866 'test_browser.js': True, 867 'support1': False, 868 'support2': False, 869 }, 870 }, 871 'mochitest.ini': { 872 'flavor': 'mochitest', 873 'installs': { 874 'mochitest.ini': False, 875 'test_mochitest.js': True, 876 }, 877 'external': { 878 'external1', 879 'external2', 880 }, 881 }, 882 'chrome.ini': { 883 'flavor': 'chrome', 884 'installs': { 885 'chrome.ini': False, 886 'test_chrome.js': True, 887 }, 888 }, 889 'xpcshell.ini': { 890 'flavor': 'xpcshell', 891 'dupe': True, 892 'installs': { 893 'xpcshell.ini': False, 894 'test_xpcshell.js': True, 895 'head1': False, 896 'head2': False, 897 }, 898 }, 899 'reftest.list': { 900 'flavor': 'reftest', 901 'installs': {}, 902 }, 903 'crashtest.list': { 904 'flavor': 'crashtest', 905 'installs': {}, 906 }, 907 'python.ini': { 908 'flavor': 'python', 909 'installs': { 910 'python.ini': False, 911 }, 912 } 913 } 914 915 for o in objs: 916 m = metadata[mozpath.basename(o.manifest_relpath)] 917 918 self.assertTrue(o.path.startswith(o.directory)) 919 self.assertEqual(o.flavor, m['flavor']) 920 self.assertEqual(o.dupe_manifest, m.get('dupe', False)) 921 922 external_normalized = set(mozpath.basename(p) for p in 923 o.external_installs) 924 self.assertEqual(external_normalized, m.get('external', set())) 925 926 self.assertEqual(len(o.installs), len(m['installs'])) 927 for path in o.installs.keys(): 928 self.assertTrue(path.startswith(o.directory)) 929 relpath = path[len(o.directory)+1:] 930 931 self.assertIn(relpath, m['installs']) 932 self.assertEqual(o.installs[path][1], m['installs'][relpath]) 933 934 if 'pattern-installs' in m: 935 self.assertEqual(len(o.pattern_installs), m['pattern-installs']) 936 937 def test_test_manifest_unmatched_generated(self): 938 reader = self.reader('test-manifest-unmatched-generated') 939 940 with six.assertRaisesRegex(self, SandboxValidationError, 941 'entry in generated-files not present elsewhere'): 942 self.read_topsrcdir(reader), 943 944 def test_test_manifest_parent_support_files_dir(self): 945 """support-files referencing a file in a parent directory works.""" 946 reader = self.reader('test-manifest-parent-support-files-dir') 947 948 objs = [o for o in self.read_topsrcdir(reader) 949 if isinstance(o, TestManifest)] 950 951 self.assertEqual(len(objs), 1) 952 953 o = objs[0] 954 955 expected = mozpath.join(o.srcdir, 'support-file.txt') 956 self.assertIn(expected, o.installs) 957 self.assertEqual(o.installs[expected], 958 ('testing/mochitest/tests/child/support-file.txt', False)) 959 960 def test_test_manifest_missing_test_error(self): 961 """Missing test files should result in error.""" 962 reader = self.reader('test-manifest-missing-test-file') 963 964 with six.assertRaisesRegex(self, SandboxValidationError, 965 'lists test that does not exist: test_missing.html'): 966 self.read_topsrcdir(reader) 967 968 def test_test_manifest_missing_test_error_unfiltered(self): 969 """Missing test files should result in error, even when the test list is not filtered.""" 970 reader = self.reader('test-manifest-missing-test-file-unfiltered') 971 972 with six.assertRaisesRegex(self, SandboxValidationError, 973 'lists test that does not exist: missing.js'): 974 self.read_topsrcdir(reader) 975 976 def test_ipdl_sources(self): 977 reader = self.reader('ipdl_sources', 978 extra_substs={'IPDL_ROOT': mozpath.abspath('/path/to/topobjdir')}) 979 objs = self.read_topsrcdir(reader) 980 ipdl_collection = objs[0] 981 self.assertIsInstance(ipdl_collection, IPDLCollection) 982 983 ipdls = set(mozpath.relpath(p, ipdl_collection.topsrcdir) 984 for p in ipdl_collection.all_regular_sources()) 985 expected = set([ 986 'bar/bar.ipdl', 987 'bar/bar2.ipdlh', 988 'foo/foo.ipdl', 989 'foo/foo2.ipdlh', 990 ]) 991 992 self.assertEqual(ipdls, expected) 993 994 pp_ipdls = set(mozpath.relpath(p, ipdl_collection.topsrcdir) 995 for p in ipdl_collection.all_preprocessed_sources()) 996 expected = set([ 997 'bar/bar1.ipdl', 998 'foo/foo1.ipdl', 999 ]) 1000 self.assertEqual(pp_ipdls, expected) 1001 1002 generated_sources = set(ipdl_collection.all_generated_sources()) 1003 expected = set([ 1004 'bar.cpp', 1005 'barChild.cpp', 1006 'barParent.cpp', 1007 'bar1.cpp', 1008 'bar1Child.cpp', 1009 'bar1Parent.cpp', 1010 'bar2.cpp', 1011 'foo.cpp', 1012 'fooChild.cpp', 1013 'fooParent.cpp', 1014 'foo1.cpp', 1015 'foo1Child.cpp', 1016 'foo1Parent.cpp', 1017 'foo2.cpp' 1018 ]) 1019 self.assertEqual(generated_sources, expected) 1020 1021 def test_local_includes(self): 1022 """Test that LOCAL_INCLUDES is emitted correctly.""" 1023 reader = self.reader('local_includes') 1024 objs = self.read_topsrcdir(reader) 1025 1026 local_includes = [o.path for o in objs if isinstance(o, LocalInclude)] 1027 expected = [ 1028 '/bar/baz', 1029 'foo', 1030 ] 1031 1032 self.assertEqual(local_includes, expected) 1033 1034 local_includes = [o.path.full_path 1035 for o in objs if isinstance(o, LocalInclude)] 1036 expected = [ 1037 mozpath.join(reader.config.topsrcdir, 'bar/baz'), 1038 mozpath.join(reader.config.topsrcdir, 'foo'), 1039 ] 1040 1041 self.assertEqual(local_includes, expected) 1042 1043 def test_local_includes_invalid(self): 1044 """Test that invalid LOCAL_INCLUDES are properly detected.""" 1045 reader = self.reader('local_includes-invalid/srcdir') 1046 1047 with six.assertRaisesRegex( 1048 self, 1049 SandboxValidationError, 1050 'Path specified in LOCAL_INCLUDES.*resolves to the ' 1051 'topsrcdir or topobjdir'): 1052 self.read_topsrcdir(reader) 1053 1054 reader = self.reader('local_includes-invalid/objdir') 1055 1056 with six.assertRaisesRegex( 1057 self, 1058 SandboxValidationError, 1059 'Path specified in LOCAL_INCLUDES.*resolves to the ' 1060 'topsrcdir or topobjdir'): 1061 self.read_topsrcdir(reader) 1062 1063 def test_local_includes_file(self): 1064 """Test that a filename can't be used in LOCAL_INCLUDES.""" 1065 reader = self.reader('local_includes-filename') 1066 1067 with six.assertRaisesRegex( 1068 self, 1069 SandboxValidationError, 1070 'Path specified in LOCAL_INCLUDES is a filename'): 1071 self.read_topsrcdir(reader) 1072 1073 def test_generated_includes(self): 1074 """Test that GENERATED_INCLUDES is emitted correctly.""" 1075 reader = self.reader('generated_includes') 1076 objs = self.read_topsrcdir(reader) 1077 1078 generated_includes = [o.path for o in objs if isinstance(o, LocalInclude)] 1079 expected = [ 1080 '!/bar/baz', 1081 '!foo', 1082 ] 1083 1084 self.assertEqual(generated_includes, expected) 1085 1086 generated_includes = [o.path.full_path 1087 for o in objs if isinstance(o, LocalInclude)] 1088 expected = [ 1089 mozpath.join(reader.config.topobjdir, 'bar/baz'), 1090 mozpath.join(reader.config.topobjdir, 'foo'), 1091 ] 1092 1093 self.assertEqual(generated_includes, expected) 1094 1095 def test_defines(self): 1096 reader = self.reader('defines') 1097 objs = self.read_topsrcdir(reader) 1098 1099 defines = {} 1100 for o in objs: 1101 if isinstance(o, Defines): 1102 defines = o.defines 1103 1104 expected = { 1105 'BAR': 7, 1106 'BAZ': '"abcd"', 1107 'FOO': True, 1108 'VALUE': 'xyz', 1109 'QUX': False, 1110 } 1111 1112 self.assertEqual(defines, expected) 1113 1114 def test_jar_manifests(self): 1115 reader = self.reader('jar-manifests') 1116 objs = self.read_topsrcdir(reader) 1117 1118 self.assertEqual(len(objs), 1) 1119 for obj in objs: 1120 self.assertIsInstance(obj, JARManifest) 1121 self.assertIsInstance(obj.path, Path) 1122 1123 def test_jar_manifests_multiple_files(self): 1124 with six.assertRaisesRegex(self, SandboxValidationError, 'limited to one value'): 1125 reader = self.reader('jar-manifests-multiple-files') 1126 self.read_topsrcdir(reader) 1127 1128 def test_xpidl_module_no_sources(self): 1129 """XPIDL_MODULE without XPIDL_SOURCES should be rejected.""" 1130 with six.assertRaisesRegex(self, SandboxValidationError, 'XPIDL_MODULE ' 1131 'cannot be defined'): 1132 reader = self.reader('xpidl-module-no-sources') 1133 self.read_topsrcdir(reader) 1134 1135 def test_xpidl_module_missing_sources(self): 1136 """Missing XPIDL_SOURCES should be rejected.""" 1137 with six.assertRaisesRegex(self, SandboxValidationError, 'File .* ' 1138 'from XPIDL_SOURCES does not exist'): 1139 reader = self.reader('missing-xpidl') 1140 self.read_topsrcdir(reader) 1141 1142 def test_missing_local_includes(self): 1143 """LOCAL_INCLUDES containing non-existent directories should be rejected.""" 1144 with six.assertRaisesRegex(self, SandboxValidationError, 'Path specified in ' 1145 'LOCAL_INCLUDES does not exist'): 1146 reader = self.reader('missing-local-includes') 1147 self.read_topsrcdir(reader) 1148 1149 def test_library_defines(self): 1150 """Test that LIBRARY_DEFINES is propagated properly.""" 1151 reader = self.reader('library-defines') 1152 objs = self.read_topsrcdir(reader) 1153 1154 libraries = [o for o in objs if isinstance(o, StaticLibrary)] 1155 library_flags = [o for o in objs if isinstance(o, ComputedFlags) 1156 and 'LIBRARY_DEFINES' in o.flags] 1157 expected = { 1158 'liba': '-DIN_LIBA', 1159 'libb': '-DIN_LIBB -DIN_LIBA', 1160 'libc': '-DIN_LIBA -DIN_LIBB', 1161 'libd': '' 1162 } 1163 defines = {} 1164 for lib in libraries: 1165 defines[lib.basename] = ' '.join(lib.lib_defines.get_defines()) 1166 self.assertEqual(expected, defines) 1167 defines_in_flags = {} 1168 for flags in library_flags: 1169 defines_in_flags[flags.relobjdir] = ' '.join(flags.flags['LIBRARY_DEFINES'] or []) 1170 self.assertEqual(expected, defines_in_flags) 1171 1172 def test_sources(self): 1173 """Test that SOURCES works properly.""" 1174 reader = self.reader('sources') 1175 objs = self.read_topsrcdir(reader) 1176 1177 as_flags = objs.pop() 1178 self.assertIsInstance(as_flags, ComputedFlags) 1179 computed_flags = objs.pop() 1180 self.assertIsInstance(computed_flags, ComputedFlags) 1181 # The third to last object is a Linkable. 1182 linkable = objs.pop() 1183 self.assertTrue(linkable.cxx_link) 1184 ld_flags = objs.pop() 1185 self.assertIsInstance(ld_flags, ComputedFlags) 1186 self.assertEqual(len(objs), 6) 1187 for o in objs: 1188 self.assertIsInstance(o, Sources) 1189 1190 suffix_map = {obj.canonical_suffix: obj for obj in objs} 1191 self.assertEqual(len(suffix_map), 6) 1192 1193 expected = { 1194 '.cpp': ['a.cpp', 'b.cc', 'c.cxx'], 1195 '.c': ['d.c'], 1196 '.m': ['e.m'], 1197 '.mm': ['f.mm'], 1198 '.S': ['g.S'], 1199 '.s': ['h.s', 'i.asm'], 1200 } 1201 for suffix, files in expected.items(): 1202 sources = suffix_map[suffix] 1203 self.assertEqual( 1204 sources.files, 1205 [mozpath.join(reader.config.topsrcdir, f) for f in files]) 1206 1207 for f in files: 1208 self.assertIn(mozpath.join(reader.config.topobjdir, 1209 '%s.%s' % (mozpath.splitext(f)[0], 1210 reader.config.substs['OBJ_SUFFIX'])), 1211 linkable.objs) 1212 1213 def test_sources_just_c(self): 1214 """Test that a linkable with no C++ sources doesn't have cxx_link set.""" 1215 reader = self.reader('sources-just-c') 1216 objs = self.read_topsrcdir(reader) 1217 1218 as_flags = objs.pop() 1219 self.assertIsInstance(as_flags, ComputedFlags) 1220 flags = objs.pop() 1221 self.assertIsInstance(flags, ComputedFlags) 1222 # The third to last object is a Linkable. 1223 linkable = objs.pop() 1224 self.assertFalse(linkable.cxx_link) 1225 1226 def test_linkables_cxx_link(self): 1227 """Test that linkables transitively set cxx_link properly.""" 1228 reader = self.reader('test-linkables-cxx-link') 1229 got_results = 0 1230 for obj in self.read_topsrcdir(reader): 1231 if isinstance(obj, SharedLibrary): 1232 if obj.basename == 'cxx_shared': 1233 self.assertEquals(obj.name, '%scxx_shared%s' % 1234 (reader.config.dll_prefix, 1235 reader.config.dll_suffix)) 1236 self.assertTrue(obj.cxx_link) 1237 got_results += 1 1238 elif obj.basename == 'just_c_shared': 1239 self.assertEquals(obj.name, '%sjust_c_shared%s' % 1240 (reader.config.dll_prefix, 1241 reader.config.dll_suffix)) 1242 self.assertFalse(obj.cxx_link) 1243 got_results += 1 1244 self.assertEqual(got_results, 2) 1245 1246 def test_generated_sources(self): 1247 """Test that GENERATED_SOURCES works properly.""" 1248 reader = self.reader('generated-sources') 1249 objs = self.read_topsrcdir(reader) 1250 1251 as_flags = objs.pop() 1252 self.assertIsInstance(as_flags, ComputedFlags) 1253 flags = objs.pop() 1254 self.assertIsInstance(flags, ComputedFlags) 1255 # The third to last object is a Linkable. 1256 linkable = objs.pop() 1257 self.assertTrue(linkable.cxx_link) 1258 flags = objs.pop() 1259 self.assertIsInstance(flags, ComputedFlags) 1260 self.assertEqual(len(objs), 6) 1261 1262 generated_sources = [o for o in objs if isinstance(o, GeneratedSources)] 1263 self.assertEqual(len(generated_sources), 6) 1264 1265 suffix_map = {obj.canonical_suffix: obj for obj in generated_sources} 1266 self.assertEqual(len(suffix_map), 6) 1267 1268 expected = { 1269 '.cpp': ['a.cpp', 'b.cc', 'c.cxx'], 1270 '.c': ['d.c'], 1271 '.m': ['e.m'], 1272 '.mm': ['f.mm'], 1273 '.S': ['g.S'], 1274 '.s': ['h.s', 'i.asm'], 1275 } 1276 for suffix, files in expected.items(): 1277 sources = suffix_map[suffix] 1278 self.assertEqual( 1279 sources.files, 1280 [mozpath.join(reader.config.topobjdir, f) for f in files]) 1281 1282 for f in files: 1283 self.assertIn(mozpath.join(reader.config.topobjdir, 1284 '%s.%s' % (mozpath.splitext(f)[0], 1285 reader.config.substs['OBJ_SUFFIX'])), 1286 linkable.objs) 1287 1288 def test_host_sources(self): 1289 """Test that HOST_SOURCES works properly.""" 1290 reader = self.reader('host-sources') 1291 objs = self.read_topsrcdir(reader) 1292 1293 # This objdir will generate target flags. 1294 flags = objs.pop() 1295 self.assertIsInstance(flags, ComputedFlags) 1296 # The second to last object is a Linkable 1297 linkable = objs.pop() 1298 self.assertTrue(linkable.cxx_link) 1299 # This objdir will also generate host flags. 1300 host_flags = objs.pop() 1301 self.assertIsInstance(host_flags, ComputedFlags) 1302 # ...and ldflags. 1303 ldflags = objs.pop() 1304 self.assertIsInstance(ldflags, ComputedFlags) 1305 self.assertEqual(len(objs), 3) 1306 for o in objs: 1307 self.assertIsInstance(o, HostSources) 1308 1309 suffix_map = {obj.canonical_suffix: obj for obj in objs} 1310 self.assertEqual(len(suffix_map), 3) 1311 1312 expected = { 1313 '.cpp': ['a.cpp', 'b.cc', 'c.cxx'], 1314 '.c': ['d.c'], 1315 '.mm': ['e.mm', 'f.mm'], 1316 } 1317 for suffix, files in expected.items(): 1318 sources = suffix_map[suffix] 1319 self.assertEqual( 1320 sources.files, 1321 [mozpath.join(reader.config.topsrcdir, f) for f in files]) 1322 1323 for f in files: 1324 self.assertIn(mozpath.join(reader.config.topobjdir, 1325 'host_%s.%s' % (mozpath.splitext(f)[0], 1326 reader.config.substs['OBJ_SUFFIX'])), 1327 linkable.objs) 1328 1329 def test_wasm_sources(self): 1330 """Test that WASM_SOURCES works properly.""" 1331 reader = self.reader('wasm-sources', extra_substs={'OS_TARGET': 'Linux'}) 1332 objs = list(self.read_topsrcdir(reader)) 1333 1334 # The second to last object is a linkable. 1335 linkable = objs[-2] 1336 # Other than that, we only care about the WasmSources objects. 1337 objs = objs[:2] 1338 for o in objs: 1339 self.assertIsInstance(o, WasmSources) 1340 1341 suffix_map = {obj.canonical_suffix: obj for obj in objs} 1342 self.assertEqual(len(suffix_map), 2) 1343 1344 expected = { 1345 '.cpp': ['a.cpp', 'b.cc', 'c.cxx'], 1346 '.c': ['d.c'], 1347 } 1348 for suffix, files in expected.items(): 1349 sources = suffix_map[suffix] 1350 self.assertEqual( 1351 sources.files, 1352 [mozpath.join(reader.config.topsrcdir, f) for f in files] + 1353 ([mozpath.join( 1354 reader.config.topsrcdir, 1355 'third_party/rust/rlbox_lucet_sandbox/c_src/lucet_sandbox_wrapper.c')] 1356 if suffix == '.c' else [])) 1357 for f in files: 1358 self.assertIn(mozpath.join( 1359 reader.config.topobjdir, 1360 '%s.%s' % (mozpath.splitext(f)[0], 1361 reader.config.substs['WASM_OBJ_SUFFIX'])), 1362 linkable.objs) 1363 1364 def test_unified_sources(self): 1365 """Test that UNIFIED_SOURCES works properly.""" 1366 reader = self.reader('unified-sources') 1367 objs = self.read_topsrcdir(reader) 1368 1369 # The last object is a ComputedFlags, the second to last a Linkable, 1370 # followed by ldflags, ignore them. 1371 linkable = objs[-2] 1372 objs = objs[:-3] 1373 self.assertEqual(len(objs), 3) 1374 for o in objs: 1375 self.assertIsInstance(o, UnifiedSources) 1376 1377 suffix_map = {obj.canonical_suffix: obj for obj in objs} 1378 self.assertEqual(len(suffix_map), 3) 1379 1380 expected = { 1381 '.cpp': ['bar.cxx', 'foo.cpp', 'quux.cc'], 1382 '.mm': ['objc1.mm', 'objc2.mm'], 1383 '.c': ['c1.c', 'c2.c'], 1384 } 1385 for suffix, files in expected.items(): 1386 sources = suffix_map[suffix] 1387 self.assertEqual( 1388 sources.files, 1389 [mozpath.join(reader.config.topsrcdir, f) for f in files]) 1390 self.assertTrue(sources.have_unified_mapping) 1391 1392 for f in dict(sources.unified_source_mapping).keys(): 1393 self.assertIn(mozpath.join(reader.config.topobjdir, 1394 '%s.%s' % (mozpath.splitext(f)[0], 1395 reader.config.substs['OBJ_SUFFIX'])), 1396 linkable.objs) 1397 1398 def test_unified_sources_non_unified(self): 1399 """Test that UNIFIED_SOURCES with FILES_PER_UNIFIED_FILE=1 works properly.""" 1400 reader = self.reader('unified-sources-non-unified') 1401 objs = self.read_topsrcdir(reader) 1402 1403 # The last object is a Linkable, the second to last ComputedFlags, 1404 # followed by ldflags, ignore them. 1405 objs = objs[:-3] 1406 self.assertEqual(len(objs), 3) 1407 for o in objs: 1408 self.assertIsInstance(o, UnifiedSources) 1409 1410 suffix_map = {obj.canonical_suffix: obj for obj in objs} 1411 self.assertEqual(len(suffix_map), 3) 1412 1413 expected = { 1414 '.cpp': ['bar.cxx', 'foo.cpp', 'quux.cc'], 1415 '.mm': ['objc1.mm', 'objc2.mm'], 1416 '.c': ['c1.c', 'c2.c'], 1417 } 1418 for suffix, files in expected.items(): 1419 sources = suffix_map[suffix] 1420 self.assertEqual( 1421 sources.files, 1422 [mozpath.join(reader.config.topsrcdir, f) for f in files]) 1423 self.assertFalse(sources.have_unified_mapping) 1424 1425 def test_final_target_pp_files(self): 1426 """Test that FINAL_TARGET_PP_FILES works properly.""" 1427 reader = self.reader('dist-files') 1428 objs = self.read_topsrcdir(reader) 1429 1430 self.assertEqual(len(objs), 1) 1431 self.assertIsInstance(objs[0], FinalTargetPreprocessedFiles) 1432 1433 # Ideally we'd test hierarchies, but that would just be testing 1434 # the HierarchicalStringList class, which we test separately. 1435 for path, files in objs[0].files.walk(): 1436 self.assertEqual(path, '') 1437 self.assertEqual(len(files), 2) 1438 1439 expected = {'install.rdf', 'main.js'} 1440 for f in files: 1441 self.assertTrue(six.text_type(f) in expected) 1442 1443 def test_missing_final_target_pp_files(self): 1444 """Test that FINAL_TARGET_PP_FILES with missing files throws errors.""" 1445 with six.assertRaisesRegex(self, SandboxValidationError, 'File listed in ' 1446 'FINAL_TARGET_PP_FILES does not exist'): 1447 reader = self.reader('dist-files-missing') 1448 self.read_topsrcdir(reader) 1449 1450 def test_final_target_pp_files_non_srcdir(self): 1451 '''Test that non-srcdir paths in FINAL_TARGET_PP_FILES throws errors.''' 1452 reader = self.reader('final-target-pp-files-non-srcdir') 1453 with six.assertRaisesRegex( 1454 self, 1455 SandboxValidationError, 1456 'Only source directory paths allowed in FINAL_TARGET_PP_FILES:' 1457 ): 1458 self.read_topsrcdir(reader) 1459 1460 def test_localized_files(self): 1461 """Test that LOCALIZED_FILES works properly.""" 1462 reader = self.reader('localized-files') 1463 objs = self.read_topsrcdir(reader) 1464 1465 self.assertEqual(len(objs), 1) 1466 self.assertIsInstance(objs[0], LocalizedFiles) 1467 1468 for path, files in objs[0].files.walk(): 1469 self.assertEqual(path, 'foo') 1470 self.assertEqual(len(files), 3) 1471 1472 expected = {'en-US/bar.ini', 'en-US/code/*.js', 'en-US/foo.js'} 1473 for f in files: 1474 self.assertTrue(six.text_type(f) in expected) 1475 1476 def test_localized_files_no_en_us(self): 1477 """Test that LOCALIZED_FILES errors if a path does not start with 1478 `en-US/` or contain `locales/en-US/`.""" 1479 reader = self.reader('localized-files-no-en-us') 1480 with six.assertRaisesRegex( 1481 self, 1482 SandboxValidationError, 1483 'LOCALIZED_FILES paths must start with `en-US/` or contain `locales/en-US/`: ' 1484 'foo.js' 1485 ): 1486 self.read_topsrcdir(reader) 1487 1488 def test_localized_pp_files(self): 1489 """Test that LOCALIZED_PP_FILES works properly.""" 1490 reader = self.reader('localized-pp-files') 1491 objs = self.read_topsrcdir(reader) 1492 1493 self.assertEqual(len(objs), 1) 1494 self.assertIsInstance(objs[0], LocalizedPreprocessedFiles) 1495 1496 for path, files in objs[0].files.walk(): 1497 self.assertEqual(path, 'foo') 1498 self.assertEqual(len(files), 2) 1499 1500 expected = {'en-US/bar.ini', 'en-US/foo.js'} 1501 for f in files: 1502 self.assertTrue(six.text_type(f) in expected) 1503 1504 def test_rust_library_no_cargo_toml(self): 1505 '''Test that defining a RustLibrary without a Cargo.toml fails.''' 1506 reader = self.reader('rust-library-no-cargo-toml') 1507 with six.assertRaisesRegex(self, SandboxValidationError, 1508 'No Cargo.toml file found'): 1509 self.read_topsrcdir(reader) 1510 1511 def test_rust_library_name_mismatch(self): 1512 '''Test that defining a RustLibrary that doesn't match Cargo.toml fails.''' 1513 reader = self.reader('rust-library-name-mismatch') 1514 with six.assertRaisesRegex(self, SandboxValidationError, 1515 'library.*does not match Cargo.toml-defined package'): 1516 self.read_topsrcdir(reader) 1517 1518 def test_rust_library_no_lib_section(self): 1519 '''Test that a RustLibrary Cargo.toml with no [lib] section fails.''' 1520 reader = self.reader('rust-library-no-lib-section') 1521 with six.assertRaisesRegex(self, SandboxValidationError, 1522 'Cargo.toml for.* has no \\[lib\\] section'): 1523 self.read_topsrcdir(reader) 1524 1525 def test_rust_library_invalid_crate_type(self): 1526 '''Test that a RustLibrary Cargo.toml has a permitted crate-type.''' 1527 reader = self.reader('rust-library-invalid-crate-type') 1528 with six.assertRaisesRegex(self, SandboxValidationError, 1529 'crate-type.* is not permitted'): 1530 self.read_topsrcdir(reader) 1531 1532 def test_rust_library_dash_folding(self): 1533 '''Test that on-disk names of RustLibrary objects convert dashes to underscores.''' 1534 reader = self.reader('rust-library-dash-folding', 1535 extra_substs=dict(RUST_TARGET='i686-pc-windows-msvc')) 1536 objs = self.read_topsrcdir(reader) 1537 1538 self.assertEqual(len(objs), 3) 1539 ldflags, lib, cflags = objs 1540 self.assertIsInstance(ldflags, ComputedFlags) 1541 self.assertIsInstance(cflags, ComputedFlags) 1542 self.assertIsInstance(lib, RustLibrary) 1543 self.assertRegexpMatches(lib.lib_name, "random_crate") 1544 self.assertRegexpMatches(lib.import_name, "random_crate") 1545 self.assertRegexpMatches(lib.basename, "random-crate") 1546 1547 def test_multiple_rust_libraries(self): 1548 '''Test that linking multiple Rust libraries throws an error''' 1549 reader = self.reader('multiple-rust-libraries', 1550 extra_substs=dict(RUST_TARGET='i686-pc-windows-msvc')) 1551 with six.assertRaisesRegex( 1552 self, 1553 SandboxValidationError, 1554 'Cannot link the following Rust libraries'): 1555 self.read_topsrcdir(reader) 1556 1557 def test_rust_library_features(self): 1558 '''Test that RustLibrary features are correctly emitted.''' 1559 reader = self.reader('rust-library-features', 1560 extra_substs=dict(RUST_TARGET='i686-pc-windows-msvc')) 1561 objs = self.read_topsrcdir(reader) 1562 1563 self.assertEqual(len(objs), 3) 1564 ldflags, lib, cflags = objs 1565 self.assertIsInstance(ldflags, ComputedFlags) 1566 self.assertIsInstance(cflags, ComputedFlags) 1567 self.assertIsInstance(lib, RustLibrary) 1568 self.assertEqual(lib.features, ['musthave', 'cantlivewithout']) 1569 1570 def test_rust_library_duplicate_features(self): 1571 '''Test that duplicate RustLibrary features are rejected.''' 1572 reader = self.reader('rust-library-duplicate-features') 1573 with six.assertRaisesRegex(self, SandboxValidationError, 1574 'features for .* should not contain duplicates'): 1575 self.read_topsrcdir(reader) 1576 1577 def test_rust_program_no_cargo_toml(self): 1578 '''Test that specifying RUST_PROGRAMS without a Cargo.toml fails.''' 1579 reader = self.reader('rust-program-no-cargo-toml') 1580 with six.assertRaisesRegex(self, SandboxValidationError, 1581 'No Cargo.toml file found'): 1582 self.read_topsrcdir(reader) 1583 1584 def test_host_rust_program_no_cargo_toml(self): 1585 '''Test that specifying HOST_RUST_PROGRAMS without a Cargo.toml fails.''' 1586 reader = self.reader('host-rust-program-no-cargo-toml') 1587 with six.assertRaisesRegex(self, SandboxValidationError, 1588 'No Cargo.toml file found'): 1589 self.read_topsrcdir(reader) 1590 1591 def test_rust_program_nonexistent_name(self): 1592 '''Test that specifying RUST_PROGRAMS that don't exist in Cargo.toml 1593 correctly throws an error.''' 1594 reader = self.reader('rust-program-nonexistent-name') 1595 with six.assertRaisesRegex(self, SandboxValidationError, 1596 'Cannot find Cargo.toml definition for'): 1597 self.read_topsrcdir(reader) 1598 1599 def test_host_rust_program_nonexistent_name(self): 1600 '''Test that specifying HOST_RUST_PROGRAMS that don't exist in 1601 Cargo.toml correctly throws an error.''' 1602 reader = self.reader('host-rust-program-nonexistent-name') 1603 with six.assertRaisesRegex(self, SandboxValidationError, 1604 'Cannot find Cargo.toml definition for'): 1605 self.read_topsrcdir(reader) 1606 1607 def test_rust_programs(self): 1608 '''Test RUST_PROGRAMS emission.''' 1609 reader = self.reader('rust-programs', 1610 extra_substs=dict(RUST_TARGET='i686-pc-windows-msvc', 1611 BIN_SUFFIX='.exe')) 1612 objs = self.read_topsrcdir(reader) 1613 1614 self.assertEqual(len(objs), 3) 1615 ldflags, cflags, prog = objs 1616 self.assertIsInstance(ldflags, ComputedFlags) 1617 self.assertIsInstance(cflags, ComputedFlags) 1618 self.assertIsInstance(prog, RustProgram) 1619 self.assertEqual(prog.name, 'some') 1620 1621 def test_host_rust_programs(self): 1622 '''Test HOST_RUST_PROGRAMS emission.''' 1623 reader = self.reader('host-rust-programs', 1624 extra_substs=dict(RUST_HOST_TARGET='i686-pc-windows-msvc', 1625 HOST_BIN_SUFFIX='.exe')) 1626 objs = self.read_topsrcdir(reader) 1627 1628 self.assertEqual(len(objs), 4) 1629 print(objs) 1630 ldflags, cflags, hostflags, prog = objs 1631 self.assertIsInstance(ldflags, ComputedFlags) 1632 self.assertIsInstance(cflags, ComputedFlags) 1633 self.assertIsInstance(hostflags, ComputedFlags) 1634 self.assertIsInstance(prog, HostRustProgram) 1635 self.assertEqual(prog.name, 'some') 1636 1637 def test_host_rust_libraries(self): 1638 '''Test HOST_RUST_LIBRARIES emission.''' 1639 reader = self.reader('host-rust-libraries', 1640 extra_substs=dict(RUST_HOST_TARGET='i686-pc-windows-msvc', 1641 HOST_BIN_SUFFIX='.exe')) 1642 objs = self.read_topsrcdir(reader) 1643 1644 self.assertEqual(len(objs), 3) 1645 ldflags, lib, cflags = objs 1646 self.assertIsInstance(ldflags, ComputedFlags) 1647 self.assertIsInstance(cflags, ComputedFlags) 1648 self.assertIsInstance(lib, HostRustLibrary) 1649 self.assertRegexpMatches(lib.lib_name, 'host_lib') 1650 self.assertRegexpMatches(lib.import_name, 'host_lib') 1651 1652 def test_crate_dependency_path_resolution(self): 1653 '''Test recursive dependencies resolve with the correct paths.''' 1654 reader = self.reader('crate-dependency-path-resolution', 1655 extra_substs=dict(RUST_TARGET='i686-pc-windows-msvc')) 1656 objs = self.read_topsrcdir(reader) 1657 1658 self.assertEqual(len(objs), 3) 1659 ldflags, lib, cflags = objs 1660 self.assertIsInstance(ldflags, ComputedFlags) 1661 self.assertIsInstance(cflags, ComputedFlags) 1662 self.assertIsInstance(lib, RustLibrary) 1663 1664 def test_install_shared_lib(self): 1665 """Test that we can install a shared library with TEST_HARNESS_FILES""" 1666 reader = self.reader('test-install-shared-lib') 1667 objs = self.read_topsrcdir(reader) 1668 self.assertIsInstance(objs[0], TestHarnessFiles) 1669 self.assertIsInstance(objs[1], VariablePassthru) 1670 self.assertIsInstance(objs[2], ComputedFlags) 1671 self.assertIsInstance(objs[3], SharedLibrary) 1672 self.assertIsInstance(objs[4], ComputedFlags) 1673 for path, files in objs[0].files.walk(): 1674 for f in files: 1675 self.assertEqual(str(f), '!libfoo.so') 1676 self.assertEqual(path, 'foo/bar') 1677 1678 def test_symbols_file(self): 1679 """Test that SYMBOLS_FILE works""" 1680 reader = self.reader('test-symbols-file') 1681 genfile, ldflags, shlib, flags = self.read_topsrcdir(reader) 1682 self.assertIsInstance(genfile, GeneratedFile) 1683 self.assertIsInstance(flags, ComputedFlags) 1684 self.assertIsInstance(ldflags, ComputedFlags) 1685 self.assertIsInstance(shlib, SharedLibrary) 1686 # This looks weird but MockConfig sets DLL_{PREFIX,SUFFIX} and 1687 # the reader method in this class sets OS_TARGET=WINNT. 1688 self.assertEqual(shlib.symbols_file, 'libfoo.so.def') 1689 1690 def test_symbols_file_objdir(self): 1691 """Test that a SYMBOLS_FILE in the objdir works""" 1692 reader = self.reader('test-symbols-file-objdir') 1693 genfile, ldflags, shlib, flags = self.read_topsrcdir(reader) 1694 self.assertIsInstance(genfile, GeneratedFile) 1695 self.assertEqual(genfile.script, 1696 mozpath.join(reader.config.topsrcdir, 'foo.py')) 1697 self.assertIsInstance(flags, ComputedFlags) 1698 self.assertIsInstance(ldflags, ComputedFlags) 1699 self.assertIsInstance(shlib, SharedLibrary) 1700 self.assertEqual(shlib.symbols_file, 'foo.symbols') 1701 1702 def test_symbols_file_objdir_missing_generated(self): 1703 """Test that a SYMBOLS_FILE in the objdir that's missing 1704 from GENERATED_FILES is an error. 1705 """ 1706 reader = self.reader('test-symbols-file-objdir-missing-generated') 1707 with six.assertRaisesRegex( 1708 self, 1709 SandboxValidationError, 1710 'Objdir file specified in SYMBOLS_FILE not in GENERATED_FILES:' 1711 ): 1712 self.read_topsrcdir(reader) 1713 1714 def test_wasm_compile_flags(self): 1715 reader = self.reader('wasm-compile-flags', extra_substs={'OS_TARGET': 'Linux'}) 1716 flags = list(self.read_topsrcdir(reader))[2] 1717 self.assertIsInstance(flags, ComputedFlags) 1718 self.assertEqual(flags.flags['WASM_CFLAGS'], 1719 reader.config.substs['WASM_CFLAGS']) 1720 self.assertEqual(flags.flags['MOZBUILD_WASM_CFLAGS'], 1721 ['-funroll-loops', '-wasm-arg']) 1722 self.assertEqual(set(flags.flags['WASM_DEFINES']), 1723 set(['-DFOO', '-DBAZ="abcd"', '-UQUX', '-DBAR=7', '-DVALUE=xyz'])) 1724 1725 1726if __name__ == '__main__': 1727 main() 1728