<p>neels <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/21522">View Change</a></p><div style="white-space:pre-wrap"></div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">osmo_ctrl.py: add RateCounters<br><br>First user will be the upcoming handover_2G/handover.py test in<br>I0b2671304165a1aaae2b386af46fbd8b098e3bd8.<br><br>Change-Id: Id799b3bb81eb9c04d13c26ff611e40363920300e<br>---<br>A selftest/rate_ctrs_test/_prep.py<br>A selftest/rate_ctrs_test/rate_ctrs_test.err<br>A selftest/rate_ctrs_test/rate_ctrs_test.ok<br>A selftest/rate_ctrs_test/rate_ctrs_test.py<br>M src/osmo_gsm_tester/obj/osmo_ctrl.py<br>5 files changed, 453 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/selftest/rate_ctrs_test/_prep.py b/selftest/rate_ctrs_test/_prep.py</span><br><span>new file mode 100644</span><br><span>index 0000000..773f190</span><br><span>--- /dev/null</span><br><span>+++ b/selftest/rate_ctrs_test/_prep.py</span><br><span>@@ -0,0 +1,16 @@</span><br><span style="color: hsl(120, 100%, 40%);">+import sys, os</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+script_dir = sys.path[0]</span><br><span style="color: hsl(120, 100%, 40%);">+top_dir = os.path.join(script_dir, '..', '..')</span><br><span style="color: hsl(120, 100%, 40%);">+src_dir = os.path.join(top_dir, 'src')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# to find the osmo_gsm_tester py module</span><br><span style="color: hsl(120, 100%, 40%);">+sys.path.append(src_dir)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from osmo_gsm_tester.core import log</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+log.TestsTarget()</span><br><span style="color: hsl(120, 100%, 40%);">+log.set_all_levels(log.L_DBG)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+if '-v' in sys.argv:</span><br><span style="color: hsl(120, 100%, 40%);">+    log.style_change(trace=True)</span><br><span>diff --git a/selftest/rate_ctrs_test/rate_ctrs_test.err b/selftest/rate_ctrs_test/rate_ctrs_test.err</span><br><span>new file mode 100644</span><br><span>index 0000000..e69de29</span><br><span>--- /dev/null</span><br><span>+++ b/selftest/rate_ctrs_test/rate_ctrs_test.err</span><br><span>diff --git a/selftest/rate_ctrs_test/rate_ctrs_test.ok b/selftest/rate_ctrs_test/rate_ctrs_test.ok</span><br><span>new file mode 100644</span><br><span>index 0000000..489f58f</span><br><span>--- /dev/null</span><br><span>+++ b/selftest/rate_ctrs_test/rate_ctrs_test.ok</span><br><span>@@ -0,0 +1,155 @@</span><br><span style="color: hsl(120, 100%, 40%);">+- empty RateCounters()</span><br><span style="color: hsl(120, 100%, 40%);">+| </span><br><span style="color: hsl(120, 100%, 40%);">+- initialized RateCounters, single var</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 0</span><br><span style="color: hsl(120, 100%, 40%);">+- incremented inst.var</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 1</span><br><span style="color: hsl(120, 100%, 40%);">+- incremented inst.var again</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 2</span><br><span style="color: hsl(120, 100%, 40%);">+- incremented inst.var by 5</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 7</span><br><span style="color: hsl(120, 100%, 40%);">+- initialized RateCounters, two vars</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 0</span><br><span style="color: hsl(120, 100%, 40%);">+- incremented foo and var</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 1</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 1</span><br><span style="color: hsl(120, 100%, 40%);">+- incremented var again</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 1</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 2</span><br><span style="color: hsl(120, 100%, 40%);">+- incremented foo by 5</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 6</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 2</span><br><span style="color: hsl(120, 100%, 40%);">+- initialized RateCounters, two vars, three instances</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 0</span><br><span style="color: hsl(120, 100%, 40%);">+- incremented foo and var on separate instances</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 1</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 1</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 0</span><br><span style="color: hsl(120, 100%, 40%);">+- incremented var on instance 2</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 1</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 1</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 1</span><br><span style="color: hsl(120, 100%, 40%);">+- incremented foo by 5 on instances 1,2</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 1</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 5</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 1</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 5</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 1</span><br><span style="color: hsl(120, 100%, 40%);">+- copy</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 1</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 0</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 5</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 1</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 5</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 1</span><br><span style="color: hsl(120, 100%, 40%);">+- increment two vars by 100 on all three instances</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 100</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 105</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 105</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 101</span><br><span style="color: hsl(120, 100%, 40%);">+- subtract original copy</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 100</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 100</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 100</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 100</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 100</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 100</span><br><span style="color: hsl(120, 100%, 40%);">+- add original copy</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 100</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 105</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 105</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 101</span><br><span style="color: hsl(120, 100%, 40%);">+- increment types per_hour, per_day by 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 100</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 105</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 105</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.0.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.0.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.0.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.1.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.1.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.1.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.2.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.2.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.2.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.0.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.0.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.0.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.1.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.1.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.1.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.2.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.2.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.2.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+- copy</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 100</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 105</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 105</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.0.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.0.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.0.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.1.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.1.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.1.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.2.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.2.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.2.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.0.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.0.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.0.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.1.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.1.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.1.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.2.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.2.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.2.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+- match?  True</span><br><span style="color: hsl(120, 100%, 40%);">+- increment foo</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.foo = 102</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.0.var = 100</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.foo = 105</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.1.var = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.foo = 105</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.abs.inst.2.var = 101</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.0.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.0.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.0.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.1.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.1.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.1.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.2.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.2.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_day.inst.2.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.0.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.0.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.0.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.1.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.1.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.1.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.2.foo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.2.moo = 23</span><br><span style="color: hsl(120, 100%, 40%);">+| rate_ctr.per_hour.inst.2.var = 23</span><br><span style="color: hsl(120, 100%, 40%);">+- match?  False</span><br><span>diff --git a/selftest/rate_ctrs_test/rate_ctrs_test.py b/selftest/rate_ctrs_test/rate_ctrs_test.py</span><br><span>new file mode 100755</span><br><span>index 0000000..935bd9d</span><br><span>--- /dev/null</span><br><span>+++ b/selftest/rate_ctrs_test/rate_ctrs_test.py</span><br><span>@@ -0,0 +1,56 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#!/usr/bin/env python3</span><br><span style="color: hsl(120, 100%, 40%);">+import _prep</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from osmo_gsm_tester.obj.osmo_ctrl import *</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+rc = RateCounters()</span><br><span style="color: hsl(120, 100%, 40%);">+print('- empty RateCounters()' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+rc = RateCounters('inst', 'var')</span><br><span style="color: hsl(120, 100%, 40%);">+print('- initialized RateCounters, single var' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', 'var')</span><br><span style="color: hsl(120, 100%, 40%);">+print('- incremented inst.var' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', 'var')</span><br><span style="color: hsl(120, 100%, 40%);">+print('- incremented inst.var again' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', 'var', 5)</span><br><span style="color: hsl(120, 100%, 40%);">+print('- incremented inst.var by 5' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+rc = RateCounters('inst', ('foo', 'var'))</span><br><span style="color: hsl(120, 100%, 40%);">+print('- initialized RateCounters, two vars' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', ('foo', 'var'))</span><br><span style="color: hsl(120, 100%, 40%);">+print('- incremented foo and var' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', 'var')</span><br><span style="color: hsl(120, 100%, 40%);">+print('- incremented var again' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', 'foo', 5)</span><br><span style="color: hsl(120, 100%, 40%);">+print('- incremented foo by 5' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+rc = RateCounters('inst', ('foo', 'var'), instances=range(3))</span><br><span style="color: hsl(120, 100%, 40%);">+print('- initialized RateCounters, two vars, three instances' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', 'foo', instances=0)</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', 'var', instances=1)</span><br><span style="color: hsl(120, 100%, 40%);">+print('- incremented foo and var on separate instances' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', 'var', instances=2)</span><br><span style="color: hsl(120, 100%, 40%);">+print('- incremented var on instance 2' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', 'foo', 5, instances=(1,2))</span><br><span style="color: hsl(120, 100%, 40%);">+print('- incremented foo by 5 on instances 1,2' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+rc_rel = rc.copy()</span><br><span style="color: hsl(120, 100%, 40%);">+print('- copy' + rc_rel.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', ('foo', 'var'), 100, instances=range(3))</span><br><span style="color: hsl(120, 100%, 40%);">+print('- increment two vars by 100 on all three instances' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.subtract(rc_rel)</span><br><span style="color: hsl(120, 100%, 40%);">+print('- subtract original copy' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+rc.add(rc_rel)</span><br><span style="color: hsl(120, 100%, 40%);">+print('- add original copy' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+rc.inc('inst', ('foo', 'var', 'moo'), 23, instances=range(3), kinds=('per_hour', 'per_day'))</span><br><span style="color: hsl(120, 100%, 40%);">+print('- increment types per_hour, per_day by 23' + rc.str())</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+rc2 = rc.copy()</span><br><span style="color: hsl(120, 100%, 40%);">+print('- copy' + rc2.str())</span><br><span style="color: hsl(120, 100%, 40%);">+print('- match? ', (rc == rc2))</span><br><span style="color: hsl(120, 100%, 40%);">+rc2.inc('inst', 'foo')</span><br><span style="color: hsl(120, 100%, 40%);">+print('- increment foo' + rc2.str())</span><br><span style="color: hsl(120, 100%, 40%);">+print('- match? ', (rc == rc2))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# vim: expandtab tabstop=4 shiftwidth=4</span><br><span>diff --git a/src/osmo_gsm_tester/obj/osmo_ctrl.py b/src/osmo_gsm_tester/obj/osmo_ctrl.py</span><br><span>index 644025f..6c4ac87 100644</span><br><span>--- a/src/osmo_gsm_tester/obj/osmo_ctrl.py</span><br><span>+++ b/src/osmo_gsm_tester/obj/osmo_ctrl.py</span><br><span>@@ -238,4 +238,230 @@</span><br><span>     def __exit__(self, *exc_info):</span><br><span>         self.disconnect()</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+class RateCountersExn(log.Error):</span><br><span style="color: hsl(120, 100%, 40%);">+    pass</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class RateCounters(dict):</span><br><span style="color: hsl(120, 100%, 40%);">+    '''Usage example:</span><br><span style="color: hsl(120, 100%, 40%);">+        counter_names = (</span><br><span style="color: hsl(120, 100%, 40%);">+                'handover:completed',</span><br><span style="color: hsl(120, 100%, 40%);">+                'handover:stopped',</span><br><span style="color: hsl(120, 100%, 40%);">+                'handover:no_channel',</span><br><span style="color: hsl(120, 100%, 40%);">+                'handover:timeout',</span><br><span style="color: hsl(120, 100%, 40%);">+                'handover:failed',</span><br><span style="color: hsl(120, 100%, 40%);">+                'handover:error',</span><br><span style="color: hsl(120, 100%, 40%);">+                )</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        # initialize the listing of CTRL vars of the counters to watch.</span><br><span style="color: hsl(120, 100%, 40%);">+        # First on the 'bsc' node:</span><br><span style="color: hsl(120, 100%, 40%);">+        #   rate_ctr.abs.bsc.0.handover:completed</span><br><span style="color: hsl(120, 100%, 40%);">+        #   rate_ctr.abs.bsc.0.handover:stopped</span><br><span style="color: hsl(120, 100%, 40%);">+        #   ...</span><br><span style="color: hsl(120, 100%, 40%);">+        counters = RateCounters('bsc', counter_names, from_ctrl=bsc.ctrl)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        # And also add counters for two 'bts' instances:</span><br><span style="color: hsl(120, 100%, 40%);">+        #   rate_ctr.abs.bts.0.handover:completed</span><br><span style="color: hsl(120, 100%, 40%);">+        #   rate_ctr.abs.bts.0.handover:stopped</span><br><span style="color: hsl(120, 100%, 40%);">+        #   ...</span><br><span style="color: hsl(120, 100%, 40%);">+        #   rate_ctr.abs.bts.1.handover:completed</span><br><span style="color: hsl(120, 100%, 40%);">+        #   ...</span><br><span style="color: hsl(120, 100%, 40%);">+        counters.add(RateCounters('bts', counter_names, instances=(0, 1)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        # read initial counter values, from the bsc_ctrl, as set in</span><br><span style="color: hsl(120, 100%, 40%);">+        # counters.from_ctrl in the RateCounters() constructor above.</span><br><span style="color: hsl(120, 100%, 40%);">+        counters.read()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        # Do some actions that should increment counters in the SUT</span><br><span style="color: hsl(120, 100%, 40%);">+        do_a_handover()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if approach_without_wait:</span><br><span style="color: hsl(120, 100%, 40%);">+            # increment the counters as expected</span><br><span style="color: hsl(120, 100%, 40%);">+            counters.inc('bts', 'handover:completed')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            # read counters from CTRL again, and fail if they differ</span><br><span style="color: hsl(120, 100%, 40%);">+            counters.verify()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if approach_with_wait:</span><br><span style="color: hsl(120, 100%, 40%);">+            # you can wait for counters to change. counters.changed() does not</span><br><span style="color: hsl(120, 100%, 40%);">+            # modify counters' values, just reads values from CTRL and stores</span><br><span style="color: hsl(120, 100%, 40%);">+            # the changes in counters.diff.</span><br><span style="color: hsl(120, 100%, 40%);">+            wait(counters.changed, timeout=20)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            # log which counters changed by how much, found in counters.diff</span><br><span style="color: hsl(120, 100%, 40%);">+            # after each counters.changed() call:</span><br><span style="color: hsl(120, 100%, 40%);">+            print(counters.diff.str(skip_zero_vals=True))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            if check_all_vals:</span><br><span style="color: hsl(120, 100%, 40%);">+                # Assert all values:</span><br><span style="color: hsl(120, 100%, 40%);">+                expected_diff = counters.copy().clear()</span><br><span style="color: hsl(120, 100%, 40%);">+                expected_diff.inc('bts', 'handover:completed', instances=(0, 1))</span><br><span style="color: hsl(120, 100%, 40%);">+                counters.diff.expect(expected_diff)</span><br><span style="color: hsl(120, 100%, 40%);">+            else:</span><br><span style="color: hsl(120, 100%, 40%);">+                # Assert only some specific counters:</span><br><span style="color: hsl(120, 100%, 40%);">+                expected_diff = RateCounters()</span><br><span style="color: hsl(120, 100%, 40%);">+                expected_diff.inc('bts', 'handover:completed', instances=(0, 1))</span><br><span style="color: hsl(120, 100%, 40%);">+                counters.diff.expect(expected_diff)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            # update counters to the last read values if desired</span><br><span style="color: hsl(120, 100%, 40%);">+            counters.add(counters.diff)</span><br><span style="color: hsl(120, 100%, 40%);">+    '''</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, instance_names=(), counter_names=(), instances=0, kinds='abs', init_val=0, from_ctrl=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        def init_cb(var):</span><br><span style="color: hsl(120, 100%, 40%);">+            self[var] = init_val</span><br><span style="color: hsl(120, 100%, 40%);">+        RateCounters.for_each(init_cb, instance_names, counter_names, instances, kinds, results=False)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.from_ctrl = from_ctrl</span><br><span style="color: hsl(120, 100%, 40%);">+        self.diff = None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    @staticmethod</span><br><span style="color: hsl(120, 100%, 40%);">+    def for_each(callback_func, instance_names, counter_names, instances=0, kinds='abs', results=True):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Call callback_func for a set of rate counter var names, mostly</span><br><span style="color: hsl(120, 100%, 40%);">+           called by more convenient functions. See inc() for a comprehensive</span><br><span style="color: hsl(120, 100%, 40%);">+           explanation.</span><br><span style="color: hsl(120, 100%, 40%);">+        '''</span><br><span style="color: hsl(120, 100%, 40%);">+        if type(instance_names) is str:</span><br><span style="color: hsl(120, 100%, 40%);">+            instance_names = (instance_names, )</span><br><span style="color: hsl(120, 100%, 40%);">+        if type(counter_names) is str:</span><br><span style="color: hsl(120, 100%, 40%);">+            counter_names = (counter_names, )</span><br><span style="color: hsl(120, 100%, 40%);">+        if type(kinds) is str:</span><br><span style="color: hsl(120, 100%, 40%);">+            kinds = (kinds, )</span><br><span style="color: hsl(120, 100%, 40%);">+        if type(instances) is int:</span><br><span style="color: hsl(120, 100%, 40%);">+            instances = (instances, )</span><br><span style="color: hsl(120, 100%, 40%);">+        if results is True:</span><br><span style="color: hsl(120, 100%, 40%);">+            results = RateCounters()</span><br><span style="color: hsl(120, 100%, 40%);">+        elif results is False:</span><br><span style="color: hsl(120, 100%, 40%);">+            results = None</span><br><span style="color: hsl(120, 100%, 40%);">+        for instance_name in instance_names:</span><br><span style="color: hsl(120, 100%, 40%);">+            for instance_nr in instances:</span><br><span style="color: hsl(120, 100%, 40%);">+                for counter_name in counter_names:</span><br><span style="color: hsl(120, 100%, 40%);">+                    for kind in kinds:</span><br><span style="color: hsl(120, 100%, 40%);">+                        var = 'rate_ctr.{kind}.{instance_name}.{instance_nr}.{counter_name}'.format(**locals())</span><br><span style="color: hsl(120, 100%, 40%);">+                        result = callback_func(var)</span><br><span style="color: hsl(120, 100%, 40%);">+                        if results is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+                            results[var] = result</span><br><span style="color: hsl(120, 100%, 40%);">+        return results</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.str(', ', '')</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def str(self, sep='\n| ', prefix='\n| ', vals=None, skip_zero_vals=False):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''The 'vals' arg is useful to print a plain dict() of counter values like a RateCounters class.</span><br><span style="color: hsl(120, 100%, 40%);">+           By default print self.'''</span><br><span style="color: hsl(120, 100%, 40%);">+        if vals is None:</span><br><span style="color: hsl(120, 100%, 40%);">+            vals = self</span><br><span style="color: hsl(120, 100%, 40%);">+        return prefix + sep.join('%s = %d' % (var, val) for var, val in sorted(vals.items())</span><br><span style="color: hsl(120, 100%, 40%);">+                                 if (not skip_zero_vals) or (val != 0))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def inc(self, instance_names, counter_names, inc=1, instances=0, kinds='abs'):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Increment a set of counters.</span><br><span style="color: hsl(120, 100%, 40%);">+           inc('xyz', 'val')             --> rate_ctr.abs.xyz.0.val += 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           inc('xyz', ('foo', 'bar'))    --> rate_ctr.abs.xyz.0.foo += 1</span><br><span style="color: hsl(120, 100%, 40%);">+                                             rate_ctr.abs.xyz.0.bar += 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           inc(('xyz', 'pqr'), 'val')    --> rate_ctr.abs.xyz.0.val += 1</span><br><span style="color: hsl(120, 100%, 40%);">+                                             rate_ctr.abs.pqr.0.val += 1</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           inc('xyz', 'val', instances=range(3))</span><br><span style="color: hsl(120, 100%, 40%);">+                                         --> rate_ctr.abs.xyz.0.val += 1</span><br><span style="color: hsl(120, 100%, 40%);">+                                             rate_ctr.abs.xyz.1.val += 1</span><br><span style="color: hsl(120, 100%, 40%);">+                                             rate_ctr.abs.xyz.2.val += 1</span><br><span style="color: hsl(120, 100%, 40%);">+        '''</span><br><span style="color: hsl(120, 100%, 40%);">+        def inc_cb(var):</span><br><span style="color: hsl(120, 100%, 40%);">+            val = self.get(var, 0)</span><br><span style="color: hsl(120, 100%, 40%);">+            val += inc</span><br><span style="color: hsl(120, 100%, 40%);">+            self[var] = val</span><br><span style="color: hsl(120, 100%, 40%);">+            return val</span><br><span style="color: hsl(120, 100%, 40%);">+        RateCounters.for_each(inc_cb, instance_names, counter_names, instances, kinds, results=False)</span><br><span style="color: hsl(120, 100%, 40%);">+        return self</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def add(self, rate_counters):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Add the given values up to the values in self.</span><br><span style="color: hsl(120, 100%, 40%);">+           rate_counters can be a RateCounters instance or a plain dict of CTRL</span><br><span style="color: hsl(120, 100%, 40%);">+           var as key and counter integer as value.</span><br><span style="color: hsl(120, 100%, 40%);">+        '''</span><br><span style="color: hsl(120, 100%, 40%);">+        for var, add_val in rate_counters.items():</span><br><span style="color: hsl(120, 100%, 40%);">+            val = self.get(var, 0)</span><br><span style="color: hsl(120, 100%, 40%);">+            val += add_val</span><br><span style="color: hsl(120, 100%, 40%);">+            self[var] = val</span><br><span style="color: hsl(120, 100%, 40%);">+        return self</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def subtract(self, rate_counters):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Same as add(), but subtract values from self instead.</span><br><span style="color: hsl(120, 100%, 40%);">+           Useful to verify counters relative to an arbitrary reference.'''</span><br><span style="color: hsl(120, 100%, 40%);">+        for var, subtract_val in rate_counters.items():</span><br><span style="color: hsl(120, 100%, 40%);">+            val = self.get(var, 0)</span><br><span style="color: hsl(120, 100%, 40%);">+            val -= subtract_val</span><br><span style="color: hsl(120, 100%, 40%);">+            self[var] = val</span><br><span style="color: hsl(120, 100%, 40%);">+        return self</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def clear(self, val=0):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Set all counts to 0 (or a specific value)'''</span><br><span style="color: hsl(120, 100%, 40%);">+        for var in self.keys():</span><br><span style="color: hsl(120, 100%, 40%);">+            self[var] = val</span><br><span style="color: hsl(120, 100%, 40%);">+        return self</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def copy(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Return a copy of all keys and values stored in self.'''</span><br><span style="color: hsl(120, 100%, 40%);">+        cpy = RateCounters(from_ctrl = self.from_ctrl)</span><br><span style="color: hsl(120, 100%, 40%);">+        cpy.update(self)</span><br><span style="color: hsl(120, 100%, 40%);">+        return cpy</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def read(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Read all counters from the CTRL connection passed to RateCounters(from_ctrl=x).</span><br><span style="color: hsl(120, 100%, 40%);">+           The CTRL must be connected, e.g.</span><br><span style="color: hsl(120, 100%, 40%);">+           with bsc.ctrl() as ctrl:</span><br><span style="color: hsl(120, 100%, 40%);">+               counters = RateCounters(ctrl)</span><br><span style="color: hsl(120, 100%, 40%);">+               counters.read()</span><br><span style="color: hsl(120, 100%, 40%);">+        '''</span><br><span style="color: hsl(120, 100%, 40%);">+        for var in self.keys():</span><br><span style="color: hsl(120, 100%, 40%);">+            self[var] = self.from_ctrl.get_int_var(var)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.from_ctrl.dbg('Read counters:', self.str())</span><br><span style="color: hsl(120, 100%, 40%);">+        return self</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def verify(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Read counters from CTRL and assert that they match the current counts'''</span><br><span style="color: hsl(120, 100%, 40%);">+        got_vals = self.copy()</span><br><span style="color: hsl(120, 100%, 40%);">+        got_vals.read()</span><br><span style="color: hsl(120, 100%, 40%);">+        got_vals.expect(self)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def changed(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Read counters from CTRL, and return True if anyone is different now.</span><br><span style="color: hsl(120, 100%, 40%);">+           Store the difference in counts in self.diff (replace self.diff for</span><br><span style="color: hsl(120, 100%, 40%);">+           each changed() call). The counts in self are never modified.'''</span><br><span style="color: hsl(120, 100%, 40%);">+        self.diff = None</span><br><span style="color: hsl(120, 100%, 40%);">+        got_vals = self.copy()</span><br><span style="color: hsl(120, 100%, 40%);">+        got_vals.read()</span><br><span style="color: hsl(120, 100%, 40%);">+        if self != got_vals:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.diff = got_vals</span><br><span style="color: hsl(120, 100%, 40%);">+            self.diff.subtract(self)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.from_ctrl.dbg('Changed counters:', self.diff.str(skip_zero_vals=True))</span><br><span style="color: hsl(120, 100%, 40%);">+            return True</span><br><span style="color: hsl(120, 100%, 40%);">+        return False</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def expect(self, expect_vals):</span><br><span style="color: hsl(120, 100%, 40%);">+        '''Iterate expect_vals and fail if any counter value differs from self.</span><br><span style="color: hsl(120, 100%, 40%);">+           expect_vals can be a RateCounters instance or a plain dict of CTRL</span><br><span style="color: hsl(120, 100%, 40%);">+           var as key and counter integer as value.</span><br><span style="color: hsl(120, 100%, 40%);">+        '''</span><br><span style="color: hsl(120, 100%, 40%);">+        ok = 0</span><br><span style="color: hsl(120, 100%, 40%);">+        errs = []</span><br><span style="color: hsl(120, 100%, 40%);">+        for var, expect_val in expect_vals.items():</span><br><span style="color: hsl(120, 100%, 40%);">+            got_val = self.get(var)</span><br><span style="color: hsl(120, 100%, 40%);">+            if got_val is None:</span><br><span style="color: hsl(120, 100%, 40%);">+                errs.append('expected {var} == {expect_val}, but no such value found'.format(**locals()))</span><br><span style="color: hsl(120, 100%, 40%);">+                continue</span><br><span style="color: hsl(120, 100%, 40%);">+            if got_val != expect_val:</span><br><span style="color: hsl(120, 100%, 40%);">+                errs.append('expected {var} == {expect_val}, but is {got_val}'.format(**locals()))</span><br><span style="color: hsl(120, 100%, 40%);">+                continue</span><br><span style="color: hsl(120, 100%, 40%);">+            ok += 1</span><br><span style="color: hsl(120, 100%, 40%);">+        if errs:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.from_ctrl.dbg('Expected rate counters:', self.str(vals=expect_vals))</span><br><span style="color: hsl(120, 100%, 40%);">+            self.from_ctrl.dbg('Got rate counters:', self.str())</span><br><span style="color: hsl(120, 100%, 40%);">+            raise RateCountersExn('%d of %d rate counters mismatch:' % (len(errs), len(errs) + ok), '\n| ' + '\n| '.join(errs))</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.from_ctrl.log('Verified %d rate counters' % ok)</span><br><span style="color: hsl(120, 100%, 40%);">+            self.from_ctrl.dbg('Verified %d rate counters:' % ok, expect_vals)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # vim: expandtab tabstop=4 shiftwidth=4</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/21522">change 21522</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/osmo-gsm-tester/+/21522"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-gsm-tester </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Id799b3bb81eb9c04d13c26ff611e40363920300e </div>
<div style="display:none"> Gerrit-Change-Number: 21522 </div>
<div style="display:none"> Gerrit-PatchSet: 4 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>