Qinusty pushed to branch Qinusty/531-fetch-retries-on-terminate at BuildStream / buildstream
Commits:
5 changed files:
- .gitlab-ci.yml
- + tests/frontend/interruptions.py
- + tests/frontend/interruptions/elements/delaymock.bst
- + tests/frontend/interruptions/plugins/delayed.py
- + tests/frontend/interruptions/project.conf
Changes:
| ... | ... | @@ -68,7 +68,7 @@ source_dist: |
| 68 | 68 |
|
| 69 | 69 |
# Run the tests from the source distribution, We run as a simple
|
| 70 | 70 |
# user to test for permission issues
|
| 71 |
- - su buildstream -c 'python3 setup.py test --index-url invalid://uri --addopts --integration'
|
|
| 71 |
+ - su buildstream -c 'python3 setup.py test --index-url invalid://uri --addopts "tests/frontend/interruptions.py --integration -s"'
|
|
| 72 | 72 |
|
| 73 | 73 |
# Go back to the toplevel and collect our reports
|
| 74 | 74 |
- cd ../..
|
| 1 |
+import pytest
|
|
| 2 |
+import os
|
|
| 3 |
+import signal
|
|
| 4 |
+import re
|
|
| 5 |
+import time
|
|
| 6 |
+import multiprocessing as mp
|
|
| 7 |
+from multiprocessing import Process
|
|
| 8 |
+from multiprocessing.queues import Queue
|
|
| 9 |
+ |
|
| 10 |
+from buildstream import _yaml
|
|
| 11 |
+from tests.testutils import cli
|
|
| 12 |
+ |
|
| 13 |
+ |
|
| 14 |
+DATA_DIR = os.path.join(
|
|
| 15 |
+ os.path.dirname(os.path.realpath(__file__)),
|
|
| 16 |
+ "interruptions",
|
|
| 17 |
+)
|
|
| 18 |
+ |
|
| 19 |
+ |
|
| 20 |
+def cli_run_in_process(cli, path, args):
|
|
| 21 |
+ def do_run(cli, path, args, queue):
|
|
| 22 |
+ result = cli.run(project=path, args=args)
|
|
| 23 |
+ queue.put(result.output)
|
|
| 24 |
+ queue.put(result.stderr)
|
|
| 25 |
+ |
|
| 26 |
+ queue = mp.Queue()
|
|
| 27 |
+ p = mp.Process(target=do_run, args=[cli, path, args, queue])
|
|
| 28 |
+ p.start()
|
|
| 29 |
+ return queue, p
|
|
| 30 |
+ |
|
| 31 |
+ |
|
| 32 |
+@pytest.mark.datafiles(DATA_DIR)
|
|
| 33 |
+def test_interrupt_fetch(cli, datafiles):
|
|
| 34 |
+ path = os.path.join(datafiles.dirname, datafiles.basename)
|
|
| 35 |
+ |
|
| 36 |
+ queue, proc = cli_run_in_process(cli, path, args=['--on-error', 'terminate',
|
|
| 37 |
+ 'fetch', 'delaymock.bst'])
|
|
| 38 |
+ # Wait a few seconds, fetch should then be in progress. With DelayedMockSource
|
|
| 39 |
+ # fetch should take 20s unless terminated
|
|
| 40 |
+ time.sleep(3)
|
|
| 41 |
+ os.kill(proc.pid, signal.SIGINT)
|
|
| 42 |
+ |
|
| 43 |
+ # 10 second timeout
|
|
| 44 |
+ try:
|
|
| 45 |
+ output = queue.get(timeout=10)
|
|
| 46 |
+ stderr = queue.get(timeout=10)
|
|
| 47 |
+ except mp.queues.Empty:
|
|
| 48 |
+ assert False, 'Fetch failed to terminate'
|
|
| 49 |
+ |
|
| 50 |
+ matches = re.findall(r'FAILURE\s*Fetch', stderr)
|
|
| 51 |
+ success_matches = re.findall(r'SUCCESS\s*Fetch', stderr)
|
|
| 52 |
+ assert len(matches) or len(success_matches), "BuildStream exitted unexpectedly"
|
|
| 53 |
+ assert len(matches), "Unexpected success"
|
|
| 54 |
+ |
|
| 55 |
+ matches = re.findall(r'STATUS\s*Fetch terminating', stderr)
|
|
| 56 |
+ assert len(matches) != 0, "Fetch failed to terminate"
|
|
| 57 |
+ assert len(matches) == 1, "Fetch attempted to terminate more than once"
|
| 1 |
+kind: import
|
|
| 2 |
+ |
|
| 3 |
+sources:
|
|
| 4 |
+ - kind: delayed
|
|
| \ No newline at end of file |
| 1 |
+import time
|
|
| 2 |
+from buildstream import Source, SourceError, Consistency
|
|
| 3 |
+ |
|
| 4 |
+ |
|
| 5 |
+class DelayedMockSource(Source):
|
|
| 6 |
+ |
|
| 7 |
+ def configure(self, node):
|
|
| 8 |
+ pass
|
|
| 9 |
+ |
|
| 10 |
+ def preflight(self):
|
|
| 11 |
+ pass
|
|
| 12 |
+ |
|
| 13 |
+ def get_unique_key(self):
|
|
| 14 |
+ return {}
|
|
| 15 |
+ |
|
| 16 |
+ def get_consistency(self):
|
|
| 17 |
+ return Consistency.RESOLVED
|
|
| 18 |
+ |
|
| 19 |
+ def get_ref(self):
|
|
| 20 |
+ return None
|
|
| 21 |
+ |
|
| 22 |
+ def set_ref(self, ref, node):
|
|
| 23 |
+ pass
|
|
| 24 |
+ |
|
| 25 |
+ def fetch(self):
|
|
| 26 |
+ LOOP_FOR = 40
|
|
| 27 |
+ |
|
| 28 |
+ def prog_percent(prog):
|
|
| 29 |
+ return "{}%".format((float(i) / float(LOOP_FOR)) * 100)
|
|
| 30 |
+ for i in range(LOOP_FOR):
|
|
| 31 |
+ time.sleep(0.5)
|
|
| 32 |
+ self.status("Mock Source progress {}".format(prog_percent(i)))
|
|
| 33 |
+ |
|
| 34 |
+ def stage(self, directory):
|
|
| 35 |
+ pass
|
|
| 36 |
+ |
|
| 37 |
+ |
|
| 38 |
+def setup():
|
|
| 39 |
+ return DelayedMockSource
|
| 1 |
+name: interruptions
|
|
| 2 |
+element-path: elements
|
|
| 3 |
+plugins:
|
|
| 4 |
+- origin: local
|
|
| 5 |
+ path: plugins
|
|
| 6 |
+ sources:
|
|
| 7 |
+ delayed: 0
|
|
| \ No newline at end of file |
