From 3f4506f088b17fa0db7929fc4818f11f9ad1396b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Fri, 13 Oct 2017 19:50:28 -0500 Subject: [PATCH] tests: Test concurrent operations Test that concurrent commits and prunes can succeed. Mostly this is a check that the new locking works correctly and the concurrent processes will properly wait until they've acquired the appropriate repository lock. Closes: #1343 Approved by: cgwalters --- Makefile-tests.am | 1 + tests/test-concurrency.py | 107 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100755 tests/test-concurrency.py diff --git a/Makefile-tests.am b/Makefile-tests.am index 6d0e0865d9..2b33555614 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -108,6 +108,7 @@ _installed_or_uninstalled_test_scripts = \ tests/test-xattrs.sh \ tests/test-auto-summary.sh \ tests/test-prune.sh \ + tests/test-concurrency.py \ tests/test-refs.sh \ tests/test-demo-buildsystem.sh \ tests/test-switchroot.sh \ diff --git a/tests/test-concurrency.py b/tests/test-concurrency.py new file mode 100755 index 0000000000..6aa8f26993 --- /dev/null +++ b/tests/test-concurrency.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# +# Copyright (C) 2017 Colin Walters +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +from __future__ import print_function +import os +import sys +import shutil +import subprocess +from multiprocessing import cpu_count + +def fatal(msg): + sys.stderr.write(msg) + sys.stderr.write('\n') + sys.exit(1) + +# Create 20 files with content based on @dname + a serial, basically to have +# different files with different checksums. +def mktree(dname, serial=0): + print('Creating tree', dname, file=sys.stderr) + os.mkdir(dname, 0755) + for v in xrange(20): + with open('{}/{}'.format(dname, v), 'w') as f: + f.write('{} {} {}\n'.format(dname, serial, v)) + +subprocess.check_call(['ostree', '--repo=repo', 'init', '--mode=bare']) +# like the bit in libtest, but let's do it unconditionally since it's simpler, +# and we don't need xattr coverage for this +with open('repo/config', 'a') as f: + f.write('disable-xattrs=true\n') + +def commit(v): + tdir='tree{}'.format(v) + cmd = ['ostree', '--repo=repo', 'commit', '--fsync=0', '-b', tdir, '--tree=dir='+tdir] + proc = subprocess.Popen(cmd) + print('PID {}'.format(proc.pid), *cmd, file=sys.stderr) + return proc +def prune(): + cmd = ['ostree', '--repo=repo', 'prune', '--refs-only'] + proc = subprocess.Popen(cmd) + print('PID {}:'.format(proc.pid), *cmd, file=sys.stderr) + return proc + +def wait_check(proc): + pid = proc.pid + proc.wait() + if proc.returncode != 0: + sys.stderr.write("process {} exited with code {}\n".format(proc.pid, proc.returncode)) + return False + else: + sys.stderr.write('PID {} exited OK\n'.format(pid)) + return True + +print("1..2") + +def run(n_committers, n_pruners): + # The number of committers needs to be even since we only create half as + # many trees + n_committers += n_committers % 2 + + committers = set() + pruners = set() + + print('n_committers', n_committers, 'n_pruners', n_pruners, file=sys.stderr) + n_trees = n_committers / 2 + for v in xrange(n_trees): + mktree('tree{}'.format(v)) + + for v in xrange(n_committers): + committers.add(commit(v / 2)) + for v in xrange(n_pruners): + pruners.add(prune()) + + failed = False + for committer in committers: + if not wait_check(committer): + failed = True + for pruner in pruners: + if not wait_check(pruner): + failed = True + if failed: + fatal('A child process exited abnormally') + + for v in xrange(n_trees): + shutil.rmtree('tree{}'.format(v)) + +# No concurrent pruning +run(cpu_count()/2 + 2, 0) +print("ok no concurrent prunes") + +run(cpu_count()/2 + 4, 3) +print("ok concurrent prunes")