File: TestSSyncCmd.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 (488 lines) | stat: -rw-r--r-- 19,272 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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
/*
 * 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 <boost/test/unit_test.hpp>

#include "MockServer.hpp"
#include "MyDefsFixture.hpp"
#include "TestHelper.hpp"
#include "ecflow/base/cts/user/AlterCmd.hpp"
#include "ecflow/base/cts/user/DeleteCmd.hpp"
#include "ecflow/base/cts/user/OrderNodeCmd.hpp"
#include "ecflow/base/cts/user/PlugCmd.hpp"
#include "ecflow/base/stc/SNewsCmd.hpp"
#include "ecflow/base/stc/SSyncCmd.hpp"
#include "ecflow/core/CalendarUpdateParams.hpp"
#include "ecflow/core/Ecf.hpp"
#include "ecflow/node/Defs.hpp"
#include "ecflow/node/Family.hpp"
#include "ecflow/node/Limit.hpp"
#include "ecflow/node/Suite.hpp"
#include "ecflow/node/SuiteChanged.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(U_Base)

BOOST_AUTO_TEST_SUITE(T_SSyncCmd)

/// define a function which returns nothing, and takes a defs_ptr parameter
typedef boost::function<void(defs_ptr)> defs_change_cmd;

/// Re-use the same test scaffold to modify and then resync, by passing
/// in a function that will modify the defs
static void test_sync_scaffold(defs_change_cmd the_defs_change_command,
                               const std::string& test_name,
                               bool full_sync        = false,
                               bool start_with_begin = false) {
    MyDefsFixture clientFixture;
    MyDefsFixture serverFixture;
    defs_ptr server_defs = serverFixture.create_defs();
    if (start_with_begin) {
        server_defs->beginAll();
    }
    server_defs->server_state().set_state(
        SState::HALTED); // if defs default state is RUNNING, whereas for server it is HALTED

    ServerReply server_reply;
    defs_ptr client_defs = clientFixture.create_defs();
    if (start_with_begin) {
        client_defs->beginAll();
    }
    client_defs->server_state().set_state(
        SState::HALTED); // if defs default state is RUNNING, whereas for server it is HALTED
    server_reply.set_client_defs(client_defs);

    Ecf::set_debug_equality(true); // only has affect in DEBUG build
    BOOST_CHECK_MESSAGE(*server_defs == *server_reply.client_defs(),
                        "Test:" << test_name << ": Starting point client and server defs should be the same");
    Ecf::set_debug_equality(false); // only has effect in DEBUG build

    // Get change number before any changes
    unsigned int client_state_change_no  = Ecf::state_change_no();
    unsigned int client_modify_change_no = Ecf::modify_change_no();

    // make some change to the server
    {
        Ecf::set_server(true);

        the_defs_change_command(server_defs);

        std::string error_msg;
        BOOST_REQUIRE_MESSAGE(server_defs->checkInvariants(error_msg),
                              "Test:" << test_name << ": Invariants failed: " << error_msg);
        BOOST_REQUIRE_MESSAGE(!(*server_reply.client_defs() == *server_defs),
                              "Test:" << test_name << ": Expected client and server defs to differ\n"
                                      << ecf::as_string(*server_reply.client_defs(), PrintStyle::DEFS) << "\n"
                                      << "server defs   = " << ecf::as_string(*server_defs, PrintStyle::DEFS));
        Ecf::set_server(false);
    }

    MockServer mock_server(server_defs);
    unsigned int client_handle = 0;
    SNewsCmd news_cmd(client_handle, client_state_change_no, client_modify_change_no, &mock_server);
    SSyncCmd cmd(client_handle, client_state_change_no, client_modify_change_no, &mock_server);

    std::string error_msg;
    BOOST_REQUIRE_MESSAGE(server_defs->checkInvariants(error_msg),
                          "Test:" << test_name << ": Invariants failed: " << error_msg);
    BOOST_CHECK_MESSAGE(news_cmd.get_news(), "Test:" << test_name << ": Expected server to change");
    BOOST_CHECK_MESSAGE(cmd.do_sync(server_reply), "Test:" << test_name << ": Expected server to change");
    BOOST_CHECK_MESSAGE(server_reply.in_sync(), "Test:" << test_name << ": Expected server to change");
    BOOST_CHECK_MESSAGE(server_reply.full_sync() == full_sync,
                        "Test:" << test_name << ": Expected sync not as expected");

    error_msg.clear();
    BOOST_REQUIRE_MESSAGE(server_reply.client_defs()->checkInvariants(error_msg),
                          "Test:" << test_name << ": Invariants failed: " << error_msg);

    DebugEquality debug_equality; // only has affect in DEBUG build
    BOOST_CHECK_MESSAGE(*server_defs == *server_reply.client_defs(),
                        "Test:" << test_name << ": Server and client should be same after sync");
    //	if (! (*server_defs == *server_reply.client_defs()) ) {
    //	   cout << "Server====================================================================\n";
    //	   cout << server_defs;
    //    cout << "Client====================================================================\n";
    //    cout << server_reply.client_defs();
    //	}
}

// The modifiers
void delete_some_attributes(defs_ptr defs) {
    std::vector<Task*> tasks;
    defs->getAllTasks(tasks);
    for (Task* task : tasks) {

        SuiteChanged1 changed(task->suite());

        /// Take a copy, of the objects we want to delete. since there are returned by reference
        std::vector<Event> events = task->events();
        std::vector<Meter> meters = task->meters();
        std::vector<Label> labels = task->labels();

        for (const Event& e : events) {
            task->deleteEvent(e.name_or_number());
        }
        for (const Meter& m : meters) {
            task->deleteMeter(m.name());
        }
        for (const Label& l : labels) {
            task->deleteLabel(l.name());
        }

        BOOST_REQUIRE_MESSAGE(task->events().empty(), "Expected all events to be deleted");
        BOOST_REQUIRE_MESSAGE(task->meters().empty(), "Expected all meters to be deleted");
        BOOST_REQUIRE_MESSAGE(task->labels().empty(), "Expected all labels to be deleted");
    }
}

void delete_time_attributes(defs_ptr defs) {
    std::vector<Node*> nodes;
    defs->getAllNodes(nodes);
    for (Node* node : nodes) {

        SuiteChanged1 changed(node->suite());

        /// Take a copy, of the objects we want to delete. since there are returned by reference
        std::vector<TimeAttr> times   = node->timeVec();
        std::vector<TodayAttr> todays = node->todayVec();
        std::vector<DayAttr> days     = node->days();
        std::vector<DateAttr> dates   = node->dates();
        std::vector<CronAttr> crons   = node->crons();

        for (const TimeAttr& t : times) {
            node->delete_time(t);
        }
        for (const TodayAttr& t : todays) {
            node->delete_today(t);
        }
        for (const DayAttr& t : days) {
            node->delete_day(t);
        }
        for (const DateAttr& t : dates) {
            node->delete_date(t);
        }
        for (const CronAttr& t : crons) {
            node->delete_cron(t);
        }

        BOOST_REQUIRE_MESSAGE(node->timeVec().empty(), "Expected all times to be deleted");
        BOOST_REQUIRE_MESSAGE(node->todayVec().empty(), "Expected all todays to be deleted");
        BOOST_REQUIRE_MESSAGE(node->days().empty(), "Expected all days to be deleted");
        BOOST_REQUIRE_MESSAGE(node->dates().empty(), "Expected all dates to be deleted");
        BOOST_REQUIRE_MESSAGE(node->crons().empty(), "Expected all crons to be deleted");
    }
}

void delete_misc_attributes(defs_ptr defs) {
    std::vector<Node*> nodes;
    defs->getAllNodes(nodes);
    for (Node* node : nodes) {

        SuiteChanged1 changed(node->suite());

        /// Take a copy, of the objects we want to delete. since there are returned by reference
        std::vector<ZombieAttr> zombies = node->zombies();
        // std::vector<VerifyAttr> verifys = node->verifys()();

        for (const ZombieAttr& t : zombies) {
            node->delete_zombie(t.zombie_type());
        }
        // for(const VerifyAttr & t: verifys ) { node->delete_verify( t );}

        BOOST_REQUIRE_MESSAGE(node->zombies().empty(), "Expected all zombies to be deleted");
        // BOOST_REQUIRE_MESSAGE( node->verifys().empty(),"Expected all verifys to be deleted");
    }
}

void add_some_attributes(defs_ptr defs) {
    std::vector<task_ptr> tasks;
    defs->get_all_tasks(tasks);
    for (task_ptr task : tasks) {
        SuiteChanged1 changed(task->suite());
        task->addDay(DayAttr(DayAttr::TUESDAY));
    }
}

void begin(defs_ptr defs) {
    defs->beginAll();
} // reset all attributes

void add_alias(defs_ptr defs) {

    std::vector<task_ptr> tasks;
    defs->get_all_tasks(tasks);
    BOOST_REQUIRE_MESSAGE(!tasks.empty(), "Expected at least one task");

    SuiteChanged1 changed(tasks[0]->suite());
    tasks[0]->add_alias_only();
}

void remove_all_aliases(defs_ptr defs) {

    std::vector<alias_ptr> aliases;
    defs->get_all_aliases(aliases);
    BOOST_REQUIRE_MESSAGE(!aliases.empty(), "Expected at least one alias");

    for (alias_ptr alias : aliases) {
        TestHelper::invokeRequest(defs.get(), Cmd_ptr(new DeleteCmd(alias->absNodePath())));
    }

    aliases.clear();
    defs->get_all_aliases(aliases);
    BOOST_REQUIRE_MESSAGE(aliases.empty(), "Expected at no  alias");
}

void remove_all_tasks(defs_ptr defs) {

    // Remove tasks should force a incremental sync
    std::vector<task_ptr> tasks;
    defs->get_all_tasks(tasks);
    for (task_ptr task : tasks) {
        SuiteChanged1 changed(task->suite());
        task->remove();
    }

    tasks.clear();
    defs->get_all_tasks(tasks);
    BOOST_REQUIRE_MESSAGE(tasks.empty(), "Failed to delete tasks");
}

void remove_a_family(defs_ptr defs) {

    // Remove tasks should force a incremental sync
    std::vector<Family*> vec;
    defs->getAllFamilies(vec);
    size_t family_size = vec.size();
    BOOST_REQUIRE_MESSAGE(!vec.empty(), "Expected at least one family");
    if (!vec.empty()) {
        SuiteChanged1 changed(vec[0]->suite());
        vec[0]->remove();
    }

    vec.clear();
    defs->getAllFamilies(vec);
    BOOST_REQUIRE_MESSAGE(vec.size() < family_size, "Failed to delete family");
}

void change_clock_gain(defs_ptr defs) {
    for (suite_ptr suite : defs->suiteVec()) {
        if (suite->clockAttr().get()) {
            SuiteChanged changed(suite);
            suite->changeClockGain("100001");
        }
    }
}
void change_clock_type_to_real(defs_ptr defs) {

    for (suite_ptr suite : defs->suiteVec()) {
        if (suite->clockAttr().get()) {
            SuiteChanged changed(suite);
            suite->changeClockType("real");
        }
    }
}
void change_clock_type_to_hybrid(defs_ptr defs) {

    for (suite_ptr suite : defs->suiteVec()) {
        if (suite->clockAttr().get()) {
            SuiteChanged changed(suite);
            suite->changeClockType("hybrid");
        }
    }
}
void change_clock_date(defs_ptr defs) {

    for (suite_ptr suite : defs->suiteVec()) {
        if (suite->clockAttr().get()) {
            SuiteChanged changed(suite);
            suite->changeClockDate("1.1.2001");
        }
    }
}
void change_clock_sync(defs_ptr defs) {

    for (suite_ptr suite : defs->suiteVec()) {
        if (suite->clockAttr().get()) {
            SuiteChanged changed(suite);
            suite->changeClockSync();
        }
    }
}

/// This has been split into two functions, as changing both together could mask an error
/// i.e found bug where we forgot to update state_change number when changing the limit
/// max value, however because we had, changed value as well it got masked.
void change_limit_max(defs_ptr defs) {

    for (suite_ptr s : defs->suiteVec()) {
        std::vector<limit_ptr> theLimits = s->limits();
        for (limit_ptr l : theLimits) {
            // std::cout << "found " << l->toString() << "\n";
            TestHelper::invokeRequest(defs.get(),
                                      Cmd_ptr(new AlterCmd(s->absNodePath(), AlterCmd::LIMIT_MAX, l->name(), "90")));
            limit_ptr v = s->find_limit(l->name());
            BOOST_CHECK_MESSAGE(v.get() && v->theLimit() == 90, "expected to find limit with max value of 90");
        }
    }
}
void change_limit_value(defs_ptr defs) {

    for (suite_ptr s : defs->suiteVec()) {
        std::vector<limit_ptr> theLimits = s->limits();
        for (limit_ptr l : theLimits) {
            TestHelper::invokeRequest(defs.get(),
                                      Cmd_ptr(new AlterCmd(s->absNodePath(), AlterCmd::LIMIT_VAL, l->name(), "33")));
            limit_ptr v = s->find_limit(l->name());
            BOOST_CHECK_MESSAGE(v.get() && v->value() == 33, "expected to find limit with value of 33");
        }
    }
}

void update_repeat(defs_ptr defs) {

    std::vector<Node*> nodes;
    defs->getAllNodes(nodes);

    for (Node* n : nodes) {
        if (!n->repeat().empty()) {
            SuiteChanged1 changed(n->suite());
            n->increment_repeat();
        }
    }
}

void update_calendar(defs_ptr defs) {

    // The calendar is *only* updated if the suite have been begun. Hence make sure this test scaffold
    // starts the test, with all the suites in a begun state
    CalendarUpdateParams p(
        Calendar::second_clock_time(), boost::posix_time::minutes(1), true /* server running */, false /* for Test*/);
    defs->updateCalendar(p);

    // Currently updating the calendar, does not cause change, Hence force a change
    for (suite_ptr suite : defs->suiteVec()) {
        SuiteChanged changed(suite);
        suite->add_variable("name", "value");
    }

    // Note: In the real server, persisting that calendar, the clock type is not persisted.
    //       i.e when we have hybrid calendar, when restored on the client side it will be 'real' clock since
    //       that is the default now. This is not correct and will fail invariant checking.
    //       however the memento should reset clock type on the calenadar form the clok attribute.
}

void delete_suite(defs_ptr defs) {
    std::vector<suite_ptr> vec = defs->suiteVec();
    BOOST_REQUIRE_MESSAGE(!vec.empty(), "Expected suites");
    vec[0]->remove();
}

void set_server_state_shutdown(defs_ptr defs) {
    defs->server_state().set_state(SState::SHUTDOWN);
}
void set_server_state_running(defs_ptr defs) {
    defs->server_state().set_state(SState::RUNNING);
}

void add_server_variable(defs_ptr defs) {
    TestHelper::invokeRequest(defs.get(), Cmd_ptr(new AlterCmd("/", AlterCmd::ADD_VARIABLE, "_fred_", "value")));
}
void change_server_variable(defs_ptr defs) {
    // Because the scaffold create client/server defs each time.
    // To test change/delete variables we modify the default set
    TestHelper::invokeRequest(defs.get(), Cmd_ptr(new AlterCmd("/", AlterCmd::VARIABLE, "ECF_TRIES", "4")));
}
void delete_server_variable(defs_ptr defs) {
    // Because the scaffold create client/server defs each time.
    // To test change/delete variables we modify the default set
    // ***NOTE*** we cannot delete server variables like ECF_TRIES, can only change them
    // However we can delete user variables added to the server
    TestHelper::invokeRequest(defs.get(),
                              Cmd_ptr(new AlterCmd("/", AlterCmd::DEL_VARIABLE, "MyDefsFixture_user_variable")));
}

void reorder_suites(defs_ptr defs) {
    std::vector<suite_ptr> suiteVec = defs->suiteVec();
    BOOST_REQUIRE_MESSAGE(!suiteVec.empty(), "Expected suites");
    std::string path = "/" + suiteVec[0]->name();
    TestHelper::invokeRequest(defs.get(), Cmd_ptr(new OrderNodeCmd(path, NOrder::ALPHA)));
}

void move_peers(defs_ptr defs) {
    std::vector<suite_ptr> suiteVec = defs->suiteVec();
    BOOST_REQUIRE_MESSAGE(suiteVec.size() >= 2, "Expected suites");
    TestHelper::invokeRequest(defs.get(), Cmd_ptr(new PlugCmd(suiteVec[0]->absNodePath(), suiteVec[1]->absNodePath())));
}
void move_peers1(defs_ptr defs) {
    std::vector<suite_ptr> suiteVec = defs->suiteVec();
    BOOST_REQUIRE_MESSAGE(suiteVec.size() >= 2, "Expected suites");
    TestHelper::invokeRequest(defs.get(), Cmd_ptr(new PlugCmd(suiteVec[1]->absNodePath(), suiteVec[0]->absNodePath())));
}

void set_defs_flag(defs_ptr defs) {
    defs->flag().set(ecf::Flag::MESSAGE);
}

void set_defs_state(defs_ptr defs) {
    defs->set_state(NState::ABORTED);
}

BOOST_AUTO_TEST_CASE(test_ssync_cmd) {
    // To DEBUG: enable the defines in Memento.hpp
    ECF_NAME_THIS_TEST();

    TestLog test_log("test_ssync_cmd.log"); // will create log file, and destroy log and remove file at end of scope

    test_sync_scaffold(update_repeat, "update_repeat");
    test_sync_scaffold(delete_some_attributes, "delete_some_attributes");
    test_sync_scaffold(delete_time_attributes, "delete_time_attributes");
    test_sync_scaffold(delete_misc_attributes, "delete_misc_attributes");
    test_sync_scaffold(add_some_attributes, "add_some_attributes");
    test_sync_scaffold(begin, "begin", true /* expect full_sync */);
    test_sync_scaffold(add_alias, "add_alias");
    test_sync_scaffold(remove_all_aliases, "remove_all_aliases");
    test_sync_scaffold(remove_all_tasks, "remove_all_tasks");
    test_sync_scaffold(remove_a_family, "remove_a_family");
    test_sync_scaffold(change_clock_gain, "change_clock_gain", true /* expect full_sync */);
    test_sync_scaffold(change_clock_type_to_real, "change_clock_type_to_real", true /* expect full_sync */);
    test_sync_scaffold(change_clock_type_to_hybrid, "change_clock_type_to_hybrid", true /* expect full_sync */);
    test_sync_scaffold(change_clock_date, "change_clock_date", true /* expect full_sync */);
    test_sync_scaffold(change_clock_sync, "change_clock_sync", true /* expect full_sync */);
    test_sync_scaffold(
        update_calendar, "update_calendar", false /* expect full_sync */, true /* start test with begin */);
    test_sync_scaffold(change_limit_max, "change_limit_max");
    test_sync_scaffold(change_limit_value, "change_limit_value");
    test_sync_scaffold(delete_suite, "delete_suite", true /* expect full_sync */);

    // Test Changes in Defs
    // The default server state is HALTED, hence setting to halted will not show a change
    test_sync_scaffold(set_server_state_shutdown, "set_server_state_shutdown");
    test_sync_scaffold(set_server_state_running, "set_server_state_running");

    test_sync_scaffold(add_server_variable, "add_server_variable");
    test_sync_scaffold(change_server_variable, "change_server_variable");
    test_sync_scaffold(delete_server_variable, "delete_server_variable");

    test_sync_scaffold(reorder_suites, "reorder_suites");
    test_sync_scaffold(move_peers, "move_peers");
    test_sync_scaffold(move_peers1, "move_peers1");

    test_sync_scaffold(set_defs_flag, "set_defs_flag");
    test_sync_scaffold(set_defs_state, "set_defs_state");
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()