Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 1 | module Osmocom_CTRL_Functions { |
Harald Welte | 35bb716 | 2018-01-03 21:07:52 +0100 | [diff] [blame] | 2 | |
| 3 | /* Definition of helper functions for the Osmocom CTRL interface. |
| 4 | * |
| 5 | * As opposed to many other parts of the Osmocom TTCN-3 code base, this module |
| 6 | * implements blocking functions, instead of asynchronous functions. The |
| 7 | * rationale for this is simple: One normally wants to inquire a value or set |
| 8 | * a value and not continue the main program until that operation is complete. |
Pau Espin Pedrol | 76ba541 | 2019-06-10 11:00:33 +0200 | [diff] [blame] | 9 | * |
Harald Welte | 35bb716 | 2018-01-03 21:07:52 +0100 | [diff] [blame] | 10 | * CTRL is a machine-type protocol on how external programs can interact with |
| 11 | * an Osmocom program in a structured way. It is intended for programmatic |
| 12 | * access (by other software), as opposed to the VTY interface intended for |
| 13 | * human consumption. |
| 14 | * |
| 15 | * (C) 2017 by Harald Welte <laforge@gnumonks.org> |
| 16 | * All rights reserved. |
| 17 | * |
| 18 | * Released under the terms of GNU General Public License, Version 2 or |
| 19 | * (at your option) any later version. |
| 20 | */ |
| 21 | |
| 22 | |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 23 | import from Osmocom_CTRL_Types all; |
| 24 | import from IPA_Emulation all; |
| 25 | |
Neels Hofmeyr | bd94fe7 | 2021-10-26 22:04:53 +0200 | [diff] [blame] | 26 | type record of charstring charstring_list; |
| 27 | |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 28 | private function f_gen_rand_id() return CtrlId { |
| 29 | return int2str(float2int(rnd()*999999999.0)); |
| 30 | } |
| 31 | |
| 32 | /* perform a given GET Operation */ |
| 33 | function f_ctrl_get(IPA_CTRL_PT pt, CtrlVariable variable) return CtrlValue { |
| 34 | timer T := 2.0; |
| 35 | var CtrlMessage rx; |
| 36 | var CtrlId id := f_gen_rand_id(); |
| 37 | pt.send(ts_CtrlMsgGet(id, variable)); |
| 38 | T.start; |
| 39 | alt { |
Harald Welte | fdfa046 | 2017-12-10 18:09:40 +0100 | [diff] [blame] | 40 | [] pt.receive(tr_CtrlMsgGetRepl(id, variable)) -> value rx { |
| 41 | } |
Harald Welte | 95a4781 | 2017-12-09 14:19:03 +0100 | [diff] [blame] | 42 | [] pt.receive(tr_CtrlMsgTrap) { repeat; } |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 43 | [] pt.receive(tr_CtrlMsgError) -> value rx { |
| 44 | setverdict(fail, "Error in CTRL GET ", variable, ": ", rx.err.reason); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 45 | mtc.stop; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 46 | } |
| 47 | [] T.timeout { |
| 48 | setverdict(fail, "Timeout waiting for CTRL GET REPLY ", variable); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 49 | mtc.stop; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 50 | } |
| 51 | } |
| 52 | return rx.resp.val; |
| 53 | } |
| 54 | |
| 55 | /* perform a given SET Operation */ |
| 56 | function f_ctrl_set(IPA_CTRL_PT pt, CtrlVariable variable, CtrlValue val) { |
| 57 | timer T := 2.0; |
| 58 | var CtrlMessage rx; |
| 59 | var CtrlId id := f_gen_rand_id(); |
| 60 | pt.send(ts_CtrlMsgSet(id, variable, val)); |
| 61 | T.start; |
| 62 | alt { |
| 63 | [] pt.receive(tr_CtrlMsgSetRepl(id, variable, val)) { } |
Harald Welte | 95a4781 | 2017-12-09 14:19:03 +0100 | [diff] [blame] | 64 | [] pt.receive(tr_CtrlMsgTrap) { repeat; } |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 65 | [] pt.receive(tr_CtrlMsgError) -> value rx { |
Neels Hofmeyr | 8f17e05 | 2021-09-07 14:45:31 +0200 | [diff] [blame] | 66 | setverdict(fail, "Error in CTRL SET ", variable, ": ", rx.err.reason); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 67 | mtc.stop; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 68 | } |
| 69 | [] T.timeout { |
| 70 | setverdict(fail, "Timeout waiting for CTRL SET REPLY ", variable); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 71 | mtc.stop; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 72 | } |
| 73 | } |
| 74 | } |
| 75 | |
Pau Espin Pedrol | 579cd7a | 2019-06-10 21:01:30 +0200 | [diff] [blame] | 76 | /* send a TRAP */ |
| 77 | function f_ctrl_trap(IPA_CTRL_PT pt, CtrlVariable variable, CtrlValue val) { |
| 78 | pt.send(ts_CtrlMsgTrap(variable, val)); |
| 79 | } |
| 80 | |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 81 | /* Expect a matching TRAP */ |
| 82 | function f_ctrl_exp_trap(IPA_CTRL_PT pt, template CtrlVariable variable, |
Pau Espin Pedrol | 579cd7a | 2019-06-10 21:01:30 +0200 | [diff] [blame] | 83 | template CtrlValue val := ?, float timeout_val := 2.0) |
| 84 | return CtrlValue { |
| 85 | timer T := timeout_val; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 86 | var CtrlMessage rx; |
| 87 | T.start; |
| 88 | alt { |
Harald Welte | fdfa046 | 2017-12-10 18:09:40 +0100 | [diff] [blame] | 89 | [] pt.receive(tr_CtrlMsgTrap(variable, val)) -> value rx { |
| 90 | } |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 91 | [] T.timeout { |
| 92 | setverdict(fail, "Timeout waiting for TRAP ", variable); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 93 | mtc.stop; |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 94 | } |
| 95 | } |
| 96 | return rx.trap.val; |
| 97 | } |
Harald Welte | 852a384 | 2017-12-09 14:19:36 +0100 | [diff] [blame] | 98 | |
Pau Espin Pedrol | 579cd7a | 2019-06-10 21:01:30 +0200 | [diff] [blame] | 99 | /* Expect a matching SET, optionally answer */ |
| 100 | function f_ctrl_exp_set(IPA_CTRL_PT pt, template CtrlVariable variable, |
| 101 | template CtrlValue val := ?, |
| 102 | template (omit) CtrlValue rsp := omit, |
| 103 | float timeout_val := 2.0) |
| 104 | return CtrlValue { |
| 105 | timer T := timeout_val; |
| 106 | var CtrlMessage rx; |
| 107 | T.start; |
| 108 | alt { |
| 109 | [] pt.receive(tr_CtrlMsgSet(?, variable, val)) -> value rx { |
| 110 | if (ispresent(rsp)) { |
| 111 | pt.send(ts_CtrlMsgSetRepl(rx.cmd.id, valueof(variable), valueof(rsp))); |
| 112 | } |
| 113 | } |
| 114 | [] T.timeout { |
| 115 | setverdict(fail, "Timeout waiting for SET ", variable); |
| 116 | mtc.stop; |
| 117 | } |
| 118 | } |
| 119 | return rx.cmd.val; |
| 120 | } |
| 121 | |
Pau Espin Pedrol | 9a5b8ff | 2021-01-04 19:01:31 +0100 | [diff] [blame] | 122 | /* Expect a matching SET, optionally answer */ |
| 123 | function f_ctrl_exp_get(IPA_CTRL_PT pt, template CtrlVariable variable, |
| 124 | template (omit) CtrlValue rsp := omit, |
| 125 | float timeout_val := 2.0) { |
| 126 | timer T := timeout_val; |
| 127 | var CtrlMessage rx; |
| 128 | T.start; |
| 129 | alt { |
| 130 | [] pt.receive(tr_CtrlMsgGet(?, variable)) -> value rx { |
| 131 | if (ispresent(rsp)) { |
| 132 | pt.send(ts_CtrlMsgGetRepl(rx.cmd.id, valueof(variable), valueof(rsp))); |
| 133 | } |
| 134 | } |
| 135 | [] T.timeout { |
| 136 | setverdict(fail, "Timeout waiting for GET ", variable); |
| 137 | mtc.stop; |
| 138 | } |
| 139 | } |
| 140 | } |
| 141 | |
Harald Welte | 852a384 | 2017-12-09 14:19:36 +0100 | [diff] [blame] | 142 | /* Expect a matching GET result */ |
| 143 | function f_ctrl_get_exp(IPA_CTRL_PT pt, CtrlVariable variable, template CtrlValue exp) { |
| 144 | var charstring ctrl_resp; |
| 145 | ctrl_resp := f_ctrl_get(pt, variable); |
| 146 | if (not match(ctrl_resp, exp)) { |
| 147 | setverdict(fail, "Unexpected " & variable & ":" & ctrl_resp); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 148 | mtc.stop; |
Harald Welte | 852a384 | 2017-12-09 14:19:36 +0100 | [diff] [blame] | 149 | } |
| 150 | } |
| 151 | |
Harald Welte | 3ec493f | 2017-12-09 16:25:29 +0100 | [diff] [blame] | 152 | template charstring ts_ctrl_ratectr(CtrlVariable grp, integer instance, CtrlVariable name, |
| 153 | CtrlVariable kind := "abs") := |
| 154 | "rate_ctr." & kind & "." & grp & "." & int2str(instance) & "." & name; |
| 155 | |
| 156 | function f_ctrl_get_ratectr_abs(IPA_CTRL_PT pt, CtrlVariable grp, integer instance, |
| 157 | CtrlVariable name) return integer { |
| 158 | return str2int(f_ctrl_get(pt, valueof(ts_ctrl_ratectr(grp, instance, name)))); |
| 159 | } |
| 160 | |
| 161 | function f_ctrl_get_exp_ratectr_abs(IPA_CTRL_PT pt, CtrlVariable grp, integer instance, |
| 162 | CtrlVariable name, template integer exp) { |
| 163 | var charstring ctrl_resp; |
| 164 | var CtrlVariable variable := valueof(ts_ctrl_ratectr(grp, instance, name)); |
| 165 | ctrl_resp := f_ctrl_get(pt, variable); |
| 166 | if (not match(str2int(ctrl_resp), exp)) { |
| 167 | setverdict(fail, variable & " value " & ctrl_resp & " didn't match ", exp); |
Daniel Willmann | e4ff537 | 2018-07-05 17:35:03 +0200 | [diff] [blame] | 168 | mtc.stop; |
Harald Welte | 3ec493f | 2017-12-09 16:25:29 +0100 | [diff] [blame] | 169 | } |
| 170 | } |
| 171 | |
Harald Welte | 852a384 | 2017-12-09 14:19:36 +0100 | [diff] [blame] | 172 | |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 173 | /* --- Retrieve and verify rate counter values in bulk --- |
| 174 | * |
| 175 | * BSC_Tests.ttcn shows a nice way to conveniently shorten the code needed to use these functions, see |
| 176 | * f_ctrs_msc_init() and f_ctrs_msc_expect(). |
| 177 | * |
| 178 | * Here also a full usage example: |
| 179 | * |
| 180 | * const CounterNameVals my_counternames := { |
| 181 | * { "mscpool:subscr:new", 0 }, |
| 182 | * { "mscpool:subscr:known", 0 }, |
| 183 | * { "mscpool:subscr:attach_lost", 0 }, |
| 184 | * }; |
| 185 | * |
| 186 | * var CounterNameValsList my_counters := f_counter_name_vals_get_n(instance_name := "msc", instance_count := 3, |
| 187 | * counternames := my_counternames); |
| 188 | * |
| 189 | * // run some tests that increment rate counters in the program, |
| 190 | * // and increment expected counters accordingly: |
Neels Hofmeyr | 1393d02 | 2020-08-29 01:17:33 +0000 | [diff] [blame] | 191 | * f_counter_name_vals_list_add(my_counters, instance_nr := 1, "mscpool:subscr:new", 7); |
| 192 | * f_counter_name_vals_list_add(my_counters, instance_nr := 2, "mscpool:subscr:attach_lost", 3); |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 193 | * |
| 194 | * // verify that the program reflects the expected counters: |
| 195 | * f_counter_name_vals_expect_n(instance_name := "msc", my_counters); |
| 196 | * |
| 197 | * // run some more tests... |
Neels Hofmeyr | 1393d02 | 2020-08-29 01:17:33 +0000 | [diff] [blame] | 198 | * f_counter_name_vals_list_add(my_counters, instance_nr := 0, "mscpool:subscr:known"); |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 199 | * // and verify again |
| 200 | * f_counter_name_vals_expect_n(instance_name := "msc", my_counters); |
| 201 | */ |
| 202 | |
| 203 | /* One counter value, e.g. { "name", 23 } */ |
| 204 | type record CounterNameVal { |
| 205 | charstring name, |
| 206 | integer val |
| 207 | } |
| 208 | |
| 209 | /* List of one instance's counters, |
| 210 | * e.g. { {"foo",23}, {"bar",42} } |
| 211 | */ |
| 212 | type record of CounterNameVal CounterNameVals; |
| 213 | |
| 214 | /* List of numerous instances' counters, |
| 215 | * e.g. { { {"foo",23}, {"bar",42} }, |
| 216 | * { {"foo",23}, {"bar",42} } } |
| 217 | */ |
| 218 | type record of CounterNameVals CounterNameValsList; |
| 219 | |
| 220 | /* Retrieve one instance's rate counter values of the given names. */ |
| 221 | function f_counter_name_vals_get(IPA_CTRL_PT pt, charstring instance_name, integer instance_nr, |
| 222 | CounterNameVals counternames) |
| 223 | return CounterNameVals { |
| 224 | var CounterNameVals vals; |
| 225 | for (var integer i := 0; i < lengthof(counternames); i := i + 1) { |
| 226 | vals[i] := { |
| 227 | name := counternames[i].name, |
| 228 | val := f_ctrl_get_ratectr_abs(pt, instance_name, instance_nr, counternames[i].name) |
| 229 | }; |
| 230 | } |
| 231 | return vals; |
| 232 | } |
| 233 | |
| 234 | /* Retrieve the first N instances' rate counter values of the given names */ |
| 235 | function f_counter_name_vals_get_n(IPA_CTRL_PT pt, charstring instance_name := "msc", |
| 236 | integer instance_count, CounterNameVals counternames) |
| 237 | return CounterNameValsList { |
| 238 | var CounterNameValsList valslist; |
| 239 | for (var integer instance_nr := 0; instance_nr < instance_count; instance_nr := instance_nr + 1) { |
| 240 | valslist[instance_nr] := f_counter_name_vals_get(pt, instance_name, instance_nr, counternames); |
| 241 | } |
| 242 | log("retrieved rate counters: ", instance_name, ": ", valslist); |
| 243 | return valslist; |
| 244 | } |
| 245 | |
| 246 | /* In a list of one instance's counters, increment a specifically named counter. */ |
Neels Hofmeyr | 9656e92 | 2020-06-30 01:27:01 +0200 | [diff] [blame] | 247 | function f_counter_name_vals_add(inout CounterNameVals vals, charstring countername, integer val := 1) |
| 248 | { |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 249 | for (var integer i := 0; i < lengthof(vals); i := i + 1) { |
| 250 | if (vals[i].name == countername) { |
| 251 | vals[i].val := vals[i].val + val; |
Neels Hofmeyr | 9656e92 | 2020-06-30 01:27:01 +0200 | [diff] [blame] | 252 | return; |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 253 | } |
| 254 | } |
| 255 | /* name not found, append */ |
| 256 | vals[lengthof(vals)] := { |
| 257 | name := countername, |
| 258 | val := val |
| 259 | } |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 260 | } |
| 261 | |
| 262 | /* In a list of several instances' counters, increment a specific instance's specifically named counter. */ |
Neels Hofmeyr | 9656e92 | 2020-06-30 01:27:01 +0200 | [diff] [blame] | 263 | function f_counter_name_vals_list_add(inout CounterNameValsList vals, integer instance_nr, |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 264 | charstring countername, integer val := 1) |
Neels Hofmeyr | 9656e92 | 2020-06-30 01:27:01 +0200 | [diff] [blame] | 265 | { |
| 266 | f_counter_name_vals_add(vals[instance_nr], countername, val); |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 267 | } |
| 268 | |
| 269 | /* For a specific instance, call f_counter_name_vals_get() and compare with expected counter values. |
| 270 | * Set the test verdict accordingly. */ |
| 271 | function f_counter_name_vals_expect(IPA_CTRL_PT pt, charstring instance_name, integer instance_nr, |
| 272 | CounterNameVals vals) { |
Pau Espin Pedrol | 40a0295 | 2021-02-05 12:18:06 +0100 | [diff] [blame] | 273 | var CounterNameVals last := f_counter_name_vals_get(pt, instance_name, instance_nr, vals); |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 274 | for (var integer i := 0; i < lengthof(vals); i := i + 1) { |
Pau Espin Pedrol | 40a0295 | 2021-02-05 12:18:06 +0100 | [diff] [blame] | 275 | if (last[i].name != vals[i].name) { |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 276 | setverdict(fail, "Internal error"); |
| 277 | } |
Pau Espin Pedrol | 40a0295 | 2021-02-05 12:18:06 +0100 | [diff] [blame] | 278 | if (last[i].val != vals[i].val) { |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 279 | setverdict(fail, "Rate counter mismatch: ", instance_name, " ", instance_nr, |
Pau Espin Pedrol | 40a0295 | 2021-02-05 12:18:06 +0100 | [diff] [blame] | 280 | " ", vals[i].name, " is at ", last[i].val, " but expected ", vals[i].val); |
Neels Hofmeyr | 7418059 | 2020-06-20 21:40:52 +0200 | [diff] [blame] | 281 | } |
| 282 | } |
| 283 | setverdict(pass); |
| 284 | } |
| 285 | |
| 286 | /* For N instances, call f_counter_name_vals_get() and compare with expected counter values. |
| 287 | * Set the test verdict accordingly. The number of instances is given by lengthof(valslist). */ |
| 288 | function f_counter_name_vals_expect_n(IPA_CTRL_PT pt, charstring instance_name, CounterNameValsList valslist) { |
| 289 | for (var integer instance_nr := 0; instance_nr < lengthof(valslist); instance_nr := instance_nr + 1) { |
| 290 | f_counter_name_vals_expect(pt, instance_name, instance_nr, valslist[instance_nr]); |
| 291 | } |
| 292 | } |
| 293 | |
Neels Hofmeyr | bd94fe7 | 2021-10-26 22:04:53 +0200 | [diff] [blame] | 294 | /* For a specific instance, call f_counter_name_vals_get() and indentify counters that have changed with respect |
| 295 | * to 'vals'. Return list of the changed counter names in the order they appear in 'vals'. */ |
| 296 | function f_counter_name_vals_get_changed(IPA_CTRL_PT pt, charstring instance_name, integer instance_nr, |
| 297 | CounterNameVals vals) |
| 298 | return charstring_list { |
| 299 | var charstring_list changed := {}; |
| 300 | var CounterNameVals last := f_counter_name_vals_get(pt, instance_name, instance_nr, vals); |
| 301 | for (var integer i := 0; i < lengthof(vals); i := i + 1) { |
| 302 | if (last[i].name != vals[i].name) { |
| 303 | setverdict(fail, "Internal error"); |
| 304 | } |
| 305 | if (last[i].val != vals[i].val) { |
| 306 | changed := changed & { instance_name & "." & int2str(instance_nr) & "." & vals[i].name }; |
| 307 | } |
| 308 | } |
| 309 | return changed; |
| 310 | } |
| 311 | |
| 312 | /* For N instances, call f_counter_name_vals_get() and indentify counters that have changed with respect |
| 313 | * to 'vals'. Return list of the changed counter names in the order they appear in 'vals'. */ |
| 314 | function f_counter_name_vals_get_changed_n(IPA_CTRL_PT pt, charstring instance_name, CounterNameValsList valslist) |
| 315 | return charstring_list { |
| 316 | var charstring_list changed := {}; |
| 317 | for (var integer instance_nr := 0; instance_nr < lengthof(valslist); instance_nr := instance_nr + 1) { |
| 318 | changed := changed & f_counter_name_vals_get_changed(pt, instance_name, instance_nr, valslist[instance_nr]); |
| 319 | } |
| 320 | return changed; |
| 321 | } |
| 322 | |
| 323 | function f_counter_name_vals_expect_changed(IPA_CTRL_PT pt, charstring instance_name, CounterNameValsList valslist, |
| 324 | charstring_list expect_changed) { |
| 325 | var charstring_list changed := f_counter_name_vals_get_changed_n(pt, instance_name, valslist); |
| 326 | f_counter_name_vals_expect_changed_list(changed, expect_changed); |
| 327 | } |
| 328 | |
| 329 | function f_counter_name_vals_expect_changed_list(charstring_list got_list, charstring_list expect_list) { |
| 330 | var charstring unexpected_change := ""; |
| 331 | for (var integer i := 0; i < lengthof(got_list); i := i + 1) { |
| 332 | var boolean found := false; |
| 333 | for (var integer j := 0; j < lengthof(expect_list); j := j + 1) { |
| 334 | if (got_list[i] == expect_list[j]) { |
| 335 | found := true; |
| 336 | break; |
| 337 | } |
| 338 | } |
| 339 | if (not found) { |
| 340 | unexpected_change := unexpected_change & " " & got_list[i]; |
| 341 | } |
| 342 | } |
| 343 | var charstring missing_change := ""; |
| 344 | for (var integer i := 0; i < lengthof(expect_list); i := i + 1) { |
| 345 | var boolean found := false; |
| 346 | for (var integer j := 0; j < lengthof(got_list); j := j + 1) { |
| 347 | if (expect_list[i] == got_list[j]) { |
| 348 | found := true; |
| 349 | break; |
| 350 | } |
| 351 | } |
| 352 | if (not found) { |
| 353 | missing_change := missing_change & " " & expect_list[i]; |
| 354 | } |
| 355 | } |
| 356 | var charstring diff := ""; |
| 357 | if (lengthof(unexpected_change) > 0) { |
| 358 | diff := diff & " Unexpected changes in" & unexpected_change & ";"; |
| 359 | } |
| 360 | if (lengthof(missing_change) > 0) { |
| 361 | diff := diff & " Missing changes in" & missing_change & ";"; |
| 362 | } |
| 363 | if (lengthof(diff) > 0) { |
| 364 | log("ERROR\nExpected: ", expect_list, "\nGot: ", got_list); |
| 365 | setverdict(fail, "Rate counters did not change as expected:" & diff); |
| 366 | } else { |
| 367 | setverdict(pass); |
| 368 | } |
| 369 | } |
| 370 | |
Harald Welte | 03c0e56 | 2017-12-09 02:55:12 +0100 | [diff] [blame] | 371 | } |