Verified Commit fbd22abd authored by Alberto Miranda's avatar Alberto Miranda ♨️
Browse files

Sync test server with Catch2 rewind infrastructure

parent 8edc4612
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@ mpi_tests_SOURCES = \
	$(top_srcdir)/tests/catch.hpp \
	mpi-tests-main.cpp \
	mpi-remote-transfers.cpp \
	mpi-helpers.hpp \
	mpi-helpers.cpp \
	../fake-daemon.cpp \
	../fake-daemon.hpp \
	../test-env.cpp \

tests/mpi/commands.hpp

0 → 100644
+37 −0
Original line number Diff line number Diff line
/*************************************************************************
 * Copyright (C) 2017-2018 Barcelona Supercomputing Center               *
 *                         Centro Nacional de Supercomputacion           *
 * All rights reserved.                                                  *
 *                                                                       *
 * This file is part of the NORNS Data Scheduler, a service that allows  *
 * other programs to start, track and manage asynchronous transfers of   *
 * data resources transfers requests between different storage backends. *
 *                                                                       *
 * See AUTHORS file in the top level directory for information           *
 * regarding developers and contributors.                                *
 *                                                                       *
 * The NORNS Data Scheduler 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, either          *
 * version 3 of the License, or (at your option) any later version.      *
 *                                                                       *
 * The NORNS Data Scheduler 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 GNU  *
 * Lesser General Public License for more details.                       *
 *                                                                       *
 * You should have received a copy of the GNU Lesser General             *
 * Public License along with the NORNS Data Scheduler.  If not, see      *
 * <http://www.gnu.org/licenses/>.                                       *
 *************************************************************************/

#ifndef COMMANDS_HPP
#define COMMANDS_HPP

enum class server_command : int {
    accept = 0,
    restart,
    shutdown
};

#endif // COMMANDS_HPP
+111 −0
Original line number Diff line number Diff line
/*************************************************************************
 * Copyright (C) 2017-2018 Barcelona Supercomputing Center               *
 *                         Centro Nacional de Supercomputacion           *
 * All rights reserved.                                                  *
 *                                                                       *
 * This file is part of the NORNS Data Scheduler, a service that allows  *
 * other programs to start, track and manage asynchronous transfers of   *
 * data resources transfers requests between different storage backends. *
 *                                                                       *
 * See AUTHORS file in the top level directory for information           *
 * regarding developers and contributors.                                *
 *                                                                       *
 * The NORNS Data Scheduler 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, either          *
 * version 3 of the License, or (at your option) any later version.      *
 *                                                                       *
 * The NORNS Data Scheduler 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 GNU  *
 * Lesser General Public License for more details.                       *
 *                                                                       *
 * You should have received a copy of the GNU Lesser General             *
 * Public License along with the NORNS Data Scheduler.  If not, see      *
 * <http://www.gnu.org/licenses/>.                                       *
 *************************************************************************/

#include <stdexcept>
#include <stdlib.h>
#include "mpi-helpers.hpp"

#ifdef MPI_TEST_DEBUG
#include <iostream>
#include <sstream>
#endif // MPI_TEST_DEBUG


namespace mpi {

void
initialize(int* argc, char** argv[]) {
    if(::MPI_Init(argc, argv) != MPI_SUCCESS) {
        throw std::runtime_error("Failed to initialize MPI");
    }
}

void
finalize() {
    if(::MPI_Finalize() != MPI_SUCCESS) {
        throw std::runtime_error("Failed to finalize MPI");
    }
}


int
get_rank() {
    int world_rank;
    if(::MPI_Comm_rank(MPI_COMM_WORLD, &world_rank) != MPI_SUCCESS) {
        throw std::runtime_error("Failed to determine own rank");
    }

    return world_rank;
}

void
barrier() {

#ifdef MPI_TEST_DEBUG
    std::cerr << __PRETTY_FUNCTION__ << "\n";
    std::cerr << "Entering MPI_Barrier()\n";
#endif // MPI_TEST_DEBUG

    ::MPI_Barrier(MPI_COMM_WORLD);

#ifdef MPI_TEST_DEBUG
    std::cerr << "Exiting MPI_Barrier()\n";
#endif // MPI_TEST_DEBUG

}

server_command
broadcast_command(server_command cmd) {

    int c = static_cast<int>(cmd);

#ifdef MPI_TEST_DEBUG
    std::stringstream ss;
    ss << __PRETTY_FUNCTION__ << "(" << c << ")" << "\n";
    std::cerr << ss.str();
    std::cerr << "Entering MPI_Bcast()\n";
#endif // MPI_TEST_DEBUG

    ::MPI_Bcast(&c, 1, MPI_INT, 0, MPI_COMM_WORLD);

#ifdef MPI_TEST_DEBUG

    MPI_TEST_RUN_IF(MPI_RANK_NEQ(0)) {
        std::stringstream ss;
        ss << "command was " << c << "\n";
        std::cerr << ss.str();
    }
#endif

#ifdef MPI_TEST_DEBUG
    std::cerr << "Exiting MPI_Bcast()\n";
#endif // MPI_TEST_DEBUG

    return static_cast<server_command>(c);
}

} // namespace mpi
+59 −0
Original line number Diff line number Diff line
/*************************************************************************
 * Copyright (C) 2017-2018 Barcelona Supercomputing Center               *
 *                         Centro Nacional de Supercomputacion           *
 * All rights reserved.                                                  *
 *                                                                       *
 * This file is part of the NORNS Data Scheduler, a service that allows  *
 * other programs to start, track and manage asynchronous transfers of   *
 * data resources transfers requests between different storage backends. *
 *                                                                       *
 * See AUTHORS file in the top level directory for information           *
 * regarding developers and contributors.                                *
 *                                                                       *
 * The NORNS Data Scheduler 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, either          *
 * version 3 of the License, or (at your option) any later version.      *
 *                                                                       *
 * The NORNS Data Scheduler 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 GNU  *
 * Lesser General Public License for more details.                       *
 *                                                                       *
 * You should have received a copy of the GNU Lesser General             *
 * Public License along with the NORNS Data Scheduler.  If not, see      *
 * <http://www.gnu.org/licenses/>.                                       *
 *************************************************************************/

#ifndef MPI_HELPERS_HPP
#define MPI_HELPERS_HPP

#include <mpi.h>
#include "commands.hpp"

// #define MPI_TEST_DEBUG

namespace mpi {

void
initialize(int* argc, char** argv[]);

void
finalize();

int
get_rank();

server_command
broadcast_command(server_command cmd = server_command::accept);

void
barrier();

} // namespace mpi

#define MPI_RANK_EQ(r) (mpi::get_rank() == r)
#define MPI_RANK_NEQ(r) (mpi::get_rank() != r)
#define MPI_TEST_RUN_IF(expr) if(expr)

#endif // MPI_HELPERS_HPP
+210 −143
Original line number Diff line number Diff line
@@ -25,23 +25,15 @@
 * <http://www.gnu.org/licenses/>.                                       *
 *************************************************************************/

#include <mpi.h>
#include "mpi-helpers.hpp"
#include "commands.hpp"
#include "norns.h"
#include "nornsctl.h"
#include "test-env.hpp"
#include "catch.hpp"

/******************************************************************************/
/* tests for push transfers (single files)                                    */
/******************************************************************************/
SCENARIO("copy local POSIX file to remote POSIX file", 
         "[mpi::norns_submit_push_to_posix_file]") {

    GIVEN("two running urd instances (local and remote)") {
namespace test_data {

        /**********************************************************************/
        /* setup common environment                                           */
        /**********************************************************************/
struct {
    // define input names
    const bfs::path src_file_at_root = "/file0";
@@ -75,26 +67,89 @@ SCENARIO("copy local POSIX file to remote POSIX file",
    const bfs::path dst_file_at_subdir1 = "/a/b/c/d/file1"; // same parents, different basename
    const bfs::path dst_file_at_subdir2 = "/e/f/g/h/i/file0"; // different parents, same basename
    const bfs::path dst_file_at_subdir3 = "/e/f/g/h/i/file1"; // different fullname
        } test_context;
} context;

}


/******************************************************************************/
/* tests for push transfers (single files)                                    */
/******************************************************************************/
SCENARIO("copy local POSIX file to remote POSIX file", 
         "[mpi::norns_submit_push_to_posix_file]") {

    using test_data::context;

    GIVEN("two running urd instances (local and remote)") {

        /**********************************************************************/
        /* setup common environment                                           */
        /**********************************************************************/

        test_env env(false);

        const char* remote_host = "127.0.0.1:42000";

        // Get the number of processes
        int world_size;
        if(MPI_Comm_size(MPI_COMM_WORLD, &world_size) != MPI_SUCCESS) {
            FAIL("Failed to determine number of processes");
        const char* hostname;

        if((hostname = ::getenv("MPICH_INTERFACE_HOSTNAME")) != NULL) {
            std::cerr << "hostname" << hostname << "\n";
        }

        // Get the rank of the process
        int world_rank;
        if(MPI_Comm_rank(MPI_COMM_WORLD, &world_rank) != MPI_SUCCESS) {
            FAIL("Failed to determine own rank");

        // code for the test servers
        MPI_TEST_RUN_IF(MPI_RANK_NEQ(0)) {

            bool shutdown = false;

            do {
                test_env env(false);

                std::string nsid("server" + std::to_string(mpi::get_rank()));
                bfs::path mountdir;

                // create namespaces
                std::tie(std::ignore, mountdir) = 
                    env.create_namespace(nsid, "mnt/" + nsid, 16384);

                // create required output directories
                env.add_to_namespace(nsid, context.dst_subdir1);

                // sync with client after preparing the test environment
                mpi::barrier();

                bool restart = false;

                // servers do nothing but wait for client to complete
                do {
                    int command = 0;
                    server_command cmd = mpi::broadcast_command();

                    switch(cmd) {
                        case server_command::shutdown:
                            shutdown = true;
                            break;

                        case server_command::restart:
                            restart = true;
                            break;

                        default:
                            continue;
                    }
                } while(!restart && !shutdown);

                env.notify_success();

            } while(!shutdown);

            return;
        }; // MPI_TEST_RUN_IF(MPI_RANK_NEQ(0))

        // code for the test client
        auto client = [world_rank, &env, &test_context] {
        MPI_TEST_RUN_IF(MPI_RANK_EQ(0)) {

            test_env env(false);

            std::string nsid("client");
            bfs::path mountdir;

@@ -103,118 +158,127 @@ SCENARIO("copy local POSIX file to remote POSIX file",
                env.create_namespace(nsid, "mnt/" + nsid, 16384);

            // create input data
            env.add_to_namespace(nsid, test_context.src_file_at_root, 40000);
            env.add_to_namespace(nsid, test_context.src_file_at_subdir, 80000);
            env.add_to_namespace(nsid, test_context.src_subdir0);
            env.add_to_namespace(nsid, test_context.src_subdir1);
            env.add_to_namespace(nsid, test_context.src_empty_dir);
            env.add_to_namespace(nsid, context.src_file_at_root, 40000);
            env.add_to_namespace(nsid, context.src_file_at_subdir, 80000);
            env.add_to_namespace(nsid, context.src_subdir0);
            env.add_to_namespace(nsid, context.src_subdir1);
            env.add_to_namespace(nsid, context.src_empty_dir);

            for(int i=0; i<10; ++i) {
                const bfs::path p{test_context.src_subdir0 /
                const bfs::path p{context.src_subdir0 /
                                  ("file" + std::to_string(i))};
                env.add_to_namespace(nsid, p, 4096+i*10);
            }

            for(int i=0; i<10; ++i) {
                const bfs::path p{test_context.src_subdir1 /
                const bfs::path p{context.src_subdir1 /
                                  ("file" + std::to_string(i))};
                env.add_to_namespace(nsid, p, 4096+i*10);
            }

            // create input data with special permissions
            auto p = 
                env.add_to_namespace(nsid, test_context.src_noperms_file0, 0);
                env.add_to_namespace(nsid, context.src_noperms_file0, 0);
            env.remove_access(p);

            p = env.add_to_namespace(nsid, test_context.src_noperms_file1, 0);
            p = env.add_to_namespace(nsid, context.src_noperms_file1, 0);
            env.remove_access(p);

            p = env.add_to_namespace(nsid, test_context.src_noperms_file2, 0);
            p = env.add_to_namespace(nsid, context.src_noperms_file2, 0);
            env.remove_access(p.parent_path());

            p = env.add_to_namespace(nsid, test_context.src_noperms_subdir0);
            p = env.add_to_namespace(nsid, context.src_noperms_subdir0);
            env.remove_access(p);

            p = env.add_to_namespace(nsid, test_context.src_noperms_subdir1);
            p = env.add_to_namespace(nsid, context.src_noperms_subdir1);
            env.remove_access(p);

            p = env.add_to_namespace(nsid, test_context.src_noperms_subdir2);
            p = env.add_to_namespace(nsid, context.src_noperms_subdir2);
            env.remove_access(p.parent_path());

            // add symlinks to the namespace
            env.add_to_namespace(nsid, test_context.src_file_at_root,
                                 test_context.src_symlink_at_root0);
            env.add_to_namespace(nsid, test_context.src_subdir0,
                                 test_context.src_symlink_at_root1);
            env.add_to_namespace(nsid, test_context.src_subdir1,
                                 test_context.src_symlink_at_root2);

            env.add_to_namespace(nsid, test_context.src_file_at_root,
                                 test_context.src_symlink_at_subdir0);
            env.add_to_namespace(nsid, test_context.src_subdir0,
                                 test_context.src_symlink_at_subdir1);
            env.add_to_namespace(nsid, test_context.src_subdir1,
                                 test_context.src_symlink_at_subdir2);
            env.add_to_namespace(nsid, context.src_file_at_root,
                                 context.src_symlink_at_root0);
            env.add_to_namespace(nsid, context.src_subdir0,
                                 context.src_symlink_at_root1);
            env.add_to_namespace(nsid, context.src_subdir1,
                                 context.src_symlink_at_root2);

            env.add_to_namespace(nsid, context.src_file_at_root,
                                 context.src_symlink_at_subdir0);
            env.add_to_namespace(nsid, context.src_subdir0,
                                 context.src_symlink_at_subdir1);
            env.add_to_namespace(nsid, context.src_subdir1,
                                 context.src_symlink_at_subdir2);

            // manually create a symlink leading outside namespace 0
            boost::system::error_code ec;
            const bfs::path out_symlink = "/out_symlink";
            bfs::create_symlink(env.basedir(), mountdir / out_symlink, ec);
            REQUIRE(!ec);
        };

        // code for the test servers
        auto server = [world_rank, &env, &test_context] {
            std::string nsid("server" + std::to_string(world_rank));
            bfs::path mountdir;
            /******************************************************************/
            /* begin tests                                                    */
            /******************************************************************/
            // cp -r /a/contents.* -> / = /contents.*
            WHEN("copying the contents of a NORNS_LOCAL_PATH subdir from SRC "
                "namespace's root to DST namespace's root\n"
                "  cp -r /a/contents.* -> / = /contents.* ") {

            // create namespaces
            std::tie(std::ignore, mountdir) = 
                env.create_namespace(nsid, "mnt/" + nsid, 16384);
                norns_iotask_t task = 
                    NORNS_IOTASK(NORNS_IOTASK_COPY,
                                NORNS_LOCAL_PATH("client", 
                                                context.src_subdir0.c_str()),
                                NORNS_REMOTE_PATH("server1", 
                                                remote_host, 
                                                context.dst_root.c_str()));

            // create required output directories
            env.add_to_namespace(nsid, test_context.dst_subdir1);
        };
                norns_error_t rv = norns_submit(&task);

                THEN("norns_submit() returns NORNS_SUCCESS") {
                    REQUIRE(rv == NORNS_SUCCESS);
                    REQUIRE(task.t_id != 0);

        // register code
        std::vector<std::function<void()>> functors;
        functors.reserve(world_size);
                    // wait until the task completes
                    rv = norns_wait(&task, NULL);

        functors.push_back(client);
                    THEN("norns_wait() returns NORNS_SUCCESS") {
                        REQUIRE(rv == NORNS_SUCCESS);

        for(int i = 1; i < world_size; ++i) {
            functors.push_back(server);
        }
                        THEN("norns_error() reports NORNS_EFINISHED") {
                            norns_stat_t stats;
                            rv = norns_error(&task, &stats);

        // run registered code for each process
        functors.at(world_rank)();
                            REQUIRE(rv == NORNS_SUCCESS);
                            REQUIRE(stats.st_status == NORNS_EFINISHED);

        // wait for everyone to finish preparing the test environment
        MPI_Barrier(MPI_COMM_WORLD);
#if 0
                            THEN("Copied files are identical to original") {
                                bfs::path src = 
                                    env.get_from_namespace("client", src_subdir0);
                                bfs::path dst = 
                                    env.get_from_namespace("server1", dst_root);

        if(world_rank != 0) {
            // servers do nothing but wait for client to complete
            MPI_Barrier(MPI_COMM_WORLD);
            env.notify_success();
            return;
                                REQUIRE(compare_directories(src, dst) == true);
                            }
#endif
                        }
                    }
                }
            }

        /**********************************************************************/
        /* begin tests                                                        */
        /**********************************************************************/
        // cp -r /a/contents.* -> / = /contents.*
        WHEN("copying the contents of a NORNS_LOCAL_PATH subdir from SRC "
             "namespace's root to DST namespace's root\n"
             "  cp -r /a/contents.* -> / = /contents.* ") {
            WHEN("copying the contents of a NORNS_LOCAL_PATH arbitrary subdir "
                 "to DST namespace's root\n"
                 "  cp -r /a/b/c/.../contents.* -> / = /contents.*") {

///XXX wrong test case
                norns_iotask_t task = 
                    NORNS_IOTASK(NORNS_IOTASK_COPY,
                                NORNS_LOCAL_PATH("client", 
                                              test_context.src_subdir0.c_str()),
                                                context.src_subdir0.c_str()),
                                NORNS_REMOTE_PATH("server1", 
                                                remote_host, 
                                               test_context.dst_root.c_str()));
                                                context.dst_root.c_str()));

                norns_error_t rv = norns_submit(&task);

@@ -250,7 +314,10 @@ SCENARIO("copy local POSIX file to remote POSIX file",
                }
            }

        MPI_Barrier(MPI_COMM_WORLD);
            env.notify_success();

        } // MPI_TEST_RUN_IF(MPI_RANK_EQ(0))
    }

    std::cout << "Check!\n";
}
Loading