File: TestServer.cpp

package info (click to toggle)
ecflow 5.16.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 52,220 kB
  • sloc: cpp: 273,994; python: 22,754; sh: 3,643; perl: 774; xml: 333; f90: 204; ansic: 141; makefile: 63
file content (185 lines) | stat: -rw-r--r-- 8,050 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
 * Copyright 2009- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#include <cstdlib>
#include <iostream>
#include <vector>

#include <boost/test/unit_test.hpp>

#include "ecflow/core/EcfPortLock.hpp"
#include "ecflow/core/Host.hpp"
#include "ecflow/core/Log.hpp"
#include "ecflow/node/Defs.hpp"
#include "ecflow/server/Server.hpp"
#include "ecflow/server/ServerEnvironment.hpp"
#include "ecflow/test/scaffold/Naming.hpp"

using namespace ecf;

BOOST_AUTO_TEST_SUITE(U_Server)

BOOST_AUTO_TEST_SUITE(T_Server)

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// make public the function we wish to test:
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class TestServer : public BasicServer {
public:
    explicit TestServer(boost::asio::io_context& io, ServerEnvironment& s)
        : BasicServer(io, s) {}
    ~TestServer() override = default;

    // abort server if check pt files exist, but can't be loaded
    //   bool load_check_pt_file_on_startup();
    //   void loadCheckPtFile();
    //   bool restore_from_checkpt(const std::string& filename, bool& failed);
    //   void set_server_state(SState::State);

    /// AbstractServer functions
    SState::State state() const override { return BasicServer::state(); }
    std::pair<std::string, std::string> hostPort() const override { return BasicServer::hostPort(); }
    defs_ptr defs() const override { return BasicServer::defs(); }
    // virtual void updateDefs(defs_ptr,bool force);
    void clear_defs() override { BasicServer::clear_defs(); }
    //   virtual void checkPtDefs(ecf::CheckPt::Mode m = ecf::CheckPt::UNDEFINED,
    //                               int check_pt_interval = 0,
    //                               int check_pt_save_time_alarm = 0);
    void restore_defs_from_checkpt() override { BasicServer::restore_defs_from_checkpt(); }
    void nodeTreeStateChanged() override { BasicServer::nodeTreeStateChanged(); }
    bool allowTaskCommunication() const override { return BasicServer::allowTaskCommunication(); }
    void shutdown() override { BasicServer::shutdown(); }
    void halted() override { BasicServer::halted(); }
    void restart() override { BasicServer::restart(); }

    bool reloadWhiteListFile(std::string& errorMsg) override { return BasicServer::reloadWhiteListFile(errorMsg); }

    bool lock(const std::string& user) override { return BasicServer::lock(user); }
    void unlock() override { BasicServer::unlock(); }
    const std::string& lockedUser() const override { return BasicServer::lockedUser(); }
    //   virtual void traverse_node_tree_and_job_generate(const boost::posix_time::ptime& time_now, bool
    //   user_cmd_context) const;
    int poll_interval() const override { return BasicServer::poll_interval(); }
    //   virtual void debug_server_on();
    //   virtual void debug_server_off();
    //   virtual bool debug() const;
};

void test_the_server(const std::string& port) {

    std::vector<std::string> args = {"ServerEnvironment", "--port=" + port, "--ecfinterval=12"};
    ServerEnvironment server_environment(args); // This can throw ServerEnvironmentException
    std::string errorMsg;
    BOOST_CHECK_MESSAGE(server_environment.valid(errorMsg), errorMsg);

    {
        boost::asio::io_context io;
        TestServer theServer(io, server_environment); // This can throw exception, bind address in use.

        BOOST_REQUIRE_MESSAGE(theServer.defs(), "Expected defs to be created");

        const std::vector<Variable>& server_variables = theServer.defs()->server_state().server_variables();
        BOOST_REQUIRE_MESSAGE(!server_variables.empty(), "Expected defs to be updated with the server variables");

        // for(size_t i = 0; i < server_variables.size(); ++i)  cout << server_variables[i].dump() << "\n";
        const std::string& ecf_port = theServer.defs()->server_state().find_variable("ECF_PORT");
        BOOST_REQUIRE_MESSAGE(ecf_port == port,
                              "Expected port " << port << " but found " << ecf_port
                                               << " defs server variables, should be in sync with server");

        const std::string& interval = theServer.defs()->server_state().find_variable("ECF_INTERVAL");
        BOOST_REQUIRE_MESSAGE(interval == "12",
                              "Expected interval 12 but found "
                                  << interval << " defs server variables, should be in sync with server");
        BOOST_REQUIRE_MESSAGE(theServer.poll_interval() == 12,
                              "Expected poll interval 12 but found " << theServer.poll_interval());

        BOOST_REQUIRE_MESSAGE(theServer.state() == SState::HALTED,
                              "Expected halted at server start but found " << SState::to_string(theServer.state()));
        theServer.halted();
        BOOST_REQUIRE_MESSAGE(theServer.state() == SState::HALTED, "Expected halted ");
        theServer.shutdown();
        BOOST_REQUIRE_MESSAGE(theServer.state() == SState::SHUTDOWN, "Expected shutdown ");
        theServer.restart();
        BOOST_REQUIRE_MESSAGE(theServer.state() == SState::RUNNING, "Expected shutdown ");

        BOOST_REQUIRE_MESSAGE(theServer.lock("fred"), "Expected to lock user fred");
        BOOST_REQUIRE_MESSAGE(theServer.state() == SState::SHUTDOWN, "Locking should shutdown server");
        BOOST_REQUIRE_MESSAGE(theServer.lockedUser() == "fred",
                              "Expected locked user 'fred' but found " << theServer.lockedUser());
        theServer.unlock();
        BOOST_REQUIRE_MESSAGE(theServer.lockedUser().empty(),
                              "Expected no locked user but found " << theServer.lockedUser());
        BOOST_REQUIRE_MESSAGE(theServer.state() == SState::RUNNING, "Expected unlock to restart server ");
    }
}

BOOST_AUTO_TEST_CASE(test_server) {
    ECF_NAME_THIS_TEST();

    // Create a unique port number, allowing debug and release,gnu,clang,intel to run at the same time
    // Hence the lock file is not always sufficient.
    // ECF_FREE_PORT should be unique among  gnu,clang,intel, etc
    std::string the_port1 = "3144";
    if (auto port = ecf::environment::fetch("ECF_FREE_PORT"); port) { // from metabuilder, allow parallel tests
        the_port1 = port.value();
    }
    std::cout << "  Find free port to start server, starting with port " << the_port1 << "\n";

    auto the_port = ecf::convert_to<int>(the_port1);
    while (!EcfPortLock::is_free(the_port)) {
        the_port++;
    }
    std::string port = ecf::convert_to<std::string>(the_port);
    EcfPortLock::create(port);
    std::cout << "  Found free port: " << port << " ";

    Host h;
    int count = 0;
    while (true) {
        try {
            test_the_server(port);
            std::cout << "\n";
            break;
        }
        catch (...) {
            count++;

            // cleanup
            fs::remove(h.ecf_log_file(port));
            EcfPortLock::remove(port);

            std::cout << " : port " << port << " is used, trying next port\n";

            the_port = ecf::convert_to<int>(port);
            the_port++;

            while (!EcfPortLock::is_free(the_port)) {
                the_port++;
            }
            port = ecf::convert_to<std::string>(the_port);
            EcfPortLock::create(port);
            std::cout << "  Found free port: " << port << "\n";

            BOOST_REQUIRE_MESSAGE(count < 20, "Could not find new port after 20 attempts");
        }
    }

    // cleanup
    fs::remove(h.ecf_log_file(port));
    EcfPortLock::remove(port);

    /// Destroy Log singleton to avoid valgrind from complaining
    Log::destroy();
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()