| OLD | NEW |
| (Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 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 """ Source file for floating builder testcases.""" |
| 7 |
| 8 import calendar |
| 9 import datetime |
| 10 import itertools |
| 11 import os |
| 12 import time |
| 13 import unittest |
| 14 |
| 15 import test_env # pylint: disable=W0611,W0403 |
| 16 |
| 17 import mock |
| 18 |
| 19 from master import floating_builder as fb |
| 20 |
| 21 |
| 22 def _to_timestamp(dt): |
| 23 # Calculate the offset between local timezone and UTC. |
| 24 current_time = time.mktime(dt.timetuple()) |
| 25 offset = (datetime.datetime.fromtimestamp(current_time) - |
| 26 datetime.datetime.utcfromtimestamp(current_time)) |
| 27 |
| 28 return calendar.timegm((dt - offset).timetuple()) |
| 29 |
| 30 |
| 31 class _FakeSlaveStatus(object): |
| 32 def __init__(self, name): |
| 33 self.name = name |
| 34 self.connect_times = [] |
| 35 self.last_message_received = None |
| 36 |
| 37 def lastMessageReceived(self): |
| 38 return self.last_message_received |
| 39 |
| 40 |
| 41 class _FakeSlave(object): |
| 42 def __init__(self, slavename): |
| 43 self.slavename = slavename |
| 44 self.slave_status = None |
| 45 self.offline = False |
| 46 |
| 47 def _set_last_seen(self, now, **kwargs): |
| 48 td = datetime.timedelta(**kwargs) |
| 49 self.slave_status = _FakeSlaveStatus(self.slavename) |
| 50 self.slave_status.last_message_received = _to_timestamp(now + td) |
| 51 |
| 52 def __str__(self): |
| 53 return self.slavename |
| 54 |
| 55 |
| 56 class _FakeBuilder(object): |
| 57 |
| 58 def __init__(self, name, slaves): |
| 59 self.name = name |
| 60 self._all_slaves = slaves |
| 61 |
| 62 self.botmaster = mock.MagicMock() |
| 63 self.builder_status = mock.MagicMock() |
| 64 self.builder_status.getSlaves.side_effect = lambda: [ |
| 65 s.slave_status for s in self._all_slaves |
| 66 if s.slave_status] |
| 67 |
| 68 self._online_slaves = () |
| 69 self._busy_slaves = () |
| 70 |
| 71 def __repr__(self): |
| 72 return self.name |
| 73 |
| 74 @property |
| 75 def slaves(self): |
| 76 return [_FakeSlaveBuilder(s, self) |
| 77 for s in self._all_slaves |
| 78 if s.slavename in self._online_slaves] |
| 79 |
| 80 @property |
| 81 def slavebuilders(self): |
| 82 """Returns the list of slavebuilders that would be handed to |
| 83 NextSlaveFunc. |
| 84 |
| 85 This is the set of slaves that are available for scheduling. We derive |
| 86 this by returning all slaves that are both online and not busy. |
| 87 """ |
| 88 return self._get_slave_builders(lambda s: |
| 89 s.slavename in self._online_slaves and |
| 90 s.slavename not in self._busy_slaves) |
| 91 |
| 92 def _get_slave_builders(self, fn): |
| 93 return [_FakeSlaveBuilder(slave, self) |
| 94 for slave in self._all_slaves |
| 95 if fn(slave)] |
| 96 |
| 97 def set_online_slaves(self, *slavenames): |
| 98 self._online_slaves = set(slavenames) |
| 99 |
| 100 def set_busy_slaves(self, *slavenames): |
| 101 self._busy_slaves = set(slavenames) |
| 102 |
| 103 |
| 104 class _FakeSlaveBuilder(object): |
| 105 |
| 106 def __init__(self, slave, builder): |
| 107 self.slave = slave |
| 108 self.builder = builder |
| 109 |
| 110 def __repr__(self): |
| 111 return '{%s/%s}' % (self.builder.name, self.slave.slavename) |
| 112 |
| 113 |
| 114 class FloatingBuilderTest(unittest.TestCase): |
| 115 |
| 116 def setUp(self): |
| 117 self._mocks = ( |
| 118 mock.patch('master.floating_builder._get_now'), |
| 119 mock.patch('master.floating_builder.PokeBuilderTimer.reset'), |
| 120 ) |
| 121 for patcher in self._mocks: |
| 122 patcher.start() |
| 123 |
| 124 # Mock current date/time. |
| 125 self.now = datetime.datetime(2016, 1, 1, 8, 0, 0) # 1/1/2016 @8:00 |
| 126 fb._get_now.return_value = self.now |
| 127 |
| 128 # Mock PokeBuilderTimer to record when the poke builder was set, but not |
| 129 # actually schedule any reactor magic. |
| 130 self.poke_delta = None |
| 131 def record_poke_delta(delta): |
| 132 self.poke_delta = delta |
| 133 fb.PokeBuilderTimer.reset.side_effect = record_poke_delta |
| 134 |
| 135 self._slaves = dict((s, _FakeSlave(s)) for s in ( |
| 136 'primary-a', 'primary-b', 'floating-a', 'floating-b', |
| 137 )) |
| 138 |
| 139 self.builder = _FakeBuilder( |
| 140 'Test Builder', |
| 141 [s[1] for s in sorted(self._slaves.iteritems())], |
| 142 ) |
| 143 |
| 144 def tearDown(self): |
| 145 for patcher in reversed(self._mocks): |
| 146 patcher.stop() |
| 147 |
| 148 def testPrimaryBuilderIsSelectedWhenAvailable(self): |
| 149 fs = fb.FloatingSet() |
| 150 fs.AddPrimary('primary-a') |
| 151 fs.AddFloating('floating-a', 'floating-b') |
| 152 |
| 153 self.builder.set_online_slaves('primary-a', 'floating-a', 'floating-b') |
| 154 |
| 155 fnsf = fs.NextSlaveFunc(datetime.timedelta(seconds=10)) |
| 156 nsb = fnsf(self.builder, self.builder.slavebuilders) |
| 157 self.assertIsNotNone(nsb) |
| 158 self.assertEqual(nsb.slave.slavename, 'primary-a') |
| 159 |
| 160 def testPrimaryBuilderIsSelectedWhenOneIsAvailableAndOneIsBusy(self): |
| 161 fs = fb.FloatingSet() |
| 162 fs.AddPrimary('primary-a', 'primary-b') |
| 163 fs.AddFloating('floating-a', 'floating-b') |
| 164 |
| 165 self.builder.set_online_slaves('primary-a', 'primary-b', 'floating-a', |
| 166 'floating-b') |
| 167 self.builder.set_busy_slaves('primary-a') |
| 168 |
| 169 fnsf = fs.NextSlaveFunc(datetime.timedelta(seconds=10)) |
| 170 nsb = fnsf(self.builder, self.builder.slavebuilders) |
| 171 self.assertIsNotNone(nsb) |
| 172 self.assertEqual(nsb.slave.slavename, 'primary-b') |
| 173 |
| 174 def testNoBuilderIsSelectedWhenPrimariesAreOfflineWithinGrace(self): |
| 175 fs = fb.FloatingSet() |
| 176 fs.AddPrimary('primary-a', 'primary-b') |
| 177 fs.AddFloating('floating-a', 'floating-b') |
| 178 |
| 179 self.builder.set_online_slaves('floating-a') |
| 180 self._slaves['primary-b']._set_last_seen(self.now, seconds=-1) |
| 181 |
| 182 fnsf = fs.NextSlaveFunc(datetime.timedelta(seconds=10)) |
| 183 nsb = fnsf(self.builder, self.builder.slavebuilders) |
| 184 self.assertIsNone(nsb) |
| 185 self.assertEqual(self.poke_delta, datetime.timedelta(seconds=9)) |
| 186 |
| 187 def testFloatingBuilderIsSelectedWhenPrimariesAreOfflineForAWhile(self): |
| 188 fs = fb.FloatingSet() |
| 189 fs.AddPrimary('primary-a', 'primary-b') |
| 190 fs.AddFloating('floating-a', 'floating-b') |
| 191 |
| 192 self.builder.set_online_slaves('floating-a') |
| 193 |
| 194 fnsf = fs.NextSlaveFunc(datetime.timedelta(seconds=10)) |
| 195 nsb = fnsf(self.builder, self.builder.slavebuilders) |
| 196 self.assertIsNotNone(nsb) |
| 197 self.assertEqual(nsb.slave.slavename, 'floating-a') |
| 198 |
| 199 |
| 200 if __name__ == '__main__': |
| 201 unittest.main() |
| OLD | NEW |