Loading CMake/GkfsPythonTesting.cmake +22 −13 Original line number Diff line number Diff line Loading @@ -155,6 +155,7 @@ function(gkfs_add_python_test) COMMAND ${PYTEST_VIRTUALENV_PIP} install -r requirements.txt --upgrade -q ) if(NOT TARGET venv) # ensure that the virtual environment is created by the build process # (this is required because we can't add dependencies between # "test targets" and "normal targets" Loading @@ -162,11 +163,19 @@ function(gkfs_add_python_test) ALL DEPENDS ${PYTEST_VIRTUALENV} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/requirements.txt) endif() add_test(NAME ${PYTEST_NAME} COMMAND ${PYTEST_VIRTUALENV_INTERPRETER} -m pytest -m pytest -v -s ${PYTEST_COMMAND_ARGS} ${PYTEST_COMMAND} WORKING_DIRECTORY ${PYTEST_WORKING_DIRECTORY}) # instruct Python to not create __pycache__ directories, # otherwise they will pollute ${PYTEST_WORKING_DIRECTORY} which # is typically ${PROJECT_SOURCE_DIR} set_tests_properties(${PYTEST_NAME} PROPERTIES ENVIRONMENT PYTHONDONTWRITEBYTECODE=1) endfunction() tests/CMakeLists.txt +7 −0 Original line number Diff line number Diff line Loading @@ -23,3 +23,10 @@ gkfs_add_python_test( WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND tests/directories/test_directories.py ) gkfs_add_python_test( NAME test_shell PYTHON_VERSION 3.7 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND tests/shell/ ) tests/conftest.py +22 −4 Original line number Diff line number Diff line Loading @@ -15,11 +15,11 @@ import os, sys import pytest import logging from _pytest.logging import caplog as _caplog from loguru import logger from pathlib import Path from harness.logger import logger from harness.cli import add_cli_options from harness.workspace import Workspace from harness.gkfs import Daemon, Client from harness.workspace import Workspace, FileCreator from harness.gkfs import Daemon, Client, ShellClient def pytest_addoption(parser): """ Loading Loading @@ -75,3 +75,21 @@ def gkfs_client(test_workspace): """ return Client(test_workspace) @pytest.fixture def gkfs_shell(test_workspace): """ Sets up a gekkofs environment so that shell commands (stat, ls, mkdir, etc.) can be issued to a co-running daemon. """ return ShellClient(test_workspace) @pytest.fixture def file_factory(test_workspace): """ Returns a factory that can create custom input files in the test workspace. """ return FileCreator(test_workspace) tests/directories/test_directories.py +6 −6 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ import ctypes import sh import sys import pytest from loguru import logger from harness.logger import logger nonexisting = "nonexisting" Loading tests/harness/cmd.py 0 → 100644 +89 −0 Original line number Diff line number Diff line ################################################################################ # Copyright 2018-2020, Barcelona Supercomputing Center (BSC), Spain # # Copyright 2015-2020, Johannes Gutenberg Universitaet Mainz, Germany # # # # This software was partially supported by the # # EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). # # # # This software was partially supported by the # # ADA-FS project under the SPPEXA project funded by the DFG. # # # # SPDX-License-Identifier: MIT # ################################################################################ from collections import namedtuple class Md5sumOutputSchema: """ Schema to deserialize the results of a md5sum command: $ md5sum foobar 7f45c62700402ce5f9abe5b8d70d2844 foobar """ _field_names = [ 'digest', 'filename' ] def loads(self, input): values = input.split() return namedtuple('md5sumOutput', self._field_names)(*values) class StatOutputSchema: """ Schema to deserialize the results of a stat --terse command: $ stat --terse foobar foobar 913 8 81b4 1000 1000 10308 7343758 1 0 0 1583160824 1583160634 1583160634 0 4096 Output for the command follows the format below: %n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %W %o %C %n: file name %s: total size, in bytes %b: number of blocks %f: raw mode in hex %u: owner UID %g: owner GID %D: device numer in hex %i: inode number %h: number of hard links %t: major device in hex %T: minor device in hex %X: time of last access, seconds since Epoch %Y: time of last data modification, seconds since Epoch %Z: time of last status change, seconds since Epoch %W: time of file birth, seconds since Epoch; 0 if unknown %o: optimal I/O transfer size hint """ _field_names = [ 'filename', 'size', 'blocks', 'raw_mode', 'uid', 'gid', 'device', 'inode', 'hard_links', 'major', 'minor', 'last_access', 'last_modification', 'last_status_change', 'creation', 'transfer_size' ] _field_types = [ str, int, int, str, int, int, str, int, int, str, str, int, int, int, int, int, str ] def loads(self, input): values = [ t(s) for t,s in zip(self._field_types, input.split()) ] return namedtuple('statOutput', self._field_names)(*values) class CommandParser: """ A helper parser to transform the output of some shell commands to native Python objects. """ OutputSchemas = { 'md5sum' : Md5sumOutputSchema(), 'stat' : StatOutputSchema(), } def parse(self, command, output): if command not in self.OutputSchemas: raise NotImplementedError( f"Output parser for '{command}' not implemented") return self.OutputSchemas[command].loads(output) Loading
CMake/GkfsPythonTesting.cmake +22 −13 Original line number Diff line number Diff line Loading @@ -155,6 +155,7 @@ function(gkfs_add_python_test) COMMAND ${PYTEST_VIRTUALENV_PIP} install -r requirements.txt --upgrade -q ) if(NOT TARGET venv) # ensure that the virtual environment is created by the build process # (this is required because we can't add dependencies between # "test targets" and "normal targets" Loading @@ -162,11 +163,19 @@ function(gkfs_add_python_test) ALL DEPENDS ${PYTEST_VIRTUALENV} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/requirements.txt) endif() add_test(NAME ${PYTEST_NAME} COMMAND ${PYTEST_VIRTUALENV_INTERPRETER} -m pytest -m pytest -v -s ${PYTEST_COMMAND_ARGS} ${PYTEST_COMMAND} WORKING_DIRECTORY ${PYTEST_WORKING_DIRECTORY}) # instruct Python to not create __pycache__ directories, # otherwise they will pollute ${PYTEST_WORKING_DIRECTORY} which # is typically ${PROJECT_SOURCE_DIR} set_tests_properties(${PYTEST_NAME} PROPERTIES ENVIRONMENT PYTHONDONTWRITEBYTECODE=1) endfunction()
tests/CMakeLists.txt +7 −0 Original line number Diff line number Diff line Loading @@ -23,3 +23,10 @@ gkfs_add_python_test( WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND tests/directories/test_directories.py ) gkfs_add_python_test( NAME test_shell PYTHON_VERSION 3.7 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND tests/shell/ )
tests/conftest.py +22 −4 Original line number Diff line number Diff line Loading @@ -15,11 +15,11 @@ import os, sys import pytest import logging from _pytest.logging import caplog as _caplog from loguru import logger from pathlib import Path from harness.logger import logger from harness.cli import add_cli_options from harness.workspace import Workspace from harness.gkfs import Daemon, Client from harness.workspace import Workspace, FileCreator from harness.gkfs import Daemon, Client, ShellClient def pytest_addoption(parser): """ Loading Loading @@ -75,3 +75,21 @@ def gkfs_client(test_workspace): """ return Client(test_workspace) @pytest.fixture def gkfs_shell(test_workspace): """ Sets up a gekkofs environment so that shell commands (stat, ls, mkdir, etc.) can be issued to a co-running daemon. """ return ShellClient(test_workspace) @pytest.fixture def file_factory(test_workspace): """ Returns a factory that can create custom input files in the test workspace. """ return FileCreator(test_workspace)
tests/directories/test_directories.py +6 −6 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ import ctypes import sh import sys import pytest from loguru import logger from harness.logger import logger nonexisting = "nonexisting" Loading
tests/harness/cmd.py 0 → 100644 +89 −0 Original line number Diff line number Diff line ################################################################################ # Copyright 2018-2020, Barcelona Supercomputing Center (BSC), Spain # # Copyright 2015-2020, Johannes Gutenberg Universitaet Mainz, Germany # # # # This software was partially supported by the # # EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu). # # # # This software was partially supported by the # # ADA-FS project under the SPPEXA project funded by the DFG. # # # # SPDX-License-Identifier: MIT # ################################################################################ from collections import namedtuple class Md5sumOutputSchema: """ Schema to deserialize the results of a md5sum command: $ md5sum foobar 7f45c62700402ce5f9abe5b8d70d2844 foobar """ _field_names = [ 'digest', 'filename' ] def loads(self, input): values = input.split() return namedtuple('md5sumOutput', self._field_names)(*values) class StatOutputSchema: """ Schema to deserialize the results of a stat --terse command: $ stat --terse foobar foobar 913 8 81b4 1000 1000 10308 7343758 1 0 0 1583160824 1583160634 1583160634 0 4096 Output for the command follows the format below: %n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %W %o %C %n: file name %s: total size, in bytes %b: number of blocks %f: raw mode in hex %u: owner UID %g: owner GID %D: device numer in hex %i: inode number %h: number of hard links %t: major device in hex %T: minor device in hex %X: time of last access, seconds since Epoch %Y: time of last data modification, seconds since Epoch %Z: time of last status change, seconds since Epoch %W: time of file birth, seconds since Epoch; 0 if unknown %o: optimal I/O transfer size hint """ _field_names = [ 'filename', 'size', 'blocks', 'raw_mode', 'uid', 'gid', 'device', 'inode', 'hard_links', 'major', 'minor', 'last_access', 'last_modification', 'last_status_change', 'creation', 'transfer_size' ] _field_types = [ str, int, int, str, int, int, str, int, int, str, str, int, int, int, int, int, str ] def loads(self, input): values = [ t(s) for t,s in zip(self._field_types, input.split()) ] return namedtuple('statOutput', self._field_names)(*values) class CommandParser: """ A helper parser to transform the output of some shell commands to native Python objects. """ OutputSchemas = { 'md5sum' : Md5sumOutputSchema(), 'stat' : StatOutputSchema(), } def parse(self, command, output): if command not in self.OutputSchemas: raise NotImplementedError( f"Output parser for '{command}' not implemented") return self.OutputSchemas[command].loads(output)