13from unittest.mock
import create_autospec
21 """Test the class to calculate updates to running tags"""
25 "A": [(0, 0, 0, -1), (1, 0, 1, 10), (1, 11, 1, 11), (1, 12, -1, -1)],
27 "B": [(0, 0, -1, -1)],
33 """Create a list of payloads from all keyword arguments Name of the
34 payloads will be the name of the argument, revision will increase
35 monotonic starting at start_revision for each argument
and the iovs are
36 the values of the the argument.
38 To create a list
with two payloads, A
and B
with two validities
for B
39 and one
for A one could use::
41 make_payloads(A=[(0,0,1,2)], B=[(1,2,3,4), (3,5,6,7)])
44 for name, iovs
in payloads.items():
45 for rev, iov
in enumerate(iovs, start_revision):
51 Create payloads from a plain text list of payloads
in the form of
53 payloadN, rev i, valid
from a,b to c,d
55 Return a list of lists of payloads where each block of payload definitions
56 without empty lines
in between
is returned
as one list.
60 for match
in re.findall(
r'^\s*(payload\d+), rev (\d+), valid from (\d+),(\d+) to ([0-9-]+),([0-9-]+)$|^\s*$', text, re.M):
63 all_payloads.append(payloads)
67 0, match[0], int(match[1]),
None,
None,
None,
None, tuple(map(int, match[2:]))
71 all_payloads.append(payloads)
75 def make_mock_db(self, running_state="RUNNING", staging_state="VALIDATED", running_payloads=None, staging_payloads=None):
76 """Create a mock object that behaves close enough to the ConditionsDB class but returns
77 fixed values for the globaltags
"running" and "staging" for testing
"""
80 if running_payloads
is None:
82 if staging_payloads
is None:
87 "running": {
"name":
"running",
"globalTagStatus": {
"name": running_state}},
88 "staging": {
"name":
"staging",
"globalTagStatus": {
"name": staging_state}},
93 "running": running_payloads,
94 "staging": staging_payloads}.get(name,
None)
96 db.get_globalTagInfo.side_effect = taginfo
97 db.get_all_iovs.side_effect = all_iovs
101 """Parse the operations into a single list of [operation type, name,
102 revision, iov] for each operation
"""
103 return [[op, p.name, p.revision, p.iov]
for (op, p)
in operations]
106 """Test that we get errors if the tags don't exist or are in the wrong state"""
110 db3 = self.
make_mock_db(running_state=
"INVALID", staging_state=
"PUBLISHED")
112 with self.assertRaisesRegex(RunningTagUpdaterError,
"not in RUNNING state"):
114 with self.assertRaisesRegex(RunningTagUpdaterError,
"not in VALIDATED state"):
116 with self.assertRaisesRegex(RunningTagUpdaterError,
"not in RUNNING state"):
118 with self.assertRaisesRegex(RunningTagUpdaterError,
"'funning' cannot be found"):
120 with self.assertRaisesRegex(RunningTagUpdaterError,
"'caging' cannot be found"):
122 with self.assertRaisesRegex(RunningTagUpdaterError,
"'cunning' cannot be found"):
126 """Test that we get errors on overlaps in the staging"""
130 payloads = self.
make_payloads(A=[(0, 0, 1, 0)], B=[(0, 1, 1, 0)])
131 self.assertIsNone(updater._check_staging_tag(
"testoverlaps", payloads))
134 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (0, 1, 1, 0)])
135 with self.assertRaises(RunningTagUpdaterError)
as ctx:
136 updater._check_staging_tag(
"testoverlaps", payloads)
137 self.assertEqual(ctx.exception.extra_vars[
'overlaps'], 1)
140 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 0, 1, -1)])
141 with self.assertRaises(RunningTagUpdaterError)
as ctx:
142 updater._check_staging_tag(
"testoverlaps", payloads)
143 self.assertEqual(ctx.exception.extra_vars[
'overlaps'], 1)
146 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 0, 1, -1), (1, 1, 1, -1)])
147 with self.assertRaises(RunningTagUpdaterError)
as ctx:
148 updater._check_staging_tag(
"testoverlaps", payloads)
149 self.assertEqual(ctx.exception.extra_vars[
'overlaps'], 2)
153 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)])
154 with self.assertRaises(RunningTagUpdaterError)
as ctx:
155 updater._check_staging_tag(
"testoverlaps", payloads)
156 self.assertEqual(ctx.exception.extra_vars[
'overlaps'], 2)
159 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 1, 1, -1)])
160 self.assertIsNone(updater._check_staging_tag(
"testoverlaps", payloads))
163 """Test that we get errors for gaps in the staging"""
168 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 1, -1, -1)])
169 self.assertIsNone(updater._check_staging_tag(
"testgaps", payloads))
172 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)])
173 with self.assertRaises(RunningTagUpdaterError)
as ctx:
174 updater._check_staging_tag(
"testgaps", payloads)
175 self.assertEqual(ctx.exception.extra_vars[
'gaps'], 3)
178 """Test that we get errors on iovs not being open in the staging"""
183 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 1, 1, -1)], B=[(0, 0, 1, 2), (1, 3, 3, 3)])
184 with self.assertRaises(RunningTagUpdaterError)
as ctx:
185 updater._check_staging_tag(
"testclosed", payloads)
186 self.assertEqual(ctx.exception.extra_vars[
'closed payloads'], 2)
189 payloads = self.
make_payloads(A=[(0, 0, 1, -1), (2, 0, -1, -1)], B=[(0, 0, 1, 2), (1, 3, 3, 3)])
190 with self.assertRaises(RunningTagUpdaterError)
as ctx:
191 updater._check_staging_tag(
"testclosed", payloads)
192 self.assertEqual(ctx.exception.extra_vars[
'closed payloads'], 1)
195 payloads = self.
make_payloads(A=[(0, 0, 1, -1), (2, 0, 99999, 999999)], B=[(10, 0, -1, -1), (0, 0, 9, -1)])
196 with self.assertRaises(RunningTagUpdaterError)
as ctx:
197 updater._check_staging_tag(
"testclosed", payloads)
198 self.assertEqual(ctx.exception.extra_vars[
'closed payloads'], 1)
201 """Test that we get errors if the payloads in the staging tag start too early"""
207 with self.assertRaises(RunningTagUpdaterError)
as ctx:
208 updater._check_staging_tag(
"testearly", payloads)
209 self.assertEqual(ctx.exception.extra_vars[
'starts too early'], 1)
213 updater._check_staging_tag(
"testearly", payloads)
217 payloads = self.
make_payloads(A=[(0, 0, 1, 0), (1, 1, 1, -1)])
218 with self.assertRaises(RunningTagUpdaterError)
as ctx:
219 updater._check_staging_tag(
"testearly", payloads)
220 self.assertEqual(ctx.exception.extra_vars[
'starts too early'], 1)
223 with self.assertRaises(RunningTagUpdaterError)
as ctx:
224 updater._check_staging_tag(
"testearly", payloads)
225 self.assertEqual(ctx.exception.extra_vars[
'starts too early'], 1)
228 payloads = self.
make_payloads(A=[(0, 0, 2, 5), (2, 6, 2, 6), (2, 7, 2, -1), (3, 0, -1, -1)])
229 updater._check_staging_tag(
"testearly", payloads)
232 payloads = self.
make_payloads(A=[(3, 0, -1, -1), (2, 4, 2, -1)])
233 with self.assertRaises(RunningTagUpdaterError)
as ctx:
234 updater._check_staging_tag(
"testearly", payloads)
235 self.assertEqual(ctx.exception.extra_vars[
'starts too early'], 1)
238 """Test that we only allow trivial iovs in simple mode"""
244 with self.assertRaises(RunningTagUpdaterError)
as ctx:
245 updater._check_staging_tag_simple(
"testsimple", payloads)
246 self.assertEqual(ctx.exception.extra_vars[
'wrong validity'], 1)
249 with self.assertRaises(RunningTagUpdaterError)
as ctx:
250 updater._check_staging_tag_simple(
"testsimple", payloads)
251 self.assertEqual(ctx.exception.extra_vars[
'wrong validity'], 1)
254 with self.assertRaises(RunningTagUpdaterError)
as ctx:
255 updater._check_staging_tag_simple(
"testsimple", payloads)
256 self.assertEqual(ctx.exception.extra_vars[
'wrong validity'], 1)
259 with self.assertRaises(RunningTagUpdaterError)
as ctx:
260 updater._check_staging_tag_simple(
"testsimple", payloads)
261 self.assertEqual(ctx.exception.extra_vars[
'wrong validity'], 1)
264 payloads = self.
make_payloads(A=[(0, 0, -1, -1)], B=[(0, 0, -1, -1)])
265 updater._check_staging_tag_simple(
"testsimple", payloads)
268 payloads = self.
make_payloads(A=[(0, 0, -1, -1), (0, 0, -1, -1), (1, 2, 3, 4)],
269 B=[(0, 0, -1, -1)], C=[(0, 1, 2, 3)])
270 with self.assertRaises(RunningTagUpdaterError)
as ctx:
271 updater._check_staging_tag_simple(
"testsimple", payloads)
272 self.assertEqual(ctx.exception.extra_vars[
'wrong validity'], 2)
273 self.assertEqual(ctx.exception.extra_vars[
'duplicate payloads'], 2)
276 """Test that we get errors on iovs not being open in the staging"""
281 payloads = self.
make_payloads(A=[(0, 0, 1, -1), (2, 0, 2, 4)], B=[(0, 0, -1, -1)])
282 updater._check_running_tag(
"testrunning", payloads)
285 payloads = self.
make_payloads(A=[(0, 0, 1, -1), (2, 0, 2, 5)], B=[(0, 0, -1, -1)], C=[(0, 0, 10, -1)])
286 with self.assertRaisesRegex(RunningTagUpdaterError,
"Given first valid run conflicts with running tag")
as ctx:
287 updater._check_running_tag(
"testrunning", payloads)
288 self.assertEqual(ctx.exception.extra_vars[
'payloads end after first valid run'], 2)
291 payloads = self.
make_payloads(A=[(2, 5, -1, -1)], B=[(0, 0, 2, 1), (2, 2, -1, -1)], C=[(10, 0, -1, -1)])
292 with self.assertRaisesRegex(RunningTagUpdaterError,
"Given first valid run conflicts with running tag")
as ctx:
293 updater._check_running_tag(
"testrunning", payloads)
294 self.assertEqual(ctx.exception.extra_vars[
'payloads start after first valid run'], 2)
297 """Test something useful."""
304 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
306 result = updater.calculate_update()
309 [
'CLOSE',
'A', 4, (1, 12, 1, -1)],
310 [
'CLOSE',
'B', 1, (0, 0, 1, -1)],
311 [
'CREATE',
'A', 10, (2, 0, -1, -1)],
312 [
'CREATE',
'B', 10, (2, 0, -1, -1)],
313 [
'CREATE',
'C', 10, (2, 0, -1, -1)],
318 In full mode we close B even if there
's nothing in the staging tag
319 to allow "removing" payloads
from the set of valid ones
326 db = self.make_mock_db(running_payloads=running, staging_payloads=staging)
327 updater = RunningTagUpdater(db, "running",
"staging", (2, 0), Mode.FULL_REPLACEMENT)
328 result = updater.calculate_update()
331 [
'CLOSE',
'A', 4, (1, 12, 1, -1)],
332 [
'CLOSE',
'B', 1, (0, 0, 1, -1)],
333 [
'CREATE',
'A', 10, (2, 0, -1, -1)],
334 [
'CREATE',
'C', 10, (2, 0, -1, -1)],
338 """This time B doesn't start on the spot but a bit later. And A is a list of iovs to be preserved"""
341 A=[(0, 0, 2, 5), (2, 6, 2, 10), (2, 11, -1, -1)],
345 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
347 result = updater.calculate_update()
350 [
'CLOSE',
'A', 4, (1, 12, 1, -1)],
351 [
'CLOSE',
'B', 1, (0, 0, 2, 7)],
352 [
'CREATE',
'A', 10, (2, 0, 2, 5)],
353 [
'CREATE',
'A', 11, (2, 6, 2, 10)],
354 [
'CREATE',
'A', 12, (2, 11, -1, -1)],
355 [
'CREATE',
'B', 10, (2, 8, -1, -1)],
356 [
'CREATE',
'C', 10, (2, 0, -1, -1)],
360 """If the staging payloads have same revision as the last one in running merge them"""
363 A=[(0, 0, 2, 5), (2, 6, 2, 10), (2, 11, -1, -1)],
368 staging[0].revision = 4
369 staging[-2].revision = 1
370 print([(e.name, e.revision, e.iov)
for e
in running])
371 print([(e.name, e.revision, e.iov)
for e
in staging])
372 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
374 result = updater.calculate_update()
377 [
'CLOSE',
'A', 4, (1, 12, 2, 5)],
378 [
'CREATE',
'A', 11, (2, 6, 2, 10)],
379 [
'CREATE',
'A', 12, (2, 11, -1, -1)],
380 [
'CREATE',
'C', 10, (2, 0, -1, -1)],
384 """Test automatic opening of the last iov if necessary"""
387 A=[(0, 0, 2, 5), (2, 6, 2, 10), (2, 11, 2, -1)],
388 B=[(2, 8, 2, 10), (2, 11, 2, 28)],
391 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
393 result = updater.calculate_update()
396 [
'CLOSE',
'A', 4, (1, 12, 1, -1)],
397 [
'CLOSE',
'B', 1, (0, 0, 2, 7)],
398 [
'CREATE',
'A', 10, (2, 0, 2, 5)],
399 [
'CREATE',
'A', 11, (2, 6, 2, 10)],
400 [
'CREATE',
'A', 12, (2, 11, -1, -1)],
401 [
'CREATE',
'B', 10, (2, 8, 2, 10)],
402 [
'CREATE',
'B', 11, (2, 11, -1, -1)],
403 [
'CREATE',
'C', 10, (2, 0, -1, -1)],
407 """Test that merging fails if we have gaps or overlaps"""
410 A=[(0, 0, 2, 5), (2, 5, 2, 10), (2, 11, -1, -1)],
411 B=[(2, 8, 2, 10), (2, 12, -1, -1)],
414 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
416 with self.assertRaisesRegex(RunningTagUpdaterError,
"tag 'staging' not fit for update")
as ctx:
417 updater.calculate_update()
419 self.assertLessEqual({
'overlaps': 1,
'gaps': 1}.items(), ctx.exception.extra_vars.items())
422 """Extract the example from the `b2conditionsdb tag runningupdate` docstring and run it"""
426 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
428 operations = updater.calculate_update()
433 [
'CLOSE',
'payload1', 2, (1, 1, 1, 1)],
434 [
'CLOSE',
'payload2', 1, (0, 1, 1, 4)],
435 [
'CLOSE',
'payload4', 1, (0, 1, 1, 20)],
436 [
'CREATE',
'payload1', 3, (1, 2, 1, 8)],
437 [
'CREATE',
'payload1', 4, (1, 9, 1, 20)],
438 [
'CREATE',
'payload2', 2, (1, 5, 1, 20)],
439 [
'CREATE',
'payload3', 2, (1, 2, -1, -1)],
443 for op, payload
in operations:
446 i = result.index(payload)
447 result[i].iov = result[i].iov[:2] + payload.iov[2:]
450 result.append(payload)
452 self.assertEqual(expected, result)
455 """Extract the example from the `b2conditionsdb tag runningupdate` docstring and run it"""
457 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
458 updater =
RunningTagUpdater(db,
"running",
"staging", (1, 2), Mode.FULL_REPLACEMENT)
459 operations = updater.calculate_update()
464 [
'CLOSE',
'payload1', 2, (1, 1, 1, 1)],
465 [
'CLOSE',
'payload2', 1, (0, 1, 1, 4)],
466 [
'CLOSE',
'payload4', 1, (0, 1, 1, 20)],
467 [
'CLOSE',
'payload5', 1, (0, 1, 1, 1)],
468 [
'CREATE',
'payload1', 3, (1, 2, 1, 8)],
469 [
'CREATE',
'payload1', 4, (1, 9, 1, 20)],
470 [
'CREATE',
'payload2', 2, (1, 5, 1, 20)],
471 [
'CREATE',
'payload3', 2, (1, 2, -1, -1)],
475 """Extract the example from the `b2conditionsdb tag runningupdate` docstring and run it"""
477 db = self.
make_mock_db(running_payloads=running, staging_payloads=staging)
479 operations = updater.calculate_update()
484 [
'CLOSE',
'payload1', 2, (1, 1, 1, 1)],
485 [
'CLOSE',
'payload2', 1, (0, 1, 1, 4)],
486 [
'CREATE',
'payload1', 3, (1, 2, 1, 8)],
487 [
'CREATE',
'payload1', 4, (1, 9, -1, -1)],
488 [
'CREATE',
'payload2', 2, (1, 5, -1, -1)],
489 [
'CREATE',
'payload3', 2, (1, 2, -1, -1)],
493if __name__ ==
"__main__":
def make_mock_db(self, running_state="RUNNING", staging_state="VALIDATED", running_payloads=None, staging_payloads=None)
def test_operations_full(self)
def test_doc_example(self)
dict RUNNING_BASE
Basic definition of a running tag to be used later.
def test_merge_overlapandgaps(self)
def parse_operations(self, operations)
def test_operations_simple(self)
def test_operations_fix_open(self)
def test_operations_extend(self)
def test_running_check(self)
def create_payloads_from_text(self, text)
def test_simple_mode(self)
def test_doc_example_fixclosed(self)
def test_doc_example_full(self)
def test_operations_ragged(self)
def test_start_early(self)
def make_payloads(self, start_revision=1, **payloads)