Beispiel #1
0
class TestBisector(unittest.TestCase):
    def setUp(self):
        self.handler = MagicMock(find_fix=False, ensure_good_and_bad=False)
        self.test_runner = Mock()
        self.bisector = Bisector(Mock(), self.test_runner,
                                 Mock(),
                                 dl_in_background=False)
        self.bisector.download_background = False

    def test__bisect_no_data(self):
        build_range = MyBuildData()
        result = self.bisector._bisect(self.handler, build_range)
        # test that handler methods where called
        self.handler.set_build_range.assert_called_with(build_range)
        self.handler.no_data.assert_called_once_with()
        # check return code
        self.assertEqual(result, Bisection.NO_DATA)

    def test__bisect_finished(self):
        build_range = MyBuildData([1])
        result = self.bisector._bisect(self.handler, build_range)
        # test that handler methods where called
        self.handler.set_build_range.assert_called_with(build_range)
        self.handler.finished.assert_called_once_with()
        # check return code
        self.assertEqual(result, Bisection.FINISHED)

    def do__bisect(self, build_range, verdicts):
        iter_verdict = iter(verdicts)

        def evaluate(build_info, allow_back=False):
            verdict = iter_verdict.next()
            if isinstance(verdict, Exception):
                raise verdict
            return verdict
        self.test_runner.evaluate = Mock(side_effect=evaluate)
        result = self.bisector._bisect(self.handler, build_range)
        return {
            'result': result,
        }

    def test_ensure_good_bad_invalid(self):
        self.handler.ensure_good_and_bad = True
        with self.assertRaisesRegexp(MozRegressionError,
                                     "expected to be good"):
            self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['b'])

        with self.assertRaisesRegexp(MozRegressionError,
                                     "expected to be bad"):
            self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'g'])

    def test_ensure_good_bad(self):
        self.handler.ensure_good_and_bad = True
        data = MyBuildData([1, 2, 3, 4, 5])
        self.do__bisect(data,
                        ['s', 'r', 'g', 'b', 'e'])
        self.test_runner.evaluate.assert_has_calls([
            call(data[0]),  # tested good (then skip)
            call(data[0]),  # tested good (then retry)
            call(data[0]),  # tested good
            call(data[-1]),  # tested bad
        ])
        self.assertEqual(
            self.bisector.download_manager.download_in_background.call_count,
            0
        )

    def test_ensure_good_bad_with_bg_dl(self):
        self.handler.ensure_good_and_bad = True
        self.bisector.dl_in_background = True
        data = MyBuildData([1, 2, 3, 4, 5])
        self.do__bisect(data,
                        ['s', 'r', 'g', 'e'])
        self.test_runner.evaluate.assert_has_calls([
            call(data[0]),  # tested good (then skip)
            call(data[0]),  # tested good (then retry)
            call(data[0]),  # tested good
            call(data[-1]),  # tested bad
        ])
        self.bisector.download_manager.download_in_background.assert_has_calls(
            [call(data[-1]),  # bad in backgound
             call(data[data.mid_point()])]  # and mid build
        )

    def test_ensure_good_bad_with_find_fix(self):
        self.handler.ensure_good_and_bad = True
        self.handler.find_fix = True
        data = MyBuildData([1, 2, 3, 4, 5])
        self.do__bisect(data, ['g', 'e'])
        self.test_runner.evaluate.assert_has_calls([
            call(data[-1]),  # tested good (then skip)
            call(data[0]),  # tested bad
        ])

    def test__bisect_case1(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([3, 4, 5])),
            # we answered bad
            call(MyBuildData([3, 4])),
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_good.assert_called_with(2, MyBuildData([3, 4, 5]))
        self.handler.build_bad.assert_called_with(1, MyBuildData([3, 4]))
        self.assertTrue(self.handler.build_range.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_with_launcher_exception(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]),
                                      ['g', LauncherError("err")])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([3, 4, 5])),
            # launcher exception, equivalent to a skip
            call(MyBuildData([3, 5])),
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_good.assert_called_with(2, MyBuildData([3, 4, 5]))
        self.handler.build_skip.assert_called_with(1)
        self.assertTrue(self.handler.build_range.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_case1_hunt_fix(self):
        self.handler.find_fix = True
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([1, 2, 3])),
            # we answered bad
            call(MyBuildData([2, 3])),
        ])
        # ensure that we called the handler's methods
        self.assertEqual(self.handler.initialize.mock_calls, [call()]*3)
        self.handler.build_good. \
            assert_called_once_with(2, MyBuildData([1, 2, 3]))
        self.handler.build_bad.assert_called_once_with(1, MyBuildData([2, 3]))
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_case2(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3]), ['r', 's'])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3])),
            # we asked for a retry
            call(MyBuildData([1, 2, 3])),
            # we skipped one
            call(MyBuildData([1, 3])),
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_retry.assert_called_with(1)
        self.handler.build_skip.assert_called_with(1)
        self.assertTrue(self.handler.build_range.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_with_back(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]),
                                      ['g', 'back', 'b', 'g'])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([3, 4, 5])),
            # oups! let's go back
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered bad this time
            call(MyBuildData([1, 2, 3])),
            # then good
            call(MyBuildData([2, 3])),
        ])
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_user_exit(self):
        test_result = self.do__bisect(MyBuildData(range(20)), ['e'])
        # check that set_build_range was called
        self.handler.set_build_range.\
            assert_has_calls([call(MyBuildData(range(20)))])
        # ensure that we called the handler's method
        self.handler.initialize.assert_called_once_with()
        self.handler.user_exit.assert_called_with(10)
        # user exit
        self.assertEqual(test_result['result'], Bisection.USER_EXIT)

    def test__bisect_with_background_download(self):
        self.bisector.dl_in_background = True
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            call(MyBuildData([1, 2, 3, 4, 5])),  # first call
            call(MyBuildData([3, 4, 5])),  # we answered good
            call(MyBuildData([3, 4]))  # we answered bad
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_good.assert_called_with(2, MyBuildData([3, 4, 5]))
        self.handler.build_bad.assert_called_with(1, MyBuildData([3, 4]))
        self.assertTrue(self.handler.build_range.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    @patch('mozregression.bisector.Bisector._bisect')
    def test_bisect(self, _bisect):
        _bisect.return_value = 1
        build_range = Mock()
        create_range = Mock(return_value=build_range)
        self.handler.create_range = create_range
        result = self.bisector.bisect(self.handler, 'g', 'b', s=1)
        create_range.assert_called_with(self.bisector.fetch_config,
                                        'g', 'b', s=1)
        self.assertFalse(build_range.reverse.called)
        _bisect.assert_called_with(self.handler, build_range)
        self.assertEqual(result, 1)

    @patch('mozregression.bisector.Bisector._bisect')
    def test_bisect_reverse(self, _bisect):
        build_range = Mock()
        create_range = Mock(return_value=build_range)
        self.handler.create_range = create_range
        self.handler.find_fix = True
        self.bisector.bisect(self.handler, 'g', 'b', s=1)
        create_range.assert_called_with(self.bisector.fetch_config,
                                        'b', 'g', s=1)
        _bisect.assert_called_with(self.handler, build_range)
Beispiel #2
0
class TestBisector(unittest.TestCase):
    def setUp(self):
        self.handler = MagicMock(find_fix=False)
        self.test_runner = Mock()
        self.bisector = Bisector(Mock(), self.test_runner,
                                 dl_in_background=False)
        self.bisector.download_background = False

    @patch("mozregression.bisector.BuildDownloadManager")
    def test__bisect_no_data(self, dl):
        build_data = MyBuildData()
        result = self.bisector._bisect(self.handler, build_data)
        # test that handler methods where called
        self.handler.set_build_data.assert_called_with(build_data)
        self.handler.no_data.assert_called_once_with()
        # check return code
        self.assertEqual(result, Bisection.NO_DATA)

    @patch("mozregression.bisector.BuildDownloadManager")
    def test__bisect_finished(self, dl):
        build_data = MyBuildData([1])
        result = self.bisector._bisect(self.handler, build_data)
        # test that handler methods where called
        self.handler.set_build_data.assert_called_with(build_data)
        self.handler.finished.assert_called_once_with()
        # check return code
        self.assertEqual(result, Bisection.FINISHED)

    @patch("mozregression.bisector.BuildDownloadManager")
    def do__bisect(self, build_data, verdicts, dl):
        iter_verdict = iter(verdicts)

        def evaluate(build_info, allow_back=False):
            return iter_verdict.next(), {
                'application_changeset': 'unused',
                'application_repository': 'unused'
            }
        self.test_runner.evaluate = Mock(side_effect=evaluate)
        result = self.bisector._bisect(self.handler, build_data)
        return {
            'result': result,
        }

    def test__bisect_case1(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_data was called
        self.handler.set_build_data.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([3, 4, 5])),
            # we answered bad
            call(MyBuildData([3, 4])),
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_good.assert_called_with(2, MyBuildData([3, 4, 5]))
        self.handler.build_bad.assert_called_with(1, MyBuildData([3, 4]))
        self.assertTrue(self.handler.build_data.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_case1_hunt_fix(self):
        self.handler.find_fix = True
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_data was called
        self.handler.set_build_data.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([1, 2, 3])),
            # we answered bad
            call(MyBuildData([2, 3])),
        ])
        # ensure that we called the handler's methods
        self.assertEqual(self.handler.initialize.mock_calls, [call()]*3)
        self.handler.build_good. \
            assert_called_once_with(2, MyBuildData([1, 2, 3]))
        self.handler.build_bad.assert_called_once_with(1, MyBuildData([2, 3]))
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_case2(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3]), ['r', 's'])
        # check that set_build_data was called
        self.handler.set_build_data.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3])),
            # we asked for a retry
            call(MyBuildData([1, 2, 3])),
            # we skipped one
            call(MyBuildData([1, 3])),
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_retry.assert_called_with(1)
        self.handler.build_skip.assert_called_with(1)
        self.assertTrue(self.handler.build_data.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_with_back(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]),
                                      ['g', 'back', 'b', 'g'])
        # check that set_build_data was called
        self.handler.set_build_data.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([3, 4, 5])),
            # oups! let's go back
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered bad this time
            call(MyBuildData([1, 2, 3])),
            # then good
            call(MyBuildData([2, 3])),
        ])
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_user_exit(self):
        test_result = self.do__bisect(MyBuildData(range(20)), ['e'])
        # check that set_build_data was called
        self.handler.set_build_data.\
            assert_has_calls([call(MyBuildData(range(20)))])
        # ensure that we called the handler's method
        self.handler.initialize.assert_called_once_with()
        self.handler.user_exit.assert_called_with(10)
        # user exit
        self.assertEqual(test_result['result'], Bisection.USER_EXIT)

    def test__bisect_with_background_download(self):
        self.bisector.dl_in_background = True
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_data was called
        self.handler.set_build_data.assert_has_calls([
            call(MyBuildData([1, 2, 3, 4, 5])),  # first call
            call(MyBuildData([3, 4, 5])),  # download backgound
            call(MyBuildData([1, 2, 3, 4, 5])),   # put back the right data
            call(MyBuildData([1, 2, 3])),  # download backgound
            call(MyBuildData([1, 2, 3, 4, 5])),   # put back the right data
            call(MyBuildData([3, 4, 5])),  # we answered good
            call(MyBuildData([4, 5])),  # download backgound
            call(MyBuildData([3, 4, 5])),  # put back the right data
            call(MyBuildData([3, 4])),  # download backgound
            call(MyBuildData([3, 4, 5])),  # put back the right data
            call(MyBuildData([3, 4]))  # we answered bad
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_good.assert_called_with(2, MyBuildData([3, 4, 5]))
        self.handler.build_bad.assert_called_with(1, MyBuildData([3, 4]))
        self.assertTrue(self.handler.build_data.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    @patch('mozregression.bisector.Bisector._bisect')
    def test_bisect(self, _bisect):
        _bisect.return_value = 1
        build_data = Mock()
        build_data_class = Mock(return_value=build_data)
        self.handler.build_data_class = build_data_class
        result = self.bisector.bisect(self.handler, 'g', 'b', s=1)
        build_data_class.assert_called_with(self.bisector.fetch_config,
                                            'g', 'b', s=1)
        self.assertFalse(build_data.reverse.called)
        _bisect.assert_called_with(self.handler, build_data)
        self.assertEqual(result, 1)

    @patch('mozregression.bisector.Bisector._bisect')
    def test_bisect_reverse(self, _bisect):
        build_data = Mock()
        build_data_class = Mock(return_value=build_data)
        self.handler.build_data_class = build_data_class
        self.handler.find_fix = True
        self.bisector.bisect(self.handler, 'g', 'b', s=1)
        build_data_class.assert_called_with(self.bisector.fetch_config,
                                            'b', 'g', s=1)
        _bisect.assert_called_with(self.handler, build_data)
Beispiel #3
0
class TestBisector(unittest.TestCase):
    def setUp(self):
        self.handler = Mock(find_fix=False)
        self.test_runner = Mock()
        self.bisector = Bisector(Mock(), self.test_runner)

    def test__bisect_no_data(self):
        build_data = MyBuildData()
        result = self.bisector._bisect(self.handler, build_data)
        # test that handler methods where called
        self.handler.set_build_data.assert_called_with(build_data)
        self.handler.no_data.assert_called_once_with()
        # check return code
        self.assertEqual(result, Bisector.NO_DATA)

    def test__bisect_finished(self):
        build_data = MyBuildData([1])
        result = self.bisector._bisect(self.handler, build_data)
        # test that handler methods where called
        self.handler.set_build_data.assert_called_with(build_data)
        self.handler.finished.assert_called_once_with()
        # check return code
        self.assertEqual(result, Bisector.FINISHED)

    def do__bisect(self, build_data, verdicts):
        iter_verdict = iter(verdicts)

        def evaluate(build_info, allow_back=False):
            return iter_verdict.next(), {
                'application_changeset': 'unused',
                'application_repository': 'unused'
            }
        self.test_runner.evaluate = Mock(side_effect=evaluate)
        result = self.bisector._bisect(self.handler, build_data)
        return {
            'result': result,
        }

    def test__bisect_case1(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_data was called
        self.handler.set_build_data.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([3, 4, 5])),
            # we answered bad
            call(MyBuildData([3, 4])),
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_good.assert_called_with(2, MyBuildData([3, 4, 5]))
        self.handler.build_bad.assert_called_with(1, MyBuildData([3, 4]))
        self.assertTrue(self.handler.build_data.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisector.FINISHED)

    def test__bisect_case1_hunt_fix(self):
        self.handler.find_fix = True
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_data was called
        self.handler.set_build_data.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([1, 2, 3])),
            # we answered bad
            call(MyBuildData([2, 3])),
        ])
        # ensure that we called the handler's methods
        self.assertEqual(self.handler.initialize.mock_calls, [call()]*3)
        self.handler.build_good. \
            assert_called_once_with(2, MyBuildData([1, 2, 3]))
        self.handler.build_bad.assert_called_once_with(1, MyBuildData([2, 3]))
        # bisection is finished
        self.assertEqual(test_result['result'], Bisector.FINISHED)

    def test__bisect_case2(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3]), ['r', 's'])
        # check that set_build_data was called
        self.handler.set_build_data.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3])),
            # we asked for a retry
            call(MyBuildData([1, 2, 3])),
            # we skipped one
            call(MyBuildData([1, 3])),
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_retry.assert_called_with(1)
        self.handler.build_skip.assert_called_with(1)
        self.assertTrue(self.handler.build_data.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisector.FINISHED)

    def test__bisect_with_back(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]),
                                      ['g', 'back', 'b', 'g'])
        # check that set_build_data was called
        self.handler.set_build_data.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([3, 4, 5])),
            # oups! let's go back
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered bad this time
            call(MyBuildData([1, 2, 3])),
            # then good
            call(MyBuildData([2, 3])),
        ])
        # bisection is finished
        self.assertEqual(test_result['result'], Bisector.FINISHED)

    def test__bisect_user_exit(self):
        test_result = self.do__bisect(MyBuildData(range(20)), ['e'])
        # check that set_build_data was called
        self.handler.set_build_data.\
            assert_has_calls([call(MyBuildData(range(20)))])
        # ensure that we called the handler's method
        self.handler.initialize.assert_called_once_with()
        self.handler.user_exit.assert_called_with(10)
        # user exit
        self.assertEqual(test_result['result'], Bisector.USER_EXIT)

    @patch('mozregression.bisector.Bisector._bisect')
    def test_bisect(self, _bisect):
        _bisect.return_value = 1
        build_data = Mock()
        build_data_class = Mock(return_value=build_data)
        self.handler.build_data_class = build_data_class
        result = self.bisector.bisect(self.handler, 'g', 'b', s=1)
        build_data_class.assert_called_with(self.bisector.fetch_config,
                                            'g', 'b', s=1)
        self.assertFalse(build_data.reverse.called)
        _bisect.assert_called_with(self.handler, build_data)
        self.assertEqual(result, 1)

    @patch('mozregression.bisector.Bisector._bisect')
    def test_bisect_reverse(self, _bisect):
        build_data = Mock()
        build_data_class = Mock(return_value=build_data)
        self.handler.build_data_class = build_data_class
        self.handler.find_fix = True
        self.bisector.bisect(self.handler, 'g', 'b', s=1)
        build_data_class.assert_called_with(self.bisector.fetch_config,
                                            'b', 'g', s=1)
        _bisect.assert_called_with(self.handler, build_data)
Beispiel #4
0
class TestBisector(unittest.TestCase):
    def setUp(self):
        self.handler = MagicMock(find_fix=False, ensure_good_and_bad=False)
        self.test_runner = Mock()
        self.bisector = Bisector(Mock(),
                                 self.test_runner,
                                 Mock(),
                                 dl_in_background=False)
        self.bisector.download_background = False

    def test__bisect_no_data(self):
        build_range = MyBuildData()
        result = self.bisector._bisect(self.handler, build_range)
        # test that handler methods where called
        self.handler.set_build_range.assert_called_with(build_range)
        self.handler.no_data.assert_called_once_with()
        # check return code
        self.assertEqual(result, Bisection.NO_DATA)

    def test__bisect_finished(self):
        build_range = MyBuildData([1])
        result = self.bisector._bisect(self.handler, build_range)
        # test that handler methods where called
        self.handler.set_build_range.assert_called_with(build_range)
        self.handler.finished.assert_called_once_with()
        # check return code
        self.assertEqual(result, Bisection.FINISHED)

    def do__bisect(self, build_range, verdicts):
        iter_verdict = iter(verdicts)

        def evaluate(build_info, allow_back=False):
            verdict = next(iter_verdict)
            if isinstance(verdict, Exception):
                raise verdict
            return verdict

        self.test_runner.evaluate = Mock(side_effect=evaluate)
        result = self.bisector._bisect(self.handler, build_range)
        return {
            'result': result,
        }

    def test_ensure_good_bad_invalid(self):
        self.handler.ensure_good_and_bad = True
        with self.assertRaisesRegexp(MozRegressionError,
                                     "expected to be good"):
            self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['b'])

        with self.assertRaisesRegexp(MozRegressionError, "expected to be bad"):
            self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'g'])

    def test_ensure_good_bad(self):
        self.handler.ensure_good_and_bad = True
        data = MyBuildData([1, 2, 3, 4, 5])
        self.do__bisect(data, ['s', 'r', 'g', 'b', 'e'])
        self.test_runner.evaluate.assert_has_calls([
            call(data[0]),  # tested good (then skip)
            call(data[0]),  # tested good (then retry)
            call(data[0]),  # tested good
            call(data[-1]),  # tested bad
        ])
        self.assertEqual(
            self.bisector.download_manager.download_in_background.call_count,
            0)

    def test_ensure_good_bad_with_bg_dl(self):
        self.handler.ensure_good_and_bad = True
        self.bisector.dl_in_background = True
        data = MyBuildData([1, 2, 3, 4, 5])
        self.do__bisect(data, ['s', 'r', 'g', 'e'])
        self.test_runner.evaluate.assert_has_calls([
            call(data[0]),  # tested good (then skip)
            call(data[0]),  # tested good (then retry)
            call(data[0]),  # tested good
            call(data[-1]),  # tested bad
        ])
        self.bisector.download_manager.download_in_background.assert_has_calls(
            [
                call(data[-1]),  # bad in backgound
                call(data[data.mid_point()])
            ]  # and mid build
        )

    def test_ensure_good_bad_with_find_fix(self):
        self.handler.ensure_good_and_bad = True
        self.handler.find_fix = True
        data = MyBuildData([1, 2, 3, 4, 5])
        self.do__bisect(data, ['g', 'e'])
        self.test_runner.evaluate.assert_has_calls([
            call(data[-1]),  # tested good (then skip)
            call(data[0]),  # tested bad
        ])

    def test__bisect_case1(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([3, 4, 5])),
            # we answered bad
            call(MyBuildData([3, 4])),
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_good.assert_called_with(2, MyBuildData([3, 4, 5]))
        self.handler.build_bad.assert_called_with(1, MyBuildData([3, 4]))
        self.assertTrue(self.handler.build_range.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_with_launcher_exception(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]),
                                      ['g', LauncherError("err")])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([3, 4, 5])),
            # launcher exception, equivalent to a skip
            call(MyBuildData([3, 5])),
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_good.assert_called_with(2, MyBuildData([3, 4, 5]))
        self.handler.build_skip.assert_called_with(1)
        self.assertTrue(self.handler.build_range.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_case1_hunt_fix(self):
        self.handler.find_fix = True
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([1, 2, 3])),
            # we answered bad
            call(MyBuildData([2, 3])),
        ])
        # ensure that we called the handler's methods
        self.assertEqual(self.handler.initialize.mock_calls, [call()] * 3)
        self.handler.build_good. \
            assert_called_once_with(2, MyBuildData([1, 2, 3]))
        self.handler.build_bad.assert_called_once_with(1, MyBuildData([2, 3]))
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_case2(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3]), ['r', 's'])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3])),
            # we asked for a retry
            call(MyBuildData([1, 2, 3])),
            # we skipped one
            call(MyBuildData([1, 3])),
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_retry.assert_called_with(1)
        self.handler.build_skip.assert_called_with(1)
        self.assertTrue(self.handler.build_range.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_with_back(self):
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]),
                                      ['g', 'back', 'b', 'g'])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            # first call
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered good
            call(MyBuildData([3, 4, 5])),
            # oups! let's go back
            call(MyBuildData([1, 2, 3, 4, 5])),
            # we answered bad this time
            call(MyBuildData([1, 2, 3])),
            # then good
            call(MyBuildData([2, 3])),
        ])
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    def test__bisect_user_exit(self):
        test_result = self.do__bisect(MyBuildData(list(range(20))), ['e'])
        # check that set_build_range was called
        self.handler.set_build_range.\
            assert_has_calls([call(MyBuildData(list(range(20))))])
        # ensure that we called the handler's method
        self.handler.initialize.assert_called_once_with()
        self.handler.user_exit.assert_called_with(10)
        # user exit
        self.assertEqual(test_result['result'], Bisection.USER_EXIT)

    def test__bisect_with_background_download(self):
        self.bisector.dl_in_background = True
        test_result = self.do__bisect(MyBuildData([1, 2, 3, 4, 5]), ['g', 'b'])
        # check that set_build_range was called
        self.handler.set_build_range.assert_has_calls([
            call(MyBuildData([1, 2, 3, 4, 5])),  # first call
            call(MyBuildData([3, 4, 5])),  # we answered good
            call(MyBuildData([3, 4]))  # we answered bad
        ])
        # ensure that we called the handler's methods
        self.handler.initialize.assert_called_with()
        self.handler.build_good.assert_called_with(2, MyBuildData([3, 4, 5]))
        self.handler.build_bad.assert_called_with(1, MyBuildData([3, 4]))
        self.assertTrue(self.handler.build_range.ensure_limits_called)
        # bisection is finished
        self.assertEqual(test_result['result'], Bisection.FINISHED)

    @patch('mozregression.bisector.Bisector._bisect')
    def test_bisect(self, _bisect):
        _bisect.return_value = 1
        build_range = Mock()
        create_range = Mock(return_value=build_range)
        self.handler.create_range = create_range
        result = self.bisector.bisect(self.handler, 'g', 'b', s=1)
        create_range.assert_called_with(self.bisector.fetch_config,
                                        'g',
                                        'b',
                                        s=1)
        self.assertFalse(build_range.reverse.called)
        _bisect.assert_called_with(self.handler, build_range)
        self.assertEqual(result, 1)

    @patch('mozregression.bisector.Bisector._bisect')
    def test_bisect_reverse(self, _bisect):
        build_range = Mock()
        create_range = Mock(return_value=build_range)
        self.handler.create_range = create_range
        self.handler.find_fix = True
        self.bisector.bisect(self.handler, 'g', 'b', s=1)
        create_range.assert_called_with(self.bisector.fetch_config,
                                        'b',
                                        'g',
                                        s=1)
        _bisect.assert_called_with(self.handler, build_range)