source: tags/arb-6.0/WINDOW/AW_awar.cxx

Last change on this file was 11464, checked in by westram, 10 years ago
  • un-dummy-fied transaction (renames only)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 22.5 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AW_awar.cxx                                       //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "aw_nawar.hxx"
12#include "aw_awar.hxx"
13#include "aw_root.hxx"
14#include "aw_msg.hxx"
15#include "aw_window.hxx"
16#include "aw_select.hxx"
17#include <arb_str.h>
18#include <arb_file.h>
19#include <ad_cb.h>
20#include <list>
21#include <sys/stat.h>
22#include <climits>
23#include <cfloat>
24
25#if defined(DEBUG)
26// uncomment next line to dump all awar-changes to stderr
27// #define DUMP_AWAR_CHANGES
28#endif // DEBUG
29
30#define AWAR_EPS 0.00000001
31
32#if defined(DUMP_AWAR_CHANGES)
33#define AWAR_CHANGE_DUMP(name, where, format) fprintf(stderr, "change awar '%s' " where "(" format ")\n", name, para)
34#else
35#define AWAR_CHANGE_DUMP(name, where, format)
36#endif // DEBUG
37
38
39struct AW_widget_refresh_cb : virtual Noncopyable {
40    AW_widget_refresh_cb(AW_widget_refresh_cb *previous, AW_awar *vs, AW_CL cd1, Widget w, AW_widget_type type, AW_window *awi);
41    ~AW_widget_refresh_cb();
42
43    AW_CL           cd;
44    AW_awar        *awar;
45    Widget          widget;
46    AW_widget_type  widget_type;
47    AW_window      *aw;
48
49    AW_widget_refresh_cb *next;
50};
51
52
53static void aw_cp_awar_2_widget_cb(AW_root *root, AW_widget_refresh_cb *widgetlist) {
54    if (widgetlist->widget == root->changer_of_variable) {
55        root->changer_of_variable = 0;
56        root->value_changed = false;
57        return;
58    }
59
60    {
61        char *var_value;
62        var_value = widgetlist->awar->read_as_string();
63
64        // und benachrichtigen der anderen
65        switch (widgetlist->widget_type) {
66
67            case AW_WIDGET_INPUT_FIELD:
68                widgetlist->aw->update_input_field(widgetlist->widget, var_value);
69                break;
70            case AW_WIDGET_TEXT_FIELD:
71                widgetlist->aw->update_text_field(widgetlist->widget, var_value);
72                break;
73            case AW_WIDGET_TOGGLE:
74                widgetlist->aw->update_toggle(widgetlist->widget, var_value, widgetlist->cd);
75                break;
76            case AW_WIDGET_LABEL_FIELD:
77                widgetlist->aw->update_label(widgetlist->widget, var_value);
78                break;
79            case AW_WIDGET_CHOICE_MENU:
80                widgetlist->aw->refresh_option_menu((AW_option_menu_struct*)widgetlist->cd);
81                break;
82            case AW_WIDGET_TOGGLE_FIELD:
83                widgetlist->aw->refresh_toggle_field((int)widgetlist->cd);
84                break;
85            case AW_WIDGET_SELECTION_LIST:
86                ((AW_selection_list *)widgetlist->cd)->refresh();
87            default:
88                break;
89        }
90        free(var_value);
91    }
92    root->value_changed = false;     // Maybe value changed is set because Motif calls me
93}
94
95
96AW_widget_refresh_cb::AW_widget_refresh_cb(AW_widget_refresh_cb *previous, AW_awar *vs, AW_CL cd1, Widget w, AW_widget_type type, AW_window *awi) {
97    cd          = cd1;
98    widget      = w;
99    widget_type = type;
100    awar        = vs;
101    aw          = awi;
102    next        = previous;
103
104    awar->add_callback(makeRootCallback(aw_cp_awar_2_widget_cb, this));
105}
106
107AW_widget_refresh_cb::~AW_widget_refresh_cb() {
108    if (next) delete next;
109    awar->remove_callback(makeRootCallback(aw_cp_awar_2_widget_cb, this));
110}
111
112AW_var_target::AW_var_target(void* pntr, AW_var_target *nexti) {
113    next    = nexti;
114    pointer = pntr;
115}
116
117
118bool AW_awar::allowed_to_run_callbacks = true;
119
120static GB_ERROR AW_MSG_UNMAPPED_AWAR = "Error (unmapped AWAR):\n"
121    "You cannot write to this field because it is either deleted or\n"
122    "unmapped. Try to select a different item, reselect this and retry.";
123
124#define concat(x, y) x##y
125
126#define WRITE_BODY(self,format,func)                    \
127    if (!gb_var) return AW_MSG_UNMAPPED_AWAR;           \
128    GB_transaction ta(gb_var);                          \
129    AWAR_CHANGE_DUMP(awar_name, #self, format);         \
130    GB_ERROR error = func(gb_var, para);                \
131    if (!error) update_tmp_state_during_change()
132
133#define WRITE_SKELETON(self,type,format,func)           \
134    GB_ERROR AW_awar::self(type para) {                 \
135        WRITE_BODY(self, format, func);                 \
136        return error;                                   \
137    }                                                   \
138    GB_ERROR AW_awar::concat(re,self)(type para) {      \
139        WRITE_BODY(self, format, func);                 \
140        GB_touch(gb_var);                               \
141        return error;                                   \
142    }
143
144WRITE_SKELETON(write_string, const char*, "%s", GB_write_string) // defines rewrite_string
145WRITE_SKELETON(write_int, long, "%li", GB_write_int) // defines rewrite_int
146WRITE_SKELETON(write_float, double, "%f", GB_write_float) // defines rewrite_float
147WRITE_SKELETON(write_as_string, const char*, "%s", GB_write_as_string) // defines rewrite_as_string
148WRITE_SKELETON(write_pointer, GBDATA*, "%p", GB_write_pointer) // defines rewrite_pointer
149
150#undef WRITE_SKELETON
151#undef concat
152#undef AWAR_CHANGE_DUMP
153
154
155char *AW_awar::read_as_string() {
156    if (!gb_var) return strdup("");
157    GB_transaction ta(gb_var);
158    return GB_read_as_string(gb_var);
159}
160
161const char *AW_awar::read_char_pntr() {
162    aw_assert(variable_type == AW_STRING);
163
164    if (!gb_var) return "";
165    GB_transaction ta(gb_var);
166    return GB_read_char_pntr(gb_var);
167}
168
169double AW_awar::read_float() {
170    if (!gb_var) return 0.0;
171    GB_transaction ta(gb_var);
172    return GB_read_float(gb_var);
173}
174
175long AW_awar::read_int() {
176    if (!gb_var) return 0;
177    GB_transaction ta(gb_var);
178    return (long)GB_read_int(gb_var);
179}
180
181GBDATA *AW_awar::read_pointer() {
182    if (!gb_var) return NULL;
183    GB_transaction ta(gb_var);
184    return GB_read_pointer(gb_var);
185}
186
187char *AW_awar::read_string() {
188    aw_assert(variable_type == AW_STRING);
189
190    if (!gb_var) return strdup("");
191    GB_transaction ta(gb_var);
192    return GB_read_string(gb_var);
193}
194
195void AW_awar::touch() {
196    if (gb_var) {
197        GB_transaction ta(gb_var);
198        GB_touch(gb_var);
199    }
200}
201
202void AW_awar::untie_all_widgets() {
203    delete refresh_list; refresh_list = NULL;
204}
205
206AW_awar *AW_awar::add_callback(const RootCallback& rcb) {
207    AW_root_cblist::add(callback_list, rcb);
208    return this;
209}
210
211AW_awar *AW_awar::add_target_var(char **ppchr) {
212    assert_var_type(AW_STRING);
213    target_list = new AW_var_target((void *)ppchr, target_list);
214    update_target(target_list);
215    return this;
216}
217
218AW_awar *AW_awar::add_target_var(float *pfloat) {
219    assert_var_type(AW_FLOAT);
220    target_list = new AW_var_target((void *)pfloat, target_list);
221    update_target(target_list);
222    return this;
223}
224
225AW_awar *AW_awar::add_target_var(long *pint) {
226    assert_var_type(AW_INT);
227    target_list = new AW_var_target((void *)pint, target_list);
228    update_target(target_list);
229    return this;
230}
231
232void AW_awar::tie_widget(AW_CL cd1, Widget widget, AW_widget_type type, AW_window *aww) {
233    refresh_list = new AW_widget_refresh_cb(refresh_list, this, cd1, widget, type, aww);
234}
235
236
237AW_VARIABLE_TYPE AW_awar::get_type() const {
238    return this->variable_type;
239}
240
241
242// extern "C"
243static void AW_var_gbdata_callback(GBDATA*, AW_awar *awar) {
244    awar->update();
245}
246
247
248static void AW_var_gbdata_callback_delete_intern(GBDATA *gbd, AW_awar *awar) {
249    if (awar->gb_origin == gbd) {
250        // make awar zombie
251        awar->gb_var    = NULL;
252        awar->gb_origin = NULL;
253    }
254    else {
255        aw_assert(awar->gb_var == gbd);             // forgot to remove a callback ?
256        awar->gb_var = awar->gb_origin;             // unmap
257    }
258
259    awar->update();
260}
261
262AW_awar::AW_awar(AW_VARIABLE_TYPE var_type, const char *var_name,
263                 const char *var_value, double var_double_value,
264                 AW_default default_file, AW_root *rooti) {
265    memset((char *)this, 0, sizeof(AW_awar));
266    GB_transaction ta(default_file);
267
268    aw_assert(var_name && var_name[0] != 0);
269
270#if defined(DEBUG)
271    GB_ERROR err = GB_check_hkey(var_name);
272    aw_assert(!err);
273#endif // DEBUG
274
275    this->awar_name = strdup(var_name);
276    this->root      = rooti;
277    GBDATA *gb_def  = GB_search(default_file, var_name, GB_FIND);
278
279    in_tmp_branch = strncmp(var_name, "tmp/", 4) == 0;
280
281    GB_TYPES wanted_gbtype = (GB_TYPES)var_type;
282
283    if (gb_def) { // use value stored in DB
284        GB_TYPES gbtype = GB_read_type(gb_def);
285
286        if (gbtype != wanted_gbtype) {
287            GB_warningf("Existing awar '%s' has wrong type (%i instead of %i) - recreating\n",
288                        var_name, int(gbtype), int(wanted_gbtype));
289            GB_delete(gb_def);
290            gb_def = 0;
291        }
292    }
293
294    // store default-value in member
295    switch (var_type) {
296        case AW_STRING:  default_value.s = nulldup(var_value); break;
297        case AW_INT:     default_value.l = (long)var_value; break;
298        case AW_FLOAT:   default_value.d = var_double_value; break;
299        case AW_POINTER: default_value.p = (GBDATA*)var_value; break;
300        default: aw_assert(0); break;
301    }
302
303    if (!gb_def) { // set AWAR to default value
304        gb_def = GB_search(default_file, var_name, wanted_gbtype);
305
306        switch (var_type) {
307            case AW_STRING:
308#if defined(DUMP_AWAR_CHANGES)
309                fprintf(stderr, "creating awar_string '%s' with default value '%s'\n", var_name, default_value.s);
310#endif // DUMP_AWAR_CHANGES
311                GB_write_string(gb_def, default_value.s);
312                break;
313
314            case AW_INT: {
315#if defined(DUMP_AWAR_CHANGES)
316                fprintf(stderr, "creating awar_int '%s' with default value '%li'\n", var_name, default_value.l);
317#endif // DUMP_AWAR_CHANGES
318                GB_write_int(gb_def, default_value.l);
319                break;
320            }
321            case AW_FLOAT:
322#if defined(DUMP_AWAR_CHANGES)
323                fprintf(stderr, "creating awar_float '%s' with default value '%f'\n", var_name, default_value.d);
324#endif // DUMP_AWAR_CHANGES
325                GB_write_float(gb_def, default_value.d);
326                break;
327
328            case AW_POINTER: {
329#if defined(DUMP_AWAR_CHANGES)
330                fprintf(stderr, "creating awar_pointer '%s' with default value '%p'\n", var_name, default_value.p);
331#endif // DUMP_AWAR_CHANGES
332                GB_write_pointer(gb_def, default_value.p);
333                break;
334            }
335            default:
336                GB_warningf("AWAR '%s' cannot be created because of disallowed type", var_name);
337                break;
338        }
339
340        GB_ERROR error = GB_set_temporary(gb_def);
341        if (error) GB_warningf("AWAR '%s': failed to set temporary on creation (Reason: %s)", var_name, error);
342    }
343
344    variable_type   = var_type;
345    this->gb_origin = gb_def;
346    this->map(gb_def);
347
348    aw_assert(is_valid());
349}
350
351
352void AW_awar::update() {
353    bool fix_value = false;
354
355    aw_assert(is_valid());
356
357    if (gb_var && ((pp.f.min != pp.f.max) || pp.srt)) {
358        switch (variable_type) {
359            case AW_INT: {
360                long lo = read_int();
361                if (lo < pp.f.min -.5) {
362                    fix_value = true;
363                    lo = (int)(pp.f.min + 0.5);
364                }
365                if (lo>pp.f.max + .5) {
366                    fix_value = true;
367                    lo = (int)(pp.f.max + 0.5);
368                }
369                if (fix_value) {
370                    if (root) root->changer_of_variable = 0;
371                    write_int(lo);
372                }
373                break;
374            }
375            case AW_FLOAT: {
376                float fl = read_float();
377                if (fl < pp.f.min) {
378                    fix_value = true;
379                    fl = pp.f.min+AWAR_EPS;
380                }
381                if (fl>pp.f.max) {
382                    fix_value = true;
383                    fl = pp.f.max-AWAR_EPS;
384                }
385                if (fix_value) {
386                    if (root) root->changer_of_variable = 0;
387                    write_float(fl);
388                }
389                break;
390            }
391            case AW_STRING: {
392                char *str = read_string();
393                char *n   = GBS_string_eval(str, pp.srt, 0);
394
395                if (!n) GBK_terminatef("SRT ERROR %s %s", pp.srt, GB_await_error());
396
397                if (strcmp(n, str) != 0) {
398                    fix_value = true;
399                    if (root) root->changer_of_variable = 0;
400                    write_string(n);
401                }
402                free(n);
403                free(str);
404                break;
405            }
406            default:
407                break;
408        }
409    }
410
411    if (!fix_value) { // function was already recalled by write_xxx() above
412        this->update_targets();
413        this->run_callbacks();
414    }
415
416    aw_assert(is_valid());
417}
418
419
420void AW_awar::update_target(AW_var_target *pntr) {
421    // send data to all variables
422    if (!pntr->pointer) return;
423    switch (variable_type) {
424        case AW_STRING: this->get((char **)pntr->pointer); break;
425        case AW_FLOAT:  this->get((float *)pntr->pointer); break;
426        case AW_INT:    this->get((long *)pntr->pointer); break;
427        default: aw_assert(0); GB_warning("Unknown awar type"); break;
428    }
429}
430
431
432
433void AW_awar::assert_var_type(AW_VARIABLE_TYPE wanted_type) {
434    if (wanted_type != variable_type) {
435        GBK_terminatef("AWAR '%s' has wrong type (got=%i, expected=%i)",
436                       awar_name, variable_type, wanted_type);
437    }
438}
439
440// extern "C"
441static void AW_var_gbdata_callback_delete(GBDATA *gbd, AW_awar *awar) {
442    AW_var_gbdata_callback_delete_intern(gbd, awar);
443}
444
445AW_awar *AW_awar::map(AW_default gbd) {
446    if (gb_var) {                                   // remove old mapping
447        GB_remove_callback((GBDATA *)gb_var, GB_CB_CHANGED, makeDatabaseCallback(AW_var_gbdata_callback, this));
448        if (gb_var != gb_origin) {                  // keep callback if pointing to origin!
449            GB_remove_callback((GBDATA *)gb_var, GB_CB_DELETE, makeDatabaseCallback(AW_var_gbdata_callback_delete, this));
450        }
451        gb_var = NULL;
452    }
453
454    if (!gbd) {                                     // attempt to remap to NULL -> unmap
455        gbd = gb_origin;
456    }
457
458    if (gbd) {
459        GB_transaction ta(gbd);
460
461        GB_ERROR error = GB_add_callback((GBDATA *) gbd, GB_CB_CHANGED, makeDatabaseCallback(AW_var_gbdata_callback, this));
462        if (!error && gbd != gb_origin) {           // do not add delete callback if mapping to origin
463            error = GB_add_callback((GBDATA *) gbd, GB_CB_DELETE, makeDatabaseCallback(AW_var_gbdata_callback_delete, this));
464        }
465        if (error) aw_message(error);
466
467        gb_var = gbd;
468        update();
469    }
470    else {
471        update();
472    }
473
474    aw_assert(is_valid());
475
476    return this;
477}
478
479AW_awar *AW_awar::map(AW_awar *dest) {
480    return this->map(dest->gb_var);
481}
482AW_awar *AW_awar::map(const char *awarn) {
483    return map(root->awar(awarn));
484}
485
486AW_awar *AW_awar::remove_callback(const RootCallback& rcb) {
487    AW_root_cblist::remove(callback_list, rcb);
488    return this;
489}
490
491AW_awar *AW_awar::set_minmax(float min, float max) {
492    if (variable_type == AW_STRING) GBK_terminatef("set_minmax does not apply to string AWAR '%s'", awar_name);
493    if (min>max) GBK_terminatef("illegal values in set_minmax for AWAR '%s'", awar_name);
494
495    pp.f.min = min;
496    pp.f.max = max;
497    update(); // corrects wrong default value
498    return this;
499}
500
501float AW_awar::get_min() const {
502    if (variable_type == AW_STRING) GBK_terminatef("get_min does not apply to string AWAR '%s'", awar_name);
503    bool isSet = (pp.f.min != pp.f.max); // as used in AW_awar::update
504    if (!isSet) {
505        aw_assert(float(INT_MIN)>=-FLT_MAX);
506        if (variable_type == AW_INT) return float(INT_MIN);
507        aw_assert(variable_type == AW_FLOAT);
508        return -FLT_MAX;
509    }
510    return pp.f.min;
511}
512float AW_awar::get_max() const {
513    if (variable_type == AW_STRING) GBK_terminatef("get_max does not apply to string AWAR '%s'", awar_name);
514    bool isSet = (pp.f.min != pp.f.max); // as used in AW_awar::update
515    if (!isSet) {
516        aw_assert(float(INT_MAX)<=FLT_MAX);
517        if (variable_type == AW_INT) return float(INT_MAX);
518        aw_assert(variable_type == AW_FLOAT);
519        return FLT_MAX;
520    }
521    return pp.f.max;
522}
523
524AW_awar *AW_awar::set_srt(const char *srt) {
525    assert_var_type(AW_STRING);
526    pp.srt = srt;
527    return this;
528}
529
530GB_ERROR AW_awar::toggle_toggle() {
531    char *var = this->read_as_string();
532    GB_ERROR    error = 0;
533    if (var[0] == '0' || var[0] == 'n') {
534        switch (this->variable_type) {
535            case AW_STRING:     error = this->write_string("yes"); break;
536            case AW_INT:        error = this->write_int(1); break;
537            case AW_FLOAT:      error = this->write_float(1.0); break;
538            default: break;
539        }
540    }
541    else {
542        switch (this->variable_type) {
543            case AW_STRING:     error = this->write_string("no"); break;
544            case AW_INT:        error = this->write_int(0); break;
545            case AW_FLOAT:      error = this->write_float(0.0); break;
546            default: break;
547        }
548    }
549    free(var);
550    return error;
551}
552
553AW_awar *AW_awar::unmap() {
554    return this->map(gb_origin);
555}
556
557void AW_awar::run_callbacks() {
558    if (allowed_to_run_callbacks) AW_root_cblist::call(callback_list, root);
559}
560
561
562void AW_awar::update_targets() {
563    // send data to all variables
564    AW_var_target*pntr;
565    for (pntr = target_list; pntr; pntr = pntr->next) {
566        update_target(pntr);
567    }
568}
569
570
571void AW_awar::remove_all_callbacks() {
572    AW_root_cblist::clear(callback_list);
573}
574
575// --------------------------------------------------------------------------------
576
577inline bool member_of_DB(GBDATA *gbd, GBDATA *gb_main) {
578    return gbd && GB_get_root(gbd) == gb_main;;
579}
580
581void AW_awar::unlink() {
582    aw_assert(this->is_valid());
583    remove_all_callbacks();
584    remove_all_target_vars();
585    gb_origin = NULL;                               // make zombie awar
586    unmap();                                        // unmap to nothing
587    aw_assert(is_valid());
588}
589
590bool AW_awar::unlink_from_DB(GBDATA *gb_main) {
591    bool make_zombie  = false;
592    bool mapped_to_DB = member_of_DB(gb_var, gb_main);
593    bool origin_in_DB = member_of_DB(gb_origin, gb_main);
594
595    aw_assert(is_valid());
596
597    if (mapped_to_DB) {
598        if (origin_in_DB) make_zombie = true;
599        else unmap();
600    }
601    else if (origin_in_DB) {
602        // origin is in DB, current mapping is not
603        // -> remap permanentely
604        gb_origin = gb_var;
605    }
606
607    if (make_zombie) unlink();
608
609    aw_assert(is_valid());
610
611    return make_zombie;
612}
613
614// --------------------------------------------------------------------------------
615
616
617
618void AW_awar::remove_all_target_vars() {
619    while (target_list) {
620        AW_var_target *tar = target_list;
621        target_list        = tar->next;
622        delete tar;
623    }
624}
625
626
627void AW_awar::update_tmp_state_during_change() {
628    // here it's known that the awar is inside the correct DB (main-DB or prop-DB)
629
630    if (has_managed_tmp_state()) {
631        aw_assert(GB_get_transaction_level(gb_origin) != 0);
632        // still should be inside change-TA (or inside TA opened in update_tmp_state())
633        // (or in no-TA-mode; as true for properties)
634
635        bool has_default_value = false;
636        switch (variable_type) {
637            case AW_STRING:  has_default_value = ARB_strNULLcmp(GB_read_char_pntr(gb_origin), default_value.s) == 0; break;
638            case AW_INT:     has_default_value = GB_read_int(gb_origin)     == default_value.l; break;
639            case AW_FLOAT:   has_default_value = GB_read_float(gb_origin)   == default_value.d; break;
640            case AW_POINTER: has_default_value = GB_read_pointer(gb_origin) == default_value.p; break;
641            default: aw_assert(0); GB_warning("Unknown awar type"); break;
642        }
643
644        if (GB_is_temporary(gb_origin) != has_default_value) {
645            GB_ERROR error = has_default_value ? GB_set_temporary(gb_origin) : GB_clear_temporary(gb_origin);
646            if (error) GB_warning(GBS_global_string("Failed to set temporary for AWAR '%s' (Reason: %s)", awar_name, error));
647        }
648    }
649}
650
651void AW_awar::set_temp_if_is_default(GBDATA *gb_db) {
652    if (has_managed_tmp_state() && member_of_DB(gb_origin, gb_db)) { // ignore awars in "other" DB (main-DB vs properties)
653        aw_assert(GB_get_transaction_level(gb_origin)<1); // make sure allowed_to_run_callbacks works as expected
654
655        allowed_to_run_callbacks = false;                 // avoid AWAR-change-callbacks
656        {
657            GB_transaction ta(gb_origin);
658            update_tmp_state_during_change();
659        }
660        allowed_to_run_callbacks = true;
661    }
662}
663
664AW_awar::~AW_awar() {
665    unlink();
666    untie_all_widgets();
667    if (variable_type == AW_STRING) free(default_value.s);
668    free(awar_name);
669}
670
671// --------------------------------------------------------------------------------
672
673#ifdef UNIT_TESTS
674#include <test_unit.h>
675
676static int test_cb1_called;
677static int test_cb2_called;
678
679static void test_cb1(AW_root *, AW_CL cd1, AW_CL cd2) { test_cb1_called += (cd1+cd2); }
680static void test_cb2(AW_root *, AW_CL cd1, AW_CL cd2) { test_cb2_called += (cd1+cd2); }
681
682#define TEST_EXPECT_CBS_CALLED(cbl, c1,c2)              \
683    do {                                                \
684        test_cb1_called = test_cb2_called = 0;          \
685        AW_root_cblist::call(cbl, NULL);                \
686        TEST_EXPECT_EQUAL(test_cb1_called, c1);         \
687        TEST_EXPECT_EQUAL(test_cb2_called, c2);         \
688    } while(0)
689
690void TEST_AW_root_cblist() {
691    AW_root_cblist *cb_list = NULL;
692
693    RootCallback tcb1(test_cb1, 1, 0);
694    RootCallback tcb2(test_cb2, 0, 1);
695    RootCallback wrong_tcb2(test_cb2, 1, 0);
696
697    AW_root_cblist::add(cb_list, tcb1); TEST_EXPECT_CBS_CALLED(cb_list, 1, 0);
698    AW_root_cblist::add(cb_list, tcb2); TEST_EXPECT_CBS_CALLED(cb_list, 1, 1);
699
700    AW_root_cblist::remove(cb_list, tcb1);       TEST_EXPECT_CBS_CALLED(cb_list, 0, 1);
701    AW_root_cblist::remove(cb_list, wrong_tcb2); TEST_EXPECT_CBS_CALLED(cb_list, 0, 1);
702    AW_root_cblist::remove(cb_list, tcb2);       TEST_EXPECT_CBS_CALLED(cb_list, 0, 0);
703
704    AW_root_cblist::add(cb_list, tcb1);
705    AW_root_cblist::add(cb_list, tcb1); // add callback twice
706    TEST_EXPECT_CBS_CALLED(cb_list, 1, 0);  // should only be called once
707
708    AW_root_cblist::add(cb_list, tcb2);
709    AW_root_cblist::clear(cb_list);
710    TEST_EXPECT_CBS_CALLED(cb_list, 0, 0); // list clear - nothing should be called
711}
712
713#endif // UNIT_TESTS
714
715
Note: See TracBrowser for help on using the repository browser.