diff options
Diffstat (limited to 'Tools/Scripts/webkitpy/tool/bot')
8 files changed, 283 insertions, 49 deletions
diff --git a/Tools/Scripts/webkitpy/tool/bot/botinfo.py b/Tools/Scripts/webkitpy/tool/bot/botinfo.py new file mode 100644 index 0000000..b9fd938 --- /dev/null +++ b/Tools/Scripts/webkitpy/tool/bot/botinfo.py @@ -0,0 +1,39 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +# FIXME: We should consider hanging one of these off the tool object. +class BotInfo(object): + def __init__(self, tool): + self._tool = tool + + def summary_text(self): + # bot_id is also stored on the options dictionary on the tool. + bot_id = self._tool.status_server.bot_id + bot_id_string = "Bot: %s " % (bot_id) if bot_id else "" + return "%sPort: %s Platform: %s" % (bot_id_string, self._tool.port().name(), self._tool.platform.display_name()) diff --git a/Tools/Scripts/webkitpy/tool/bot/botinfo_unittest.py b/Tools/Scripts/webkitpy/tool/bot/botinfo_unittest.py new file mode 100644 index 0000000..054acfc --- /dev/null +++ b/Tools/Scripts/webkitpy/tool/bot/botinfo_unittest.py @@ -0,0 +1,40 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import unittest + +from webkitpy.tool.bot.botinfo import BotInfo +from webkitpy.tool.mocktool import MockTool, MockStatusServer + + +class BotInfoTest(unittest.TestCase): + + def test_summary_text(self): + tool = MockTool() + tool.status_server = MockStatusServer("MockBotId") + self.assertEqual(BotInfo(tool).summary_text(), "Bot: MockBotId Port: MockPort Platform: MockPlatform 1.0") diff --git a/Tools/Scripts/webkitpy/tool/bot/commitqueuetask.py b/Tools/Scripts/webkitpy/tool/bot/commitqueuetask.py index c5d9001..93cbcc8 100644 --- a/Tools/Scripts/webkitpy/tool/bot/commitqueuetask.py +++ b/Tools/Scripts/webkitpy/tool/bot/commitqueuetask.py @@ -28,6 +28,7 @@ from webkitpy.common.system.executive import ScriptError from webkitpy.common.net.layouttestresults import LayoutTestResults +from webkitpy.tool.bot.expectedfailures import ExpectedFailures class CommitQueueTaskDelegate(object): @@ -59,6 +60,8 @@ class CommitQueueTask(object): self._delegate = delegate self._patch = patch self._script_error = None + self._results_archive_from_patch_test_run = None + self._expected_failures = ExpectedFailures() def _validate(self): # Bugs might get closed, or patches might be obsoleted or r-'d while the @@ -132,7 +135,7 @@ class CommitQueueTask(object): "Unable to build without patch") def _test(self): - return self._run_command([ + success = self._run_command([ "build-and-test", "--no-clean", "--no-update", @@ -143,8 +146,11 @@ class CommitQueueTask(object): "Passed tests", "Patch does not pass tests") + self._expected_failures.shrink_expected_failures(self._delegate.layout_test_results(), success) + return success + def _build_and_test_without_patch(self): - return self._run_command([ + success = self._run_command([ "build-and-test", "--force-clean", "--no-update", @@ -155,11 +161,8 @@ class CommitQueueTask(object): "Able to pass tests without patch", "Unable to pass tests without patch (tree is red?)") - def _failing_results_from_last_run(self): - results = self._delegate.layout_test_results() - if not results: - return [] # Makes callers slighty cleaner to not have to deal with None - return results.failing_test_results() + self._expected_failures.shrink_expected_failures(self._delegate.layout_test_results(), success) + return success def _land(self): # Unclear if this should pass --quiet or not. If --parent-command always does the reporting, then it should. @@ -177,36 +180,59 @@ class CommitQueueTask(object): def _report_flaky_tests(self, flaky_test_results, results_archive): self._delegate.report_flaky_tests(self._patch, flaky_test_results, results_archive) + def _results_failed_different_tests(self, first, second): + first_failing_tests = [] if not first else first.failing_tests() + second_failing_tests = [] if not second else second.failing_tests() + return first_failing_tests != second_failing_tests + def _test_patch(self): if self._test(): return True - first_results = self._failing_results_from_last_run() - first_failing_tests = [result.filename for result in first_results] + # Note: archive_last_layout_test_results deletes the results directory, making these calls order-sensitve. + # We could remove this dependency by building the layout_test_results from the archive. + first_results = self._delegate.layout_test_results() first_results_archive = self._delegate.archive_last_layout_test_results(self._patch) + + if self._expected_failures.failures_were_expected(first_results): + return True + if self._test(): - # Only report flaky tests if we were successful at archiving results. - if first_results_archive: - self._report_flaky_tests(first_results, first_results_archive) + # Only report flaky tests if we were successful at parsing results.html and archiving results. + if first_results and first_results_archive: + self._report_flaky_tests(first_results.failing_test_results(), first_results_archive) return True - second_results = self._failing_results_from_last_run() - second_failing_tests = [result.filename for result in second_results] - if first_failing_tests != second_failing_tests: - # We could report flaky tests here, but since run-webkit-tests - # is run with --exit-after-N-failures=1, we would need to - # be careful not to report constant failures as flaky due to earlier - # flaky test making them not fail (no results) in one of the runs. + second_results = self._delegate.layout_test_results() + if self._results_failed_different_tests(first_results, second_results): + # We could report flaky tests here, but we would need to be careful + # to use similar checks to ExpectedFailures._can_trust_results + # to make sure we don't report constant failures as flakes when + # we happen to hit the --exit-after-N-failures limit. # See https://bugs.webkit.org/show_bug.cgi?id=51272 return False + # Archive (and remove) second results so layout_test_results() after + # build_and_test_without_patch won't use second results instead of the clean-tree results. + second_results_archive = self._delegate.archive_last_layout_test_results(self._patch) + if self._build_and_test_without_patch(): - return self.report_failure() # The error from the previous ._test() run is real, report it. - return False # Tree must be red, just retry later. + # The error from the previous ._test() run is real, report it. + return self.report_failure(first_results_archive) + + clean_tree_results = self._delegate.layout_test_results() + self._expected_failures.grow_expected_failures(clean_tree_results) + + return False # Tree must be redder than we expected, just retry later. + + def results_archive_from_patch_test_run(self, patch): + assert(self._patch.id() == patch.id()) # CommitQueueTask is not currently re-useable. + return self._results_archive_from_patch_test_run - def report_failure(self): + def report_failure(self, results_archive=None): if not self._validate(): return False + self._results_archive_from_patch_test_run = results_archive raise self._script_error def run(self): diff --git a/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py b/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py index 87d0ab5..7324d78 100644 --- a/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py +++ b/Tools/Scripts/webkitpy/tool/bot/commitqueuetask_unittest.py @@ -30,6 +30,7 @@ from datetime import datetime import unittest from webkitpy.common.net import bugzilla +from webkitpy.common.net.layouttestresults import LayoutTestResults from webkitpy.common.system.deprecated_logging import error, log from webkitpy.common.system.outputcapture import OutputCapture from webkitpy.layout_tests.layout_package import test_results @@ -77,9 +78,6 @@ class MockCommitQueue(CommitQueueTaskDelegate): class CommitQueueTaskTest(unittest.TestCase): - def _mock_test_result(self, testname): - return test_results.TestResult(testname, [test_failures.FailureTextMismatch()]) - def _run_through_task(self, commit_queue, expected_stderr, expected_exception=None, expect_retry=False): tool = MockTool(log_executive=True) patch = tool.bugs.fetch_attachment(197) @@ -190,6 +188,9 @@ command_failed: failure_message='Unable to build without patch' script_error='MO None, ScriptError("MOCK tests failure"), ]) + # CommitQueueTask will only report flaky tests if we successfully parsed + # results.html and returned a LayoutTestResults object, so we fake one. + commit_queue.layout_test_results = lambda: LayoutTestResults([]) expected_stderr = """run_webkit_patch: ['clean'] command_passed: success_message='Cleaned working directory' patch='197' run_webkit_patch: ['update'] @@ -217,6 +218,7 @@ command_passed: success_message='Landed patch' patch='197' None, ScriptError("MOCK tests failure"), ]) + commit_queue.layout_test_results = lambda: LayoutTestResults([]) # It's possible delegate to fail to archive layout tests, don't try to report # flaky tests when that happens. commit_queue.archive_last_layout_test_results = lambda patch: None @@ -237,10 +239,25 @@ command_passed: success_message='Landed patch' patch='197' """ self._run_through_task(commit_queue, expected_stderr) - _double_flaky_test_counter = 0 - def test_double_flaky_test_failure(self): - commit_queue = MockCommitQueue([ + class DoubleFlakyCommitQueue(MockCommitQueue): + def __init__(self, error_plan): + MockCommitQueue.__init__(self, error_plan) + self._double_flaky_test_counter = 0 + + def run_command(self, command): + self._double_flaky_test_counter += 1 + MockCommitQueue.run_command(self, command) + + def _mock_test_result(self, testname): + return test_results.TestResult(testname, [test_failures.FailureTextMismatch()]) + + def layout_test_results(self): + if self._double_flaky_test_counter % 2: + return LayoutTestResults([self._mock_test_result('foo.html')]) + return LayoutTestResults([self._mock_test_result('bar.html')]) + + commit_queue = DoubleFlakyCommitQueue([ None, None, None, @@ -268,15 +285,6 @@ command_failed: failure_message='Patch does not pass tests' script_error='MOCK t tool = MockTool(log_executive=True) patch = tool.bugs.fetch_attachment(197) task = CommitQueueTask(commit_queue, patch) - self._double_flaky_test_counter = 0 - - def mock_failing_results_from_last_run(): - CommitQueueTaskTest._double_flaky_test_counter += 1 - if CommitQueueTaskTest._double_flaky_test_counter % 2: - return [self._mock_test_result('foo.html')] - return [self._mock_test_result('bar.html')] - - task._failing_results_from_last_run = mock_failing_results_from_last_run success = OutputCapture().assert_outputs(self, task.run, expected_stderr=expected_stderr) self.assertEqual(success, False) @@ -302,6 +310,7 @@ command_failed: failure_message='Patch does not pass tests' script_error='MOCK t archive_last_layout_test_results: patch='197' run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive'] command_failed: failure_message='Patch does not pass tests' script_error='MOCK test failure again' patch='197' +archive_last_layout_test_results: patch='197' run_webkit_patch: ['build-and-test', '--force-clean', '--no-update', '--build', '--test', '--non-interactive'] command_passed: success_message='Able to pass tests without patch' patch='197' """ @@ -330,6 +339,7 @@ command_failed: failure_message='Patch does not pass tests' script_error='MOCK t archive_last_layout_test_results: patch='197' run_webkit_patch: ['build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive'] command_failed: failure_message='Patch does not pass tests' script_error='MOCK test failure again' patch='197' +archive_last_layout_test_results: patch='197' run_webkit_patch: ['build-and-test', '--force-clean', '--no-update', '--build', '--test', '--non-interactive'] command_failed: failure_message='Unable to pass tests without patch (tree is red?)' script_error='MOCK clean test failure' patch='197' """ diff --git a/Tools/Scripts/webkitpy/tool/bot/expectedfailures.py b/Tools/Scripts/webkitpy/tool/bot/expectedfailures.py new file mode 100644 index 0000000..8736ac0 --- /dev/null +++ b/Tools/Scripts/webkitpy/tool/bot/expectedfailures.py @@ -0,0 +1,55 @@ +# Copyright (c) 2011 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +class ExpectedFailures(object): + def __init__(self): + self._failures = set() + + def _can_trust_results(self, results): + if not results or not results.failure_limit_count(): + return False + return len(results.failing_tests()) != 0 and len(results.failing_tests()) != results.failure_limit_count() + + def failures_were_expected(self, results): + if not self._can_trust_results(results): + return False + return set(results.failing_tests()) <= self._failures + + def shrink_expected_failures(self, results, run_success): + if run_success: + self._failures = set() + elif self._can_trust_results(results): + # Remove all expected failures which are not in the new failing results. + self._failures.intersection_update(set(results.failing_tests())) + + def grow_expected_failures(self, results): + if not self._can_trust_results(results): + return + self._failures.update(results.failing_tests()) + # FIXME: Should we assert() here that expected_failures never crosses a certain size? diff --git a/Tools/Scripts/webkitpy/tool/bot/expectedfailures_unittest.py b/Tools/Scripts/webkitpy/tool/bot/expectedfailures_unittest.py new file mode 100644 index 0000000..8a2702b --- /dev/null +++ b/Tools/Scripts/webkitpy/tool/bot/expectedfailures_unittest.py @@ -0,0 +1,73 @@ +# Copyright (c) 2009 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import unittest + +from webkitpy.tool.bot.expectedfailures import ExpectedFailures + + +class MockResults(object): + def __init__(self, failing_tests=[], failure_limit=10): + self._failing_tests = failing_tests + self._failure_limit_count = failure_limit + + def failure_limit_count(self): + return self._failure_limit_count + + def failing_tests(self): + return self._failing_tests + + +class ExpectedFailuresTest(unittest.TestCase): + def _assert_can_trust(self, results, can_trust): + self.assertEquals(ExpectedFailures()._can_trust_results(results), can_trust) + + def test_can_trust_results(self): + self._assert_can_trust(None, False) + self._assert_can_trust(MockResults(failing_tests=[], failure_limit=None), False) + self._assert_can_trust(MockResults(failing_tests=[], failure_limit=10), False) + self._assert_can_trust(MockResults(failing_tests=[1], failure_limit=None), False) + self._assert_can_trust(MockResults(failing_tests=[1], failure_limit=2), True) + self._assert_can_trust(MockResults(failing_tests=[1], failure_limit=1), False) + + def _assert_expected(self, expected_failures, failures, expected): + self.assertEqual(expected_failures.failures_were_expected(MockResults(failures)), expected) + + def test_failures_were_expected(self): + failures = ExpectedFailures() + failures.grow_expected_failures(MockResults(['foo.html'])) + self._assert_expected(failures, ['foo.html'], True) + self._assert_expected(failures, ['bar.html'], False) + failures.shrink_expected_failures(MockResults(['baz.html']), False) + self._assert_expected(failures, ['foo.html'], False) + self._assert_expected(failures, ['baz.html'], False) + + failures.grow_expected_failures(MockResults(['baz.html'])) + self._assert_expected(failures, ['baz.html'], True) + failures.shrink_expected_failures(MockResults(), True) + self._assert_expected(failures, ['baz.html'], False) diff --git a/Tools/Scripts/webkitpy/tool/bot/flakytestreporter.py b/Tools/Scripts/webkitpy/tool/bot/flakytestreporter.py index bec593b..68e1c94 100644 --- a/Tools/Scripts/webkitpy/tool/bot/flakytestreporter.py +++ b/Tools/Scripts/webkitpy/tool/bot/flakytestreporter.py @@ -33,6 +33,7 @@ import os.path from webkitpy.common.net.layouttestresults import path_for_layout_test, LayoutTestResults from webkitpy.common.config import urls +from webkitpy.tool.bot.botinfo import BotInfo from webkitpy.tool.grammar import plural, pluralize, join_with_separators _log = logging.getLogger(__name__) @@ -42,6 +43,7 @@ class FlakyTestReporter(object): def __init__(self, tool, bot_name): self._tool = tool self._bot_name = bot_name + self._bot_info = BotInfo(tool) def _author_emails_for_test(self, flaky_test): test_path = path_for_layout_test(flaky_test) @@ -121,15 +123,10 @@ If you would like to track this test fix with another bug, please close this bug authors_string = join_with_separators(sorted(author_emails)) return " (%s: %s)" % (heading_string, authors_string) - def _bot_information(self): - bot_id = self._tool.status_server.bot_id - bot_id_string = "Bot: %s " % (bot_id) if bot_id else "" - return "%sPort: %s Platform: %s" % (bot_id_string, self._tool.port().name(), self._tool.platform.display_name()) - def _latest_flake_message(self, flaky_result, patch): failure_messages = [failure.message() for failure in flaky_result.failures] flake_message = "The %s just saw %s flake (%s) while processing attachment %s on bug %s." % (self._bot_name, flaky_result.filename, ", ".join(failure_messages), patch.id(), patch.bug_id()) - return "%s\n%s" % (flake_message, self._bot_information()) + return "%s\n%s" % (flake_message, self._bot_info.summary_text()) def _results_diff_path_for_test(self, test_path): # FIXME: This is a big hack. We should get this path from results.json diff --git a/Tools/Scripts/webkitpy/tool/bot/flakytestreporter_unittest.py b/Tools/Scripts/webkitpy/tool/bot/flakytestreporter_unittest.py index 26c98c1..1e3f35a 100644 --- a/Tools/Scripts/webkitpy/tool/bot/flakytestreporter_unittest.py +++ b/Tools/Scripts/webkitpy/tool/bot/flakytestreporter_unittest.py @@ -97,12 +97,6 @@ blocked: 50856 bug = tool.bugs.fetch_bug(78) self.assertEqual(reporter._follow_duplicate_chain(bug).id(), 76) - def test_bot_information(self): - tool = MockTool() - tool.status_server = MockStatusServer("MockBotId") - reporter = FlakyTestReporter(tool, 'dummy-queue') - self.assertEqual(reporter._bot_information(), "Bot: MockBotId Port: MockPort Platform: MockPlatform 1.0") - def test_report_flaky_tests_creating_bug(self): tool = MockTool() tool.filesystem = MockFileSystem({"/mock/foo/bar-diffs.txt": "mock"}) |