1# Copyright (C) 2020 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Test manifest split.""" 15 16import json 17import os 18import re 19import subprocess 20import tempfile 21import unittest 22import unittest.mock 23import xml.etree.ElementTree as ET 24 25import manifest_split 26 27 28class ManifestSplitTest(unittest.TestCase): 29 30 def test_read_config(self): 31 with tempfile.NamedTemporaryFile('w+t') as test_config: 32 test_config.write(""" 33 <config> 34 <add_project name="add1" /> 35 <add_project name="add2" /> 36 <remove_project name="remove1" /> 37 <remove_project name="remove2" /> 38 <path_mapping pattern="p1.*" sub="$0" /> 39 </config>""") 40 test_config.flush() 41 config = manifest_split.ManifestSplitConfig.from_config_files( 42 [test_config.name]) 43 self.assertEqual(config.remove_projects, { 44 'remove1': test_config.name, 45 'remove2': test_config.name 46 }) 47 self.assertEqual(config.add_projects, { 48 'add1': test_config.name, 49 'add2': test_config.name 50 }) 51 self.assertEqual(config.path_mappings, [ 52 manifest_split.PathMappingConfig(re.compile('p1.*'), '$0'), 53 ]) 54 55 def test_get_repo_projects_from_manifest(self): 56 manifest_contents = """ 57 <manifest> 58 <project name="platform/project1" path="system/project1" /> 59 <project name="platform/project2" path="system/project2" /> 60 <project name="platform/project3" path="system/project3" /> 61 </manifest>""" 62 manifest = ET.ElementTree(ET.fromstring(manifest_contents)) 63 projects = manifest_split.get_repo_projects( 64 None, manifest, path_mappings=[]) 65 self.assertDictEqual( 66 { 67 'system/project1': 'platform/project1', 68 'system/project2': 'platform/project2', 69 'system/project3': 'platform/project3', 70 }, projects) 71 72 73 def test_get_repo_projects(self): 74 with tempfile.NamedTemporaryFile('w+t') as repo_list_file: 75 repo_list_file.write(""" 76 system/project1 : platform/project1 77 system/project2 : platform/project2""") 78 repo_list_file.flush() 79 repo_projects = manifest_split.get_repo_projects( 80 repo_list_file.name, None, path_mappings=[]) 81 self.assertEqual( 82 repo_projects, { 83 'system/project1': 'platform/project1', 84 'system/project2': 'platform/project2', 85 }) 86 87 def test_get_repo_projects_with_mappings(self): 88 with tempfile.NamedTemporaryFile('w+t') as repo_list_file: 89 repo_list_file.write(""" 90 overlay/system/project1 : platform/project1 91 system/project2 : platform/project2 92 hide/this/one : platform/project3""") 93 repo_list_file.flush() 94 path_mappings = [ 95 manifest_split.PathMappingConfig(re.compile('^overlay/(.*)'), '\\1'), 96 manifest_split.PathMappingConfig(re.compile('^hide/this/one.*'), ''), 97 ] 98 99 repo_projects = manifest_split.get_repo_projects(repo_list_file.name, 100 None, 101 path_mappings) 102 self.assertEqual( 103 repo_projects, { 104 'system/project1': 'platform/project1', 105 'system/project2': 'platform/project2', 106 }) 107 108 def test_get_module_info(self): 109 with tempfile.NamedTemporaryFile('w+t') as module_info_file: 110 module_info_file.write("""{ 111 "target1a": { "class": ["EXECUTABLES"], "path": ["system/project1"], "dependencies": ["target2"] }, 112 "target1b": { "class": ["EXECUTABLES"], "path": ["system/project1"], "dependencies": ["target3", "target42"] }, 113 "target2": { "class": ["SHARED_LIBRARIES"], "path": ["out/project2"], "dependencies": [] }, 114 "target3": { "class": ["SHARED_LIBRARIES"], "path": ["vendor/google/project3"], "dependencies": ["x", "y", "z"] }, 115 "target4a": { "class": ["APPS"], "path": ["system/project4"], "dependencies": ["out/target/common/obj/JAVA_LIBRARIES/target4b_intermediates/classes-header.jar"] }, 116 "target4b": { "class": ["JAVA_LIBRARIES"], "path": ["system/project4"], "dependencies": [] } 117 }""") 118 module_info_file.flush() 119 repo_projects = { 120 'system/project1': 'platform/project1', 121 'system/project4': 'platform/project4', 122 'vendor/google/project3': 'vendor/project3', 123 } 124 ignore_paths = set(['out/']) 125 module_info = manifest_split.ModuleInfo(module_info_file.name, 126 repo_projects, ignore_paths) 127 self.assertEqual( 128 module_info.project_modules, { 129 'platform/project1': set(['target1a', 'target1b']), 130 'platform/project4': set(['target4a', 'target4b']), 131 'vendor/project3': set(['target3']), 132 }) 133 self.assertEqual( 134 module_info.module_project, { 135 'target1a': 'platform/project1', 136 'target1b': 'platform/project1', 137 'target3': 'vendor/project3', 138 'target4a': 'platform/project4', 139 'target4b': 'platform/project4', 140 }) 141 self.assertEqual( 142 module_info.module_class, { 143 'target1a': 'EXECUTABLES', 144 'target1b': 'EXECUTABLES', 145 'target2': 'SHARED_LIBRARIES', 146 'target3': 'SHARED_LIBRARIES', 147 'target4a': 'APPS', 148 'target4b': 'JAVA_LIBRARIES', 149 }) 150 self.assertEqual( 151 module_info.module_deps, { 152 'target1a': ['target2'], 153 'target1b': ['target3', 'target42'], 154 'target2': [], 155 'target3': ['x', 'y', 'z'], 156 'target4a': ['target4b'], 157 'target4b': [], 158 }) 159 160 def test_get_module_info_raises_on_unknown_module_path(self): 161 with tempfile.NamedTemporaryFile('w+t') as module_info_file: 162 module_info_file.write("""{ 163 "target1": { "class": ["EXECUTABLES"], "path": ["system/unknown/project1"], "dependencies": [] } 164 }""") 165 module_info_file.flush() 166 repo_projects = {} 167 ignore_paths = set() 168 with self.assertRaisesRegex(ValueError, 169 'Unknown module path for module target1'): 170 manifest_split.ModuleInfo(module_info_file.name, repo_projects, 171 ignore_paths) 172 173 @unittest.mock.patch.object(subprocess, 'check_output', autospec=True) 174 def test_get_ninja_inputs(self, mock_check_output): 175 mock_check_output.return_value = b""" 176 path/to/input1 177 path/to/input2 178 path/to/TEST_MAPPING 179 path/to/MODULE_LICENSE_GPL 180 """ 181 182 inputs = manifest_split.get_ninja_inputs('unused', 'unused', ['droid']) 183 self.assertEqual(inputs, {'path/to/input1', 'path/to/input2'}) 184 185 @unittest.mock.patch.object(subprocess, 'check_output', autospec=True) 186 def test_get_ninja_inputs_includes_test_mapping(self, mock_check_output): 187 mock_check_output.return_value = b""" 188 path/to/input1 189 path/to/input2 190 path/to/TEST_MAPPING 191 """ 192 193 inputs = manifest_split.get_ninja_inputs('unused', 'unused', 194 ['droid', 'test_mapping']) 195 self.assertEqual( 196 inputs, {'path/to/input1', 'path/to/input2', 'path/to/TEST_MAPPING'}) 197 198 @unittest.mock.patch.object(subprocess, 'check_output', autospec=True) 199 def test_get_kati_makefiles(self, mock_check_output): 200 with tempfile.TemporaryDirectory() as temp_dir: 201 os.chdir(temp_dir) 202 203 makefiles = [ 204 'device/oem1/product1.mk', 205 'device/oem2/product2.mk', 206 'device/google/google_product.mk', 207 'overlays/oem_overlay/device/oem3/product3.mk', 208 'packages/apps/Camera/Android.mk', 209 ] 210 for makefile in makefiles: 211 os.makedirs(os.path.dirname(makefile)) 212 os.mknod(makefile) 213 214 symlink_src = os.path.join(temp_dir, 'vendor/oem4/symlink_src.mk') 215 os.makedirs(os.path.dirname(symlink_src)) 216 os.mknod(symlink_src) 217 symlink_dest = 'device/oem4/symlink_dest.mk' 218 os.makedirs(os.path.dirname(symlink_dest)) 219 os.symlink(symlink_src, symlink_dest) 220 # Only append the symlink destination, not where the symlink points to. 221 # (The Kati stamp file does not resolve symlink sources.) 222 makefiles.append(symlink_dest) 223 224 # Mock the output of ckati_stamp_dump: 225 mock_check_output.return_value = '\n'.join(makefiles).encode() 226 227 kati_makefiles = manifest_split.get_kati_makefiles( 228 'stamp-file', ['overlays/oem_overlay/']) 229 self.assertEqual( 230 kati_makefiles, 231 set([ 232 # Regular product makefiles 233 'device/oem1/product1.mk', 234 'device/oem2/product2.mk', 235 # Product makefile remapped from an overlay 236 'device/oem3/product3.mk', 237 # Product makefile symlink and its source 238 'device/oem4/symlink_dest.mk', 239 'vendor/oem4/symlink_src.mk', 240 ])) 241 242 def test_scan_repo_projects(self): 243 repo_projects = { 244 'system/project1': 'platform/project1', 245 'system/project2': 'platform/project2', 246 } 247 self.assertEqual( 248 manifest_split.scan_repo_projects(repo_projects, 249 'system/project1/path/to/file.h'), 250 'system/project1') 251 self.assertEqual( 252 manifest_split.scan_repo_projects( 253 repo_projects, 'system/project2/path/to/another_file.cc'), 254 'system/project2') 255 self.assertIsNone( 256 manifest_split.scan_repo_projects( 257 repo_projects, 'system/project3/path/to/unknown_file.h')) 258 259 def test_get_input_projects(self): 260 repo_projects = { 261 'system/project1': 'platform/project1', 262 'system/project2': 'platform/project2', 263 'system/project4': 'platform/project4', 264 } 265 inputs = [ 266 'system/project1/path/to/file.h', 267 'out/path/to/out/file.h', 268 'system/project2/path/to/another_file.cc', 269 'system/project3/path/to/unknown_file.h', 270 '/tmp/absolute/path/file.java', 271 ] 272 self.assertEqual( 273 manifest_split.get_input_projects(repo_projects, inputs), { 274 'platform/project1': ['system/project1/path/to/file.h'], 275 'platform/project2': ['system/project2/path/to/another_file.cc'], 276 }) 277 278 def test_update_manifest(self): 279 manifest_contents = """ 280 <manifest> 281 <project name="platform/project1" path="system/project1" /> 282 <project name="platform/project2" path="system/project2" /> 283 <project name="platform/project3" path="system/project3" /> 284 </manifest>""" 285 input_projects = set(['platform/project1', 'platform/project3']) 286 remove_projects = set(['platform/project3']) 287 manifest = manifest_split.update_manifest( 288 ET.ElementTree(ET.fromstring(manifest_contents)), input_projects, 289 remove_projects) 290 291 projects = manifest.getroot().findall('project') 292 self.assertEqual(len(projects), 1) 293 self.assertEqual( 294 ET.tostring(projects[0]).strip().decode(), 295 '<project name="platform/project1" path="system/project1" />') 296 297 @unittest.mock.patch.object(subprocess, 'check_output', autospec=True) 298 def test_create_split_manifest(self, mock_check_output): 299 with tempfile.NamedTemporaryFile('w+t') as repo_list_file, \ 300 tempfile.NamedTemporaryFile('w+t') as manifest_file, \ 301 tempfile.NamedTemporaryFile('w+t') as module_info_file, \ 302 tempfile.NamedTemporaryFile('w+t') as config_file, \ 303 tempfile.NamedTemporaryFile('w+t') as split_manifest_file, \ 304 tempfile.TemporaryDirectory() as temp_dir: 305 306 os.chdir(temp_dir) 307 308 repo_list_file.write(""" 309 system/project1 : platform/project1 310 system/project2 : platform/project2 311 system/project3 : platform/project3 312 system/project4 : platform/project4 313 system/project5 : platform/project5 314 system/project6 : platform/project6 315 system/project7 : platform/project7 316 system/project8 : platform/project8 317 system/project9 : platform/project9 318 vendor/project1 : vendor/project1""") 319 repo_list_file.flush() 320 321 manifest_file.write(""" 322 <manifest> 323 <project name="platform/project1" path="system/project1" /> 324 <project name="platform/project2" path="system/project2" /> 325 <project name="platform/project3" path="system/project3" /> 326 <project name="platform/project4" path="system/project4" /> 327 <project name="platform/project5" path="system/project5" /> 328 <project name="platform/project6" path="system/project6" /> 329 <project name="platform/project7" path="system/project7" /> 330 <project name="platform/project8" path="system/project8" /> 331 <project name="platform/project9" path="system/project9" /> 332 <project name="vendor/project1" path="vendor/project1" /> 333 </manifest>""") 334 manifest_file.flush() 335 336 module_info_file.write("""{ 337 "droid": { "class": ["EXECUTABLES"], "path": ["system/project1"], "dependencies": [] }, 338 "target_a": { "class": ["EXECUTABLES"], "path": ["out/project2"], "dependencies": ["unknown_module_a"] }, 339 "target_b": { "class": ["EXECUTABLES"], "path": ["system/project3"], "dependencies": ["target_f", "unknown_module_b"] }, 340 "target_c": { "class": ["EXECUTABLES"], "path": ["system/project4"], "dependencies": [] }, 341 "target_d": { "class": ["EXECUTABLES"], "path": ["system/project5"], "dependencies": [] }, 342 "target_e": { "class": ["EXECUTABLES"], "path": ["system/project6"], "dependencies": [] }, 343 "target_f": { "class": ["HEADER_LIBRARIES"], "path": ["system/project7"], "dependencies": [] }, 344 "target_g": { "class": ["SHARED_LIBRARIES"], "path": ["system/project8"], "dependencies": ["target_h"] }, 345 "target_h": { "class": ["HEADER_LIBRARIES"], "path": ["system/project9"], "dependencies": [] } 346 }""") 347 module_info_file.flush() 348 349 # droid needs inputs from project1 and project3 350 ninja_inputs_droid = b""" 351 system/project1/file1 352 system/project1/file2 353 system/project3/file1 354 """ 355 356 # target_b (indirectly included due to being in project3) needs inputs 357 # from project3 and project4 358 ninja_inputs_target_b = b""" 359 system/project3/file2 360 system/project4/file1 361 """ 362 363 # target_c (indirectly included due to being in project4) needs inputs 364 # from only project4 365 ninja_inputs_target_c = b""" 366 system/project4/file2 367 system/project4/file3 368 """ 369 370 product_makefile = 'vendor/project1/product.mk' 371 os.makedirs(os.path.dirname(product_makefile)) 372 os.mknod(product_makefile) 373 kati_stamp_dump = product_makefile.encode() 374 375 mock_check_output.side_effect = [ 376 ninja_inputs_droid, 377 kati_stamp_dump, 378 ninja_inputs_target_b, 379 ninja_inputs_target_c, 380 ] 381 382 # The config file says to manually include project6 383 config_file.write(""" 384 <config> 385 <add_project name="platform/project6" /> 386 </config>""") 387 config_file.flush() 388 389 debug_file = os.path.join(temp_dir, 'debug.json') 390 391 manifest_split.create_split_manifest( 392 ['droid'], manifest_file.name, split_manifest_file.name, 393 [config_file.name], repo_list_file.name, 'build-target.ninja', 394 'ninja', module_info_file.name, 'unused kati stamp', 395 ['unused overlay'], [], debug_file) 396 split_manifest = ET.parse(split_manifest_file.name) 397 split_manifest_projects = [ 398 child.attrib['name'] 399 for child in split_manifest.getroot().findall('project') 400 ] 401 self.assertEqual( 402 split_manifest_projects, 403 [ 404 # From droid 405 'platform/project1', 406 # From droid 407 'platform/project3', 408 # From target_b (module within project3, indirect dependency) 409 'platform/project4', 410 # Manual inclusion from config file 411 'platform/project6', 412 # From target_b (depends on target_f header library) 413 'platform/project7', 414 # Inclusion from the Kati makefile stamp 415 'vendor/project1', 416 ]) 417 418 with open(debug_file) as debug_fp: 419 debug_data = json.load(debug_fp) 420 421 # Dependency for droid, but no other adjacent modules 422 self.assertTrue(debug_data['platform/project1']['direct_input']) 423 self.assertFalse(debug_data['platform/project1']['adjacent_input']) 424 self.assertFalse(debug_data['platform/project1']['deps_input']) 425 426 # Dependency for droid and an adjacent module 427 self.assertTrue(debug_data['platform/project3']['direct_input']) 428 self.assertTrue(debug_data['platform/project3']['adjacent_input']) 429 self.assertFalse(debug_data['platform/project3']['deps_input']) 430 431 # Dependency only for an adjacent module 432 self.assertFalse(debug_data['platform/project4']['direct_input']) 433 self.assertTrue(debug_data['platform/project4']['adjacent_input']) 434 self.assertFalse(debug_data['platform/project4']['deps_input']) 435 436 # Included via header library 437 self.assertFalse(debug_data['platform/project7']['direct_input']) 438 self.assertFalse(debug_data['platform/project7']['adjacent_input']) 439 self.assertTrue(debug_data['platform/project7']['deps_input']) 440 441 # Included due to the config file 442 self.assertEqual( 443 debug_data['platform/project6']['manual_add_config'], 444 config_file.name) 445 446 # Included due to the Kati makefile stamp 447 self.assertEqual(debug_data['vendor/project1']['kati_makefiles'][0], 448 product_makefile) 449 450 @unittest.mock.patch.object(manifest_split, 'get_ninja_inputs', autospec=True) 451 @unittest.mock.patch.object(manifest_split, 'get_kati_makefiles', autospec=True) 452 @unittest.mock.patch.object(manifest_split.ModuleInfo, '__init__', autospec=True) 453 def test_create_split_manifest_skip_kati_module_info(self, mock_init, 454 mock_get_kati_makefiles, 455 mock_get_ninja_inputs): 456 with tempfile.NamedTemporaryFile('w+t') as repo_list_file, \ 457 tempfile.NamedTemporaryFile('w+t') as manifest_file, \ 458 tempfile.NamedTemporaryFile('w+t') as module_info_file, \ 459 tempfile.NamedTemporaryFile('w+t') as config_file, \ 460 tempfile.NamedTemporaryFile('w+t') as split_manifest_file, \ 461 tempfile.TemporaryDirectory() as temp_dir: 462 463 os.chdir(temp_dir) 464 465 manifest_file.write(""" 466 <manifest> 467 </manifest>""") 468 manifest_file.flush() 469 470 manifest_split.create_split_manifest( 471 targets=['droid'], 472 manifest_file=manifest_file.name, 473 split_manifest_file=split_manifest_file.name, 474 config_files=[], 475 repo_list_file=repo_list_file.name, 476 ninja_build_file='build-target.ninja', 477 ninja_binary='ninja', 478 kati_stamp_file=None, 479 module_info_file=None, 480 overlays=[], 481 installed_prebuilts=[], 482 debug_file=None) 483 484 mock_get_ninja_inputs.assert_called_with( 485 'ninja', 'build-target.ninja', ['droid']) 486 mock_get_kati_makefiles.assert_not_called() 487 mock_init.assert_not_called() 488 489 @unittest.mock.patch.object(subprocess, 'check_output', autospec=True) 490 def test_create_split_manifest_installed_prebuilt(self, mock_check_output): 491 492 # The purpose of this test is to verify that create_split_manifests treats 493 # installed prebuilts as projects, even though the installed prebuilts are 494 # not in the manifest. This use case occurs when installed prebuilts 495 # contribute modules to the build, but the installed prebuilts themselves 496 # aren't sourced from the manifest. 497 498 with tempfile.NamedTemporaryFile('w+t') as repo_list_file, \ 499 tempfile.NamedTemporaryFile('w+t') as manifest_file, \ 500 tempfile.NamedTemporaryFile('w+t') as module_info_file, \ 501 tempfile.NamedTemporaryFile('w+t') as split_manifest_file, \ 502 tempfile.TemporaryDirectory() as temp_dir: 503 504 os.chdir(temp_dir) 505 506 repo_list_file.write(""" 507 system/project1 : platform/project1 508 vendor/project1 : vendor/project1""") 509 repo_list_file.flush() 510 511 # Here we have small manifest that does not include "prebuilt/project3" 512 # or "prebuilt/project4". 513 514 manifest_file.write(""" 515 <manifest> 516 <project name="platform/project1" path="system/project1" /> 517 <project name="vendor/project1" path="vendor/project1" /> 518 </manifest>""") 519 manifest_file.flush() 520 521 # Here's the module_info.json file. It contains modules whose paths are 522 # "prebuilt/project3" and "prebult/project4", which are not found in the 523 # manifest. Normally create_split_manifest doesn't tolerate a path that 524 # doesn't correspond to a manifest project. However, this test verifies 525 # that you can use these modules if you tell create_split_manifest about 526 # the installed prebuilts via a parameter. 527 528 module_info_file.write("""{ 529 "droid": { "class": ["EXECUTABLES"], "path": ["system/project1"], "dependencies": [] }, 530 "target_a": { "class": ["EXECUTABLES"], "path": ["system/project1"], "dependencies": ["target_b", "target_c"] }, 531 "target_b": { "class": ["SHARED_LIBRARIES"], "path": ["prebuilt/project3"], "dependencies": [] }, 532 "target_c": { "class": ["SHARED_LIBRARIES"], "path": ["prebuilt/project4"], "dependencies": [] } 533 }""") 534 module_info_file.flush() 535 536 # droid needs inputs from project1 537 ninja_inputs_droid = b""" 538 system/project1/file1 539 """ 540 541 # target_a needs inputs from prebuilt/project3 and prebuilt/project4 542 ninja_inputs_target_a = b""" 543 prebuilt/project3/file2 544 prebuilt/project4/file3 545 """ 546 547 # target_b needs inputs from prebuilt/project3 548 ninja_inputs_target_b = b""" 549 prebuilt/project3/file4 550 """ 551 552 # target_c needs inputs from prebuilt/project4 553 ninja_inputs_target_c = b""" 554 prebuilt/project4/file5 555 """ 556 557 product_makefile = 'vendor/project1/product.mk' 558 os.makedirs(os.path.dirname(product_makefile)) 559 os.mknod(product_makefile) 560 kati_stamp_dump = product_makefile.encode() 561 562 mock_check_output.side_effect = [ 563 ninja_inputs_droid, 564 kati_stamp_dump, 565 ninja_inputs_target_a, 566 ninja_inputs_target_b, 567 ninja_inputs_target_c, 568 ] 569 570 debug_file = os.path.join(temp_dir, 'debug.json') 571 572 manifest_split.create_split_manifest( 573 targets=['droid'], 574 manifest_file=manifest_file.name, 575 split_manifest_file=split_manifest_file.name, 576 config_files=[], 577 repo_list_file=repo_list_file.name, 578 ninja_build_file='build-target.ninja', 579 ninja_binary='ninja', 580 module_info_file=module_info_file.name, 581 kati_stamp_file='unused kati stamp', 582 overlays=['unused overlay'], 583 584 # This is a key part of the test. Passing these two "projects" as 585 # prebuilts allows create_split_manifest to recognize them as 586 # projects even though they are not in the manifest. 587 588 installed_prebuilts=['prebuilt/project3', 'prebuilt/project4'], 589 590 debug_file = debug_file) 591 592 split_manifest = ET.parse(split_manifest_file.name) 593 594 split_manifest_projects = [ 595 child.attrib['name'] 596 for child in split_manifest.getroot().findall('project') 597 ] 598 599 # Note that the installed prebuilts do not appear in the final split 600 # manfiest output because they were not in the manifest to begin with. 601 602 self.assertEqual( 603 split_manifest_projects, 604 [ 605 # From droid 606 'platform/project1', 607 # Inclusion from the Kati makefile stamp 608 'vendor/project1', 609 ]) 610 611 with open(debug_file) as debug_fp: 612 debug_data = json.load(debug_fp) 613 614 # Dependency for droid, but no other adjacent modules 615 self.assertTrue(debug_data['platform/project1']['direct_input']) 616 self.assertFalse(debug_data['platform/project1']['adjacent_input']) 617 self.assertFalse(debug_data['platform/project1']['deps_input']) 618 619 # Included due to the Kati makefile stamp 620 self.assertEqual(debug_data['vendor/project1']['kati_makefiles'][0], 621 product_makefile) 622 623 624if __name__ == '__main__': 625 unittest.main() 626