aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2021-11-13 23:19:33 +0100
committerlaforge <laforge@osmocom.org>2021-11-15 09:06:03 +0000
commit25c9741445ead27a2f646b97a41804518e469bb5 (patch)
tree0b09f91613c584eacb04a66bf93ad19d977fc456 /tests
parent5eb67c2d666445bb8b01129634de3e955236adf5 (diff)
add osmo_time_cc, moved from osmo-bsc
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile.am7
-rw-r--r--tests/testsuite.at6
-rw-r--r--tests/time_cc/time_cc_test.c768
-rw-r--r--tests/time_cc/time_cc_test.ok328
4 files changed, 1109 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b72619f1..c44b6f08 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -42,6 +42,7 @@ check_PROGRAMS = timer/timer_test sms/sms_test ussd/ussd_test \
bsslap/bsslap_test \
bssmap_le/bssmap_le_test \
it_q/it_q_test \
+ time_cc/time_cc_test \
gsm48/rest_octets_test \
base64/base64_test \
$(NULL)
@@ -324,6 +325,9 @@ bssmap_le_bssmap_le_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la
it_q_it_q_test_SOURCES = it_q/it_q_test.c
it_q_it_q_test_LDADD = $(LDADD)
+time_cc_time_cc_test_SOURCES = time_cc/time_cc_test.c
+time_cc_time_cc_test_LDADD = $(LDADD)
+
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
:;{ \
@@ -413,6 +417,7 @@ EXTRA_DIST = testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
bsslap/bsslap_test.ok \
bssmap_le/bssmap_le_test.ok \
it_q/it_q_test.ok \
+ time_cc/time_cc_test.ok \
gsm48/rest_octets_test.ok \
base64/base64_test.ok \
$(NULL)
@@ -609,6 +614,8 @@ endif
>$(srcdir)/bssmap_le/bssmap_le_test.ok
it_q/it_q_test \
>$(srcdir)/it_q/it_q_test.ok
+ time_cc/time_cc_test \
+ >$(srcdir)/time_cc/time_cc_test.ok
check-local: atconfig $(TESTSUITE)
[ -e /proc/cpuinfo ] && cat /proc/cpuinfo
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 6ac59709..975b51e3 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -452,3 +452,9 @@ AT_KEYWORDS([base64])
cat $abs_srcdir/base64/base64_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/base64/base64_test], [0], [expout], [ignore])
AT_CLEANUP
+
+AT_SETUP([time_cc])
+AT_KEYWORDS([time_cc])
+cat $abs_srcdir/time_cc/time_cc_test.ok > expout
+AT_CHECK([$abs_top_builddir/tests/time_cc/time_cc_test], [0], [expout], [ignore])
+AT_CLEANUP
diff --git a/tests/time_cc/time_cc_test.c b/tests/time_cc/time_cc_test.c
new file mode 100644
index 00000000..22ea7f65
--- /dev/null
+++ b/tests/time_cc/time_cc_test.c
@@ -0,0 +1,768 @@
+/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <inttypes.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/tdef.h>
+#include <osmocom/core/time_cc.h>
+
+enum my_ctrs {
+ CTR_CEIL,
+ CTR_ROUND,
+ CTR_FLOOR,
+};
+
+const struct rate_ctr_desc my_ctr_desc[] = {
+ [CTR_CEIL] = {"ceil", "testing round_threshold_usec = 1"},
+ [CTR_ROUND] = {"round", "testing round_threshold_usec = 0 = gran_usec/2"},
+ [CTR_FLOOR] = {"floor", "testing round_threshold_usec = gran_usec"},
+};
+
+const struct rate_ctr_group_desc my_ctrg_desc = {
+ "time_cc_test",
+ "Counters for osmo_time_cc test",
+ 0,
+ ARRAY_SIZE(my_ctr_desc),
+ my_ctr_desc,
+};
+
+struct rate_ctr_group *my_ctrg;
+
+
+enum my_obj_timers {
+ T_GRAN = -23,
+ T_ROUND_THRESH = -24,
+ T_FORGET_SUM = -25,
+};
+
+struct osmo_tdef g_my_obj_tdefs[] = {
+ { .T = T_GRAN, .default_val = 0, .unit = OSMO_TDEF_MS, .desc = "flag_cc granularity, or zero for 1 second" },
+ { .T = T_ROUND_THRESH, .default_val = 0, .unit = OSMO_TDEF_MS,
+ .desc = "flag_cc rounding threshold, or zero for half a granularity" },
+ { .T = T_FORGET_SUM, .default_val = 0, .unit = OSMO_TDEF_MS,
+ .desc = "flag_cc inactivity forget period, or zero to not forget any timings" },
+ {}
+};
+
+
+struct my_obj {
+ struct osmo_time_cc flag_cc_ceil;
+ struct osmo_time_cc flag_cc_round;
+ struct osmo_time_cc flag_cc_floor;
+};
+
+void my_obj_init(struct my_obj *my_obj)
+{
+ osmo_time_cc_init(&my_obj->flag_cc_ceil);
+ my_obj->flag_cc_ceil.cfg = (struct osmo_time_cc_cfg){
+ .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, CTR_CEIL),
+ .round_threshold_usec = 1,
+ .T_gran = T_GRAN,
+ .T_forget_sum = T_FORGET_SUM,
+ .T_defs = g_my_obj_tdefs,
+ };
+
+ osmo_time_cc_init(&my_obj->flag_cc_round);
+ my_obj->flag_cc_round.cfg = (struct osmo_time_cc_cfg){
+ .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, CTR_ROUND),
+ .T_gran = T_GRAN,
+ .T_round_threshold = T_ROUND_THRESH,
+ .T_forget_sum = T_FORGET_SUM,
+ .T_defs = g_my_obj_tdefs,
+ };
+
+ osmo_time_cc_init(&my_obj->flag_cc_floor);
+ my_obj->flag_cc_floor.cfg = (struct osmo_time_cc_cfg){
+ .rate_ctr = rate_ctr_group_get_ctr(my_ctrg, CTR_FLOOR),
+ .round_threshold_usec = UINT64_MAX, /* always >= gran_usec */
+ .T_gran = T_GRAN,
+ .T_forget_sum = T_FORGET_SUM,
+ .T_defs = g_my_obj_tdefs,
+ };
+}
+
+void my_obj_event(struct my_obj *my_obj, bool flag)
+{
+ osmo_time_cc_set_flag(&my_obj->flag_cc_ceil, flag);
+ osmo_time_cc_set_flag(&my_obj->flag_cc_round, flag);
+ osmo_time_cc_set_flag(&my_obj->flag_cc_floor, flag);
+}
+
+void my_obj_destruct(struct my_obj *my_obj)
+{
+ osmo_time_cc_cleanup(&my_obj->flag_cc_ceil);
+ osmo_time_cc_cleanup(&my_obj->flag_cc_round);
+ osmo_time_cc_cleanup(&my_obj->flag_cc_floor);
+}
+
+static const struct log_info_cat log_categories[] = {
+};
+
+static const struct log_info log_info = {
+ .cat = log_categories,
+ .num_cat = ARRAY_SIZE(log_categories),
+};
+
+int main()
+{
+ void *ctx = talloc_named_const(NULL, 0, "time_cc_test");
+ struct timespec *now;
+ struct my_obj my_obj = {0};
+
+ osmo_init_logging2(ctx, &log_info);
+
+ /* enable override for CLOCK_MONOTONIC */
+ osmo_clock_override_enable(CLOCK_MONOTONIC, true);
+ now = osmo_clock_override_gettimespec(CLOCK_MONOTONIC);
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+
+ /* enable override for osmo_gettimeofday(), for osmo_timer_schedule() */
+ osmo_gettimeofday_override = true;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_ctrg = rate_ctr_group_alloc(ctx, &my_ctrg_desc, 0);
+
+#define CHECK_RATE_CTRS(exp_ceil, exp_round, exp_floor) do { \
+ printf("%d CHECK_RATE_CTRS(" #exp_ceil ", " #exp_round ", " #exp_floor ")", \
+ my_obj.flag_cc_round.flag_state); \
+ while (osmo_select_main_ctx(1) > 0); \
+ if (exp_ceil != my_obj.flag_cc_ceil.cfg.rate_ctr->current \
+ || exp_round != my_obj.flag_cc_round.cfg.rate_ctr->current \
+ || exp_floor != my_obj.flag_cc_floor.cfg.rate_ctr->current) \
+ printf("\n ERROR on line %d: ctr_ceil=%"PRIu64" ctr_round=%"PRIu64" ctr_floor=%"PRIu64"\n", \
+ __LINE__, \
+ my_obj.flag_cc_ceil.cfg.rate_ctr->current, \
+ my_obj.flag_cc_round.cfg.rate_ctr->current, \
+ my_obj.flag_cc_floor.cfg.rate_ctr->current); \
+ else \
+ printf(" ok\n"); \
+ } while (0)
+
+#define ADD_MILLISECS_NO_SELECT(ms) do { \
+ osmo_clock_override_add(CLOCK_MONOTONIC, ms / 1000, (uint64_t)(ms % 1000) * 1000000); \
+ osmo_gettimeofday_override_add(ms / 1000, (uint64_t)(ms % 1000) * 1000); \
+ printf("%d ADD_MILLISECS(" #ms ") --> %ld.%03ld", my_obj.flag_cc_round.flag_state, \
+ now->tv_sec, now->tv_nsec/1000000); \
+ printf("\n"); \
+ } while (0)
+
+#define ADD_MILLISECS(ms) do { \
+ ADD_MILLISECS_NO_SELECT(ms); \
+ while (osmo_select_main_ctx(1) > 0); \
+ } while (0)
+
+#define FLAG(VAL) do { \
+ printf(" flag: %s -> %s\n", my_obj.flag_cc_round.flag_state ? "TRUE" : "FALSE", VAL ? "TRUE" : "FALSE"); \
+ my_obj_event(&my_obj, VAL); \
+ } while (0)
+
+ /*
+ * sum ^
+ * | ________
+ * | /
+ * | /
+ * | /
+ * 3*gran --+--------------------------------------+
+ * | /:
+ * | / :
+ * | - - - - - - - - - - - - - - - - - / :
+ * | /. :
+ * | / . :
+ * 2*gran --+--------------------------------+ . :
+ * | /: . :
+ * | / : . :
+ * | - - - - - - - - - -_________/ : . :
+ * | / . : . :
+ * | / . : . :
+ * 1*gran --+-----------------+ . : . :
+ * | /: . : . :
+ * | / : . : . :
+ * | - - - - - - -/ : . : . :
+ * | /. : . : . :
+ * | ....-------' . : . : . :
+ * 0 +----------------------------------------------------------> elapsed time
+ * . : . : . :
+ * _ _ _______ ____________
+ * flag: __| |_| |____| . : |_______|. : . : |__________
+ * f t f t f t . : f t. : . : f
+ * round_threshold_usec : . : . : . :
+ * = 1 usec: 0 1 . :2 . :3 . :4 = "ceil()"
+ * = 0 == gran_usec/2: 0 1 : 2 : 3 : = "round()"
+ * = gran_usec: 0 1 2 3 = "floor()"
+ */
+
+ printf("\n----------- cumulating time, without forget_sum\n\n");
+
+ my_obj_init(&my_obj);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 0.001s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(99);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(100);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(100);
+ /* flag has been true for 0.2s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(300);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(299);
+ /* flag has been true for 0.499s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 0.5s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS(499);
+ /* flag has been true for 0.999s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 1.0s */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS(1);
+ /* flag has been true for 1.001s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(299);
+ /* flag has been true for 1.3s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(false);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ ADD_MILLISECS(400);
+
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(true);
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(199);
+ /* flag has been true for 1.499s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(2);
+ /* flag has been true for 1.501s */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(498);
+ /* flag has been true for 1.999s */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(2);
+ /* flag has been true for 2.001s */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS(500);
+ /* flag has been true for 2.501s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(498);
+ /* flag has been true for 2.999s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(3);
+ /* flag has been true for 3.003s */
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS(200);
+ /* flag has been true for 3.203s */
+ CHECK_RATE_CTRS(4, 3, 3);
+ FLAG(false);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ ADD_MILLISECS(4321);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS(5678);
+ CHECK_RATE_CTRS(9, 9, 8);
+ FLAG(false);
+ CHECK_RATE_CTRS(9, 9, 8);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+ printf("\n----------- test forget_sum_usec\n\n");
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, 10, OSMO_TDEF_S);
+
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_obj_init(&my_obj);
+
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS(100);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(1000);
+ /* 1 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(8999);
+ /* 9.999 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS(1);
+ /* 10 s of being false, forget_sum_usec has occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(1);
+ /* Since previous sums were forgotton, ceil() triggers again */
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* If the sum had not been forgotten, adding 400 ms to the initial 100 ms would have triggered round(). Verify
+ * that this does not occur, since now full 500 ms are required */
+ ADD_MILLISECS(399);
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* Adding another 100 ms will trigger round() */
+ ADD_MILLISECS(99);
+ CHECK_RATE_CTRS(2, 0, 0);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* If the sum had not been forgotten, adding 900 ms to the initial 100 ms would have triggered floor(). Verify
+ * that this does not occur, since now full 1000 ms are required. We already added 500 ms above. */
+ ADD_MILLISECS(400);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* Adding another 100 ms will trigger floor() */
+ ADD_MILLISECS(99);
+ CHECK_RATE_CTRS(2, 1, 0);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ /* Test that durations of false below forget_sum_usec never trigger a forget */
+ ADD_MILLISECS(300);
+ CHECK_RATE_CTRS(3, 1, 1);
+ /* internal counter is now at 0.3s above the last reported rate counter */
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ FLAG(false);
+ ADD_MILLISECS(9999);
+ FLAG(true);
+ ADD_MILLISECS(25);
+ /* internal counter is now at 0.4s above the last reported rate counter */
+ CHECK_RATE_CTRS(3, 1, 1);
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(3, 2, 1);
+ ADD_MILLISECS(500);
+ CHECK_RATE_CTRS(3, 2, 2);
+
+ /* Test that repeated osmo_time_cc_set_flag(false) does not cancel a forget_sum_usec */
+ ADD_MILLISECS(300);
+ /* internal counter is now at 0.3s above the last reported rate counter */
+ CHECK_RATE_CTRS(4, 2, 2);
+ FLAG(false);
+ ADD_MILLISECS(5000);
+ /* Repeat 'false', must not affect forget_sum_usec */
+ FLAG(false);
+ ADD_MILLISECS(5000);
+ CHECK_RATE_CTRS(4, 2, 2);
+ /* 10 s have passed, forget_sum_usec has occurred.
+ * Hence ceil() will trigger again right away: */
+ FLAG(true);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(5, 2, 2);
+ /* Adding 200 ms to the initial 300 ms would have triggered round(), but no more after forget_sum_usec */
+ ADD_MILLISECS(199);
+ CHECK_RATE_CTRS(5, 2, 2);
+ /* Adding another 300 ms will trigger round() */
+ ADD_MILLISECS(299);
+ CHECK_RATE_CTRS(5, 2, 2);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(5, 3, 2);
+ /* Adding 700 ms to the initial 300 ms would have triggered ceil(), but no more after forget_sum_usec */
+ ADD_MILLISECS(200);
+ CHECK_RATE_CTRS(5, 3, 2);
+ /* Adding another 300 ms will trigger ceil() */
+ ADD_MILLISECS(299);
+ CHECK_RATE_CTRS(5, 3, 2);
+ ADD_MILLISECS(1);
+ CHECK_RATE_CTRS(5, 3, 3);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+
+ /* Verify correctness when select() lags and runs timer callbacks too late */
+ printf("\n----------- cumulating time, without forget_sum, when timer cb are invoked late\n\n");
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, 0, OSMO_TDEF_S);
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_obj_init(&my_obj);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(100);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS_NO_SELECT(100);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(100);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS_NO_SELECT(100);
+ /* flag has been true for 0.2s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(300);
+
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS_NO_SELECT(799);
+ /* flag has been true for 0.999s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 1.0s */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS_NO_SELECT(300);
+ /* flag has been true for 1.3s */
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(false);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ ADD_MILLISECS_NO_SELECT(400);
+
+ CHECK_RATE_CTRS(2, 1, 1);
+ FLAG(true);
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS_NO_SELECT(699);
+ /* flag has been true for 1.999s */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 2.0s */
+ CHECK_RATE_CTRS(2, 2, 2);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 2.001s */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS_NO_SELECT(499);
+ /* flag has been true for 2.5s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS_NO_SELECT(499);
+ /* flag has been true for 2.999s */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* flag has been true for 3.0s */
+ CHECK_RATE_CTRS(3, 3, 3);
+ ADD_MILLISECS_NO_SELECT(200);
+ /* flag has been true for 3.2s */
+ CHECK_RATE_CTRS(4, 3, 3);
+ FLAG(false);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ ADD_MILLISECS_NO_SELECT(4321);
+ CHECK_RATE_CTRS(4, 3, 3);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS_NO_SELECT(5678);
+ CHECK_RATE_CTRS(9, 9, 8);
+ FLAG(false);
+ CHECK_RATE_CTRS(9, 9, 8);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+
+ printf("\n----------- test forget_sum, when timer cb are invoked late\n\n");
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, 10, OSMO_TDEF_S);
+
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ my_obj_init(&my_obj);
+
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS_NO_SELECT(100);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ FLAG(false);
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(1000);
+ /* 1 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(8999);
+ /* 9.999 s of being false, forget_sum_usec has not yet occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ ADD_MILLISECS_NO_SELECT(1);
+ /* 10 s of being false, forget_sum_usec has occurred */
+ CHECK_RATE_CTRS(1, 0, 0);
+
+ FLAG(true);
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ /* Since previous sums were forgotton, ceil() triggers again */
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* If the sum had not been forgotten, adding 400 ms to the initial 100 ms would have triggered round(). Verify
+ * that this does not occur, since now full 500 ms are required */
+ ADD_MILLISECS_NO_SELECT(399);
+ CHECK_RATE_CTRS(2, 0, 0);
+ /* Adding another 100 ms will trigger round() */
+ ADD_MILLISECS_NO_SELECT(99);
+ CHECK_RATE_CTRS(2, 0, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* If the sum had not been forgotten, adding 900 ms to the initial 100 ms would have triggered floor(). Verify
+ * that this does not occur, since now full 1000 ms are required. We already added 500 ms above. */
+ ADD_MILLISECS_NO_SELECT(400);
+ CHECK_RATE_CTRS(2, 1, 0);
+ /* Adding another 100 ms will trigger floor() */
+ ADD_MILLISECS_NO_SELECT(99);
+ CHECK_RATE_CTRS(2, 1, 0);
+ ADD_MILLISECS_NO_SELECT(1);
+ CHECK_RATE_CTRS(2, 1, 1);
+
+ /* Test that durations of false below forget_sum_usec never trigger a forget */
+ ADD_MILLISECS_NO_SELECT(300);
+ CHECK_RATE_CTRS(3, 1, 1);
+ /* internal counter is now at 0.3s above the last reported rate counter */
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ FLAG(false);
+ ADD_MILLISECS_NO_SELECT(9999);
+ FLAG(true);
+ ADD_MILLISECS_NO_SELECT(25);
+ /* internal counter is now at 0.4s above the last reported rate counter */
+ CHECK_RATE_CTRS(3, 1, 1);
+ ADD_MILLISECS_NO_SELECT(100);
+ CHECK_RATE_CTRS(3, 2, 1);
+ ADD_MILLISECS_NO_SELECT(500);
+ CHECK_RATE_CTRS(3, 2, 2);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+
+#define SET_TDEFS(gran, round_thresh, forget_sum) do { \
+ osmo_tdef_set(g_my_obj_tdefs, T_GRAN, gran, OSMO_TDEF_MS); \
+ osmo_tdef_set(g_my_obj_tdefs, T_ROUND_THRESH, round_thresh, OSMO_TDEF_MS); \
+ osmo_tdef_set(g_my_obj_tdefs, T_FORGET_SUM, forget_sum, OSMO_TDEF_S); \
+ printf("T_defs: T_gran=%luusec T_round_threshold=%luusec T_forget_sum=%luusec\n", \
+ osmo_tdef_get(g_my_obj_tdefs, T_GRAN, OSMO_TDEF_US, -1), \
+ osmo_tdef_get(g_my_obj_tdefs, T_ROUND_THRESH, OSMO_TDEF_US, -1), \
+ osmo_tdef_get(g_my_obj_tdefs, T_FORGET_SUM, OSMO_TDEF_US, -1)); \
+ } while (0)
+
+ printf("\n----------- test T_defs\n\n");
+ now->tv_sec = 23000;
+ now->tv_nsec = 0;
+ osmo_gettimeofday_override_time = (struct timeval){23000, 0};
+
+ SET_TDEFS(100, 10, 0);
+
+ my_obj_init(&my_obj);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(0, 0, 0);
+
+ FLAG(true);
+ /* flag has just turned true the first time */
+ CHECK_RATE_CTRS(0, 0, 0);
+ ADD_MILLISECS(9);
+ /* flag has been true for 0.009s */
+ CHECK_RATE_CTRS(1, 0, 0);
+ ADD_MILLISECS(1);
+ /* flag has been true for 0.010s */
+ CHECK_RATE_CTRS(1, 1, 0);
+ ADD_MILLISECS(90);
+ /* flag has been true for 0.1s */
+ CHECK_RATE_CTRS(1, 1, 1);
+
+ SET_TDEFS(200, 190, 1);
+ /* gran is changed to 200ms, but still continues until the next scheduled event until the change is picked up.
+ * For ceil(), it is 1 ms ahead.
+ * For round(), it is 10 ms ahead.
+ * For floor(), it is at the next full (previous) gran 100 ms ahead.
+ * When T_defs change, all internal sums are reset to zero without reporting.
+ */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS(1);
+ /* 1ms elapsed: ceil() picks up the T_gran change, starts anew. */
+ /* elapsed: ceil 0 ms */
+ CHECK_RATE_CTRS(1, 1, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 1 ms */
+ /* ceil() increments because flag has been true for more than 1 us after reset */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(8);
+ /* 10 ms elapsed: round() picks up the T_gran change, starts anew */
+ /* elapsed: ceil 9 ms, round 0 ms */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(90);
+ /* 100 ms elapsed: floor() picks up the T_gran change, starts anew */
+ /* elapsed: ceil 99 ms, round 90 ms, floor 0 ms */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(99);
+ /* elapsed: ceil 198 ms, round 189 ms, floor 99 ms */
+ CHECK_RATE_CTRS(2, 1, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 199 ms, round 190 ms, floor 100 ms */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 200 ms, round 191 ms, floor 101 ms */
+ CHECK_RATE_CTRS(2, 2, 1);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 201 ms, round 192 ms, floor 102 ms */
+ CHECK_RATE_CTRS(3, 2, 1);
+ ADD_MILLISECS(98);
+ /* elapsed: ceil 299 ms, round 290 ms, floor 200 ms */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS(99);
+ /* elapsed: ceil 398 ms, round 389 ms, floor 299 ms */
+ CHECK_RATE_CTRS(3, 2, 2);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 399 ms, round 390 ms, floor 300 ms */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 400 ms, round 391 ms, floor 301 ms */
+ CHECK_RATE_CTRS(3, 3, 2);
+ ADD_MILLISECS(1);
+ /* elapsed: ceil 401 ms, round 392 ms, floor 302 ms */
+ CHECK_RATE_CTRS(4, 3, 2);
+ ADD_MILLISECS(98);
+ /* elapsed: ceil 499 ms, round 490 ms, floor 400 ms */
+ CHECK_RATE_CTRS(4, 3, 3);
+
+
+ SET_TDEFS(100, 0, 0);
+ /* T_defs change, but they only get picked up upon the next event:
+ * For ceil(), it is 102 ms ahead.
+ * For round(), it is 100 ms ahead (thresh is still 190, currently at 90).
+ * For floor(), it is 200 ms ahead.
+ * When T_defs change, all internal sums are reset to zero without reporting.
+ */
+ CHECK_RATE_CTRS(4, 3, 3);
+ ADD_MILLISECS(100);
+ CHECK_RATE_CTRS(4, 3, 3);
+ /* round() picks up the new T_defs. Internal sum resets, nothing else happens yet.
+ * round() schedules the next event 50 ms ahead. */
+ ADD_MILLISECS(2);
+ CHECK_RATE_CTRS(4, 3, 3);
+ /* ceil() picks up the change, its next event is 1 ms ahead. */
+ ADD_MILLISECS(1);
+ /* ceil: 0.001
+ * round: 0.003
+ * floor: still 97 ms until it picks up the change */
+ CHECK_RATE_CTRS(5, 3, 3);
+ ADD_MILLISECS(46);
+ CHECK_RATE_CTRS(5, 3, 3);
+ ADD_MILLISECS(1);
+ /* round() has first counter trigger after T_defs change. */
+ CHECK_RATE_CTRS(5, 4, 3);
+ /* ceil: 0.048
+ * round: 0.050
+ * floor: still 50 ms until it picks up the change */
+ ADD_MILLISECS(50);
+ /* floor() picks up the change. nothing happens yet. */
+ /* ceil: 0.098
+ * round: 0.100
+ * floor: 0.0 */
+ ADD_MILLISECS(2);
+ /* ceil: 0.100
+ * round: 0.102
+ * floor: 0.002 */
+ CHECK_RATE_CTRS(5, 4, 3);
+ ADD_MILLISECS(1);
+ /* ceil: 0.101
+ * round: 0.103
+ * floor: 0.003 */
+ CHECK_RATE_CTRS(6, 4, 3);
+ ADD_MILLISECS(46);
+ /* ceil: 0.147
+ * round: 0.149
+ * floor: 0.049 */
+ CHECK_RATE_CTRS(6, 4, 3);
+ ADD_MILLISECS(1);
+ /* ceil: 0.148
+ * round: 0.150
+ * floor: 0.050 */
+ CHECK_RATE_CTRS(6, 5, 3);
+
+ my_obj_destruct(&my_obj);
+ rate_ctr_group_reset(my_ctrg);
+
+ return 0;
+}
diff --git a/tests/time_cc/time_cc_test.ok b/tests/time_cc/time_cc_test.ok
new file mode 100644
index 00000000..ccf84d95
--- /dev/null
+++ b/tests/time_cc/time_cc_test.ok
@@ -0,0 +1,328 @@
+
+----------- cumulating time, without forget_sum
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.100
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23000.101
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(99) --> 23000.200
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.300
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.400
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(300) --> 23000.700
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(299) --> 23000.999
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23001.000
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(499) --> 23001.499
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23001.500
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23001.501
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(299) --> 23001.800
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+0 ADD_MILLISECS(400) --> 23002.200
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(199) --> 23002.399
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(2) --> 23002.401
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(498) --> 23002.899
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(2) --> 23002.901
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(500) --> 23003.401
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(498) --> 23003.899
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(3) --> 23003.902
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(200) --> 23004.102
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+0 ADD_MILLISECS(4321) --> 23008.423
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(5678) --> 23014.101
+1 CHECK_RATE_CTRS(9, 9, 8) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(9, 9, 8) ok
+
+----------- test forget_sum_usec
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.100
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1000) --> 23001.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(8999) --> 23010.099
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1) --> 23010.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.101
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(399) --> 23010.500
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(99) --> 23010.599
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.600
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(400) --> 23011.000
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(99) --> 23011.099
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23011.100
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(300) --> 23011.400
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23021.399
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23021.424
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23031.423
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23031.448
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23041.447
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23041.472
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23051.471
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23051.496
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+1 ADD_MILLISECS(100) --> 23051.596
+1 CHECK_RATE_CTRS(3, 2, 1) ok
+1 ADD_MILLISECS(500) --> 23052.096
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(300) --> 23052.396
+1 CHECK_RATE_CTRS(4, 2, 2) ok
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(5000) --> 23057.396
+ flag: FALSE -> FALSE
+0 ADD_MILLISECS(5000) --> 23062.396
+0 CHECK_RATE_CTRS(4, 2, 2) ok
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(1) --> 23062.397
+1 CHECK_RATE_CTRS(5, 2, 2) ok
+1 ADD_MILLISECS(199) --> 23062.596
+1 CHECK_RATE_CTRS(5, 2, 2) ok
+1 ADD_MILLISECS(299) --> 23062.895
+1 CHECK_RATE_CTRS(5, 2, 2) ok
+1 ADD_MILLISECS(1) --> 23062.896
+1 CHECK_RATE_CTRS(5, 3, 2) ok
+1 ADD_MILLISECS(200) --> 23063.096
+1 CHECK_RATE_CTRS(5, 3, 2) ok
+1 ADD_MILLISECS(299) --> 23063.395
+1 CHECK_RATE_CTRS(5, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23063.396
+1 CHECK_RATE_CTRS(5, 3, 3) ok
+
+----------- cumulating time, without forget_sum, when timer cb are invoked late
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.100
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.200
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.300
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.400
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(300) --> 23000.700
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(799) --> 23001.499
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23001.500
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(300) --> 23001.800
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+0 ADD_MILLISECS(400) --> 23002.200
+0 CHECK_RATE_CTRS(2, 1, 1) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(699) --> 23002.899
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(1) --> 23002.900
+1 CHECK_RATE_CTRS(2, 2, 2) ok
+1 ADD_MILLISECS(1) --> 23002.901
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(499) --> 23003.400
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(499) --> 23003.899
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23003.900
+1 CHECK_RATE_CTRS(3, 3, 3) ok
+1 ADD_MILLISECS(200) --> 23004.100
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+0 ADD_MILLISECS(4321) --> 23008.421
+0 CHECK_RATE_CTRS(4, 3, 3) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(5678) --> 23014.099
+1 CHECK_RATE_CTRS(9, 9, 8) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(9, 9, 8) ok
+
+----------- test forget_sum, when timer cb are invoked late
+
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(100) --> 23000.100
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: TRUE -> FALSE
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1000) --> 23001.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(8999) --> 23010.099
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+0 ADD_MILLISECS(1) --> 23010.100
+0 CHECK_RATE_CTRS(1, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.101
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(399) --> 23010.500
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(99) --> 23010.599
+1 CHECK_RATE_CTRS(2, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23010.600
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(400) --> 23011.000
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(99) --> 23011.099
+1 CHECK_RATE_CTRS(2, 1, 0) ok
+1 ADD_MILLISECS(1) --> 23011.100
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(300) --> 23011.400
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23021.399
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23021.424
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23031.423
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23031.448
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23041.447
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23041.472
+ flag: TRUE -> FALSE
+0 ADD_MILLISECS(9999) --> 23051.471
+ flag: FALSE -> TRUE
+1 ADD_MILLISECS(25) --> 23051.496
+1 CHECK_RATE_CTRS(3, 1, 1) ok
+1 ADD_MILLISECS(100) --> 23051.596
+1 CHECK_RATE_CTRS(3, 2, 1) ok
+1 ADD_MILLISECS(500) --> 23052.096
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+
+----------- test T_defs
+
+T_defs: T_gran=100000usec T_round_threshold=10000usec T_forget_sum=0usec
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+0 ADD_MILLISECS(100) --> 23000.100
+0 CHECK_RATE_CTRS(0, 0, 0) ok
+ flag: FALSE -> TRUE
+1 CHECK_RATE_CTRS(0, 0, 0) ok
+1 ADD_MILLISECS(9) --> 23000.109
+1 CHECK_RATE_CTRS(1, 0, 0) ok
+1 ADD_MILLISECS(1) --> 23000.110
+1 CHECK_RATE_CTRS(1, 1, 0) ok
+1 ADD_MILLISECS(90) --> 23000.200
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+T_defs: T_gran=200000usec T_round_threshold=190000usec T_forget_sum=1000000usec
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23000.201
+1 CHECK_RATE_CTRS(1, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23000.202
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(8) --> 23000.210
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(90) --> 23000.300
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(99) --> 23000.399
+1 CHECK_RATE_CTRS(2, 1, 1) ok
+1 ADD_MILLISECS(1) --> 23000.400
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(1) --> 23000.401
+1 CHECK_RATE_CTRS(2, 2, 1) ok
+1 ADD_MILLISECS(1) --> 23000.402
+1 CHECK_RATE_CTRS(3, 2, 1) ok
+1 ADD_MILLISECS(98) --> 23000.500
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(99) --> 23000.599
+1 CHECK_RATE_CTRS(3, 2, 2) ok
+1 ADD_MILLISECS(1) --> 23000.600
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23000.601
+1 CHECK_RATE_CTRS(3, 3, 2) ok
+1 ADD_MILLISECS(1) --> 23000.602
+1 CHECK_RATE_CTRS(4, 3, 2) ok
+1 ADD_MILLISECS(98) --> 23000.700
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+T_defs: T_gran=100000usec T_round_threshold=0usec T_forget_sum=0usec
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(100) --> 23000.800
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(2) --> 23000.802
+1 CHECK_RATE_CTRS(4, 3, 3) ok
+1 ADD_MILLISECS(1) --> 23000.803
+1 CHECK_RATE_CTRS(5, 3, 3) ok
+1 ADD_MILLISECS(46) --> 23000.849
+1 CHECK_RATE_CTRS(5, 3, 3) ok
+1 ADD_MILLISECS(1) --> 23000.850
+1 CHECK_RATE_CTRS(5, 4, 3) ok
+1 ADD_MILLISECS(50) --> 23000.900
+1 ADD_MILLISECS(2) --> 23000.902
+1 CHECK_RATE_CTRS(5, 4, 3) ok
+1 ADD_MILLISECS(1) --> 23000.903
+1 CHECK_RATE_CTRS(6, 4, 3) ok
+1 ADD_MILLISECS(46) --> 23000.949
+1 CHECK_RATE_CTRS(6, 4, 3) ok
+1 ADD_MILLISECS(1) --> 23000.950
+1 CHECK_RATE_CTRS(6, 5, 3) ok