File: TestCtsWaitCmd.cpp

package info (click to toggle)
ecflow 5.15.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 51,868 kB
  • sloc: cpp: 269,341; python: 22,756; sh: 3,609; perl: 770; xml: 333; f90: 204; ansic: 141; makefile: 70
file content (233 lines) | stat: -rw-r--r-- 9,844 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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
/*
 * 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 <fstream>
#include <iostream>

#include <boost/test/unit_test.hpp>

#include "ServerTestHarness.hpp"
#include "TestFixture.hpp"
#include "ecflow/attribute/VerifyAttr.hpp"
#include "ecflow/core/AssertTimer.hpp"
#include "ecflow/core/Timer.hpp"
#include "ecflow/node/Defs.hpp"
#include "ecflow/node/Family.hpp"
#include "ecflow/node/Suite.hpp"
#include "ecflow/node/Task.hpp"
#include "ecflow/node/formatter/DefsWriter.hpp"
#include "ecflow/test/scaffold/Naming.hpp"

using namespace std;
using namespace ecf;

BOOST_AUTO_TEST_SUITE(S_Test)

BOOST_AUTO_TEST_SUITE(T_CtsWaitCmd)

static void create_defs(Defs& theDefs, const std::string& suite_name) {
    // suite test_wait_cmd
    //   family family0
    //       task wait
    //   family family1
    //       task a
    //       task b
    //   endfamily
    //   family family2
    //          task aa
    //          task bb
    //    endfamily
    // endsuite
    suite_ptr suite = theDefs.add_suite(suite_name);
    family_ptr fam0 = suite->add_family("family0");
    task_ptr wait   = fam0->add_task("wait");
    wait->addVerify(VerifyAttr(NState::COMPLETE, 1)); // task should complete 1 times

    family_ptr fam1 = suite->add_family("family1");
    fam1->add_task("a");
    fam1->add_task("b");

    family_ptr fam2 = suite->add_family("family2");
    fam2->add_task("aa");
    fam2->add_task("bb");
}

static bool wait_for_state(std::vector<std::pair<std::string, NState::State>>& path_state_vec, int max_time_to_wait) {
    AssertTimer assertTimer(max_time_to_wait, false); // Bomb out after n seconds, fall back if test fail
    while (1) {
        BOOST_REQUIRE_MESSAGE(TestFixture::client().sync_local() == 0,
                              "sync_local failed should return 0\n"
                                  << TestFixture::client().errorMsg());
        defs_ptr defs      = TestFixture::client().defs();
        bool all_states_ok = true;
        for (size_t i = 0; i < path_state_vec.size(); ++i) {
            node_ptr node = defs->findAbsNode(path_state_vec[i].first);
            BOOST_REQUIRE_MESSAGE(node,
                                  "Could not find path '" << path_state_vec[i].first << "' in the defs\n"
                                                          << ecf::as_string(*defs, PrintStyle::DEFS));
            if (node->state() != path_state_vec[i].second) {
                all_states_ok = false;
                break;
            }
        }
        if (all_states_ok) {
            return true;
        }

        // make sure test does not take too long.
        if (assertTimer.duration() >= assertTimer.timeConstraint()) {
            BOOST_REQUIRE_MESSAGE(assertTimer.duration() < assertTimer.timeConstraint(),
                                  "wait_for_state: Test wait " << assertTimer.duration()
                                                               << " taking longer than time constraint of "
                                                               << assertTimer.timeConstraint() << " aborting\n"
                                                               << ecf::as_string(*defs, PrintStyle::DEFS));
            break;
        }
        sleep(1);
    }
    return false;
}

// Test the wait command. The Wait command is a child command.
// The wait command is provided an expression. This expression is evaluated
// in the server. If the evaluate returns false then the client should
// blocks until the evaluation is true.
// In this case the job associated with task 'wait' should block until the expression evaluates
// to true, which should be after the completion of all other tasks
BOOST_AUTO_TEST_CASE(test_wait_cmd) {
    ECF_NAME_THIS_TEST();

    DurationTimer timer;
    TestClean clean_at_start_and_end;

    Defs theDefs;
    create_defs(theDefs, "test_wait_cmd");

    // Create a custom sms file for test_wait_cmd/family0/wait to invoke the child wait command
    // with an expression that forces it to block, until all other tasks complete
    std::string templateEcfFileForWait;
    templateEcfFileForWait += "%include <head.h>\n";
    templateEcfFileForWait += "\n";
    templateEcfFileForWait += "echo do some work\n";
    templateEcfFileForWait += "%ECF_CLIENT_EXE_PATH% --wait=\"../family1/a eq complete and ../family1/b eq complete "
                              "and ../family2/aa eq complete and ../family2/bb eq complete\"\n";
    templateEcfFileForWait += "\n";
    templateEcfFileForWait += "%include <tail.h>\n";

    // The test harness will create corresponding directory structure
    // Override the default ECF_ file, with our custom ECF_ file
    std::map<std::string, std::string> taskEcfFileMap;
    taskEcfFileMap.insert(std::make_pair(TestFixture::taskAbsNodePath(theDefs, "wait"), templateEcfFileForWait));

    ServerTestHarness serverTestHarness;
    serverTestHarness.run(theDefs, ServerTestHarness::testDataDefsLocation("test_wait_cmd.def"), taskEcfFileMap);

    cout << timer.duration() << " update-calendar-count(" << serverTestHarness.serverUpdateCalendarCount() << ")\n";
}

BOOST_AUTO_TEST_CASE(test_wait_cmd_parse_fail) {
    ECF_NAME_THIS_TEST();

    DurationTimer timer;

    // This time we add a wait expression that
    // should fail to parse, and we should return an error
    // The error should captured by the trap in .ecf script, which will
    // then abort the task
    Defs theDefs;
    create_defs(theDefs, "test_wait_cmd_parse_fail");

    // Create a custom ecf file for test_wait_cmd/family0/wait to invoke the child wait command
    std::string templateEcfFileForWait;
    templateEcfFileForWait += "%include <head.h>\n";
    templateEcfFileForWait += "\n";
    templateEcfFileForWait += "echo do some work\n";
    templateEcfFileForWait += "%ECF_CLIENT_EXE_PATH% --wait=\"(((((((((../family1/a eq complete and ../family1/b eq "
                              "complete and ../family2/aa eq complete and ../family2/bb eq complete\"\n";
    templateEcfFileForWait += "\n";
    templateEcfFileForWait += "%include <tail.h>\n";

    std::map<std::string, std::string> taskEcfFileMap;
    taskEcfFileMap.insert(std::make_pair(TestFixture::taskAbsNodePath(theDefs, "wait"), templateEcfFileForWait));

    ServerTestHarness serverTestHarness;
    serverTestHarness.run(theDefs,
                          ServerTestHarness::testDataDefsLocation("test_wait_cmd_parse_fail.def"),
                          taskEcfFileMap,
                          1 /*timeout*/,
                          false /* don't wait for test to finish */);

    // wait for family1 and family2 to complete
    std::vector<std::pair<std::string, NState::State>> path_state_vec;
    path_state_vec.push_back(std::make_pair(std::string("/test_wait_cmd_parse_fail/family1"), NState::COMPLETE));
    path_state_vec.push_back(std::make_pair(std::string("/test_wait_cmd_parse_fail/family2"), NState::COMPLETE));
    wait_for_state(path_state_vec, 10);

    // wait for 'wait' task to abort
    path_state_vec.clear();
    path_state_vec.push_back(std::make_pair(std::string("/test_wait_cmd_parse_fail/family0/wait"), NState::ABORTED));
    wait_for_state(path_state_vec, 10);

    cout << timer.duration() << " update-calendar-count(" << serverTestHarness.serverUpdateCalendarCount() << ")\n";
}

BOOST_AUTO_TEST_CASE(test_wait_cmd_non_existant_paths) {
    ECF_NAME_THIS_TEST();

    DurationTimer timer;

    // This time we add a wait expression that should fail
    // because the paths referenced in the expression don't exist
    Defs theDefs;
    create_defs(theDefs, "test_wait_cmd_non_existant_paths");

    // Create a custom ecf file for test_wait_cmd/family0/wait to invoke the child wait command
    // NOTE: ../family1/FRED does not exist
    // clang-format off
    std::string templateEcfFileForWait =
        "%include <head.h>\n"
        "\n"
        "echo do some work\n"
        "%ECF_CLIENT_EXE_PATH% --wait=\"../family1/FRED eq complete and ../family1/b eq complete and ../family2/aa eq complete and ../family2/bb eq complete\"\n"
        "\n"
        "%include <tail.h>\n";
    // clang-format on

    std::map<std::string, std::string> taskEcfFileMap;
    taskEcfFileMap.insert(std::make_pair(TestFixture::taskAbsNodePath(theDefs, "wait"), templateEcfFileForWait));

    ServerTestHarness serverTestHarness;
    serverTestHarness.run(theDefs,
                          ServerTestHarness::testDataDefsLocation("test_wait_cmd_non_existant_paths.def"),
                          taskEcfFileMap,
                          1 /*timeout*/,
                          false /* don't wait for test to finish */);

    // wait for family1 and family2 to complete
    std::vector<std::pair<std::string, NState::State>> path_state_vec;
    path_state_vec.push_back(
        std::make_pair(std::string("/test_wait_cmd_non_existant_paths/family1"), NState::COMPLETE));
    path_state_vec.push_back(
        std::make_pair(std::string("/test_wait_cmd_non_existant_paths/family2"), NState::COMPLETE));
    wait_for_state(path_state_vec, 10);

    // wait for 'wait' task to abort
    path_state_vec.clear();
    path_state_vec.push_back(
        std::make_pair(std::string("/test_wait_cmd_non_existant_paths/family0/wait"), NState::ABORTED));
    wait_for_state(path_state_vec, 10);

    cout << timer.duration() << " update-calendar-count(" << serverTestHarness.serverUpdateCalendarCount() << ")\n";
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()