5 from unittest.mock
import create_autospec
13 """Test the class to calculate updates to running tags"""
17 "A": [(0, 0, 0, -1), (1, 0, 1, 10), (1, 11, 1, 11), (1, 12, -1, -1)],
19 "B": [(0, 0, -1, -1)],
25 """Create a list of payloads from all keyword arguments Name of the
26 payloads will be the name of the argument, revision will increase
27 monotonic starting at start_revision for each argument and the iovs are
28 the values of the the argument.
30 To create a list with two payloads, A and B with two validities for B
31 and one for A one could use::
33 make_payloads(A=[(0,0,1,2)], B=[(1,2,3,4), (3,5,6,7)])
36 for name, iovs
in payloads.items():
37 for rev, iov
in enumerate(iovs, start_revision):
43 Create payloads from a plain text list of payloads in the form of
45 payloadN, rev i, valid from a,b to c,d
47 Return a list of lists of payloads where each block of payload definitions
48 without empty lines in between is returned as one list.
52 for match
in re.findall(
r'^\s*(payload\d+), rev (\d+), valid from (\d+),(\d+) to ([0-9-]+),([0-9-]+)$|^\s*$', text, re.M):
55 all_payloads.append(payloads)
59 0, match[0], int(match[1]),
None,
None,
None,
None, tuple(map(int, match[2:]))
63 all_payloads.append(payloads)
67 def make_mock_db(self, running_state="RUNNING", staging_state="VALIDATED", running_payloads=None, staging_payloads=None):
68 """Create a mock object that behaves close enough to the ConditionsDB class but returns
69 fixed values for the globaltags "running" and "staging" for testing"""
72 if running_payloads
is None:
74 if staging_payloads
is None:
79 "running": {
"name":
"running",
"globalTagStatus": {
"name": running_state}},
80 "staging": {
"name":
"staging",
"globalTagStatus": {
"name": staging_state}},
85 "running": running_payloads,
86 "staging": staging_payloads}.get(name,
None)
88 db.get_globalTagInfo.side_effect = taginfo
89 db.get_all_iovs.side_effect = all_iovs
93 """Parse the operations into a single list of [operation type, name,
94 revision, iov] for each operation"""
95 return [[op, p.name, p.revision, p.iov]
for (op, p)
in operations]
98 """Test that we get errors if the tags don't exist or are in the wrong state"""
102 db3 = self.
make_mock_db(running_state=
"INVALID", staging_state=
"PUBLISHED")
104 with self.assertRaisesRegex(RunningTagUpdaterError,
"not in RUNNING state"):
106 with self.assertRaisesRegex(RunningTagUpdaterError,
"not in VALIDATED state"):
108 with self.assertRaisesRegex(RunningTagUpdaterError,
"not in RUNNING state"):
110 with self.assertRaisesRegex(RunningTagUpdaterError,
"'funning' cannot be found"):
112 with self.assertRaisesRegex(RunningTagUpdaterError,
"'caging' cannot be found"):
114 with self.assertRaisesRegex(RunningTagUpdaterError,
"'cunning' cannot be found"):
118 """Test that we get errors on overlaps in the staging"""
122 payloads = self.
make_payloads(A=[(0, 0, 1, 0)], B=[(0, 1, 1, 0)])
123 self.assertIsNone(updater._check_staging_tag(
"testoverlaps", payloads))
126 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (0, 1, 1, 0)])
127 with self.assertRaises(RunningTagUpdaterError)
as ctx:
128 updater._check_staging_tag(
"testoverlaps", payloads)
129 self.assertEqual(ctx.exception.extra_vars[
'overlaps'], 1)
132 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 0, 1, -1)])
133 with self.assertRaises(RunningTagUpdaterError)
as ctx:
134 updater._check_staging_tag(
"testoverlaps", payloads)
135 self.assertEqual(ctx.exception.extra_vars[
'overlaps'], 1)
138 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 0, 1, -1), (1, 1, 1, -1)])
139 with self.assertRaises(RunningTagUpdaterError)
as ctx:
140 updater._check_staging_tag(
"testoverlaps", payloads)
141 self.assertEqual(ctx.exception.extra_vars[
'overlaps'], 2)
145 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (3, 0, -1, -1), (1, 0, 1, -1), (2, 0, 2, -1), (1, 1, 1, -1)])
146 with self.assertRaises(RunningTagUpdaterError)
as ctx:
147 updater._check_staging_tag(
"testoverlaps", payloads)
148 self.assertEqual(ctx.exception.extra_vars[
'overlaps'], 2)
151 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 1, 1, -1)])
152 self.assertIsNone(updater._check_staging_tag(
"testoverlaps", payloads))
155 """Test that we get errors for gaps in the staging"""
160 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 1, -1, -1)])
161 self.assertIsNone(updater._check_staging_tag(
"testgaps", payloads))
164 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 2, 1, -1), (2, 2, -1, -1)], B=[(0, 0, 1, 2), (2, 1, 3, 3)])
165 with self.assertRaises(RunningTagUpdaterError)
as ctx:
166 updater._check_staging_tag(
"testgaps", payloads)
167 self.assertEqual(ctx.exception.extra_vars[
'gaps'], 3)
170 """Test that we get errors on iovs not being open in the staging"""
175 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 1, 1, -1)], B=[(0, 0, 1, 2), (1, 3, 3, 3)])
176 with self.assertRaises(RunningTagUpdaterError)
as ctx:
177 updater._check_staging_tag(
"testclosed", payloads)
178 self.assertEqual(ctx.exception.extra_vars[
'closed payloads'], 2)
181 payloads = self.
make_payloads(A=[(0, 0, 1, -1), (2, 0, -1, -1)], B=[(0, 0, 1, 2), (1, 3, 3, 3)])
182 with self.assertRaises(RunningTagUpdaterError)
as ctx:
183 updater._check_staging_tag(
"testclosed", payloads)
184 self.assertEqual(ctx.exception.extra_vars[
'closed payloads'], 1)
187 payloads = self.
make_payloads(A=[(0, 0, 1, -1), (2, 0, 99999, 999999)], B=[(10, 0, -1, -1), (0, 0, 9, -1)])
188 with self.assertRaises(RunningTagUpdaterError)
as ctx:
189 updater._check_staging_tag(
"testclosed", payloads)
190 self.assertEqual(ctx.exception.extra_vars[
'closed payloads'], 1)
193 """Test that we get errors if the payloads in the staging tag start too early"""
199 with self.assertRaises(RunningTagUpdaterError)
as ctx:
200 updater._check_staging_tag(
"testearly", payloads)
201 self.assertEqual(ctx.exception.extra_vars[
'starts too early'], 1)
205 updater._check_staging_tag(
"testearly", payloads)
209 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 1, 1, -1)])
210 with self.assertRaises(RunningTagUpdaterError)
as ctx:
211 updater._check_staging_tag(
"testearly", payloads)
212 self.assertEqual(ctx.exception.extra_vars[
'starts too early'], 1)
215 with self.assertRaises(RunningTagUpdaterError)
as ctx:
216 updater._check_staging_tag(
"testearly", payloads)
217 self.assertEqual(ctx.exception.extra_vars[
'starts too early'], 1)
220 payloads = self.
make_payloads(A=[(0, 0, 2, 5), (2, 6, 2, 6), (2, 7, 2, -1), (3, 0, -1, -1)])
221 updater._check_staging_tag(
"testearly", payloads)
224 payloads = self.
make_payloads(A=[(3, 0, -1, -1), (2, 4, 2, -1)])
225 with self.assertRaises(RunningTagUpdaterError)
as ctx:
226 updater._check_staging_tag(
"testearly", payloads)
227 self.assertEqual(ctx.exception.extra_vars[
'starts too early'], 1)
230 """Test that we only allow trivial iovs in simple mode"""
236 with self.assertRaises(RunningTagUpdaterError)
as ctx:
237 updater._check_staging_tag_simple(
"testsimple", payloads)
238 self.assertEqual(ctx.exception.extra_vars[
'wrong validity'], 1)
241 with self.assertRaises(RunningTagUpdaterError)
as ctx:
242 updater._check_staging_tag_simple(
"testsimple", payloads)
243 self.assertEqual(ctx.exception.extra_vars[
'wrong validity'], 1)
246 with self.assertRaises(RunningTagUpdaterError)
as ctx:
247 updater._check_staging_tag_simple(
"testsimple", payloads)
248 self.assertEqual(ctx.exception.extra_vars[
'wrong validity'], 1)
251 with self.assertRaises(RunningTagUpdaterError)
as ctx:
252 updater._check_staging_tag_simple(
"testsimple", payloads)
253 self.assertEqual(ctx.exception.extra_vars[
'wrong validity'], 1)
256 payloads = self.
make_payloads(A=[(0, 0, -1, -1)], B=[(0, 0, -1, -1)])
257 updater._check_staging_tag_simple(
"testsimple", payloads)
260 payloads = self.
make_payloads(A=[(0, 0, -1, -1), (0, 0, -1, -1), (1, 2, 3, 4)],
261 B=[(0, 0, -1, -1)], C=[(0, 1, 2, 3)])
262 with self.assertRaises(RunningTagUpdaterError)
as ctx:
263 updater._check_staging_tag_simple(
"testsimple", payloads)
264 self.assertEqual(ctx.exception.extra_vars[
'wrong validity'], 2)
265 self.assertEqual(ctx.exception.extra_vars[
'duplicate payloads'], 2)
268 """Test that we get errors on iovs not being open in the staging"""
273 payloads = self.
make_payloads(A=[(0, 0, 1, -1), (2, 0, 2, 4)], B=[(0, 0, -1, -1)])
274 updater._check_running_tag(
"testrunning", payloads)
277 payloads = self.
make_payloads(A=[(0, 0, 1, -1), (2, 0, 2, 5)], B=[(0, 0, -1, -1)], C=[(0, 0, 10, -1)])
278 with self.assertRaisesRegex(RunningTagUpdaterError,
"Given first valid run conflicts with running tag")
as ctx:
279 updater._check_running_tag(
"testrunning", payloads)
280 self.assertEqual(ctx.exception.extra_vars[
'payloads end after first valid run'], 2)
283 payloads = self.
make_payloads(A=[(2, 5, -1, -1)], B=[(0, 0, 2, 1), (2, 2, -1, -1)], C=[(10, 0, -1, -1)])
284 with self.assertRaisesRegex(RunningTagUpdaterError,
"Given first valid run conflicts with running tag")
as ctx:
285 updater._check_running_tag(
"testrunning", payloads)
286 self.assertEqual(ctx.exception.extra_vars[
'payloads start after first valid run'], 2)
288 def test_operations_simple(self):
295 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
297 result = updater.calculate_update()
300 [
'CLOSE',
'A', 4, (1, 12, 1, -1)],
301 [
'CLOSE',
'B', 1, (0, 0, 1, -1)],
302 [
'CREATE',
'A', 10, (2, 0, -1, -1)],
303 [
'CREATE',
'B', 10, (2, 0, -1, -1)],
304 [
'CREATE',
'C', 10, (2, 0, -1, -1)],
309 In full mode we close B even if there's nothing in the staging tag
310 to allow "removing" payloads from the set of valid ones
317 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
318 updater =
RunningTagUpdater(db,
"running",
"staging", (2, 0), Mode.FULL_REPLACEMENT)
319 result = updater.calculate_update()
322 [
'CLOSE',
'A', 4, (1, 12, 1, -1)],
323 [
'CLOSE',
'B', 1, (0, 0, 1, -1)],
324 [
'CREATE',
'A', 10, (2, 0, -1, -1)],
325 [
'CREATE',
'C', 10, (2, 0, -1, -1)],
329 """This time B doesn't start on the spot but a bit later. And A is a list of iovs to be preserved"""
332 A=[(0, 0, 2, 5), (2, 6, 2, 10), (2, 11, -1, -1)],
336 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
338 result = updater.calculate_update()
341 [
'CLOSE',
'A', 4, (1, 12, 1, -1)],
342 [
'CLOSE',
'B', 1, (0, 0, 2, 7)],
343 [
'CREATE',
'A', 10, (2, 0, 2, 5)],
344 [
'CREATE',
'A', 11, (2, 6, 2, 10)],
345 [
'CREATE',
'A', 12, (2, 11, -1, -1)],
346 [
'CREATE',
'B', 10, (2, 8, -1, -1)],
347 [
'CREATE',
'C', 10, (2, 0, -1, -1)],
351 """If the staging payloads have same revision as the last one in running merge them"""
354 A=[(0, 0, 2, 5), (2, 6, 2, 10), (2, 11, -1, -1)],
359 staging[0].revision = 4
360 staging[-2].revision = 1
361 print([(e.name, e.revision, e.iov)
for e
in running])
362 print([(e.name, e.revision, e.iov)
for e
in staging])
363 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
365 result = updater.calculate_update()
368 [
'CLOSE',
'A', 4, (1, 12, 2, 5)],
369 [
'CREATE',
'A', 11, (2, 6, 2, 10)],
370 [
'CREATE',
'A', 12, (2, 11, -1, -1)],
371 [
'CREATE',
'C', 10, (2, 0, -1, -1)],
375 """Test automatic opening of the last iov if necessary"""
378 A=[(0, 0, 2, 5), (2, 6, 2, 10), (2, 11, 2, -1)],
379 B=[(2, 8, 2, 10), (2, 11, 2, 28)],
382 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
384 result = updater.calculate_update()
387 [
'CLOSE',
'A', 4, (1, 12, 1, -1)],
388 [
'CLOSE',
'B', 1, (0, 0, 2, 7)],
389 [
'CREATE',
'A', 10, (2, 0, 2, 5)],
390 [
'CREATE',
'A', 11, (2, 6, 2, 10)],
391 [
'CREATE',
'A', 12, (2, 11, -1, -1)],
392 [
'CREATE',
'B', 10, (2, 8, 2, 10)],
393 [
'CREATE',
'B', 11, (2, 11, -1, -1)],
394 [
'CREATE',
'C', 10, (2, 0, -1, -1)],
398 """Test that merging fails if we have gaps or overlaps"""
401 A=[(0, 0, 2, 5), (2, 5, 2, 10), (2, 11, -1, -1)],
402 B=[(2, 8, 2, 10), (2, 12, -1, -1)],
405 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
407 with self.assertRaisesRegex(RunningTagUpdaterError,
"tag 'staging' not fit for update")
as ctx:
408 updater.calculate_update()
410 self.assertLessEqual({
'overlaps': 1,
'gaps': 1}.items(), ctx.exception.extra_vars.items())
413 """Extract the example from the `b2conditionsdb tag runningupdate` docstring and run it"""
417 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
419 operations = updater.calculate_update()
424 [
'CLOSE',
'payload1', 2, (1, 1, 1, 1)],
425 [
'CLOSE',
'payload2', 1, (0, 1, 1, 4)],
426 [
'CLOSE',
'payload4', 1, (0, 1, 1, 20)],
427 [
'CREATE',
'payload1', 3, (1, 2, 1, 8)],
428 [
'CREATE',
'payload1', 4, (1, 9, 1, 20)],
429 [
'CREATE',
'payload2', 2, (1, 5, 1, 20)],
430 [
'CREATE',
'payload3', 2, (1, 2, -1, -1)],
434 for op, payload
in operations:
437 i = result.index(payload)
438 result[i].iov = result[i].iov[:2] + payload.iov[2:]
441 result.append(payload)
443 self.assertEqual(expected, result)
446 """Extract the example from the `b2conditionsdb tag runningupdate` docstring and run it"""
450 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
451 updater =
RunningTagUpdater(db,
"running",
"staging", (1, 2), Mode.FULL_REPLACEMENT)
452 operations = updater.calculate_update()
457 [
'CLOSE',
'payload1', 2, (1, 1, 1, 1)],
458 [
'CLOSE',
'payload2', 1, (0, 1, 1, 4)],
459 [
'CLOSE',
'payload4', 1, (0, 1, 1, 20)],
460 [
'CLOSE',
'payload5', 1, (0, 1, 1, 1)],
461 [
'CREATE',
'payload1', 3, (1, 2, 1, 8)],
462 [
'CREATE',
'payload1', 4, (1, 9, 1, 20)],
463 [
'CREATE',
'payload2', 2, (1, 5, 1, 20)],
464 [
'CREATE',
'payload3', 2, (1, 2, -1, -1)],
468 """Extract the example from the `b2conditionsdb tag runningupdate` docstring and run it"""
472 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
474 operations = updater.calculate_update()
479 [
'CLOSE',
'payload1', 2, (1, 1, 1, 1)],
480 [
'CLOSE',
'payload2', 1, (0, 1, 1, 4)],
481 [
'CREATE',
'payload1', 3, (1, 2, 1, 8)],
482 [
'CREATE',
'payload1', 4, (1, 9, -1, -1)],
483 [
'CREATE',
'payload2', 2, (1, 5, -1, -1)],
484 [
'CREATE',
'payload3', 2, (1, 2, -1, -1)],
488 if __name__ ==
"__main__":