From b9af52a88802ffa94dd549a0528ca787590b0fe7 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Fri, 29 Jun 2018 18:33:17 -0400 Subject: tests: Separate tests for llnl.util.lock and spack.util.lock - llnl.util.lock tests are now independent of Spack --- lib/spack/spack/cmd/test.py | 2 +- lib/spack/spack/test/cmd/__init__.py | 24 ---- lib/spack/spack/test/llnl/util/lock.py | 181 ++++++------------------ lib/spack/spack/test/util/spack_lock_wrapper.py | 123 ++++++++++++++++ 4 files changed, 169 insertions(+), 161 deletions(-) delete mode 100644 lib/spack/spack/test/cmd/__init__.py create mode 100644 lib/spack/spack/test/util/spack_lock_wrapper.py (limited to 'lib') diff --git a/lib/spack/spack/cmd/test.py b/lib/spack/spack/cmd/test.py index bc93981374..8b3a924b0c 100644 --- a/lib/spack/spack/cmd/test.py +++ b/lib/spack/spack/cmd/test.py @@ -96,7 +96,7 @@ def test(parser, args, unknown_args): pytest.main(['-h']) return - # pytest.ini lives in the root of the spack repository. + # pytest.ini lives in lib/spack/spack/test with working_dir(spack.paths.test_path): # --list and --long-list print the test output better. if args.list or args.long_list: diff --git a/lib/spack/spack/test/cmd/__init__.py b/lib/spack/spack/test/cmd/__init__.py deleted file mode 100644 index 0e81cb3b36..0000000000 --- a/lib/spack/spack/test/cmd/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -############################################################################## -# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC. -# Produced at the Lawrence Livermore National Laboratory. -# -# This file is part of Spack. -# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. -# LLNL-CODE-647188 -# -# For details, see https://github.com/spack/spack -# Please also see the NOTICE and LICENSE files for our notice and the LGPL. -# -# This program 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) version 2.1, February 1999. -# -# This program 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 terms and -# conditions of 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 program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -############################################################################## diff --git a/lib/spack/spack/test/llnl/util/lock.py b/lib/spack/spack/test/llnl/util/lock.py index 9b32b9fd15..7c38a2547f 100644 --- a/lib/spack/spack/test/llnl/util/lock.py +++ b/lib/spack/spack/test/llnl/util/lock.py @@ -72,11 +72,9 @@ from multiprocessing import Process import pytest +import llnl.util.lock as lk import llnl.util.multiproc as mp -from llnl.util.filesystem import touch, group_ids - -import spack.util.lock -from spack.util.lock import Lock, WriteTransaction, ReadTransaction, LockError +from llnl.util.filesystem import touch # @@ -267,7 +265,7 @@ multiproc_test = mpi_multiproc_test if mpi else local_multiproc_test # def acquire_write(lock_path, start=0, length=0): def fn(barrier): - lock = Lock(lock_path, start, length) + lock = lk.Lock(lock_path, start, length) lock.acquire_write() # grab exclusive lock barrier.wait() barrier.wait() # hold the lock until timeout in other procs. @@ -276,7 +274,7 @@ def acquire_write(lock_path, start=0, length=0): def acquire_read(lock_path, start=0, length=0): def fn(barrier): - lock = Lock(lock_path, start, length) + lock = lk.Lock(lock_path, start, length) lock.acquire_read() # grab shared lock barrier.wait() barrier.wait() # hold the lock until timeout in other procs. @@ -285,9 +283,9 @@ def acquire_read(lock_path, start=0, length=0): def timeout_write(lock_path, start=0, length=0): def fn(barrier): - lock = Lock(lock_path, start, length) + lock = lk.Lock(lock_path, start, length) barrier.wait() # wait for lock acquire in first process - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_write(lock_fail_timeout) barrier.wait() return fn @@ -295,9 +293,9 @@ def timeout_write(lock_path, start=0, length=0): def timeout_read(lock_path, start=0, length=0): def fn(barrier): - lock = Lock(lock_path, start, length) + lock = lk.Lock(lock_path, start, length) barrier.wait() # wait for lock acquire in first process - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_read(lock_fail_timeout) barrier.wait() return fn @@ -552,7 +550,7 @@ def test_upgrade_read_to_write(private_lock_path): # to begin wtih. touch(private_lock_path) - lock = Lock(private_lock_path) + lock = lk.Lock(private_lock_path) assert lock._reads == 0 assert lock._writes == 0 @@ -577,16 +575,14 @@ def test_upgrade_read_to_write(private_lock_path): assert lock._file is None -# -# Test that read-only file can be read-locked but not write-locked. -# def test_upgrade_read_to_write_fails_with_readonly_file(private_lock_path): - # ensure lock file exists the first time, so we open it read-only - # to begin wtih. + """Test that read-only file can be read-locked but not write-locked.""" + # ensure lock file exists the first time touch(private_lock_path) + # open it read-only to begin wtih. with read_only(private_lock_path): - lock = Lock(private_lock_path) + lock = lk.Lock(private_lock_path) assert lock._reads == 0 assert lock._writes == 0 @@ -595,7 +591,8 @@ def test_upgrade_read_to_write_fails_with_readonly_file(private_lock_path): assert lock._writes == 0 assert lock._file.mode == 'r' - with pytest.raises(LockError): + # upgrade to writ here + with pytest.raises(lk.LockError): lock.acquire_write() @@ -605,7 +602,7 @@ def test_upgrade_read_to_write_fails_with_readonly_file(private_lock_path): # def test_complex_acquire_and_release_chain(lock_path): def p1(barrier): - lock = Lock(lock_path) + lock = lk.Lock(lock_path) lock.acquire_write() barrier.wait() # ---------------------------------------- 1 @@ -613,7 +610,7 @@ def test_complex_acquire_and_release_chain(lock_path): barrier.wait() # ---------------------------------------- 2 lock.release_write() # release and others acquire read barrier.wait() # ---------------------------------------- 3 - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_write(lock_fail_timeout) lock.acquire_read() barrier.wait() # ---------------------------------------- 4 @@ -622,9 +619,9 @@ def test_complex_acquire_and_release_chain(lock_path): # p2 upgrades read to write barrier.wait() # ---------------------------------------- 6 - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_write(lock_fail_timeout) - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_read(lock_fail_timeout) barrier.wait() # ---------------------------------------- 7 # p2 releases write and read @@ -634,9 +631,9 @@ def test_complex_acquire_and_release_chain(lock_path): barrier.wait() # ---------------------------------------- 9 # p3 upgrades read to write barrier.wait() # ---------------------------------------- 10 - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_write(lock_fail_timeout) - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_read(lock_fail_timeout) barrier.wait() # ---------------------------------------- 11 # p3 releases locks @@ -646,13 +643,13 @@ def test_complex_acquire_and_release_chain(lock_path): lock.release_read() def p2(barrier): - lock = Lock(lock_path) + lock = lk.Lock(lock_path) # p1 acquires write barrier.wait() # ---------------------------------------- 1 - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_write(lock_fail_timeout) - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_read(lock_fail_timeout) barrier.wait() # ---------------------------------------- 2 lock.acquire_read() @@ -674,9 +671,9 @@ def test_complex_acquire_and_release_chain(lock_path): barrier.wait() # ---------------------------------------- 9 # p3 upgrades read to write barrier.wait() # ---------------------------------------- 10 - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_write(lock_fail_timeout) - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_read(lock_fail_timeout) barrier.wait() # ---------------------------------------- 11 # p3 releases locks @@ -686,13 +683,13 @@ def test_complex_acquire_and_release_chain(lock_path): lock.release_read() def p3(barrier): - lock = Lock(lock_path) + lock = lk.Lock(lock_path) # p1 acquires write barrier.wait() # ---------------------------------------- 1 - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_write(lock_fail_timeout) - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_read(lock_fail_timeout) barrier.wait() # ---------------------------------------- 2 lock.acquire_read() @@ -704,9 +701,9 @@ def test_complex_acquire_and_release_chain(lock_path): # p2 upgrades read to write barrier.wait() # ---------------------------------------- 6 - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_write(lock_fail_timeout) - with pytest.raises(LockError): + with pytest.raises(lk.LockError): lock.acquire_read(lock_fail_timeout) barrier.wait() # ---------------------------------------- 7 # p2 releases write & read @@ -736,9 +733,9 @@ def test_transaction(lock_path): vals['exited'] = True vals['exception'] = (t or v or tb) - lock = Lock(lock_path) + lock = lk.Lock(lock_path) vals = {'entered': False, 'exited': False, 'exception': False} - with ReadTransaction(lock, enter_fn, exit_fn): + with lk.ReadTransaction(lock, enter_fn, exit_fn): pass assert vals['entered'] @@ -746,7 +743,7 @@ def test_transaction(lock_path): assert not vals['exception'] vals = {'entered': False, 'exited': False, 'exception': False} - with WriteTransaction(lock, enter_fn, exit_fn): + with lk.WriteTransaction(lock, enter_fn, exit_fn): pass assert vals['entered'] @@ -762,14 +759,14 @@ def test_transaction_with_exception(lock_path): vals['exited'] = True vals['exception'] = (t or v or tb) - lock = Lock(lock_path) + lock = lk.Lock(lock_path) def do_read_with_exception(): - with ReadTransaction(lock, enter_fn, exit_fn): + with lk.ReadTransaction(lock, enter_fn, exit_fn): raise Exception() def do_write_with_exception(): - with WriteTransaction(lock, enter_fn, exit_fn): + with lk.WriteTransaction(lock, enter_fn, exit_fn): raise Exception() vals = {'entered': False, 'exited': False, 'exception': False} @@ -801,11 +798,11 @@ def test_transaction_with_context_manager(lock_path): vals['exited_fn'] = True vals['exception_fn'] = (t or v or tb) - lock = Lock(lock_path) + lock = lk.Lock(lock_path) vals = {'entered': False, 'exited': False, 'exited_fn': False, 'exception': False, 'exception_fn': False} - with ReadTransaction(lock, TestContextManager, exit_fn): + with lk.ReadTransaction(lock, TestContextManager, exit_fn): pass assert vals['entered'] @@ -816,7 +813,7 @@ def test_transaction_with_context_manager(lock_path): vals = {'entered': False, 'exited': False, 'exited_fn': False, 'exception': False, 'exception_fn': False} - with ReadTransaction(lock, TestContextManager): + with lk.ReadTransaction(lock, TestContextManager): pass assert vals['entered'] @@ -827,7 +824,7 @@ def test_transaction_with_context_manager(lock_path): vals = {'entered': False, 'exited': False, 'exited_fn': False, 'exception': False, 'exception_fn': False} - with WriteTransaction(lock, TestContextManager, exit_fn): + with lk.WriteTransaction(lock, TestContextManager, exit_fn): pass assert vals['entered'] @@ -838,7 +835,7 @@ def test_transaction_with_context_manager(lock_path): vals = {'entered': False, 'exited': False, 'exited_fn': False, 'exception': False, 'exception_fn': False} - with WriteTransaction(lock, TestContextManager): + with lk.WriteTransaction(lock, TestContextManager): pass assert vals['entered'] @@ -861,14 +858,14 @@ def test_transaction_with_context_manager_and_exception(lock_path): vals['exited_fn'] = True vals['exception_fn'] = (t or v or tb) - lock = Lock(lock_path) + lock = lk.Lock(lock_path) def do_read_with_exception(exit_fn): - with ReadTransaction(lock, TestContextManager, exit_fn): + with lk.ReadTransaction(lock, TestContextManager, exit_fn): raise Exception() def do_write_with_exception(exit_fn): - with WriteTransaction(lock, TestContextManager, exit_fn): + with lk.WriteTransaction(lock, TestContextManager, exit_fn): raise Exception() vals = {'entered': False, 'exited': False, 'exited_fn': False, @@ -910,91 +907,3 @@ def test_transaction_with_context_manager_and_exception(lock_path): assert vals['exception'] assert not vals['exited_fn'] assert not vals['exception_fn'] - - -def test_disable_locking(private_lock_path): - """Ensure that locks do no real locking when disabled.""" - old_value = spack.config.get('config:locks') - - with spack.config.override('config:locks', False): - lock = Lock(private_lock_path) - - lock.acquire_read() - assert not os.path.exists(private_lock_path) - - lock.acquire_write() - assert not os.path.exists(private_lock_path) - - lock.release_write() - assert not os.path.exists(private_lock_path) - - lock.release_read() - assert not os.path.exists(private_lock_path) - - assert old_value == spack.config.get('config:locks') - - -def test_lock_checks_user(tmpdir): - """Ensure lock checks work with a self-owned, self-group repo.""" - uid = os.getuid() - if uid not in group_ids(): - pytest.skip("user has no group with gid == uid") - - # self-owned, own group - tmpdir.chown(uid, uid) - - # safe - path = str(tmpdir) - tmpdir.chmod(0o744) - spack.util.lock.check_lock_safety(path) - - # safe - tmpdir.chmod(0o774) - spack.util.lock.check_lock_safety(path) - - # unsafe - tmpdir.chmod(0o777) - with pytest.raises(spack.error.SpackError): - spack.util.lock.check_lock_safety(path) - - # safe - tmpdir.chmod(0o474) - spack.util.lock.check_lock_safety(path) - - # safe - tmpdir.chmod(0o477) - spack.util.lock.check_lock_safety(path) - - -def test_lock_checks_group(tmpdir): - """Ensure lock checks work with a self-owned, non-self-group repo.""" - uid = os.getuid() - gid = next((g for g in group_ids() if g != uid), None) - if not gid: - pytest.skip("user has no group with gid != uid") - - # self-owned, another group - tmpdir.chown(uid, gid) - - # safe - path = str(tmpdir) - tmpdir.chmod(0o744) - spack.util.lock.check_lock_safety(path) - - # unsafe - tmpdir.chmod(0o774) - with pytest.raises(spack.error.SpackError): - spack.util.lock.check_lock_safety(path) - - # unsafe - tmpdir.chmod(0o777) - with pytest.raises(spack.error.SpackError): - spack.util.lock.check_lock_safety(path) - - # safe - tmpdir.chmod(0o474) - spack.util.lock.check_lock_safety(path) - - # safe - tmpdir.chmod(0o477) - spack.util.lock.check_lock_safety(path) diff --git a/lib/spack/spack/test/util/spack_lock_wrapper.py b/lib/spack/spack/test/util/spack_lock_wrapper.py new file mode 100644 index 0000000000..03fbb7d791 --- /dev/null +++ b/lib/spack/spack/test/util/spack_lock_wrapper.py @@ -0,0 +1,123 @@ +############################################################################## +# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/spack/spack +# Please also see the NOTICE and LICENSE files for our notice and the LGPL. +# +# This program 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) version 2.1, February 1999. +# +# This program 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 terms and +# conditions of 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 program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +"""Tests for Spack's wrapper module around llnl.util.lock.""" +import os + +import pytest + +from llnl.util.filesystem import group_ids + +import spack.config +import spack.util.lock as lk + + +def test_disable_locking(tmpdir): + """Ensure that locks do no real locking when disabled.""" + lock_path = str(tmpdir.join('lockfile')) + + old_value = spack.config.get('config:locks') + + with spack.config.override('config:locks', False): + lock = lk.Lock(lock_path) + + lock.acquire_read() + assert not os.path.exists(lock_path) + + lock.acquire_write() + assert not os.path.exists(lock_path) + + lock.release_write() + assert not os.path.exists(lock_path) + + lock.release_read() + assert not os.path.exists(lock_path) + + assert old_value == spack.config.get('config:locks') + + +def test_lock_checks_user(tmpdir): + """Ensure lock checks work with a self-owned, self-group repo.""" + uid = os.getuid() + if uid not in group_ids(): + pytest.skip("user has no group with gid == uid") + + # self-owned, own group + tmpdir.chown(uid, uid) + + # safe + path = str(tmpdir) + tmpdir.chmod(0o744) + lk.check_lock_safety(path) + + # safe + tmpdir.chmod(0o774) + lk.check_lock_safety(path) + + # unsafe + tmpdir.chmod(0o777) + with pytest.raises(spack.error.SpackError): + lk.check_lock_safety(path) + + # safe + tmpdir.chmod(0o474) + lk.check_lock_safety(path) + + # safe + tmpdir.chmod(0o477) + lk.check_lock_safety(path) + + +def test_lock_checks_group(tmpdir): + """Ensure lock checks work with a self-owned, non-self-group repo.""" + uid = os.getuid() + gid = next((g for g in group_ids() if g != uid), None) + if not gid: + pytest.skip("user has no group with gid != uid") + + # self-owned, another group + tmpdir.chown(uid, gid) + + # safe + path = str(tmpdir) + tmpdir.chmod(0o744) + lk.check_lock_safety(path) + + # unsafe + tmpdir.chmod(0o774) + with pytest.raises(spack.error.SpackError): + lk.check_lock_safety(path) + + # unsafe + tmpdir.chmod(0o777) + with pytest.raises(spack.error.SpackError): + lk.check_lock_safety(path) + + # safe + tmpdir.chmod(0o474) + lk.check_lock_safety(path) + + # safe + tmpdir.chmod(0o477) + lk.check_lock_safety(path) -- cgit v1.2.3-60-g2f50