File: TestSSyncCmd_CH1.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 (716 lines) | stat: -rw-r--r-- 35,474 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
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
/*
 * 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 "TestHelper.hpp"
#include "ecflow/base/cts/user/AlterCmd.hpp"
#include "ecflow/base/cts/user/ClientHandleCmd.hpp"
#include "ecflow/base/stc/SNewsCmd.hpp"
#include "ecflow/base/stc/SSyncCmd.hpp"
#include "ecflow/core/Converter.hpp"
#include "ecflow/core/Ecf.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/test/scaffold/Naming.hpp"

using namespace std;
using namespace ecf;

/// This test, is used to check sync with the client handles.
/// The client handles will register interest in a set of suites
/// When calling sync, check will only receive notification on our
/// our suites
// In particular when a set of suites are registered to a handle
// and we add/delete/check point/order the server passes back to the client
// the a *new* defs with all the suites corresponding to the handle
// There is a complication however, the suite are not copied/cloned
// This causes a problem, since if we create a new defs, and add
// the suites, the suite defs pointer is *NOW* corrupted, as it will point
// to the new defs. This is not an issue in real apps, since searisation
// fixes up these ptrs.

// The client handle commands do not change state & modify change number, hence need to bypass these checks
static bool bypass_state_modify_change_check = false;

BOOST_AUTO_TEST_SUITE(U_Base)

BOOST_AUTO_TEST_SUITE(T_SSyncCmd_CH1)

static defs_ptr create_client_defs(defs_ptr defs) {
    for (size_t j = 0; j < 5; j++) {
        suite_ptr suite = defs->add_suite("s" + ecf::convert_to<std::string>(j));
        family_ptr f    = suite->add_family("f");
        f->add_task("t");
        if (j == 0) {
            suite->addLimit(Limit("suiteLimit", 10));
            suite->addRepeat(RepeatDate("YMD", 20090916, 20090916, 1));
        }
    }
    return defs;
}

static defs_ptr create_server_defs() {
    defs_ptr defs = Defs::create();

    // Create server defs, with a port other than default.
    // This allows additional testing. i.e server variables
    std::vector<Variable> server_variables;
    ServerState::setup_default_server_variables(server_variables, "3141");
    defs->server_state().set_server_variables(server_variables);

    // ensure client/server start out the same
    return create_client_defs(defs);
}

/// define a function which returns nothing, and takes a defs_ptr parameter
typedef boost::function<bool(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
void test_sync_scaffold(defs_change_cmd the_defs_change_command, const std::string& test_name, bool full_sync = false) {
    // Create the defs
    defs_ptr server_defs = create_server_defs();
    ServerReply server_reply;
    server_reply.set_client_defs(create_client_defs(Defs::create()));

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

    // set handle and change numbers, before any changes
    Ecf::set_state_change_no(0);
    Ecf::set_modify_change_no(0);
    unsigned int client_state_change_no  = Ecf::state_change_no();
    unsigned int client_modify_change_no = Ecf::modify_change_no();
    unsigned int client_handle           = 0;

    bool expected_change;

    // make some change to the server
    {
        /// create client handle which references suites s0 and s4, in the server defs
        /// Registering suites should change handle_changed boolean
        std::vector<std::string> suite_names;
        suite_names.emplace_back("s0");
        suite_names.emplace_back("s4");
        TestHelper::invokeRequest(
            server_defs.get(), Cmd_ptr(new ClientHandleCmd(0, suite_names, false)), bypass_state_modify_change_check);

        BOOST_CHECK_MESSAGE(server_defs->client_suite_mgr().clientSuites().size() == 1,
                            test_name << ": Expected 1 Client suites but found "
                                      << server_defs->client_suite_mgr().clientSuites().size());
        client_handle = server_defs->client_suite_mgr().clientSuites().front().handle();
        BOOST_CHECK_MESSAGE(client_handle == 1, "");

        /// Check that handle_changed set, required for syncing, i.e needed for full sync , without change state/modify
        /// numbers
        BOOST_CHECK_MESSAGE(server_defs->client_suite_mgr().handle_changed(client_handle) == true,
                            "Expected handle_changed to be set when suites are registered ");

        /// called create Defs should clear the flag. make sure server state, is synced
        defs_ptr the_client_defs = server_defs->client_suite_mgr().create_defs(client_handle, server_defs);
        BOOST_CHECK_MESSAGE(the_client_defs->suiteVec().size() == 2, test_name << ": Expected 2 suites");
        BOOST_CHECK_MESSAGE(server_defs->client_suite_mgr().handle_changed(client_handle) == false,
                            test_name << ": Expected handle_changed to be cleared after create_defs()");
        BOOST_CHECK_MESSAGE(server_defs->server_state().compare(the_client_defs->server_state()),
                            test_name << ": Server state does not match");

        Ecf::set_server(true);
        expected_change = the_defs_change_command(server_defs);
        std::string error_msg;
        BOOST_REQUIRE_MESSAGE(server_defs->checkInvariants(error_msg),
                              test_name << ": Invariants failed: " << error_msg);
        Ecf::set_server(false);

        /// Call create defs again, after change in server defs, check server state is synced
        the_client_defs = server_defs->client_suite_mgr().create_defs(client_handle, server_defs);
        BOOST_CHECK_MESSAGE(server_defs->server_state().compare(the_client_defs->server_state()),
                            test_name << ": Server state does not match");
        BOOST_CHECK_MESSAGE(server_defs->get_flag() == the_client_defs->get_flag(),
                            test_name << ": Server flags do not match");
    }

    MockServer mock_server(server_defs);
    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);

    if (expected_change) {
        BOOST_CHECK_MESSAGE(news_cmd.get_news(), test_name << " : get_news : Expected server to change");
        BOOST_CHECK_MESSAGE(cmd.do_sync(server_reply), test_name << " : do_sync : Expected server to change");
        BOOST_CHECK_MESSAGE(server_reply.in_sync(), test_name << " : in_sync : Expected client server to be in sync");
        BOOST_CHECK_MESSAGE(server_reply.full_sync() == full_sync,
                            test_name << ": Expected sync not as expected. client: " << server_reply.full_sync()
                                      << " full_sync: " << full_sync);
        BOOST_CHECK_MESSAGE(server_defs->state() == server_reply.client_defs()->state(),
                            test_name << ": Expected server State(" << NState::toString(server_defs->state())
                                      << ") to be same as client state("
                                      << NState::toString(server_reply.client_defs()->state()) << ")");
        if (full_sync) {
            Ecf::set_debug_equality(true); // only has affect in DEBUG build
            BOOST_CHECK_MESSAGE(server_defs->server_state().compare(server_reply.client_defs()->server_state()),
                                test_name << ": Server state does not match");
        }

        // * Note we expect client defs to fail invariant checking when doing a full sync with handles
        // * Under real server this should be ok since, we fix up the defs ptr, during serialisation

        // * note. We can't really compare server and client defs, since when we sync with
        // * with handles, we only return a sub set of the suites, in our handle

        // DO a sync again. hence we should expect no changes
        server_reply.clear_for_invoke(false);
        Ecf::set_server(true);
        /* server side */ SNewsCmd news_cmd1(client_handle,
                                             server_reply.client_defs()->state_change_no(),
                                             server_reply.client_defs()->modify_change_no(),
                                             &mock_server);
        /* server side */ SSyncCmd cmd1(client_handle,
                                        server_reply.client_defs()->state_change_no(),
                                        server_reply.client_defs()->modify_change_no(),
                                        &mock_server);
        Ecf::set_server(false);
        /* client side */ BOOST_CHECK_MESSAGE(!news_cmd1.get_news(),
                                              test_name << ": Expected no changes to client, we should be in sync");
        /* client side */ BOOST_CHECK_MESSAGE(!cmd1.do_sync(server_reply),
                                              test_name << ": Expected no changes to client, we should be in sync");
    }
    else {

        BOOST_CHECK_MESSAGE(!news_cmd.get_news(), test_name << ": Expected no change");
        BOOST_CHECK_MESSAGE(!cmd.do_sync(server_reply), test_name << ": Expected no change");
        BOOST_CHECK_MESSAGE(!server_reply.in_sync(), test_name << ": Expected no change");
        BOOST_CHECK_MESSAGE(!(*server_defs == *server_reply.client_defs()),
                            test_name << ": Server and client defs expected to differ");
    }
}

static bool set_server_state_shutdown(defs_ptr defs) {
    defs->server_state().set_state(SState::SHUTDOWN);
    return true; // expect changes
}
static bool set_server_state_running(defs_ptr defs) {
    defs->server_state().set_state(SState::RUNNING);
    return true; // expect changes
}

static bool set_defs_flag(defs_ptr defs) {
    defs->flag().set(ecf::Flag::BYRULE);
    defs->flag().set(ecf::Flag::EDIT_FAILED);
    return true; // expect changes
}

static bool change_suites_s3_outside_of_handle(defs_ptr defs) {
    /// Make a state change to suites s3, in the server. This is *not* in the client handle
    /// Note: we do *NOT* make a state change as this will be propagated to the defs, and hence will be synced
    /// We *NEED* MockSuiteChangedServer, so that change is propagated to the suite.
    MockSuiteChangedServer mockServer(defs->findSuite("s3")); // Increment suite state/modify change number
    defs->findSuite("s3")->suspend();                         // small scale state change
    return false;                                             // expect no changes
}

static bool change_suites_s3_outside_of_handle_add_variable(defs_ptr defs) {
    /// make a state change to suites s3, in the server. This is *not* in the client handle
    /// We *NEED* MockSuiteChangedServer, so that change is propagated to the suite.
    MockSuiteChangedServer mockServer(defs->findSuite("s3")); // Increment suite state/modify change number
    defs->findSuite("s3")->add_variable("Var", "value");      // small scale state change
    return false;                                             // expect no changes in sync
}

static bool add_task_to_suite_s3(defs_ptr defs) {
    /// make a modify change to suites s3, in the server. This is *not* in the client handle
    MockSuiteChangedServer mockServer(defs->findSuite("s3")); // Increment suite state/modify change number
    defs->findSuite("s3")->addTask(Task::create("s3_task"));  // small scale change
    return false;                                             //  expect no changes, since suite s3 not in handle
}

static bool delete_task_on_suite_s3(defs_ptr defs) {
    /// make a modify change to suites s3, in the server. This is *not* in the client handle
    MockSuiteChangedServer mockServer(defs->findSuite("s3")); // Increment suite state/modify change number
    defs->findAbsNode("/s3/f/t")->remove();                   // small scale change
    return false;                                             // expect no changes, since suite s3 not in handle
}
static bool delete_family_on_suite_s3(defs_ptr defs) {
    /// make a modify change to suites s3, in the server. This is *not* in the client handle
    MockSuiteChangedServer mockServer(defs->findSuite("s3")); // Increment suite state/modify change number
    defs->findAbsNode("/s3/f")->remove();                     // small scale change
    return false;                                             // expect no changes, since suite s3 not in handle
}

static bool change_state_of_s4(defs_ptr defs) {
    /// Ok now make state change to s4, which **is** in the handle
    /// We need MockSuiteChangedServer, so that change is propagated to the suite.
    MockSuiteChangedServer mockServer(defs->findSuite("s4")); // Increment suite state/modify change number
    defs->findSuite("s4")->set_state(NState::SUBMITTED);      // small scale change
    return true;                                              // expect changes
}

static bool add_variable_to_suite_s4(defs_ptr defs) {
    /// Ok now make state change to s4, which **is** in the handle
    /// We need MockSuiteChangedServer, so that change is propagated to the suite.
    MockSuiteChangedServer mockServer(defs->findSuite("s4")); // Increment suite state/modify change number
    defs->findSuite("s4")->add_variable("Var", "value");      // small scale state change
    return true;                                              // expect changes
}

static bool add_server_user_variables(defs_ptr defs) {
    // Change server. This is outside of any suites
    std::vector<Variable> user_variables;
    user_variables.emplace_back("a", "b");
    user_variables.emplace_back("c", "d");
    defs->server_state().set_user_variables(user_variables);
    return true; // expect change
}

static bool add_task_to_suite_s4(defs_ptr defs) {
    /// Ok now make modify change to s4, which **is** in the handle
    MockSuiteChangedServer mockServer(defs->findSuite("s4")); // Increment suite state/modify change number
    defs->findSuite("s4")->addTask(Task::create("s4_task"));  // small scale change
    return true;                                              // expect changes
}

static bool delete_task_on_suite_s4(defs_ptr defs) {
    /// Ok now make modify change to s4, which **is** in the handle
    MockSuiteChangedServer mockServer(defs->findSuite("s4")); // Increment suite state/modify change number
    defs->findAbsNode("/s4/f/t")->remove();                   // small scale change
    return true;                                              // expect changes, since suite s4 is in handle
}

static bool delete_family_on_suite_s4(defs_ptr defs) {
    /// Ok now make modify change to s4, which **is** in the handle
    MockSuiteChangedServer mockServer(defs->findSuite("s4")); // Increment suite state/modify change number
    defs->findAbsNode("/s4/f")->remove();                     // small scale change
    return true;                                              // expect changes, since suite s4 is in handle
}

static bool change_order_of_s4_top(defs_ptr defs) {
    /// Ok make modify change to s4, which **is** in the handle
    suite_ptr s4 = defs->findSuite("s4");
    MockSuiteChangedServer mockServer(s4); // Increment suite state/modify change number
    defs->order(s4.get(), NOrder::TOP);    // small scale scale change
    return true;
}

static bool change_order_of_s4_bottom(defs_ptr defs) {
    /// Ok make modify change to s4, which **is** in the handle
    suite_ptr s4 = defs->findSuite("s4");
    MockSuiteChangedServer mockServer(s4); // Increment suite state/modify change number
    defs->order(s4.get(), NOrder::BOTTOM); // small scale change
    return true;
}

static bool delete_suite_s4(defs_ptr defs) {
    /// Ok now make delete s4 which, which **is** in the handle
    MockSuiteChangedServer mockServer(defs->findSuite("s4")); // Increment suite state/modify change number
    defs->findSuite("s4")->remove();                          // large scale change
    return true;                                              // expect changes
}

static bool set_defs_state(defs_ptr defs) {
    defs->set_state(NState::ABORTED); // changes the defs state
    return true;                      // expect changes
}

static bool set_defs_state_2(defs_ptr defs) {
    defs->set_state(NState::ABORTED); // changes the defs state
    return delete_suite_s4(defs);     // large scale change
}

static bool add_server_variable(defs_ptr defs) {
    // Change defs server state. small scale change
    TestHelper::invokeRequest(defs.get(), Cmd_ptr(new AlterCmd("/", AlterCmd::ADD_VARIABLE, "_fred_", "value")));
    return true;
}

static bool 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")));
    return true;
}

// ===============================================================================
// The modifiers, do this for suite s0 which is in a handle
// ===============================================================================

static bool s0_delete_some_attributes(defs_ptr defs) {
    /// Ok now make state change to s4, which **is** in the handle
    /// We need MockSuiteChangedServer, so that change is propagated to the suite.
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    MockSuiteChangedServer mockServer(suite); // Increment suite state/modify change number

    std::vector<Task*> tasks;
    suite->getAllTasks(tasks);
    BOOST_REQUIRE_MESSAGE(!tasks.empty(), "Expected at least one task");

    for (Task* task : tasks) {
        SuiteChanged1 changed(task->suite());
        task->addMeter(Meter("meter", 0, 100));
    }
    return true;
}

static bool s0_add_some_attributes(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    MockSuiteChangedServer mockServer(suite); // Increment suite state/modify change number

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

    for (task_ptr task : tasks) {
        SuiteChanged1 changed(suite.get());
        task->addDay(DayAttr(DayAttr::TUESDAY));
    }
    return true;
}

static bool s0_begin(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    MockSuiteChangedServer mockServer(suite); // Increment suite state/modify change number

    suite->begin();
    return true;
}

static bool s0_add_alias(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    MockSuiteChangedServer mockServer(suite); // Increment suite state/modify change number

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

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

static bool s0_remove_all_tasks(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    MockSuiteChangedServer mockServer(suite); // Increment suite state/modify change number

    // Remove tasks should force a incremental sync
    std::vector<task_ptr> tasks;
    suite->get_all_tasks(tasks);
    BOOST_REQUIRE_MESSAGE(!tasks.empty(), "Expected at least one task");
    for (task_ptr task : tasks) {
        SuiteChanged1 changed(task->suite());
        task->remove();
    }

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

static bool s0_remove_a_family(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    MockSuiteChangedServer mockServer(suite); // Increment suite state/modify change number

    std::vector<Family*> vec;
    suite->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();
    suite->getAllFamilies(vec);
    BOOST_REQUIRE_MESSAGE(vec.size() < family_size, "Failed to delete family");
    return true;
}

static bool s0_change_clock_gain(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    MockSuiteChangedServer mockServer(suite); // Increment suite state/modify change number

    suite->changeClockGain("100001");
    return true;
}

static bool s0_change_clock_type_to_real(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    MockSuiteChangedServer mockServer(suite); // Increment suite state/modify change number

    suite->changeClockType("hybrid");
    return true;
}

static bool s0_change_clock_date(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");

    suite->changeClockDate("1.1.2001");
    return true;
}

static bool s0_change_clock_sync(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    MockSuiteChangedServer mockServer(suite); // Increment suite state/modify change number

    suite->changeClockSync();
    return true;
}

/// 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.
static bool s0_change_limit_max(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    // Note: we ONLY* need MockSuiteChangedServer, when we make changes via functions and not commands

    std::vector<limit_ptr> theLimits = suite->limits();
    BOOST_REQUIRE_MESSAGE(!theLimits.empty(), "The limit are empty on suite s0 " << defs);
    for (limit_ptr l : theLimits) {
        // std::cout << "found " << l->toString() << "\n";
        TestHelper::invokeRequest(defs.get(),
                                  Cmd_ptr(new AlterCmd(suite->absNodePath(), AlterCmd::LIMIT_MAX, l->name(), "90")));
        limit_ptr v = suite->find_limit(l->name());
        BOOST_CHECK_MESSAGE(v.get() && v->theLimit() == 90, "expected to find limit with max value of 90");
    }
    return true;
}

static bool s0_change_limit_value(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    // Note: we ONLY* need MockSuiteChangedServer, when we make changes via functions and not commands

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

static bool s0_update_repeat(defs_ptr defs) {
    suite_ptr suite = defs->findSuite("s0");
    BOOST_REQUIRE_MESSAGE(suite, "Could not find suite");
    MockSuiteChangedServer mockServer(suite); // Increment suite state/modify change number

    SuiteChanged1 changed(suite.get());
    suite->increment_repeat();
    return true;
}

BOOST_AUTO_TEST_CASE(test_ssync_using_handle) {
    ECF_NAME_THIS_TEST();

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

    // =======================================================================================
    // Note: where we update Suite::modify_change_no()  we should *EXPECT* a full sync
    // =======================================================================================

    // test_sync_scaffold will created 5 suites  s0,s1,s2,s3,s4,s5 and add suites s0 & s4 to a handle
    // The following test will perform changes in/out of handles

    { // Change defs state in the presence of handles. These should sync regardless of handles
        // 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(set_defs_flag, "set_defs_flag");

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

        test_sync_scaffold(set_defs_state, "set_defs_state");
        test_sync_scaffold(set_defs_state_2, "set_defs_state_2", true /* expect a full sync */);
    }

    test_sync_scaffold(change_suites_s3_outside_of_handle, "change_suites_s3_outside_of_handle");
    test_sync_scaffold(change_suites_s3_outside_of_handle_add_variable,
                       "change_suites_s3_outside_of_handle_add_variable");
    test_sync_scaffold(add_task_to_suite_s3, "add_task_to_suite_s3");
    test_sync_scaffold(delete_task_on_suite_s3, "delete_task_on_suite_s3");
    test_sync_scaffold(delete_family_on_suite_s3, "delete_family_on_suite_s3");

    test_sync_scaffold(change_order_of_s4_top, "change_order_of_s4_top");       // change order is an incremental sync
    test_sync_scaffold(change_order_of_s4_bottom, "change_order_of_s4_bottom"); // change order is an incremental sync

    test_sync_scaffold(change_state_of_s4, "change_state_of_s4");
    test_sync_scaffold(add_variable_to_suite_s4, "add_variable_to_suite_s4");
    test_sync_scaffold(add_server_user_variables, "add_server_user_variables");
    test_sync_scaffold(add_task_to_suite_s4, "add_task_to_suite_s4");
    test_sync_scaffold(delete_task_on_suite_s4, "delete_task_on_suite_s4");
    test_sync_scaffold(delete_family_on_suite_s4, "delete_family_on_suite_s4");
    test_sync_scaffold(delete_suite_s4, "delete_suite_s4", true /* expect a full sync */);

    test_sync_scaffold(s0_delete_some_attributes, "s0_delete_some_attributes");
    test_sync_scaffold(s0_add_some_attributes, "s0_add_some_attributes");
    test_sync_scaffold(s0_add_alias, "s0_add_alias");
    test_sync_scaffold(s0_update_repeat, "s0_update_repeat");
    test_sync_scaffold(s0_change_limit_max, "s0_change_limit_max");
    test_sync_scaffold(s0_change_limit_value, "s0_change_limit_value");
    test_sync_scaffold(s0_begin, "s0_begin", true /* expect a full sync */);

    test_sync_scaffold(s0_remove_all_tasks, "s0_remove_all_tasks");
    test_sync_scaffold(s0_remove_a_family, "s0_remove_a_family");

    test_sync_scaffold(s0_change_clock_gain, "s0_change_clock_gain", true /* expect a full sync */);
    test_sync_scaffold(s0_change_clock_type_to_real, "s0_change_clock_type_to_real", true /* expect a full sync */);
    test_sync_scaffold(s0_change_clock_date, "s0_change_clock_date", true /* expect a full sync */);
    test_sync_scaffold(s0_change_clock_sync, "s0_change_clock_sync", true /* expect a full sync */);
}

// =============================================================================================

static defs_ptr create_the_server_defs() {
    defs_ptr defs                    = create_server_defs();
    std::vector<suite_ptr> suite_vec = defs->suiteVec();
    for (size_t j = 0; j < suite_vec.size(); j++) {
        suite_vec[j]->set_state_change_no(j);
        suite_vec[j]->set_modify_change_no(j);
    }
    return defs;
}

BOOST_AUTO_TEST_CASE(test_ssync_full_sync_using_handle) {
    /// This test checks that when user has registered with all the suites.
    /// Syncing should use the global change numbers
    /// **ADDITIONALLY* the newsCmd  must reflect this.
    /// This is handled in ClientSuites::create_defs, in that we *MUST* update the
    /// local change numbers to be the same as the global change numbers
    /// This is important since the NewsCmd must be in *SYNC* with SYNCCmd

    ECF_NAME_THIS_TEST();

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

    // Create the server defs with some changes, in state & modify numbers
    defs_ptr server_defs = create_the_server_defs();

    // Create Client defs, without any changes
    ServerReply server_reply;
    server_reply.set_client_defs(create_client_defs(Defs::create()));

    // Server & client should be the same, since we ignore change numbers in the comparison
    DebugEquality debug_equality; // only as affect in DEBUG build
    BOOST_CHECK_MESSAGE(*server_defs == *server_reply.client_defs(),
                        "Starting point client and server defs should be the same" << "SERVER\n"
                                                                                   << server_defs << "CLIENT\n"
                                                                                   << server_reply.client_defs());

    /// register interest in **ALL** the suites
    std::vector<std::string> suite_names;
    suite_names.emplace_back("s0");
    suite_names.emplace_back("s1");
    suite_names.emplace_back("s2");
    suite_names.emplace_back("s3");
    suite_names.emplace_back("s4");
    TestHelper::invokeRequest(
        server_defs.get(), Cmd_ptr(new ClientHandleCmd(0, suite_names, false)), bypass_state_modify_change_check);

    /// make sure handle created.
    BOOST_CHECK_MESSAGE(server_defs->client_suite_mgr().clientSuites().size() == 1,
                        "Expected 1 Client suites but found " << server_defs->client_suite_mgr().clientSuites().size());
    unsigned int client_handle = server_defs->client_suite_mgr().clientSuites().front().handle();
    BOOST_CHECK_MESSAGE(client_handle == 1, "");

    // make sure server suites have different state/modify numbers from the client
    {
        unsigned int server_state_change_no  = 0;
        unsigned int server_modify_change_no = 0;
        server_defs->client_suite_mgr().max_change_no(client_handle, server_state_change_no, server_modify_change_no);
        BOOST_CHECK_MESSAGE(server_state_change_no == 4, "");
        BOOST_CHECK_MESSAGE(server_modify_change_no == 4, "");

        // *MAKE* sure global change numbers are different to handle suite change numbers
        // This is the key part of this test.
        // Since syncing should transfer these change numbers to the client
        Ecf::set_state_change_no(server_state_change_no + 10);
        Ecf::set_modify_change_no(server_modify_change_no + 20);
    }

    // Now sync from server
    Ecf::set_server(true);
    MockServer mock_server(server_defs);
    /* server side */ SNewsCmd news_cmd(client_handle,
                                        server_reply.client_defs()->state_change_no(),
                                        server_reply.client_defs()->modify_change_no(),
                                        &mock_server);
    /* server side */ SSyncCmd cmd(client_handle,
                                   server_reply.client_defs()->state_change_no(),
                                   server_reply.client_defs()->modify_change_no(),
                                   &mock_server);
    Ecf::set_server(false);

    // make sure SSyncCmd updated the server change numbers, to use global change numbers
    unsigned int server_state_change_no  = 0;
    unsigned int server_modify_change_no = 0;
    {
        server_defs->client_suite_mgr().max_change_no(client_handle, server_state_change_no, server_modify_change_no);
        BOOST_CHECK_MESSAGE(server_state_change_no == 14, "");
        BOOST_CHECK_MESSAGE(server_modify_change_no == 24, "");
    }

    // SYNC the client, via do_sync( this should transfer change numbers to the client )
    BOOST_CHECK_MESSAGE(news_cmd.get_news(), "Expected server to change");
    BOOST_CHECK_MESSAGE(cmd.do_sync(server_reply), "Expected server to change");
    BOOST_CHECK_MESSAGE(server_reply.in_sync(), "Expected server to change");
    BOOST_CHECK_MESSAGE(server_defs->state() == server_reply.client_defs()->state(),
                        "Expected server State(" << NState::toString(server_defs->state())
                                                 << ") to be same as client state("
                                                 << NState::toString(server_reply.client_defs()->state()) << ")");

    // After do_sync client and server change number should be in sync
    BOOST_CHECK_MESSAGE(server_reply.client_defs()->state_change_no() == server_state_change_no,
                        "Expected " << server_reply.client_defs()->state_change_no()
                                    << " state change number but found " << server_state_change_no);
    BOOST_CHECK_MESSAGE(server_reply.client_defs()->modify_change_no() == server_modify_change_no,
                        "Expected " << server_reply.client_defs()->modify_change_no()
                                    << " modify change number but found " << server_modify_change_no);

    // Do final sync, there should not be ANY changes
    Ecf::set_server(true);
    /* server side */ SNewsCmd news_cmd1(client_handle,
                                         server_reply.client_defs()->state_change_no(),
                                         server_reply.client_defs()->modify_change_no(),
                                         &mock_server);
    /* server side */ SSyncCmd cmd1(client_handle,
                                    server_reply.client_defs()->state_change_no(),
                                    server_reply.client_defs()->modify_change_no(),
                                    &mock_server);
    Ecf::set_server(false);
    /* client side */ BOOST_CHECK_MESSAGE(!news_cmd1.get_news(), "Expected no changes to client, we should be in sync");
    /* client side */ BOOST_CHECK_MESSAGE(!cmd1.do_sync(server_reply),
                                          "Expected no changes to client, we should be in sync");
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE_END()