| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 """Compare the artifacts from two builds.""" | |
| 7 | |
| 8 import difflib | |
| 9 import json | |
| 10 import optparse | |
| 11 import os | |
| 12 import struct | |
| 13 import sys | |
| 14 import time | |
| 15 | |
| 16 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| 17 | |
| 18 | |
| 19 # List of files that are known to be non deterministic. This is a "temporary" | |
| 20 # workaround to find regression on the deterministic builders. | |
| 21 # | |
| 22 # PNaCl general bug: https://crbug.com/429358 | |
| 23 # | |
| 24 # TODO(sebmarchand): Remove this once all the files are deterministic. | |
| 25 WHITELIST = { | |
| 26 # https://crbug.com/383340 | |
| 27 'android': { | |
| 28 'd8', | |
| 29 'mksnapshot', | |
| 30 }, | |
| 31 | |
| 32 # https://crbug.com/330263 | |
| 33 'linux': { | |
| 34 # Completed. | |
| 35 }, | |
| 36 | |
| 37 # https://crbug.com/330262 | |
| 38 'mac': { | |
| 39 'accessibility_unittests', | |
| 40 'accessibility_unittests.isolated', | |
| 41 'angle_end2end_tests', | |
| 42 'angle_unittests', | |
| 43 'app_list_demo', | |
| 44 'app_list_unittests', | |
| 45 'app_list_unittests.isolated', | |
| 46 'app_shell_unittests', | |
| 47 'app_shell_unittests.isolated', | |
| 48 'ar_sample_test_driver', | |
| 49 'audio_unittests', | |
| 50 'audio_unittests.isolated', | |
| 51 'base_i18n_perftests', | |
| 52 'base_perftests', | |
| 53 'base_unittests', | |
| 54 'base_unittests.isolated', | |
| 55 'battor_agent', | |
| 56 'bitmaptools', | |
| 57 'blink_heap_unittests', | |
| 58 'blink_heap_unittests.isolated', | |
| 59 'blink_platform_unittests', | |
| 60 'blink_platform_unittests.isolated', | |
| 61 'boringssl_aead_test', | |
| 62 'boringssl_aes_test', | |
| 63 'boringssl_base64_test', | |
| 64 'boringssl_bio_test', | |
| 65 'boringssl_bn_test', | |
| 66 'boringssl_bytestring_test', | |
| 67 'boringssl_cipher_test', | |
| 68 'boringssl_cmac_test', | |
| 69 'boringssl_constant_time_test', | |
| 70 'boringssl_dh_test', | |
| 71 'boringssl_digest_test', | |
| 72 'boringssl_dsa_test', | |
| 73 'boringssl_ec_test', | |
| 74 'boringssl_ecdsa_test', | |
| 75 'boringssl_err_test', | |
| 76 'boringssl_evp_extra_test', | |
| 77 'boringssl_evp_test', | |
| 78 'boringssl_example_mul', | |
| 79 'boringssl_gcm_test', | |
| 80 'boringssl_hkdf_test', | |
| 81 'boringssl_hmac_test', | |
| 82 'boringssl_lhash_test', | |
| 83 'boringssl_pbkdf_test', | |
| 84 'boringssl_pkcs12_test', | |
| 85 'boringssl_pkcs7_test', | |
| 86 'boringssl_pkcs8_test', | |
| 87 'boringssl_poly1305_test', | |
| 88 'boringssl_pqueue_test', | |
| 89 'boringssl_refcount_test', | |
| 90 'boringssl_rsa_test', | |
| 91 'boringssl_ssl_test', | |
| 92 'boringssl_tab_test', | |
| 93 'boringssl_thread_test', | |
| 94 'boringssl_unittests', | |
| 95 'boringssl_v3name_test', | |
| 96 'browser_tests', | |
| 97 'browser_tests.isolated', | |
| 98 'build_utf8_validator_tables', | |
| 99 'cacheinvalidation_unittests', | |
| 100 'cacheinvalidation_unittests.isolated', | |
| 101 'cast_benchmarks', | |
| 102 'cast_h264_vt_encoder_unittests', | |
| 103 'cast_receiver_app', | |
| 104 'cast_sender_app', | |
| 105 'cast_simulator', | |
| 106 'cast_unittests', | |
| 107 'cast_unittests.isolated', | |
| 108 'cc_blink_unittests', | |
| 109 'cc_perftests', | |
| 110 'cc_unittests', | |
| 111 'cc_unittests.isolated', | |
| 112 'check_example', | |
| 113 'chrome.isolated', | |
| 114 'chrome_app_unittests', | |
| 115 'chromedriver', | |
| 116 'chromedriver_tests', | |
| 117 'chromedriver_unittests', | |
| 118 'chromedriver_unittests.isolated', | |
| 119 'chromoting_test_driver', | |
| 120 'clear_system_cache', | |
| 121 'cloud_print_unittests', | |
| 122 'codesighs', | |
| 123 'components_browsertests', | |
| 124 'components_browsertests.isolated', | |
| 125 'components_perftests', | |
| 126 'components_unittests', | |
| 127 'components_unittests.isolated', | |
| 128 'compositor_unittests', | |
| 129 'compositor_unittests.isolated', | |
| 130 'content_browsertests', | |
| 131 'content_browsertests.isolated', | |
| 132 'content_gl_benchmark', | |
| 133 'content_gl_tests', | |
| 134 'content_perftests', | |
| 135 'content_unittests', | |
| 136 'content_unittests.isolated', | |
| 137 'courgette', | |
| 138 'courgette_fuzz', | |
| 139 'courgette_minimal_tool', | |
| 140 'courgette_unittests', | |
| 141 'courgette_unittests.isolated', | |
| 142 'crash_cache', | |
| 143 'crash_inspector', | |
| 144 'crashpad_handler', | |
| 145 'crl_set_dump', | |
| 146 'crypto_unittests', | |
| 147 'crypto_unittests.isolated', | |
| 148 'd8', | |
| 149 'device_unittests', | |
| 150 'device_unittests.isolated', | |
| 151 'display_unittests', | |
| 152 'dns_fuzz_stub', | |
| 153 'dump_cache', | |
| 154 'dump_syms', | |
| 155 'env_chromium_unittests', | |
| 156 'events_unittests', | |
| 157 'events_unittests.isolated', | |
| 158 'exif.so', | |
| 159 'extensions_browsertests', | |
| 160 'extensions_browsertests.isolated', | |
| 161 'extensions_unittests', | |
| 162 'extensions_unittests.isolated', | |
| 163 'ffmpeg_regression_tests', | |
| 164 'ffmpegsumo.so', | |
| 165 'filter_fuzz_stub', | |
| 166 'frame_analyzer', | |
| 167 'gcapi_example', | |
| 168 'gcm_unit_tests', | |
| 169 'gcm_unit_tests.isolated', | |
| 170 'gdig', | |
| 171 'generate_barcode_video', | |
| 172 'generate_test_gn_data', | |
| 173 'generate_timecode_audio', | |
| 174 'genmacro', | |
| 175 'genmodule', | |
| 176 'genperf', | |
| 177 'genstring', | |
| 178 'genversion', | |
| 179 'get_server_time', | |
| 180 'gfx_unittests', | |
| 181 'gin_shell', | |
| 182 'gin_unittests', | |
| 183 'gl_tests', | |
| 184 'gl_unittests', | |
| 185 'gl_unittests.isolated', | |
| 186 'gles2_conform_support', | |
| 187 'gles2_conform_test', | |
| 188 'gn', | |
| 189 'gn_unittests', | |
| 190 'gn_unittests.isolated', | |
| 191 'goobsdiff', | |
| 192 'goobspatch', | |
| 193 'google_apis_unittests', | |
| 194 'google_apis_unittests.isolated', | |
| 195 'gpu_perftests', | |
| 196 'gpu_unittests', | |
| 197 'gpu_unittests.isolated', | |
| 198 'hpack_example_generator', | |
| 199 'hpack_fuzz_mutator', | |
| 200 'hpack_fuzz_wrapper', | |
| 201 'image_diff', | |
| 202 'image_operations_bench', | |
| 203 'infoplist_strings_tool', | |
| 204 'interactive_ui_tests', | |
| 205 'interactive_ui_tests.isolated', | |
| 206 'ipc_mojo_perftests', | |
| 207 'ipc_mojo_unittests', | |
| 208 'ipc_perftests', | |
| 209 'ipc_tests', | |
| 210 'ipc_tests.isolated', | |
| 211 'jingle_unittests', | |
| 212 'jingle_unittests.isolated', | |
| 213 'jtl_compiler', | |
| 214 'khronos_glcts_test', | |
| 215 'layout_test_helper', | |
| 216 'libEGL.dylib', | |
| 217 'libGLESv2.dylib', | |
| 218 'libaddressinput_unittests', | |
| 219 'libclearkeycdm.dylib', | |
| 220 'libcommand_buffer_gles2.dylib', | |
| 221 'liblzma_decompress.dylib', | |
| 222 'libmojo_public_test_support.dylib', | |
| 223 'libphonenumber_unittests', | |
| 224 'load_library_perf_tests', | |
| 225 'macviews_interactive_ui_tests', | |
| 226 'maptsvdifftool', | |
| 227 'mcs_probe', | |
| 228 'media_blink_unittests', | |
| 229 'media_blink_unittests.isolated', | |
| 230 'media_perftests', | |
| 231 'media_unittests', | |
| 232 'media_unittests.isolated', | |
| 233 'message_center_unittests', | |
| 234 'message_center_unittests.isolated', | |
| 235 'midi_unittests', | |
| 236 'midi_unittests.isolated', | |
| 237 'minidump_stackwalk', | |
| 238 'mksnapshot', | |
| 239 'mojo_common_unittests', | |
| 240 'mojo_common_unittests.isolated', | |
| 241 'mojo_js_integration_tests', | |
| 242 'mojo_js_unittests', | |
| 243 'mojo_message_pipe_perftests', | |
| 244 'mojo_public_application_unittests', | |
| 245 'mojo_public_bindings_perftests', | |
| 246 'mojo_public_bindings_unittests', | |
| 247 'mojo_public_bindings_unittests.isolated', | |
| 248 'mojo_public_system_perftests', | |
| 249 'mojo_public_system_unittests', | |
| 250 'mojo_public_system_unittests.isolated', | |
| 251 'mojo_system_unittests', | |
| 252 'nacl_loader_unittests', | |
| 253 'nacl_loader_unittests.isolated', | |
| 254 'net_perftests', | |
| 255 'net_unittests', | |
| 256 'net_unittests.isolated', | |
| 257 'net_watcher', | |
| 258 'nm2tsv', | |
| 259 'openh264_unittests', | |
| 260 'osmesa.so', | |
| 261 'pdfium_diff', | |
| 262 'pdfium_test', | |
| 263 'pdfsqueeze', | |
| 264 'peerconnection_server', | |
| 265 'pepper_hash_for_uma', | |
| 266 'performance_browser_tests', | |
| 267 'ppapi_perftests', | |
| 268 'ppapi_unittests', | |
| 269 'printing_unittests', | |
| 270 'printing_unittests.isolated', | |
| 271 'protoc', | |
| 272 'qcms_test', | |
| 273 'qcms_tests', | |
| 274 'quic_client', | |
| 275 'quic_server', | |
| 276 're2c', | |
| 277 'remoting_perftests', | |
| 278 'remoting_start_host', | |
| 279 'remoting_unittests', | |
| 280 'remoting_unittests.isolated', | |
| 281 'rgba_to_i420_converter', | |
| 282 'rlz_id', | |
| 283 'rlz_unittests', | |
| 284 'run_sync_testserver', | |
| 285 'run_testserver', | |
| 286 'sandbox_mac_unittests', | |
| 287 'sandbox_mac_unittests.isolated', | |
| 288 'skia_runner', | |
| 289 'skia_unittests', | |
| 290 'skia_unittests.isolated', | |
| 291 'snapshot_unittests', | |
| 292 'sql_unittests', | |
| 293 'sql_unittests.isolated', | |
| 294 'stress_cache', | |
| 295 'symupload', | |
| 296 'sync_client', | |
| 297 'sync_integration_tests', | |
| 298 'sync_integration_tests.isolated', | |
| 299 'sync_listen_notifications', | |
| 300 'sync_performance_tests', | |
| 301 'telemetry_gpu_unittests.isolated', | |
| 302 'tld_cleanup', | |
| 303 'tls_edit', | |
| 304 'udp_proxy', | |
| 305 'ui_base_unittests', | |
| 306 'ui_touch_selection_unittests', | |
| 307 'ui_touch_selection_unittests.isolated', | |
| 308 'unit_tests', | |
| 309 'unit_tests.isolated', | |
| 310 'url_unittests', | |
| 311 'url_unittests.isolated', | |
| 312 'views_examples_with_content_exe', | |
| 313 'views_unittests', | |
| 314 'webkit_unit_tests', | |
| 315 'webkit_unit_tests.isolated', | |
| 316 'wifi_test', | |
| 317 'wtf_unittests', | |
| 318 'wtf_unittests.isolated', | |
| 319 'xz', | |
| 320 'xzdec', | |
| 321 'yasm', | |
| 322 }, | |
| 323 | |
| 324 # https://crbug.com/330260 | |
| 325 'win': { | |
| 326 'accessibility_unittests.exe', | |
| 327 'accessibility_unittests.isolated', | |
| 328 'angle_end2end_tests.exe', | |
| 329 'angle_unittests.exe', | |
| 330 'angle_perftests.exe', | |
| 331 'app_list_demo.exe', | |
| 332 'app_list_unittests.exe', | |
| 333 'app_list_unittests.isolated', | |
| 334 'app_shell.exe', | |
| 335 'app_shell_unittests.exe', | |
| 336 'app_shell_unittests.isolated', | |
| 337 'ar_sample_test_driver.exe', | |
| 338 'ash_shell.exe', | |
| 339 'ash_unittests.exe', | |
| 340 'ash_unittests.isolated', | |
| 341 'audio_unittests.exe', | |
| 342 'audio_unittests.isolated', | |
| 343 'base_i18n_perftests.exe', | |
| 344 'base_perftests.exe', | |
| 345 'base_unittests.exe', | |
| 346 'base_unittests.isolated', | |
| 347 'blink_heap_unittests.exe', | |
| 348 'blink_platform_unittests.exe', | |
| 349 'browser_tests.exe', | |
| 350 'browser_tests.isolated', | |
| 351 'cast_unittests.exe', | |
| 352 'cast_unittests.isolated', | |
| 353 'cc_blink_unittests.exe', | |
| 354 'cc_perftests.exe', | |
| 355 'cc_unittests.exe', | |
| 356 'cc_unittests.isolated', | |
| 357 'chrome.dll', | |
| 358 'chrome.exe', | |
| 359 'chrome.isolated', | |
| 360 'chrome_app_unittests.exe', | |
| 361 'chrome_child.dll', | |
| 362 'chrome_elf_unittests.exe', | |
| 363 'chrome_watcher.dll', | |
| 364 'chromedriver.exe', | |
| 365 'chromedriver_tests.exe', | |
| 366 'chromoting_test_driver.exe', | |
| 367 'clearkeycdm.dll', | |
| 368 'cloud_print_service.exe', | |
| 369 'cloud_print_service_config.exe', | |
| 370 'cloud_print_unittests.exe', | |
| 371 'components_browsertests.exe', | |
| 372 'components_browsertests.isolated', | |
| 373 'components_unittests.exe', | |
| 374 'components_unittests.isolated', | |
| 375 'compositor_unittests.exe', | |
| 376 'compositor_unittests.isolated', | |
| 377 'content_browsertests.exe', | |
| 378 'content_browsertests.isolated', | |
| 379 'content_gl_benchmark.exe', | |
| 380 'content_gl_tests.exe', | |
| 381 'content_perftests.exe', | |
| 382 'content_shell.exe', | |
| 383 'content_unittests.exe', | |
| 384 'content_unittests.isolated', | |
| 385 'courgette64.exe', | |
| 386 'crash_service64.exe', | |
| 387 'crypto_unittests.exe', | |
| 388 'crypto_unittests.isolated', | |
| 389 'd8.exe', | |
| 390 'delegate_execute.exe', | |
| 391 'delegate_execute_unittests.exe', | |
| 392 'device_unittests.exe', | |
| 393 'device_unittests.isolated', | |
| 394 'events_unittests.exe', | |
| 395 'events_unittests.isolated', | |
| 396 'extensions_browsertests.exe', | |
| 397 'extensions_browsertests.isolated', | |
| 398 'extensions_unittests.exe', | |
| 399 'extensions_unittests.isolated', | |
| 400 'gcapi_test.exe', | |
| 401 'gcm_unit_tests.exe', | |
| 402 'gcm_unit_tests.isolated', | |
| 403 'gcp20_device.exe', | |
| 404 'gcp20_device_unittests.exe', | |
| 405 'gcp_portmon64.dll', | |
| 406 'get_server_time.exe', | |
| 407 'gfx_unittests.exe', | |
| 408 'gin_shell.exe', | |
| 409 'gin_unittests.exe', | |
| 410 'gl_unittests.exe', | |
| 411 'gl_unittests.isolated', | |
| 412 'google_apis_unittests.exe', | |
| 413 'google_apis_unittests.isolated', | |
| 414 'gpu_perftests.exe', | |
| 415 'gpu_unittests.exe', | |
| 416 'gpu_unittests.isolated', | |
| 417 'interactive_ui_tests.exe', | |
| 418 'interactive_ui_tests.isolated', | |
| 419 'ipc_mojo_perftests.exe', | |
| 420 'ipc_mojo_unittests.exe', | |
| 421 'ipc_perftests.exe', | |
| 422 'ipc_tests.exe', | |
| 423 'ipc_tests.isolated', | |
| 424 'jingle_unittests.exe', | |
| 425 'jingle_unittests.isolated', | |
| 426 'keyboard_unittests.exe', | |
| 427 'libaddressinput_unittests.exe', | |
| 428 'media_unittests.exe', | |
| 429 'media_unittests.isolated', | |
| 430 'metro_driver.dll', | |
| 431 'midi_unittests.exe', | |
| 432 'midi_unittests.isolated', | |
| 433 'mini_installer.exe', | |
| 434 'mksnapshot.exe', | |
| 435 'mock_nacl_gdb.exe', | |
| 436 'mojo_js_integration_tests.exe', | |
| 437 'mojo_js_unittests.exe', | |
| 438 'mojo_message_pipe_perftests.exe', | |
| 439 'mojo_public_bindings_perftests.exe', | |
| 440 'mojo_public_bindings_unittests.exe', | |
| 441 'mojo_public_bindings_unittests.isolated', | |
| 442 'mojo_public_system_perftests.exe', | |
| 443 'mojo_public_system_unittests.exe', | |
| 444 'mojo_public_system_unittests.isolated', | |
| 445 'mojo_system_unittests.exe', | |
| 446 'nacl_loader_unittests.exe', | |
| 447 'nacl_loader_unittests.isolated', | |
| 448 'net_perftests.exe', | |
| 449 'net_unittests.exe', | |
| 450 'net_unittests.isolated', | |
| 451 'np_test_netscape_plugin.dll', | |
| 452 'npapi_test_plugin.dll', | |
| 453 'pdfium_test.exe', | |
| 454 'peerconnection_server.exe', | |
| 455 'performance_browser_tests.exe', | |
| 456 'ppapi_perftests.exe', | |
| 457 'ppapi_unittests.exe', | |
| 458 'printing_unittests.exe', | |
| 459 'printing_unittests.isolated', | |
| 460 'remoting_core.dll', | |
| 461 'remoting_start_host.exe', | |
| 462 'remoting_unittests.exe', | |
| 463 'remoting_unittests.isolated', | |
| 464 'sbox_integration_tests.isolated', | |
| 465 'sbox_unittests.exe', | |
| 466 'sbox_unittests.isolated', | |
| 467 'sbox_validation_tests.isolated', | |
| 468 'setup_unittests.exe', | |
| 469 'setup_unittests.isolated', | |
| 470 'skia_unittests.exe', | |
| 471 'skia_unittests.isolated', | |
| 472 'sql_unittests.exe', | |
| 473 'sql_unittests.isolated', | |
| 474 'sync_client.exe', | |
| 475 'sync_integration_tests.exe', | |
| 476 'sync_integration_tests.isolated', | |
| 477 'sync_performance_tests.exe', | |
| 478 'test_registrar.exe', | |
| 479 'ui_base_unittests.exe', | |
| 480 'unit_tests.exe', | |
| 481 'unit_tests.isolated', | |
| 482 'url_unittests.exe', | |
| 483 'url_unittests.isolated', | |
| 484 'video_decode_accelerator_unittest.exe', | |
| 485 'views_examples_with_content_exe.exe', | |
| 486 'views_unittests.exe', | |
| 487 'webkit_unit_tests.exe', | |
| 488 'wtf_unittests.exe', | |
| 489 }, | |
| 490 } | |
| 491 | |
| 492 def get_files_to_compare(build_dir, recursive=False): | |
| 493 """Get the list of files to compare.""" | |
| 494 allowed = frozenset( | |
| 495 ('', '.apk', '.app', '.dll', '.dylib', '.exe', '.nexe', '.so')) | |
| 496 non_x_ok_exts = frozenset(('.apk', '.isolated')) | |
| 497 def check(f): | |
| 498 if not os.path.isfile(f): | |
| 499 return False | |
| 500 if os.path.basename(f).startswith('.'): | |
| 501 return False | |
| 502 ext = os.path.splitext(f)[1] | |
| 503 if ext in non_x_ok_exts: | |
| 504 return True | |
| 505 return ext in allowed and os.access(f, os.X_OK) | |
| 506 | |
| 507 ret_files = set() | |
| 508 for root, dirs, files in os.walk(build_dir): | |
| 509 if not recursive: | |
| 510 dirs[:] = [d for d in dirs if d.endswith('_apk')] | |
| 511 for f in (f for f in files if check(os.path.join(root, f))): | |
| 512 ret_files.add(os.path.relpath(os.path.join(root, f), build_dir)) | |
| 513 return ret_files | |
| 514 | |
| 515 | |
| 516 def diff_dict(a, b): | |
| 517 """Returns a yaml-like textural diff of two dict. | |
| 518 | |
| 519 It is currently optimized for the .isolated format. | |
| 520 """ | |
| 521 out = '' | |
| 522 for key in set(a) | set(b): | |
| 523 va = a.get(key) | |
| 524 vb = b.get(key) | |
| 525 if va.__class__ != vb.__class__: | |
| 526 out += '- %s: %r != %r\n' % (key, va, vb) | |
| 527 elif isinstance(va, dict): | |
| 528 c = diff_dict(va, vb) | |
| 529 if c: | |
| 530 out += '- %s:\n%s\n' % ( | |
| 531 key, '\n'.join(' ' + l for l in c.splitlines())) | |
| 532 elif va != vb: | |
| 533 out += '- %s: %s != %s\n' % (key, va, vb) | |
| 534 return out.rstrip() | |
| 535 | |
| 536 | |
| 537 def diff_binary(first_filepath, second_filepath, file_len): | |
| 538 """Returns a compact binary diff if the diff is small enough.""" | |
| 539 CHUNK_SIZE = 32 | |
| 540 MAX_STREAMS = 10 | |
| 541 diffs = 0 | |
| 542 streams = [] | |
| 543 offset = 0 | |
| 544 with open(first_filepath, 'rb') as lhs: | |
| 545 with open(second_filepath, 'rb') as rhs: | |
| 546 while True: | |
| 547 lhs_data = lhs.read(CHUNK_SIZE) | |
| 548 rhs_data = rhs.read(CHUNK_SIZE) | |
| 549 if not lhs_data: | |
| 550 break | |
| 551 if lhs_data != rhs_data: | |
| 552 diffs += sum(l != r for l, r in zip(lhs_data, rhs_data)) | |
| 553 if streams is not None: | |
| 554 if len(streams) < MAX_STREAMS: | |
| 555 streams.append((offset, lhs_data, rhs_data)) | |
| 556 else: | |
| 557 streams = None | |
| 558 offset += len(lhs_data) | |
| 559 del lhs_data | |
| 560 del rhs_data | |
| 561 if not diffs: | |
| 562 return None | |
| 563 result = '%d out of %d bytes are different (%.2f%%)' % ( | |
| 564 diffs, file_len, 100.0 * diffs / file_len) | |
| 565 if streams: | |
| 566 encode = lambda text: ''.join(i if 31 < ord(i) < 128 else '.' for i in text) | |
| 567 for offset, lhs_data, rhs_data in streams: | |
| 568 lhs_line = '%s \'%s\'' % (lhs_data.encode('hex'), encode(lhs_data)) | |
| 569 rhs_line = '%s \'%s\'' % (rhs_data.encode('hex'), encode(rhs_data)) | |
| 570 diff = list(difflib.Differ().compare([lhs_line], [rhs_line]))[-1][2:-1] | |
| 571 result += '\n 0x%-8x: %s\n %s\n %s' % ( | |
| 572 offset, lhs_line, rhs_line, diff) | |
| 573 return result | |
| 574 | |
| 575 | |
| 576 def compare_files(first_filepath, second_filepath): | |
| 577 """Compares two binaries and return the number of differences between them. | |
| 578 | |
| 579 Returns None if the files are equal, a string otherwise. | |
| 580 """ | |
| 581 if first_filepath.endswith('.isolated'): | |
| 582 with open(first_filepath, 'rb') as f: | |
| 583 lhs = json.load(f) | |
| 584 with open(second_filepath, 'rb') as f: | |
| 585 rhs = json.load(f) | |
| 586 diff = diff_dict(lhs, rhs) | |
| 587 if diff: | |
| 588 return '\n' + '\n'.join(' ' + line for line in diff.splitlines()) | |
| 589 # else, falls through binary comparison, it must be binary equal too. | |
| 590 | |
| 591 file_len = os.stat(first_filepath).st_size | |
| 592 if file_len != os.stat(second_filepath).st_size: | |
| 593 return 'different size: %d != %d' % ( | |
| 594 file_len, os.stat(second_filepath).st_size) | |
| 595 | |
| 596 return diff_binary(first_filepath, second_filepath, file_len) | |
| 597 | |
| 598 | |
| 599 def compare_build_artifacts(first_dir, second_dir, target_platform, | |
| 600 recursive=False): | |
| 601 """Compares the artifacts from two distinct builds.""" | |
| 602 if not os.path.isdir(first_dir): | |
| 603 print >> sys.stderr, '%s isn\'t a valid directory.' % first_dir | |
| 604 return 1 | |
| 605 if not os.path.isdir(second_dir): | |
| 606 print >> sys.stderr, '%s isn\'t a valid directory.' % second_dir | |
| 607 return 1 | |
| 608 | |
| 609 epoch_hex = struct.pack('<I', int(time.time())).encode('hex') | |
| 610 print('Epoch: %s' % | |
| 611 ' '.join(epoch_hex[i:i+2] for i in xrange(0, len(epoch_hex), 2))) | |
| 612 | |
| 613 with open(os.path.join(BASE_DIR, 'deterministic_build_blacklist.json')) as f: | |
| 614 blacklist = frozenset(json.load(f)) | |
| 615 whitelist = WHITELIST[target_platform] | |
| 616 | |
| 617 # The two directories. | |
| 618 first_list = get_files_to_compare(first_dir, recursive) - blacklist | |
| 619 second_list = get_files_to_compare(second_dir, recursive) - blacklist | |
| 620 | |
| 621 equals = [] | |
| 622 expected_diffs = [] | |
| 623 unexpected_diffs = [] | |
| 624 unexpected_equals = [] | |
| 625 all_files = sorted(first_list & second_list) | |
| 626 missing_files = sorted(first_list.symmetric_difference(second_list)) | |
| 627 if missing_files: | |
| 628 print >> sys.stderr, 'Different list of files in both directories:' | |
| 629 print >> sys.stderr, '\n'.join(' ' + i for i in missing_files) | |
| 630 unexpected_diffs.extend(missing_files) | |
| 631 | |
| 632 max_filepath_len = max(len(n) for n in all_files) | |
| 633 for f in all_files: | |
| 634 first_file = os.path.join(first_dir, f) | |
| 635 second_file = os.path.join(second_dir, f) | |
| 636 result = compare_files(first_file, second_file) | |
| 637 if not result: | |
| 638 tag = 'equal' | |
| 639 equals.append(f) | |
| 640 if f in whitelist: | |
| 641 unexpected_equals.append(f) | |
| 642 else: | |
| 643 if f in whitelist: | |
| 644 expected_diffs.append(f) | |
| 645 tag = 'expected' | |
| 646 else: | |
| 647 unexpected_diffs.append(f) | |
| 648 tag = 'unexpected' | |
| 649 result = 'DIFFERENT (%s): %s' % (tag, result) | |
| 650 print('%-*s: %s' % (max_filepath_len, f, result)) | |
| 651 unexpected_diffs.sort() | |
| 652 | |
| 653 print('Equals: %d' % len(equals)) | |
| 654 print('Expected diffs: %d' % len(expected_diffs)) | |
| 655 print('Unexpected diffs: %d' % len(unexpected_diffs)) | |
| 656 if unexpected_diffs: | |
| 657 print('Unexpected files with diffs:\n') | |
| 658 for u in unexpected_diffs: | |
| 659 print(' %s' % u) | |
| 660 if unexpected_equals: | |
| 661 print('Unexpected files with no diffs:\n') | |
| 662 for u in unexpected_equals: | |
| 663 print(' %s' % u) | |
| 664 | |
| 665 return int(bool(unexpected_diffs)) | |
| 666 | |
| 667 | |
| 668 def main(): | |
| 669 parser = optparse.OptionParser(usage='%prog [options]') | |
| 670 parser.add_option( | |
| 671 '-f', '--first-build-dir', help='The first build directory.') | |
| 672 parser.add_option( | |
| 673 '-s', '--second-build-dir', help='The second build directory.') | |
| 674 parser.add_option('-r', '--recursive', action='store_true', default=False, | |
| 675 help='Indicates if the comparison should be recursive.') | |
| 676 parser.add_option('-t', '--target-platform', help='The target platform.') | |
| 677 options, _ = parser.parse_args() | |
| 678 | |
| 679 if not options.first_build_dir: | |
| 680 parser.error('--first-build-dir is required') | |
| 681 if not options.second_build_dir: | |
| 682 parser.error('--second-build-dir is required') | |
| 683 if not options.target_platform: | |
| 684 parser.error('--target-platform is required') | |
| 685 | |
| 686 return compare_build_artifacts(os.path.abspath(options.first_build_dir), | |
| 687 os.path.abspath(options.second_build_dir), | |
| 688 options.target_platform, | |
| 689 options.recursive) | |
| 690 | |
| 691 | |
| 692 if __name__ == '__main__': | |
| 693 sys.exit(main()) | |
| OLD | NEW |