Ejegg has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/342968 )
Change subject: Tests for the lock module ...................................................................... Tests for the lock module Change-Id: I242a42518e1eaab5c8b039af2ac75615db9d01cc --- M lock.py M tests/data/timeout.yaml M tests/test_job_wrapper.py A tests/test_lock.py M tox.ini 5 files changed, 101 insertions(+), 21 deletions(-) Approvals: Ejegg: Verified; Looks good to me, approved diff --git a/lock.py b/lock.py index 523b56c..67e2cdd 100644 --- a/lock.py +++ b/lock.py @@ -3,6 +3,7 @@ Self-corrects stale locks unless "failopen" is True. ''' +from __future__ import print_function import os import os.path import sys @@ -17,7 +18,7 @@ filename = "/tmp/{name}.lock".format(name=job_tag) if os.path.exists(filename): - log.warn("Lockfile found!") + print("Lockfile found!", file=sys.stderr) f = open(filename, "r") pid = None try: @@ -26,18 +27,16 @@ pass f.close() if not pid: - log.error("Invalid lockfile contents.") + print("Invalid lockfile contents.", file=sys.stderr) else: try: os.getpgid(pid) - log.error("Aborting! Previous process ({pid}) is still alive. Remove lockfile manually if in error: {path}".format(pid=pid, path=filename)) - sys.exit(1) + raise LockError("Aborting! Previous process ({pid}) is still alive. Remove lockfile manually if in error: {path}".format(pid=pid, path=filename)) except OSError: if failopen: - log.fatal("Aborting until stale lockfile is investigated: {path}".format(path=filename)) - sys.exit(1) - log.error("Lockfile is stale.") - log.info("Removing old lockfile.") + raise LockError("Aborting until stale lockfile is investigated: {path}".format(path=filename)) + print("Lockfile is stale.", file=sys.stderr) + print("Removing old lockfile.", file=sys.stderr) os.unlink(filename) f = open(filename, "w") @@ -50,7 +49,11 @@ def end(): global lockfile - if lockfile: + if lockfile and os.path.exists(lockfile): os.unlink(lockfile) else: - raise RuntimeError("Already unlocked!") + raise LockError("Already unlocked!") + + +class LockError(RuntimeError): + pass diff --git a/tests/data/timeout.yaml b/tests/data/timeout.yaml index 5dfe4ee..9e01920 100644 --- a/tests/data/timeout.yaml +++ b/tests/data/timeout.yaml @@ -1,3 +1,3 @@ name: Timing out job -command: /bin/sleep 2 +command: /bin/sleep 10 timeout: 0.1 diff --git a/tests/test_job_wrapper.py b/tests/test_job_wrapper.py index 463edbd..cea8ef6 100644 --- a/tests/test_job_wrapper.py +++ b/tests/test_job_wrapper.py @@ -1,5 +1,6 @@ import datetime import iocapture +import nose import os import job_wrapper @@ -31,9 +32,9 @@ assert captured.stderr == "Job False job failed with code 1\n" +# Must finish in less than two seconds, i.e. must have timed out. +@nose.tools.timed(2) def test_timeout(): - start_time = datetime.datetime.utcnow() - with iocapture.capture() as captured: run_job("timeout.yaml") @@ -42,11 +43,6 @@ "Job Timing out job timed out after 0.1 seconds\n" "Job Timing out job failed with code -9\n" ) - - end_time = datetime.datetime.utcnow() - delta = end_time - start_time - - assert delta.total_seconds() < 2 def test_stderr(): diff --git a/tests/test_lock.py b/tests/test_lock.py new file mode 100644 index 0000000..cc3d71b --- /dev/null +++ b/tests/test_lock.py @@ -0,0 +1,84 @@ +import nose +import os.path + +import lock + + +def tearDown(): + # Clean up any old lockfiles. + if lock.lockfile != None: + if os.path.exists(lock.lockfile): + os.unlink(lock.lockfile) + + +def test_success(): + tag = "success" + lock.begin(job_tag=tag) + assert lock.lockfile + assert os.path.exists(lock.lockfile) + + lock.end() + assert not os.path.exists(lock.lockfile) + + +@nose.tools.raises(lock.LockError) +def test_live_lock(): + tag = "live" + lock.begin(job_tag=tag) + + # Will die because the process (this one) is still running. + lock.begin(job_tag=tag) + + +def test_stale_lock(): + tag = "stale" + lock.begin(job_tag=tag) + + # Make the lockfile stale by changing the process ID. + assert lock.lockfile + f = open(lock.lockfile, "w") + f.write("-1") + f.close() + + lock.begin(job_tag=tag) + + # Check that we overwrote the contents with the current PID. + assert lock.lockfile + f = open(lock.lockfile, "r") + pid = int(f.read()) + f.close() + assert pid == os.getpid() + + lock.end() + + +def test_invalid_lock(): + tag = "stale" + lock.begin(job_tag=tag) + + # Make the lockfile invalid by changing the process ID to non-numeric. + assert lock.lockfile + f = open(lock.lockfile, "w") + f.write("ABC") + f.close() + + lock.begin(job_tag=tag) + + # Check that we overwrote the contents with the current PID. + assert lock.lockfile + f = open(lock.lockfile, "r") + pid = int(f.read()) + f.close() + assert pid == os.getpid() + + lock.end() + + +@nose.tools.raises(lock.LockError) +def test_double_unlock(): + tag = "unlock-unlock" + lock.begin(job_tag=tag) + lock.end() + + # Should throw an exception. + lock.end() diff --git a/tox.ini b/tox.ini index a26b3ba..e950c92 100644 --- a/tox.ini +++ b/tox.ini @@ -17,6 +17,3 @@ ignore=E501 exclude = .tox - -[pytest] -python_paths = . -- To view, visit https://gerrit.wikimedia.org/r/342968 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I242a42518e1eaab5c8b039af2ac75615db9d01cc Gerrit-PatchSet: 3 Gerrit-Project: wikimedia/fundraising/process-control Gerrit-Branch: master Gerrit-Owner: Awight <awi...@wikimedia.org> Gerrit-Reviewer: Awight <awi...@wikimedia.org> Gerrit-Reviewer: Ejegg <eeggles...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits