| OLD | NEW |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import os | 5 import os |
| 6 import re | 6 import re |
| 7 | 7 |
| 8 from google.appengine.ext import testbed | 8 from google.appengine.ext import testbed |
| 9 import webapp2 | 9 import webapp2 |
| 10 import webtest | 10 import webtest |
| 11 | 11 |
| 12 from testing_utils import testing | 12 from testing_utils import testing |
| 13 | 13 |
| 14 from handlers import build_failure | 14 from handlers import build_failure |
| 15 from handlers import handlers_util |
| 15 from model.wf_analysis import WfAnalysis | 16 from model.wf_analysis import WfAnalysis |
| 16 from model import wf_analysis_status | 17 from model import wf_analysis_status |
| 17 from waterfall import buildbot | 18 from waterfall import buildbot |
| 18 from waterfall import waterfall_config | 19 from waterfall import waterfall_config |
| 19 | 20 |
| 20 | 21 |
| 21 # Root directory appengine/findit. | 22 # Root directory appengine/findit. |
| 22 ROOT_DIR = os.path.join(os.path.dirname(__file__), | 23 ROOT_DIR = os.path.join(os.path.dirname(__file__), |
| 23 os.path.pardir, os.path.pardir) | 24 os.path.pardir, os.path.pardir) |
| 24 | 25 |
| 26 SAMPLE_TRY_JOB_INFO = { |
| 27 'm/b/119': { |
| 28 'step1 on platform':{ |
| 29 'try_jobs': [ |
| 30 { |
| 31 'ref_name': 'step1', |
| 32 'try_job_key': 'm/b/119', |
| 33 'task_id': 'task1', |
| 34 'task_url': 'url/task1', |
| 35 'status': wf_analysis_status.ANALYZED, |
| 36 'try_job_url': ( |
| 37 'http://build.chromium.org/p/tryserver.chromium.' |
| 38 'linux/builders/linux_variable/builds/121'), |
| 39 'try_job_build_number': 121, |
| 40 'tests': ['test3'], |
| 41 'culprit': {} |
| 42 }, |
| 43 { |
| 44 'ref_name': 'step1', |
| 45 'try_job_key': 'm/b/119', |
| 46 'task_id': 'task1', |
| 47 'task_url': 'url/task1', |
| 48 'status': wf_analysis_status.ANALYZED, |
| 49 'try_job_url': ( |
| 50 'http://build.chromium.org/p/tryserver.chromium.' |
| 51 'linux/builders/linux_variable/builds/121'), |
| 52 'try_job_build_number': 121, |
| 53 'culprit': { |
| 54 'revision': 'rev2', |
| 55 'commit_position': '2', |
| 56 'review_url': 'url_2' |
| 57 }, |
| 58 'tests': ['test2'] |
| 59 }, |
| 60 { |
| 61 'ref_name': 'step1', |
| 62 'try_job_key': 'm/b/119', |
| 63 'status': handlers_util.FLAKY, |
| 64 'task_id': 'task1', |
| 65 'task_url': 'url/task1', |
| 66 'tests': ['test4'] |
| 67 }, |
| 68 { |
| 69 'ref_name': 'step1', |
| 70 'try_job_key': 'm/b/120', |
| 71 'status': handlers_util.NO_TRY_JOB_REASON_MAP[ |
| 72 wf_analysis_status.PENDING], |
| 73 'task_id': 'task2', |
| 74 'task_url': 'url/task2', |
| 75 'tests': ['test1'] |
| 76 } |
| 77 ] |
| 78 } |
| 79 }, |
| 80 'm/b/120': { |
| 81 'compile': { |
| 82 'try_jobs': [ |
| 83 { |
| 84 'try_job_key': 'm/b/120', |
| 85 'status': wf_analysis_status.ANALYZED, |
| 86 'try_job_build_number': 120, |
| 87 'try_job_url': ( |
| 88 'http://build.chromium.org/p/tryserver.chromium.' |
| 89 'linux/builders/linux_variable/builds/120'), |
| 90 'culprit': { |
| 91 'revision': 'rev2', |
| 92 'commit_position': '2', |
| 93 'review_url': 'url_2' |
| 94 } |
| 95 } |
| 96 ] |
| 97 } |
| 98 } |
| 99 } |
| 100 |
| 25 | 101 |
| 26 class BuildFailureTest(testing.AppengineTestCase): | 102 class BuildFailureTest(testing.AppengineTestCase): |
| 27 app_module = webapp2.WSGIApplication([ | 103 app_module = webapp2.WSGIApplication([ |
| 28 ('/build-failure', build_failure.BuildFailure), | 104 ('/build-failure', build_failure.BuildFailure), |
| 29 ], debug=True) | 105 ], debug=True) |
| 30 | 106 |
| 31 def setUp(self): | 107 def setUp(self): |
| 32 super(BuildFailureTest, self).setUp() | 108 super(BuildFailureTest, self).setUp() |
| 33 | 109 |
| 34 # Setup clean task queues. | 110 # Setup clean task queues. |
| 35 self.testbed.init_taskqueue_stub(root_path=ROOT_DIR) | 111 self.testbed.init_taskqueue_stub(root_path=ROOT_DIR) |
| 36 self.taskqueue_stub = self.testbed.get_stub(testbed.TASKQUEUE_SERVICE_NAME) | 112 self.taskqueue_stub = self.testbed.get_stub(testbed.TASKQUEUE_SERVICE_NAME) |
| 37 for queue in self.taskqueue_stub.GetQueues(): | 113 for queue in self.taskqueue_stub.GetQueues(): |
| 38 self.taskqueue_stub.FlushQueue(queue['name']) | 114 self.taskqueue_stub.FlushQueue(queue['name']) |
| 39 | 115 |
| 116 def MockedGetAllTryJobResults(master_name, builder_name, build_number): |
| 117 build_key = '%s/%s/%d' % (master_name, builder_name, build_number) |
| 118 return SAMPLE_TRY_JOB_INFO.get(build_key, None) |
| 119 self.mock(handlers_util, 'GetAllTryJobResults', MockedGetAllTryJobResults) |
| 120 |
| 40 def testGetTriageHistoryWhenUserIsNotAdmin(self): | 121 def testGetTriageHistoryWhenUserIsNotAdmin(self): |
| 41 analysis = WfAnalysis.Create('m', 'b', 1) | 122 analysis = WfAnalysis.Create('m', 'b', 1) |
| 42 analysis.status = wf_analysis_status.ANALYZED | 123 analysis.status = wf_analysis_status.ANALYZED |
| 43 analysis.triage_history = [ | 124 analysis.triage_history = [ |
| 44 { | 125 { |
| 45 'triage_timestamp': 1438380761, | 126 'triage_timestamp': 1438380761, |
| 46 'user_name': 'test', | 127 'user_name': 'test', |
| 47 'result_status': 'dummy status', | 128 'result_status': 'dummy status', |
| 48 'version': 'dummy version', | 129 'version': 'dummy version', |
| 49 } | 130 } |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 138 master_name, builder_name, build_number) | 219 master_name, builder_name, build_number) |
| 139 | 220 |
| 140 def MockMasterIsSupported(*_): | 221 def MockMasterIsSupported(*_): |
| 141 return True | 222 return True |
| 142 self.mock(waterfall_config, 'MasterIsSupported', MockMasterIsSupported) | 223 self.mock(waterfall_config, 'MasterIsSupported', MockMasterIsSupported) |
| 143 | 224 |
| 144 response = self.test_app.get('/build-failure', params={'url': build_url}) | 225 response = self.test_app.get('/build-failure', params={'url': build_url}) |
| 145 self.assertEquals(200, response.status_int) | 226 self.assertEquals(200, response.status_int) |
| 146 | 227 |
| 147 self.assertEqual(1, len(self.taskqueue_stub.get_filtered_tasks())) | 228 self.assertEqual(1, len(self.taskqueue_stub.get_filtered_tasks())) |
| 229 |
| 230 def testGetOrganizedAnalysisResultBySuspectedCLNonSwarming(self): |
| 231 analysis_result = { |
| 232 'failures': [ |
| 233 { |
| 234 'step_name': 'a', |
| 235 'first_failure': 98, |
| 236 'last_pass': None, |
| 237 'supported': True, |
| 238 'suspected_cls': [ |
| 239 { |
| 240 'build_number': 99, |
| 241 'repo_name': 'chromium', |
| 242 'revision': 'r99_2', |
| 243 'commit_position': None, |
| 244 'url': None, |
| 245 'score': 2, |
| 246 'hints': { |
| 247 'modified f99_2.cc (and it was in log)': 2, |
| 248 }, |
| 249 } |
| 250 ], |
| 251 } |
| 252 ] |
| 253 } |
| 254 |
| 255 result = build_failure._GetOrganizedAnalysisResultBySuspectedCL( |
| 256 analysis_result) |
| 257 |
| 258 expected_result = { |
| 259 'a': [ |
| 260 { |
| 261 'first_failure': 98, |
| 262 'last_pass': None, |
| 263 'supported': True, |
| 264 'suspected_cls': [ |
| 265 { |
| 266 'build_number': 99, |
| 267 'repo_name': 'chromium', |
| 268 'revision': 'r99_2', |
| 269 'commit_position': None, |
| 270 'url': None, |
| 271 'score': 2, |
| 272 'hints': { |
| 273 'modified f99_2.cc (and it was in log)': 2, |
| 274 }, |
| 275 } |
| 276 ], |
| 277 'tests': [] |
| 278 } |
| 279 ] |
| 280 } |
| 281 self.assertEqual(expected_result, result) |
| 282 |
| 283 def testGetOrganizedAnalysisResultBySuspectedCLSwarming(self): |
| 284 analysis_result = { |
| 285 'failures': [ |
| 286 { |
| 287 'step_name': 'b', |
| 288 'first_failure': 98, |
| 289 'last_pass': 96, |
| 290 'supported': True, |
| 291 'suspected_cls': [ |
| 292 { |
| 293 'build_number': 98, |
| 294 'repo_name': 'chromium', |
| 295 'revision': 'r98_1', |
| 296 'commit_position': None, |
| 297 'url': None, |
| 298 'score': 4, |
| 299 'hints': { |
| 300 'modified f98.cc[123, 456] (and it was in log)': 4, |
| 301 }, |
| 302 } |
| 303 ], |
| 304 'tests': [ |
| 305 { |
| 306 'test_name': 'Unittest2.Subtest1', |
| 307 'first_failure': 98, |
| 308 'last_pass': 97, |
| 309 'suspected_cls': [ |
| 310 { |
| 311 'build_number': 98, |
| 312 'repo_name': 'chromium', |
| 313 'revision': 'r98_1', |
| 314 'commit_position': None, |
| 315 'url': None, |
| 316 'score': 4, |
| 317 'hints': { |
| 318 ('modified f98.cc[123] ' |
| 319 '(and it was in log)'): 4, |
| 320 }, |
| 321 } |
| 322 ] |
| 323 }, |
| 324 { |
| 325 'test_name': 'Unittest3.Subtest2', |
| 326 'first_failure': 98, |
| 327 'last_pass': 96, |
| 328 'suspected_cls': [ |
| 329 { |
| 330 'build_number': 98, |
| 331 'repo_name': 'chromium', |
| 332 'revision': 'r98_1', |
| 333 'commit_position': None, |
| 334 'url': None, |
| 335 'score': 4, |
| 336 'hints': { |
| 337 ('modified f98.cc[456] ' |
| 338 '(and it was in log)'): 4, |
| 339 }, |
| 340 } |
| 341 ] |
| 342 }, |
| 343 { |
| 344 'test_name': 'Unittest3.Subtest3', |
| 345 'first_failure': 98, |
| 346 'last_pass': 96, |
| 347 'suspected_cls': [] |
| 348 } |
| 349 ] |
| 350 } |
| 351 ] |
| 352 } |
| 353 |
| 354 result = build_failure._GetOrganizedAnalysisResultBySuspectedCL( |
| 355 analysis_result) |
| 356 |
| 357 expected_result = { |
| 358 'b': [ |
| 359 { |
| 360 'supported': True, |
| 361 'first_failure': 98, |
| 362 'last_pass': 97, |
| 363 'suspected_cls': [ |
| 364 { |
| 365 'build_number': 98, |
| 366 'repo_name': 'chromium', |
| 367 'revision': 'r98_1', |
| 368 'commit_position': None, |
| 369 'url': None, |
| 370 'score': 4, |
| 371 'hints': { |
| 372 'modified f98.cc[123, 456] (and it was in log)': 4, |
| 373 }, |
| 374 } |
| 375 ], |
| 376 'tests': ['Unittest2.Subtest1', 'Unittest3.Subtest2'] |
| 377 }, |
| 378 { |
| 379 'first_failure': 98, |
| 380 'last_pass': 96, |
| 381 'supported': True, |
| 382 'suspected_cls': [], |
| 383 'tests': ['Unittest3.Subtest3'] |
| 384 } |
| 385 ] |
| 386 } |
| 387 self.assertEqual(expected_result, result) |
| 388 |
| 389 def testUpdateAnalysisResultWithTryJobInfo(self): |
| 390 master_name = 'm' |
| 391 builder_name = 'b' |
| 392 build_number = 119 |
| 393 organized_results = { |
| 394 'step1 on platform': [ |
| 395 { |
| 396 'supported': True, |
| 397 'first_failure': 119, |
| 398 'last_pass': 118, |
| 399 'suspected_cls': [ |
| 400 { |
| 401 'build_number': 119, |
| 402 'repo_name': 'chromium', |
| 403 'revision': 'r98_1', |
| 404 'commit_position': None, |
| 405 'url': None, |
| 406 'score': 4, |
| 407 'hints': { |
| 408 'modified f98.cc[123, 456] (and it was in log)': 4, |
| 409 }, |
| 410 } |
| 411 ], |
| 412 'tests': ['test2', 'test3'] |
| 413 }, |
| 414 { |
| 415 'first_failure': 119, |
| 416 'last_pass': 118, |
| 417 'supported': True, |
| 418 'suspected_cls': [], |
| 419 'tests': ['test4'] |
| 420 }, |
| 421 { |
| 422 'first_failure': 120, |
| 423 'last_pass': 119, |
| 424 'supported': True, |
| 425 'suspected_cls': [], |
| 426 'tests': ['test1'] |
| 427 } |
| 428 ] |
| 429 } |
| 430 |
| 431 updated_result = build_failure._UpdateAnalysisResultWithTryJobInfo( |
| 432 organized_results, master_name, builder_name, build_number) |
| 433 |
| 434 expected_result = { |
| 435 'step1 on platform':{ |
| 436 'results': { |
| 437 'determined_results': [ |
| 438 { |
| 439 'try_job':{ |
| 440 'ref_name': 'step1', |
| 441 'try_job_key': 'm/b/119', |
| 442 'task_id': 'task1', |
| 443 'task_url': 'url/task1', |
| 444 'status': wf_analysis_status.ANALYZED, |
| 445 'try_job_url': ( |
| 446 'http://build.chromium.org/p/tryserver.chromium' |
| 447 '.linux/builders/linux_variable/builds/121'), |
| 448 'try_job_build_number': 121, |
| 449 'tests': ['test3'], |
| 450 'culprit': {} |
| 451 }, |
| 452 'heuristic_analysis': { |
| 453 'suspected_cls': [ |
| 454 { |
| 455 'build_number': 119, |
| 456 'repo_name': 'chromium', |
| 457 'revision': 'r98_1', |
| 458 'commit_position': None, |
| 459 'url': None, |
| 460 'score': 4, |
| 461 'hints': { |
| 462 ('modified f98.cc[123, 456] ' |
| 463 '(and it was in log)'): 4, |
| 464 }, |
| 465 } |
| 466 ] |
| 467 }, |
| 468 'tests': ['test3'], |
| 469 'first_failure': 119, |
| 470 'last_pass': 118, |
| 471 'supported': True |
| 472 }, |
| 473 { |
| 474 'try_job':{ |
| 475 'ref_name': 'step1', |
| 476 'try_job_key': 'm/b/119', |
| 477 'task_id': 'task1', |
| 478 'task_url': 'url/task1', |
| 479 'status': wf_analysis_status.ANALYZED, |
| 480 'try_job_url': ( |
| 481 'http://build.chromium.org/p/tryserver.chromium' |
| 482 '.linux/builders/linux_variable/builds/121'), |
| 483 'try_job_build_number': 121, |
| 484 'culprit': { |
| 485 'revision': 'rev2', |
| 486 'commit_position': '2', |
| 487 'review_url': 'url_2' |
| 488 }, |
| 489 'tests': ['test2'] |
| 490 }, |
| 491 'heuristic_analysis': { |
| 492 'suspected_cls': [ |
| 493 { |
| 494 'build_number': 119, |
| 495 'repo_name': 'chromium', |
| 496 'revision': 'r98_1', |
| 497 'commit_position': None, |
| 498 'url': None, |
| 499 'score': 4, |
| 500 'hints': { |
| 501 ('modified f98.cc[123, 456] ' |
| 502 '(and it was in log)'): 4, |
| 503 }, |
| 504 } |
| 505 ] |
| 506 }, |
| 507 'tests': ['test2'], |
| 508 'first_failure': 119, |
| 509 'last_pass': 118, |
| 510 'supported': True |
| 511 } |
| 512 ], |
| 513 'flaky_results': [ |
| 514 { |
| 515 'try_job':{ |
| 516 'ref_name': 'step1', |
| 517 'try_job_key': 'm/b/119', |
| 518 'status': handlers_util.FLAKY, |
| 519 'task_id': 'task1', |
| 520 'task_url': 'url/task1', |
| 521 'tests': ['test4'] |
| 522 }, |
| 523 'heuristic_analysis': { |
| 524 'suspected_cls': [] |
| 525 }, |
| 526 'tests': ['test4'], |
| 527 'first_failure': 119, |
| 528 'last_pass': 118, |
| 529 'supported': True |
| 530 } |
| 531 ], |
| 532 'undetermined_results': [ |
| 533 { |
| 534 'try_job':{ |
| 535 'ref_name': 'step1', |
| 536 'try_job_key': 'm/b/120', |
| 537 'status': handlers_util.NO_TRY_JOB_REASON_MAP[ |
| 538 wf_analysis_status.PENDING], |
| 539 'task_id': 'task2', |
| 540 'task_url': 'url/task2', |
| 541 'tests': ['test1'] |
| 542 }, |
| 543 'heuristic_analysis': { |
| 544 'suspected_cls': [] |
| 545 }, |
| 546 'tests': ['test1'], |
| 547 'first_failure': 120, |
| 548 'last_pass': 119, |
| 549 'supported': True |
| 550 } |
| 551 ] |
| 552 } |
| 553 } |
| 554 } |
| 555 |
| 556 self.assertEqual(expected_result, updated_result) |
| 557 |
| 558 def testUpdateAnalysisResultWithTryJobInfoNoTryJobInfo(self): |
| 559 organized_results = { |
| 560 'step1 on platform':{} |
| 561 } |
| 562 result = build_failure._UpdateAnalysisResultWithTryJobInfo( |
| 563 organized_results, 'n', 'b', 120) |
| 564 self.assertEqual({}, result) |
| 565 |
| 566 def testUpdateAnalysisResultWithTryJobInfoCompile(self): |
| 567 organized_results = { |
| 568 'compile': [ |
| 569 { |
| 570 'first_failure': 120, |
| 571 'last_pass': 119, |
| 572 'supported': True, |
| 573 'suspected_cls': [ |
| 574 { |
| 575 'build_number': 120, |
| 576 'repo_name': 'chromium', |
| 577 'revision': 'rev2', |
| 578 'commit_position': None, |
| 579 'url': None, |
| 580 'score': 2, |
| 581 'hints': { |
| 582 'modified f99_2.cc (and it was in log)': 2, |
| 583 }, |
| 584 } |
| 585 ], |
| 586 'tests': [] |
| 587 } |
| 588 ] |
| 589 } |
| 590 result = build_failure._UpdateAnalysisResultWithTryJobInfo( |
| 591 organized_results, 'm', 'b', 120) |
| 592 |
| 593 expected_result = { |
| 594 'compile':{ |
| 595 'results': { |
| 596 'determined_results': [ |
| 597 { |
| 598 'try_job': { |
| 599 'try_job_key': 'm/b/120', |
| 600 'status': wf_analysis_status.ANALYZED, |
| 601 'try_job_build_number': 120, |
| 602 'try_job_url': ( |
| 603 'http://build.chromium.org/p/tryserver.chromium' |
| 604 '.linux/builders/linux_variable/builds/120'), |
| 605 'culprit': { |
| 606 'revision': 'rev2', |
| 607 'commit_position': '2', |
| 608 'review_url': 'url_2' |
| 609 } |
| 610 }, |
| 611 'heuristic_analysis': { |
| 612 'suspected_cls': [ |
| 613 { |
| 614 'build_number': 120, |
| 615 'repo_name': 'chromium', |
| 616 'revision': 'rev2', |
| 617 'commit_position': None, |
| 618 'url': None, |
| 619 'score': 2, |
| 620 'hints': {('modified f99_2.cc ' |
| 621 '(and it was in log)'): 2 |
| 622 }, |
| 623 } |
| 624 ] |
| 625 }, |
| 626 'tests': [], |
| 627 'first_failure': 120, |
| 628 'last_pass': 119, |
| 629 'supported': True |
| 630 } |
| 631 ] |
| 632 } |
| 633 } |
| 634 } |
| 635 self.assertEqual(expected_result, result) |
| OLD | NEW |