Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 1 | /* Test implementation for osmo_tdef API. */ |
| 2 | /* |
| 3 | * (C) 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> |
| 4 | * |
| 5 | * All Rights Reserved |
| 6 | * |
| 7 | * SPDX-License-Identifier: GPL-2.0+ |
| 8 | * |
| 9 | * Author: Neels Hofmeyr <neels@hofmeyr.de> |
| 10 | * |
| 11 | * This program is free software; you can redistribute it and/or modify |
| 12 | * it under the terms of the GNU General Public License as published by |
| 13 | * the Free Software Foundation; either version 2 of the License, or |
| 14 | * (at your option) any later version. |
| 15 | * |
| 16 | * This program is distributed in the hope that it will be useful, |
| 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 19 | * GNU General Public License for more details. |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 20 | */ |
| 21 | |
| 22 | #include <stdio.h> |
| 23 | #include <errno.h> |
| 24 | #include <limits.h> |
| 25 | |
| 26 | #include <osmocom/core/logging.h> |
| 27 | #include <osmocom/core/application.h> |
| 28 | #include <osmocom/core/fsm.h> |
| 29 | |
| 30 | #include <osmocom/core/tdef.h> |
| 31 | |
| 32 | static void *ctx = NULL; |
| 33 | |
| 34 | static struct osmo_tdef tdefs[] = { |
| 35 | { .T=1, .default_val=100, .desc="100s" }, |
| 36 | { .T=2, .default_val=100, .unit=OSMO_TDEF_MS, .desc="100ms" }, |
Pau Espin Pedrol | e81c2f4 | 2020-11-15 17:52:09 +0100 | [diff] [blame] | 37 | { .T=3, .default_val=50, .unit=OSMO_TDEF_M, .desc="50m" }, |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 38 | { .T=4, .default_val=100, .unit=OSMO_TDEF_CUSTOM, .desc="100 potatoes" }, |
| 39 | |
Vadim Yanitskiy | e30d22a | 2023-12-30 04:22:26 +0700 | [diff] [blame] | 40 | { .T=-5, .default_val=100, .unit=OSMO_TDEF_MS, .desc="X-100ms" }, |
| 41 | { .T=-6, .default_val=100, .unit=OSMO_TDEF_US, .desc="X-100us" }, |
| 42 | |
Pau Espin Pedrol | 0cbe8f0 | 2019-09-17 13:13:52 +0200 | [diff] [blame] | 43 | { .T=7, .default_val=50, .desc="Water Boiling Timeout", .min_val=20, .max_val=800 }, // default is .unit=OSMO_TDEF_S == 0 |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 44 | { .T=8, .default_val=300, .desc="Tea brewing" }, |
| 45 | { .T=9, .default_val=5, .unit=OSMO_TDEF_M, .desc="Let tea cool down before drinking" }, |
| 46 | { .T=10, .default_val=20, .unit=OSMO_TDEF_M, .desc="Forgot to drink tea while it's warm" }, |
| 47 | |
| 48 | /* test conversions */ |
| 49 | { .T=1000, .default_val=2*1000, .unit=OSMO_TDEF_MS, .desc="two seconds from ms" }, |
Pau Espin Pedrol | 2299cb5 | 2020-11-15 17:58:10 +0100 | [diff] [blame] | 50 | { .T=1001, .default_val=60*1000, .unit=OSMO_TDEF_MS, .desc="one minute from ms" }, |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 51 | { .T=1004, .default_val=1, .unit=OSMO_TDEF_MS, .desc="one ms" }, |
| 52 | { .T=1005, .default_val=0, .unit=OSMO_TDEF_MS, .desc="zero ms" }, |
| 53 | { .T=1006, .default_val=0, .unit=OSMO_TDEF_S, .desc="zero s" }, |
| 54 | { .T=1007, .default_val=0, .unit=OSMO_TDEF_M, .desc="zero m" }, |
| 55 | { .T=1008, .default_val=0, .unit=OSMO_TDEF_CUSTOM, .desc="zero" }, |
Pau Espin Pedrol | 1b75e4b | 2020-11-04 20:00:38 +0100 | [diff] [blame] | 56 | { .T=1009, .default_val=0, .unit=OSMO_TDEF_US, .desc="zero us" }, |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 57 | |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 58 | { .T=0, .default_val=1, .unit=OSMO_TDEF_CUSTOM, .desc="zero" }, |
| 59 | |
| 60 | /* no desc */ |
| 61 | { .T=123, .default_val=1 }, |
| 62 | |
| 63 | {} // <-- important! last entry shall be zero |
| 64 | }; |
| 65 | |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 66 | static struct osmo_tdef tdefs_range[] = { |
| 67 | { .T=1002, .default_val=(ULONG_MAX/60), .unit=OSMO_TDEF_M, .desc="almost too many seconds" }, |
| 68 | { .T=1003, .default_val=ULONG_MAX, .unit=OSMO_TDEF_M, .desc="too many seconds" }, |
| 69 | |
| 70 | { .T=INT_MAX, .default_val=ULONG_MAX, .unit=OSMO_TDEF_S, .desc="very large" }, |
| 71 | { .T=INT_MAX-1, .default_val=ULONG_MAX-1, .unit=OSMO_TDEF_S, .desc="very large" }, |
| 72 | { .T=INT_MAX-2, .default_val=LONG_MAX, .unit=OSMO_TDEF_S, .desc="very large" }, |
| 73 | { .T=INT_MAX-3, .default_val=ULONG_MAX, .unit=OSMO_TDEF_M, .desc="very large in minutes" }, |
| 74 | { .T=INT_MIN, .default_val=ULONG_MAX, .unit=OSMO_TDEF_S, .desc="negative" }, |
| 75 | |
| 76 | {} |
| 77 | }; |
| 78 | |
| 79 | #define print_tdef_get(TDEFS, T, AS_UNIT) do { \ |
| 80 | unsigned long val = osmo_tdef_get(TDEFS, T, AS_UNIT, 999); \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 81 | printf("osmo_tdef_get(tdefs, %d, %s, 999)\t= %lu\n", T, osmo_tdef_unit_name(AS_UNIT), val); \ |
| 82 | } while (0) |
| 83 | |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 84 | #define print_tdef_get_short(TDEFS, T, AS_UNIT) do { \ |
| 85 | unsigned long val = osmo_tdef_get(TDEFS, T, AS_UNIT, 999); \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 86 | printf("osmo_tdef_get(%d, %s)\t= %lu\n", T, osmo_tdef_unit_name(AS_UNIT), val); \ |
| 87 | } while (0) |
| 88 | |
| 89 | void print_tdef_info(unsigned int T) |
| 90 | { |
| 91 | const struct osmo_tdef *t = osmo_tdef_get_entry(tdefs, T); |
| 92 | if (!t) { |
| 93 | printf("T%d=NULL", T); |
| 94 | return; |
| 95 | } |
| 96 | printf("T%d=%lu%s", T, t->val, osmo_tdef_unit_name(t->unit)); |
| 97 | if (t->val != t->default_val) |
| 98 | printf("(def=%lu)", t->default_val); |
| 99 | printf("\n"); |
| 100 | } |
| 101 | |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 102 | static void test_tdef_get(bool test_range) |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 103 | { |
| 104 | int i; |
| 105 | enum osmo_tdef_unit as_unit; |
| 106 | |
| 107 | printf("\n%s()\n", __func__); |
| 108 | |
| 109 | osmo_tdefs_reset(tdefs); // make all values the default |
| 110 | |
| 111 | for (i = 0; i < ARRAY_SIZE(tdefs)-1; i++) { |
| 112 | unsigned int T = tdefs[i].T; |
| 113 | print_tdef_info(T); |
Pau Espin Pedrol | 1b75e4b | 2020-11-04 20:00:38 +0100 | [diff] [blame] | 114 | for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_US; as_unit++) { |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 115 | print_tdef_get_short(tdefs, T, as_unit); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | if (!test_range) |
| 120 | return; |
| 121 | |
| 122 | for (i = 0; i < ARRAY_SIZE(tdefs_range)-1; i++) { |
| 123 | unsigned int T = tdefs_range[i].T; |
| 124 | print_tdef_info(T); |
Pau Espin Pedrol | 1b75e4b | 2020-11-04 20:00:38 +0100 | [diff] [blame] | 125 | for (as_unit = OSMO_TDEF_S; as_unit <= OSMO_TDEF_US; as_unit++) { |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 126 | print_tdef_get_short(tdefs_range, T, as_unit); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 127 | } |
| 128 | } |
| 129 | } |
| 130 | |
Harald Welte | e61d459 | 2022-11-03 11:05:58 +0100 | [diff] [blame] | 131 | static void test_tdef_get_nonexisting(void) |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 132 | { |
| 133 | printf("\n%s()\n", __func__); |
| 134 | |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 135 | print_tdef_get(tdefs, 5, OSMO_TDEF_S); |
| 136 | print_tdef_get(tdefs, 5, OSMO_TDEF_MS); |
| 137 | print_tdef_get(tdefs, 5, OSMO_TDEF_M); |
| 138 | print_tdef_get(tdefs, 5, OSMO_TDEF_CUSTOM); |
Pau Espin Pedrol | 1b75e4b | 2020-11-04 20:00:38 +0100 | [diff] [blame] | 139 | print_tdef_get(tdefs, 5, OSMO_TDEF_US); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 140 | } |
| 141 | |
Harald Welte | e61d459 | 2022-11-03 11:05:58 +0100 | [diff] [blame] | 142 | static void test_tdef_set_and_get(void) |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 143 | { |
| 144 | struct osmo_tdef *t; |
| 145 | printf("\n%s()\n", __func__); |
| 146 | |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 147 | printf("setting 7 = 42\n"); |
Pau Espin Pedrol | 0cbe8f0 | 2019-09-17 13:13:52 +0200 | [diff] [blame] | 148 | t = osmo_tdef_get_entry(tdefs, 7); |
Vadim Yanitskiy | 342a522 | 2022-07-20 05:10:04 +0700 | [diff] [blame] | 149 | OSMO_ASSERT(t != NULL); |
Pau Espin Pedrol | 0cbe8f0 | 2019-09-17 13:13:52 +0200 | [diff] [blame] | 150 | OSMO_ASSERT(osmo_tdef_val_in_range(t, 42)); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 151 | t->val = 42; |
| 152 | print_tdef_info(7); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 153 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_MS); |
| 154 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_S); |
| 155 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_M); |
| 156 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM); |
Pau Espin Pedrol | 1b75e4b | 2020-11-04 20:00:38 +0100 | [diff] [blame] | 157 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_US); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 158 | |
| 159 | printf("setting 7 = 420\n"); |
Pau Espin Pedrol | 0cbe8f0 | 2019-09-17 13:13:52 +0200 | [diff] [blame] | 160 | OSMO_ASSERT(osmo_tdef_set(tdefs, 7, 420, OSMO_TDEF_S) == 0); |
| 161 | print_tdef_info(7); |
| 162 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_MS); |
| 163 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_S); |
| 164 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_M); |
| 165 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM); |
Pau Espin Pedrol | 1b75e4b | 2020-11-04 20:00:38 +0100 | [diff] [blame] | 166 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_US); |
Pau Espin Pedrol | 0cbe8f0 | 2019-09-17 13:13:52 +0200 | [diff] [blame] | 167 | |
| 168 | printf("setting 7 = 10 (ERANGE)\n"); |
| 169 | OSMO_ASSERT(!osmo_tdef_val_in_range(t, 10)); |
| 170 | OSMO_ASSERT(osmo_tdef_set(tdefs, 7, 10, OSMO_TDEF_S) == -ERANGE); |
| 171 | print_tdef_info(7); |
| 172 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_MS); |
| 173 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_S); |
| 174 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_M); |
| 175 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM); |
Pau Espin Pedrol | 1b75e4b | 2020-11-04 20:00:38 +0100 | [diff] [blame] | 176 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_US); |
Pau Espin Pedrol | 0cbe8f0 | 2019-09-17 13:13:52 +0200 | [diff] [blame] | 177 | |
| 178 | printf("setting 7 = 900 (ERANGE)\n"); |
| 179 | OSMO_ASSERT(!osmo_tdef_val_in_range(t, 900)); |
| 180 | OSMO_ASSERT(osmo_tdef_set(tdefs, 7, 900, OSMO_TDEF_S) == -ERANGE); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 181 | print_tdef_info(7); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 182 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_MS); |
| 183 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_S); |
| 184 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_M); |
| 185 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_CUSTOM); |
Pau Espin Pedrol | 1b75e4b | 2020-11-04 20:00:38 +0100 | [diff] [blame] | 186 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_US); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 187 | |
Pau Espin Pedrol | 9168cc9 | 2019-09-18 13:18:18 +0200 | [diff] [blame] | 188 | printf("setting 23 = 50 (EEXIST)\n"); |
| 189 | OSMO_ASSERT(osmo_tdef_set(tdefs, 23, 50, OSMO_TDEF_S) == -EEXIST); |
| 190 | |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 191 | printf("resetting\n"); |
| 192 | osmo_tdefs_reset(tdefs); |
| 193 | print_tdef_info(7); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 194 | print_tdef_get_short(tdefs, 7, OSMO_TDEF_S); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | enum test_tdef_fsm_states { |
| 198 | S_A = 0, |
| 199 | S_B, |
| 200 | S_C, |
| 201 | S_D, |
Vadim Yanitskiy | e30d22a | 2023-12-30 04:22:26 +0700 | [diff] [blame] | 202 | S_E, |
| 203 | S_F, |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 204 | S_G, |
| 205 | S_H, |
| 206 | S_I, |
| 207 | S_J, |
| 208 | S_K, |
| 209 | S_L, |
| 210 | S_M, |
| 211 | S_N, |
| 212 | S_O, |
Vadim Yanitskiy | e30d22a | 2023-12-30 04:22:26 +0700 | [diff] [blame] | 213 | /* ... gap ... */ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 214 | S_X, |
| 215 | S_Y, |
| 216 | S_Z, |
| 217 | }; |
| 218 | |
| 219 | static const struct osmo_tdef_state_timeout test_tdef_state_timeouts[32] = { |
| 220 | [S_A] = { .T = 1 }, |
| 221 | [S_B] = { .T = 2 }, |
| 222 | [S_C] = { .T = 3 }, |
| 223 | [S_D] = { .T = 4 }, |
| 224 | |
Vadim Yanitskiy | e30d22a | 2023-12-30 04:22:26 +0700 | [diff] [blame] | 225 | [S_E] = { .T = -5 }, |
| 226 | [S_F] = { .T = -6 }, |
| 227 | |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 228 | [S_G] = { .T = 7 }, |
| 229 | [S_H] = { .T = 8 }, |
| 230 | [S_I] = { .T = 9 }, |
| 231 | [S_J] = { .T = 10 }, |
| 232 | |
| 233 | /* keep_timer: adopt whichever T was running before and continue the timeout. */ |
| 234 | [S_K] = { .keep_timer = true }, |
| 235 | /* S_F defines an undefined T, but should continue previous state's timeout. */ |
| 236 | [S_L] = { .T = 123, .keep_timer = true }, |
| 237 | |
| 238 | /* range */ |
| 239 | [S_M] = { .T = INT_MAX }, |
| 240 | [S_N] = { .T = INT_MIN }, |
| 241 | |
| 242 | /* T0 is not addressable from osmo_tdef_state_timeout, since it is indistinguishable from an unset entry. Even |
| 243 | * though a timeout value is set for T=0, the transition to state S_O will show "no timer configured". */ |
| 244 | [S_O] = { .T = 0 }, |
| 245 | |
| 246 | /* S_X undefined on purpose */ |
| 247 | /* S_Y defines a T that does not exist */ |
| 248 | [S_Y] = { .T = 666 }, |
| 249 | /* S_Z undefined on purpose */ |
| 250 | }; |
| 251 | |
| 252 | #define S(x) (1 << (x)) |
| 253 | |
| 254 | static const struct osmo_fsm_state test_tdef_fsm_states[] = { |
| 255 | #define DEF_STATE(NAME) \ |
| 256 | [S_##NAME] = { \ |
| 257 | .name = #NAME, \ |
Vadim Yanitskiy | e30d22a | 2023-12-30 04:22:26 +0700 | [diff] [blame] | 258 | .out_state_mask = 0xffffffff, \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 259 | } |
| 260 | |
| 261 | DEF_STATE(A), |
| 262 | DEF_STATE(B), |
| 263 | DEF_STATE(C), |
| 264 | DEF_STATE(D), |
Vadim Yanitskiy | e30d22a | 2023-12-30 04:22:26 +0700 | [diff] [blame] | 265 | DEF_STATE(E), |
| 266 | DEF_STATE(F), |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 267 | DEF_STATE(G), |
| 268 | DEF_STATE(H), |
| 269 | DEF_STATE(I), |
| 270 | DEF_STATE(J), |
| 271 | |
| 272 | DEF_STATE(K), |
| 273 | DEF_STATE(L), |
| 274 | |
| 275 | DEF_STATE(M), |
| 276 | DEF_STATE(N), |
| 277 | DEF_STATE(O), |
| 278 | |
| 279 | DEF_STATE(X), |
| 280 | DEF_STATE(Y), |
| 281 | /* Z: test not being allowed to transition to other states. */ |
| 282 | [S_Z] = { |
| 283 | .name = "Z", |
| 284 | .out_state_mask = 0 |
| 285 | | S(S_A) |
| 286 | , |
| 287 | }, |
| 288 | }; |
| 289 | |
| 290 | static const struct value_string test_tdef_fsm_event_names[] = { {} }; |
| 291 | |
| 292 | static struct osmo_fsm test_tdef_fsm = { |
| 293 | .name = "tdef_test", |
| 294 | .states = test_tdef_fsm_states, |
| 295 | .event_names = test_tdef_fsm_event_names, |
| 296 | .num_states = ARRAY_SIZE(test_tdef_fsm_states), |
| 297 | .log_subsys = DLGLOBAL, |
| 298 | }; |
| 299 | |
| 300 | const struct timeval fake_time_start_time = { 123, 456 }; |
| 301 | |
| 302 | #define fake_time_passes(secs, usecs) do \ |
| 303 | { \ |
| 304 | struct timeval diff; \ |
| 305 | osmo_gettimeofday_override_add(secs, usecs); \ |
| 306 | osmo_clock_override_add(CLOCK_MONOTONIC, secs, usecs * 1000); \ |
| 307 | timersub(&osmo_gettimeofday_override_time, &fake_time_start_time, &diff); \ |
Neels Hofmeyr | 4f54c6c | 2019-03-06 06:03:24 +0100 | [diff] [blame] | 308 | printf("Time passes: %ld.%06ld s\n", (long)secs, (long)usecs); \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 309 | osmo_timers_prepare(); \ |
| 310 | osmo_timers_update(); \ |
| 311 | } while (0) |
| 312 | |
Harald Welte | e61d459 | 2022-11-03 11:05:58 +0100 | [diff] [blame] | 313 | void fake_time_start(void) |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 314 | { |
| 315 | struct timespec *clock_override; |
| 316 | |
| 317 | osmo_gettimeofday_override_time = fake_time_start_time; |
| 318 | osmo_gettimeofday_override = true; |
| 319 | clock_override = osmo_clock_override_gettimespec(CLOCK_MONOTONIC); |
| 320 | OSMO_ASSERT(clock_override); |
| 321 | clock_override->tv_sec = fake_time_start_time.tv_sec; |
| 322 | clock_override->tv_nsec = fake_time_start_time.tv_usec * 1000; |
| 323 | osmo_clock_override_enable(CLOCK_MONOTONIC, true); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 324 | } |
| 325 | |
| 326 | static void print_fsm_state(struct osmo_fsm_inst *fi) |
| 327 | { |
| 328 | struct timeval remaining; |
| 329 | printf("state=%s T=%d", osmo_fsm_inst_state_name(fi), fi->T); |
| 330 | |
| 331 | if (!osmo_timer_pending(&fi->timer)) { |
| 332 | printf(", no timeout\n"); |
| 333 | return; |
| 334 | } |
| 335 | |
| 336 | osmo_timer_remaining(&fi->timer, &osmo_gettimeofday_override_time, &remaining); |
| 337 | printf(", %lu.%06lu s remaining\n", remaining.tv_sec, remaining.tv_usec); |
| 338 | } |
| 339 | |
| 340 | |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 341 | #define test_tdef_fsm_state_chg(tdefs, NEXT_STATE) do { \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 342 | const struct osmo_tdef_state_timeout *st = osmo_tdef_get_state_timeout(NEXT_STATE, \ |
| 343 | test_tdef_state_timeouts); \ |
Neels Hofmeyr | 4ea6982 | 2019-03-06 06:14:01 +0100 | [diff] [blame] | 344 | int rc = osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, test_tdef_state_timeouts, tdefs, 999); \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 345 | if (!st) { \ |
Neels Hofmeyr | 4ea6982 | 2019-03-06 06:14:01 +0100 | [diff] [blame] | 346 | printf(" --> %s (no timer configured for this state) rc=%d;\t", \ |
| 347 | osmo_fsm_state_name(&test_tdef_fsm, NEXT_STATE), rc); \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 348 | } else { \ |
| 349 | struct osmo_tdef *t = osmo_tdef_get_entry(tdefs, st->T); \ |
Neels Hofmeyr | 4f54c6c | 2019-03-06 06:03:24 +0100 | [diff] [blame] | 350 | printf(" --> %s (configured as T%d%s %lu %s) rc=%d;\t", \ |
| 351 | osmo_fsm_state_name(&test_tdef_fsm, NEXT_STATE), \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 352 | st->T, st->keep_timer ? "(keep_timer)" : "", \ |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 353 | t? t->val : 0, t? osmo_tdef_unit_name(t->unit) : "-", \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 354 | rc); \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 355 | } \ |
Neels Hofmeyr | 4f54c6c | 2019-03-06 06:03:24 +0100 | [diff] [blame] | 356 | print_fsm_state(fi); \ |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 357 | } while(0) |
| 358 | |
| 359 | |
| 360 | |
| 361 | static void test_tdef_state_timeout(bool test_range) |
| 362 | { |
| 363 | struct osmo_fsm_inst *fi; |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 364 | unsigned long m_secs; |
| 365 | printf("\n%s()\n", __func__); |
| 366 | |
| 367 | osmo_tdefs_reset(tdefs); |
| 368 | |
| 369 | fake_time_start(); |
| 370 | |
| 371 | fi = osmo_fsm_inst_alloc(&test_tdef_fsm, ctx, NULL, LOGL_DEBUG, __func__); |
| 372 | OSMO_ASSERT(fi); |
| 373 | print_fsm_state(fi); |
| 374 | |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 375 | test_tdef_fsm_state_chg(tdefs, S_A); |
| 376 | test_tdef_fsm_state_chg(tdefs, S_B); |
| 377 | test_tdef_fsm_state_chg(tdefs, S_C); |
| 378 | test_tdef_fsm_state_chg(tdefs, S_D); |
Vadim Yanitskiy | e30d22a | 2023-12-30 04:22:26 +0700 | [diff] [blame] | 379 | test_tdef_fsm_state_chg(tdefs, S_E); |
| 380 | test_tdef_fsm_state_chg(tdefs, S_F); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 381 | test_tdef_fsm_state_chg(tdefs, S_G); |
| 382 | test_tdef_fsm_state_chg(tdefs, S_H); |
| 383 | test_tdef_fsm_state_chg(tdefs, S_I); |
| 384 | test_tdef_fsm_state_chg(tdefs, S_J); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 385 | |
| 386 | printf("- test keep_timer:\n"); |
| 387 | fake_time_passes(123, 45678); |
| 388 | print_fsm_state(fi); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 389 | test_tdef_fsm_state_chg(tdefs, S_K); |
| 390 | test_tdef_fsm_state_chg(tdefs, S_A); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 391 | fake_time_passes(23, 45678); |
| 392 | print_fsm_state(fi); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 393 | test_tdef_fsm_state_chg(tdefs, S_K); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 394 | |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 395 | test_tdef_fsm_state_chg(tdefs, S_A); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 396 | fake_time_passes(23, 45678); |
| 397 | print_fsm_state(fi); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 398 | test_tdef_fsm_state_chg(tdefs, S_L); |
Neels Hofmeyr | 4f54c6c | 2019-03-06 06:03:24 +0100 | [diff] [blame] | 399 | test_tdef_fsm_state_chg(tdefs, S_O); |
| 400 | test_tdef_fsm_state_chg(tdefs, S_L); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 401 | |
| 402 | printf("- test T=0:\n"); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 403 | test_tdef_fsm_state_chg(tdefs, S_O); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 404 | |
| 405 | printf("- test no timer:\n"); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 406 | test_tdef_fsm_state_chg(tdefs, S_X); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 407 | |
| 408 | printf("- test undefined timer, using default_val arg of osmo_tdef_fsm_inst_state_chg(), here passed as 999:\n"); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 409 | test_tdef_fsm_state_chg(tdefs, S_Y); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 410 | |
| 411 | /* the range of unsigned long is architecture dependent. This test can be invoked manually to see whether |
| 412 | * clamping the timeout values works, but the output will be of varying lengths depending on the system's |
| 413 | * unsigned long range, and would cause differences in expected output. */ |
| 414 | if (test_range) { |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 415 | struct osmo_tdef *m; |
| 416 | |
| 417 | printf("- test large T:\n"); |
| 418 | test_tdef_fsm_state_chg(tdefs_range, S_M); |
| 419 | |
| 420 | printf("- test T<0:\n"); |
| 421 | test_tdef_fsm_state_chg(tdefs_range, S_N); |
| 422 | |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 423 | printf("- test range:\n"); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 424 | test_tdef_fsm_state_chg(tdefs_range, S_M); |
| 425 | |
| 426 | m = osmo_tdef_get_entry(tdefs_range, INT_MAX); |
| 427 | OSMO_ASSERT(m); |
| 428 | |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 429 | /* sweep through all the bits, shifting in 0xfffff.. from the right. */ |
| 430 | m_secs = 0; |
| 431 | do { |
| 432 | m_secs = (m_secs << 1) + 1; |
| 433 | switch (m_secs) { |
| 434 | case 0x7fff: |
| 435 | printf("--- int32_t max ---\n"); |
| 436 | break; |
| 437 | case 0xffff: |
| 438 | printf("--- uint32_t max ---\n"); |
| 439 | break; |
| 440 | case 0x7fffffff: |
| 441 | printf("--- int64_t max ---\n"); |
| 442 | break; |
| 443 | case 0xffffffff: |
| 444 | printf("--- uint64_t max ---\n"); |
| 445 | break; |
| 446 | default: |
| 447 | break; |
| 448 | } |
| 449 | |
| 450 | m->val = m_secs - 1; |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 451 | test_tdef_fsm_state_chg(tdefs_range, S_M); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 452 | m->val = m_secs; |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 453 | test_tdef_fsm_state_chg(tdefs_range, S_M); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 454 | m->val = m_secs + 1; |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 455 | test_tdef_fsm_state_chg(tdefs_range, S_M); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 456 | } while (m_secs < ULONG_MAX); |
| 457 | } |
| 458 | |
| 459 | printf("- test disallowed transition:\n"); |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 460 | test_tdef_fsm_state_chg(tdefs, S_Z); |
| 461 | test_tdef_fsm_state_chg(tdefs, S_B); |
| 462 | test_tdef_fsm_state_chg(tdefs, S_C); |
| 463 | test_tdef_fsm_state_chg(tdefs, S_D); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 464 | } |
| 465 | |
| 466 | int main(int argc, char **argv) |
| 467 | { |
| 468 | ctx = talloc_named_const(NULL, 0, "tdef_test.c"); |
| 469 | osmo_init_logging2(ctx, NULL); |
| 470 | |
Pau Espin Pedrol | 01e0d3e | 2021-02-18 19:25:44 +0100 | [diff] [blame] | 471 | log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE); |
Vadim Yanitskiy | 3c5518c | 2023-12-30 23:39:09 +0700 | [diff] [blame^] | 472 | log_set_print_level(osmo_stderr_target, 1); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 473 | log_set_print_category(osmo_stderr_target, 1); |
Vadim Yanitskiy | 3c5518c | 2023-12-30 23:39:09 +0700 | [diff] [blame^] | 474 | log_set_print_category_hex(osmo_stderr_target, 0); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 475 | log_set_use_color(osmo_stderr_target, 0); |
| 476 | |
Vadim Yanitskiy | 3c5518c | 2023-12-30 23:39:09 +0700 | [diff] [blame^] | 477 | osmo_fsm_log_addr(false); |
| 478 | osmo_fsm_log_timeouts(true); |
| 479 | |
| 480 | log_set_category_filter(osmo_stderr_target, DLGLOBAL, 1, LOGL_DEBUG); |
| 481 | |
Harald Welte | ae5016f | 2019-12-01 13:38:20 +0100 | [diff] [blame] | 482 | OSMO_ASSERT(osmo_fsm_register(&test_tdef_fsm) == 0); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 483 | |
Neels Hofmeyr | 7b740f7 | 2019-02-06 01:08:43 +0100 | [diff] [blame] | 484 | test_tdef_get(argc > 1); |
Neels Hofmeyr | 0fd615f | 2019-01-26 20:36:12 +0100 | [diff] [blame] | 485 | test_tdef_get_nonexisting(); |
| 486 | test_tdef_set_and_get(); |
| 487 | /* Run range test iff any argument is passed on the cmdline. For the rationale, see the comment in |
| 488 | * test_tdef_state_timeout(). */ |
| 489 | test_tdef_state_timeout(argc > 1); |
| 490 | |
| 491 | return EXIT_SUCCESS; |
| 492 | } |