1# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import unittest
6import subprocess
7
8bisect_builds = __import__('bisect-builds')
9
10
11class FakeProcess:
12  called_num_times = 0
13
14  def __init__(self, returncode):
15    self.returncode = returncode
16    FakeProcess.called_num_times += 1
17
18  def communicate(self):
19    return ('', '')
20
21
22class BisectTest(unittest.TestCase):
23
24  patched = []
25  max_rev = 10000
26  fake_process_return_code = 0
27
28  def monkey_patch(self, obj, name, new):
29    self.patched.append((obj, name, getattr(obj, name)))
30    setattr(obj, name, new)
31
32  def clear_patching(self):
33    for obj, name, old in self.patched:
34      setattr(obj, name, old)
35    self.patched = []
36
37  def setUp(self):
38    FakeProcess.called_num_times = 0
39    self.fake_process_return_code = 0
40    self.monkey_patch(bisect_builds.DownloadJob, 'Start', lambda *args: None)
41    self.monkey_patch(bisect_builds.DownloadJob, 'Stop', lambda *args: None)
42    self.monkey_patch(bisect_builds.DownloadJob, 'WaitFor', lambda *args: None)
43    self.monkey_patch(bisect_builds, 'UnzipFilenameToDir', lambda *args: None)
44    self.monkey_patch(
45        subprocess, 'Popen',
46        lambda *args, **kwargs: FakeProcess(self.fake_process_return_code))
47    self.monkey_patch(bisect_builds.PathContext, 'ParseDirectoryIndex',
48                      lambda *args: range(self.max_rev))
49
50  def tearDown(self):
51    self.clear_patching()
52
53  def bisect(self, good_rev, bad_rev, evaluate, num_runs=1):
54    base_url = bisect_builds.CHROMIUM_BASE_URL
55    archive = 'linux'
56    asan = False
57    use_local_cache = False
58    context = bisect_builds.PathContext(base_url, archive, good_rev, bad_rev,
59                                        asan, use_local_cache)
60    (minrev, maxrev, _) = bisect_builds.Bisect(
61        context=context,
62        evaluate=evaluate,
63        num_runs=num_runs,
64        profile=None,
65        try_args=[])
66    return (minrev, maxrev)
67
68  def testBisectConsistentAnswer(self):
69    self.assertEqual(self.bisect(1000, 100, lambda *args: 'g'), (100, 101))
70    self.assertEqual(self.bisect(100, 1000, lambda *args: 'b'), (100, 101))
71    self.assertEqual(self.bisect(2000, 200, lambda *args: 'b'), (1999, 2000))
72    self.assertEqual(self.bisect(200, 2000, lambda *args: 'g'), (1999, 2000))
73
74  def testBisectMultipleRunsEarlyReturn(self):
75    self.fake_process_return_code = 1
76    self.assertEqual(self.bisect(1, 3, lambda *args: 'b', num_runs=10), (1, 2))
77    self.assertEqual(FakeProcess.called_num_times, 1)
78
79  def testBisectAllRunsWhenAllSucceed(self):
80    self.assertEqual(self.bisect(1, 3, lambda *args: 'b', num_runs=10), (1, 2))
81    self.assertEqual(FakeProcess.called_num_times, 10)
82
83
84if __name__ == '__main__':
85  unittest.main()
86