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 25from treble.split import 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 --dump_stamp_tool: 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