Skip to content

Commit a0a8506

Browse files
New OsOperations methods: makedir, rmdir (#253)
Signatures: def makedir(self, path: str) def rmdir(self, path: str) It is a part of work for #247.
1 parent df4f545 commit a0a8506

File tree

4 files changed

+294
-0
lines changed

4 files changed

+294
-0
lines changed

testgres/operations/local_ops.py

+8
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ def makedirs(self, path, remove_existing=False):
250250
except FileExistsError:
251251
pass
252252

253+
def makedir(self, path: str):
254+
assert type(path) == str # noqa: E721
255+
os.mkdir(path)
256+
253257
# [2025-02-03] Old name of parameter attempts is "retries".
254258
def rmdirs(self, path, ignore_errors=True, attempts=3, delay=1):
255259
"""
@@ -293,6 +297,10 @@ def rmdirs(self, path, ignore_errors=True, attempts=3, delay=1):
293297
# OK!
294298
return True
295299

300+
def rmdir(self, path: str):
301+
assert type(path) == str # noqa: E721
302+
os.rmdir(path)
303+
296304
def listdir(self, path):
297305
return os.listdir(path)
298306

testgres/operations/os_ops.py

+8
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,17 @@ def get_name(self):
5353
def makedirs(self, path, remove_existing=False):
5454
raise NotImplementedError()
5555

56+
def makedir(self, path: str):
57+
assert type(path) == str # noqa: E721
58+
raise NotImplementedError()
59+
5660
def rmdirs(self, path, ignore_errors=True):
5761
raise NotImplementedError()
5862

63+
def rmdir(self, path: str):
64+
assert type(path) == str # noqa: E721
65+
raise NotImplementedError()
66+
5967
def listdir(self, path):
6068
raise NotImplementedError()
6169

testgres/operations/remote_ops.py

+10
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,11 @@ def makedirs(self, path, remove_existing=False):
225225
raise Exception("Couldn't create dir {} because of error {}".format(path, error))
226226
return result
227227

228+
def makedir(self, path: str):
229+
assert type(path) == str # noqa: E721
230+
cmd = ["mkdir", path]
231+
self.exec_command(cmd)
232+
228233
def rmdirs(self, path, ignore_errors=True):
229234
"""
230235
Remove a directory in the remote server.
@@ -265,6 +270,11 @@ def rmdirs(self, path, ignore_errors=True):
265270
return False
266271
return True
267272

273+
def rmdir(self, path: str):
274+
assert type(path) == str # noqa: E721
275+
cmd = ["rmdir", path]
276+
self.exec_command(cmd)
277+
268278
def listdir(self, path):
269279
"""
270280
List all files and directories in a directory.

tests/test_os_ops_common.py

+268
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@
1313
import socket
1414
import threading
1515
import typing
16+
import uuid
1617

1718
from testgres import InvalidOperationException
1819
from testgres import ExecUtilException
1920

21+
from concurrent.futures import ThreadPoolExecutor
22+
from concurrent.futures import Future as ThreadFuture
23+
2024

2125
class TestOsOpsCommon:
2226
sm_os_ops_descrs: typing.List[OsOpsDescr] = [
@@ -812,3 +816,267 @@ def LOCAL_server(s: socket.socket):
812816

813817
if ok_count == 0:
814818
raise RuntimeError("No one free port was found.")
819+
820+
class tagData_OS_OPS__NUMS:
821+
os_ops_descr: OsOpsDescr
822+
nums: int
823+
824+
def __init__(self, os_ops_descr: OsOpsDescr, nums: int):
825+
assert isinstance(os_ops_descr, OsOpsDescr)
826+
assert type(nums) == int # noqa: E721
827+
828+
self.os_ops_descr = os_ops_descr
829+
self.nums = nums
830+
831+
sm_test_exclusive_creation__mt__data = [
832+
tagData_OS_OPS__NUMS(OsOpsDescrs.sm_local_os_ops_descr, 100000),
833+
tagData_OS_OPS__NUMS(OsOpsDescrs.sm_remote_os_ops_descr, 120),
834+
]
835+
836+
@pytest.fixture(
837+
params=sm_test_exclusive_creation__mt__data,
838+
ids=[x.os_ops_descr.sign for x in sm_test_exclusive_creation__mt__data]
839+
)
840+
def data001(self, request: pytest.FixtureRequest) -> tagData_OS_OPS__NUMS:
841+
assert isinstance(request, pytest.FixtureRequest)
842+
return request.param
843+
844+
def test_mkdir__mt(self, data001: tagData_OS_OPS__NUMS):
845+
assert type(data001) == __class__.tagData_OS_OPS__NUMS # noqa: E721
846+
847+
N_WORKERS = 4
848+
N_NUMBERS = data001.nums
849+
assert type(N_NUMBERS) == int # noqa: E721
850+
851+
os_ops = data001.os_ops_descr.os_ops
852+
assert isinstance(os_ops, OsOperations)
853+
854+
lock_dir_prefix = "test_mkdir_mt--" + uuid.uuid4().hex
855+
856+
lock_dir = os_ops.mkdtemp(prefix=lock_dir_prefix)
857+
858+
logging.info("A lock file [{}] is creating ...".format(lock_dir))
859+
860+
assert os.path.exists(lock_dir)
861+
862+
def MAKE_PATH(lock_dir: str, num: int) -> str:
863+
assert type(lock_dir) == str # noqa: E721
864+
assert type(num) == int # noqa: E721
865+
return os.path.join(lock_dir, str(num) + ".lock")
866+
867+
def LOCAL_WORKER(os_ops: OsOperations,
868+
workerID: int,
869+
lock_dir: str,
870+
cNumbers: int,
871+
reservedNumbers: typing.Set[int]) -> None:
872+
assert isinstance(os_ops, OsOperations)
873+
assert type(workerID) == int # noqa: E721
874+
assert type(lock_dir) == str # noqa: E721
875+
assert type(cNumbers) == int # noqa: E721
876+
assert type(reservedNumbers) == set # noqa: E721
877+
assert cNumbers > 0
878+
assert len(reservedNumbers) == 0
879+
880+
assert os.path.exists(lock_dir)
881+
882+
def LOG_INFO(template: str, *args: list) -> None:
883+
assert type(template) == str # noqa: E721
884+
assert type(args) == tuple # noqa: E721
885+
886+
msg = template.format(*args)
887+
assert type(msg) == str # noqa: E721
888+
889+
logging.info("[Worker #{}] {}".format(workerID, msg))
890+
return
891+
892+
LOG_INFO("HELLO! I am here!")
893+
894+
for num in range(cNumbers):
895+
assert not (num in reservedNumbers)
896+
897+
file_path = MAKE_PATH(lock_dir, num)
898+
899+
try:
900+
os_ops.makedir(file_path)
901+
except Exception as e:
902+
LOG_INFO(
903+
"Can't reserve {}. Error ({}): {}",
904+
num,
905+
type(e).__name__,
906+
str(e)
907+
)
908+
continue
909+
910+
LOG_INFO("Number {} is reserved!", num)
911+
assert os_ops.path_exists(file_path)
912+
reservedNumbers.add(num)
913+
continue
914+
915+
n_total = cNumbers
916+
n_ok = len(reservedNumbers)
917+
assert n_ok <= n_total
918+
919+
LOG_INFO("Finish! OK: {}. FAILED: {}.", n_ok, n_total - n_ok)
920+
return
921+
922+
# -----------------------
923+
logging.info("Worker are creating ...")
924+
925+
threadPool = ThreadPoolExecutor(
926+
max_workers=N_WORKERS,
927+
thread_name_prefix="ex_creator"
928+
)
929+
930+
class tadWorkerData:
931+
future: ThreadFuture
932+
reservedNumbers: typing.Set[int]
933+
934+
workerDatas: typing.List[tadWorkerData] = list()
935+
936+
nErrors = 0
937+
938+
try:
939+
for n in range(N_WORKERS):
940+
logging.info("worker #{} is creating ...".format(n))
941+
942+
workerDatas.append(tadWorkerData())
943+
944+
workerDatas[n].reservedNumbers = set()
945+
946+
workerDatas[n].future = threadPool.submit(
947+
LOCAL_WORKER,
948+
os_ops,
949+
n,
950+
lock_dir,
951+
N_NUMBERS,
952+
workerDatas[n].reservedNumbers
953+
)
954+
955+
assert workerDatas[n].future is not None
956+
957+
logging.info("OK. All the workers were created!")
958+
except Exception as e:
959+
nErrors += 1
960+
logging.error("A problem is detected ({}): {}".format(type(e).__name__, str(e)))
961+
962+
logging.info("Will wait for stop of all the workers...")
963+
964+
nWorkers = 0
965+
966+
assert type(workerDatas) == list # noqa: E721
967+
968+
for i in range(len(workerDatas)):
969+
worker = workerDatas[i].future
970+
971+
if worker is None:
972+
continue
973+
974+
nWorkers += 1
975+
976+
assert isinstance(worker, ThreadFuture)
977+
978+
try:
979+
logging.info("Wait for worker #{}".format(i))
980+
worker.result()
981+
except Exception as e:
982+
nErrors += 1
983+
logging.error("Worker #{} finished with error ({}): {}".format(
984+
i,
985+
type(e).__name__,
986+
str(e),
987+
))
988+
continue
989+
990+
assert nWorkers == N_WORKERS
991+
992+
if nErrors != 0:
993+
raise RuntimeError("Some problems were detected. Please examine the log messages.")
994+
995+
logging.info("OK. Let's check worker results!")
996+
997+
reservedNumbers: typing.Dict[int, int] = dict()
998+
999+
for i in range(N_WORKERS):
1000+
logging.info("Worker #{} is checked ...".format(i))
1001+
1002+
workerNumbers = workerDatas[i].reservedNumbers
1003+
assert type(workerNumbers) == set # noqa: E721
1004+
1005+
for n in workerNumbers:
1006+
if n < 0 or n >= N_NUMBERS:
1007+
nErrors += 1
1008+
logging.error("Unexpected number {}".format(n))
1009+
continue
1010+
1011+
if n in reservedNumbers.keys():
1012+
nErrors += 1
1013+
logging.error("Number {} was already reserved by worker #{}".format(
1014+
n,
1015+
reservedNumbers[n]
1016+
))
1017+
else:
1018+
reservedNumbers[n] = i
1019+
1020+
file_path = MAKE_PATH(lock_dir, n)
1021+
if not os_ops.path_exists(file_path):
1022+
nErrors += 1
1023+
logging.error("File {} is not found!".format(file_path))
1024+
continue
1025+
1026+
continue
1027+
1028+
logging.info("OK. Let's check reservedNumbers!")
1029+
1030+
for n in range(N_NUMBERS):
1031+
if not (n in reservedNumbers.keys()):
1032+
nErrors += 1
1033+
logging.error("Number {} is not reserved!".format(n))
1034+
continue
1035+
1036+
file_path = MAKE_PATH(lock_dir, n)
1037+
if not os_ops.path_exists(file_path):
1038+
nErrors += 1
1039+
logging.error("File {} is not found!".format(file_path))
1040+
continue
1041+
1042+
# OK!
1043+
continue
1044+
1045+
logging.info("Verification is finished! Total error count is {}.".format(nErrors))
1046+
1047+
if nErrors == 0:
1048+
logging.info("Root lock-directory [{}] will be deleted.".format(
1049+
lock_dir
1050+
))
1051+
1052+
for n in range(N_NUMBERS):
1053+
file_path = MAKE_PATH(lock_dir, n)
1054+
try:
1055+
os_ops.rmdir(file_path)
1056+
except Exception as e:
1057+
nErrors += 1
1058+
logging.error("Cannot delete directory [{}]. Error ({}): {}".format(
1059+
file_path,
1060+
type(e).__name__,
1061+
str(e)
1062+
))
1063+
continue
1064+
1065+
if os_ops.path_exists(file_path):
1066+
nErrors += 1
1067+
logging.error("Directory {} is not deleted!".format(file_path))
1068+
continue
1069+
1070+
if nErrors == 0:
1071+
try:
1072+
os_ops.rmdir(lock_dir)
1073+
except Exception as e:
1074+
nErrors += 1
1075+
logging.error("Cannot delete directory [{}]. Error ({}): {}".format(
1076+
lock_dir,
1077+
type(e).__name__,
1078+
str(e)
1079+
))
1080+
1081+
logging.info("Test is finished! Total error count is {}.".format(nErrors))
1082+
return

0 commit comments

Comments
 (0)