Commit 073e5cca authored by Alberto Miranda's avatar Alberto Miranda ♨️ Committed by Marc Vef
Browse files

Add support for testing shell commands

Additional tests for shell commands
Hide loguru in harness.logger
Code cleanup
parent 06e1faff
Loading
Loading
Loading
Loading
+22 −13
Original line number Diff line number Diff line
@@ -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"
@@ -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()
+7 −0
Original line number Diff line number Diff line
@@ -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/
)
+22 −4
Original line number Diff line number Diff line
@@ -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):
    """
@@ -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)
+6 −6
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@ import ctypes
import sh
import sys
import pytest
from loguru import logger
from harness.logger import logger

nonexisting = "nonexisting"

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