diff --git a/alice_main.py b/alice_main.py index 1462bbd..d3ae6e8 100644 --- a/alice_main.py +++ b/alice_main.py @@ -26,8 +26,11 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import sys, getopt +import math from time import sleep +from .contrib.objgraph import show_most_common_types + from sippy.MsgBody import MsgBody from sippy.SipLogger import SipLogger from sippy.SipConf import SipConf @@ -60,7 +63,7 @@ def main_func(): global_config = {} try: - opts, args = getopt.getopt(sys.argv[1:], 'Dp:t:l:P:T:46n:N:w:') + opts, args = getopt.getopt(sys.argv[1:], 'Dp:t:l:P:T:46n:N:w:cC:') except getopt.GetoptError: usage(global_config) @@ -111,6 +114,16 @@ def main_func(): continue if o == '-D': dry_run = True + if o == '-c': + tcfg.continuous = True + continue + if o == '-C': + if not a.startswith('='): + cpsval = float(a) + tcfg.cps = lambda now: cpsval + else: + cpsfunc = eval(F'lambda now: {a[1:]}') + tcfg.cps = cpsfunc continue if len(ttype) > 0: tcfg.ttype = tuple(ttype) @@ -129,4 +142,6 @@ def main_func(): if not dry_run: ED2.loop() + show_most_common_types(50) + sys.exit(acore.rval) diff --git a/bob_main.py b/bob_main.py index ae44a23..3ed4272 100644 --- a/bob_main.py +++ b/bob_main.py @@ -68,7 +68,7 @@ def main_func(): global_config = {} try: - opts, args = getopt.getopt(sys.argv[1:], 'p:l:P:T:n:N:w:') + opts, args = getopt.getopt(sys.argv[1:], 'p:l:P:T:n:N:w:c') except getopt.GetoptError: usage(global_config) tcfg = test_config(global_config) @@ -102,6 +102,9 @@ def main_func(): if o == '-w': pre_wait = float(a) continue + if o == '-c': + tcfg.continuous = True + continue bodys = [MsgBody(x) for x in BODIES_ALL] for body in bodys: diff --git a/lib/alice_testcore.py b/lib/alice_testcore.py index a6a125b..857fdd5 100644 --- a/lib/alice_testcore.py +++ b/lib/alice_testcore.py @@ -23,13 +23,18 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -##import sys +import sys +from signal import SIGINT ##sys.path.insert(0, 'dist/b2bua') from sippy.SipTransactionManager import SipTransactionManager -from sippy.Time.Timeout import Timeout +from sippy.Signal import Signal +from sippy.Time.Timeout import Timeout, TimeoutAbsMono from sippy.Core.EventDispatcher import ED2 -from random import shuffle +from sippy.Math.recfilter import recfilter +from random import shuffle, choice + +from .spinor import spinor from ..test_cases.t1 import a_test1 from ..test_cases.t2 import a_test2 @@ -71,9 +76,15 @@ class a_cfg(object): def __init__(self, test_class): self.test_class = test_class + def init_test(self): + return self.test_class(self.tcfg) + class a_test(object): nsubtests_running = 0 rval = 1 + tcfg = None + last_ulen = 0 + nextr = None def __init__(self, tcfg): tcfg.global_config['_sip_tm'] = SipTransactionManager(tcfg.global_config, self.recvRequest) @@ -97,17 +108,74 @@ def __init__(self, tcfg): atype = ttype[0] cli += '_ipv%s' % atype[-1] subtest_cfg.tcfg = tcfg.gen_tccfg(atype, self.subtest_done, cli) - print('tcfg.gen_tccfg(%s, self.subtest_done, %s)' % (atype, cli)) + #print('tcfg.gen_tccfg(%s, self.subtest_done, %s)' % (atype, cli)) + if tcfg.continuous: + subtest_class.debug_lvl = -1 + subtest_class.godead_timeout = 1.0 i += 1 + atests = [x for x in atests if not x.disabled] + self.tcfg = tcfg + if tcfg.continuous: + self.atests = atests + self.ntime = tcfg.ntime.getCopy() + #Timeout(self.runnext, 0.1, -1) + self.spinor = spinor() + self.rcf = recfilter(0.999, 1.0) + self.update_stats() + Timeout(self.idle_update, 1.0, -1) + self.runnext() + Signal(SIGINT, self.deorbit) + return shuffle(atests) for subtest_cfg in atests: - if subtest_cfg.disabled: - continue - subtest = subtest_cfg.test_class(subtest_cfg.tcfg) + subtest = subtest_cfg.init_test() self.nsubtests_running += 1 self.rval = self.nsubtests_running Timeout(self.timeout, tcfg.test_timeout, 1) + def deorbit(self, signum = None): + self.nextr.cancel() + Timeout(self.slowexit, 0.1, -1) + Timeout(self.timeout, 140.0) + + def slowexit(self): + self.update_stats() + if self.nsubtests_running > 0: + return + sys.stdout.write('\n') + sys.stdout.flush() + ED2.breakLoop() + + def update_stats(self): + if self.nsubtests_running == 0: + self.spinor.idle = True + else: + self.spinor.idle = False + omsg = '\rAlice: %d tests running %s, %f' % (self.nsubtests_running, \ + self.spinor.tick(), self.rcf.lastval) + sys.stdout.write(omsg) + if len(omsg) < self.last_ulen: + pad = ' ' * (self.last_ulen - len(omsg)) + sys.stdout.write(pad) + sys.stdout.flush() + self.last_ulen = len(omsg) + + def idle_update(self): + if self.spinor.idle: + self.update_stats() + + def runnext(self): + subtest_cfg = choice(self.atests) + subtest = subtest_cfg.init_test() + self.nsubtests_running += 1 + if self.tcfg.cps != None: + self.ntime.offset(1.0 / self.tcfg.cps(self.ntime - self.tcfg.ntime)) + self.nextr = TimeoutAbsMono(self.runnext, self.ntime) + else: + self.runnext() + if self.tcfg.continuous: + self.update_stats() + def recvRequest(self, req, sip_t): return (req.genResponse(501, 'Not Implemented'), None, None) @@ -115,7 +183,13 @@ def subtest_done(self, subtest): self.nsubtests_running -= 1 if subtest.rval == 0: self.rval -= 1 - if self.nsubtests_running == 0: + if self.tcfg.continuous: + if subtest.rval == 0: + self.rcf.apply(1.0) + else: + self.rcf.apply(0.0) + self.update_stats() + elif self.nsubtests_running == 0: ED2.breakLoop() def timeout(self): diff --git a/lib/bob_testcore.py b/lib/bob_testcore.py index 4dd5d24..56ccebd 100644 --- a/lib/bob_testcore.py +++ b/lib/bob_testcore.py @@ -23,15 +23,20 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -##import sys +import sys +from signal import SIGUSR1 ##sys.path.insert(0, 'dist/b2bua') from sippy.Time.Timeout import Timeout from sippy.SipTransactionManager import SipTransactionManager +from sippy.Math.recfilter import recfilter +from sippy.Signal import Signal from sippy.Core.EventDispatcher import ED2 + from random import random from .test_config import fillhostport +from .spinor import spinor from ..test_cases.t1 import b_test1, AuthRequired, AuthFailed from ..test_cases.t2 import b_test2 @@ -93,14 +98,35 @@ def transmitData(self, userv, data, address, cachesum = None, \ class b_test(object): rval = 1 nsubtests_running = 0 + nsubtests_completed = 0 tcfg = None + debug = False + spinor = None + last_ulen = 0 + active_subtests = None def __init__(self, tcfg): tcfg.global_config['_sip_tm'] = BobSTM(tcfg.global_config, self.recvRequest) - Timeout(self.timeout, tcfg.test_timeout, 1) + if not tcfg.continuous: + Timeout(self.timeout, tcfg.test_timeout, 1) + else: + self.spinor = spinor() + self.active_subtests = [] + self.rcf = recfilter(0.999, 1.0) + self.update_stats() + Timeout(self.idle_update, 1.0, -1) + Signal(SIGUSR1, self.dumpcalls) self.tcfg = tcfg + def dumpcalls(self): + sys.stderr.write('BOB: Active tests:\n') + for c in self.active_subtests: + sys.stderr.write('\t%d %s\n' % (id(c), str(c))) + sys.stderr.flush() + def recvRequest(self, req, sip_t): + if self.debug: + print('recvRequest') if req.getHFBody('to').getTag() is not None: # Request within dialog, but no such dialog return (req.genResponse(481, 'Call Leg/Transaction Does Not Exist'), None, None) @@ -120,14 +146,15 @@ def recvRequest(self, req, sip_t): atype = 'IP4' tccfg = self.tcfg.gen_tccfg(atype, self.subtest_done) + if self.tcfg.continuous: + tclass.debug_lvl = -1 + tclass.godead_timeout = 1.0 subtest = tclass(tccfg) - self.nsubtests_running += 1 - self.rval += 1 sdp_body = self.tcfg.bodys[0 if random() < 0.5 else 1].getCopy() fillhostport(sdp_body, self.tcfg.portrange, tccfg.atype) try: - return subtest.answer(self.tcfg.global_config, sdp_body, req, sip_t) + rval = subtest.answer(self.tcfg.global_config, sdp_body, req, sip_t) except AuthRequired as ce: resp = req.genResponse(401, 'Unauthorized') resp.appendHeaders(ce.challenges) @@ -139,16 +166,50 @@ def recvRequest(self, req, sip_t): except AuthFailed: resp = req.genResponse(403, 'Auth Failed') return (resp, None, None) + + self.nsubtests_running += 1 + if self.tcfg.continuous: + self.active_subtests.append(subtest) + self.update_stats() + self.rval += 1 + return rval return (req.genResponse(501, 'Not Implemented'), None, None) def timeout(self): ED2.breakLoop() + def update_stats(self): + if self.nsubtests_running == 0: + self.spinor.idle = True + else: + self.spinor.idle = False + omsg = '\rBob: %d tests running %s, %f' % (self.nsubtests_running, \ + self.spinor.tick(), self.rcf.lastval) + sys.stdout.write(omsg) + if len(omsg) < self.last_ulen: + pad = ' ' * (self.last_ulen - len(omsg)) + sys.stdout.write(pad) + sys.stdout.flush() + self.last_ulen = len(omsg) + + def idle_update(self): + if self.spinor.idle: + self.update_stats() + def subtest_done(self, subtest): self.nsubtests_running -= 1 + if self.tcfg.continuous: + self.active_subtests.remove(subtest) + self.nsubtests_completed += 1 if subtest.rval == 0: self.rval -= 1 - if self.nsubtests_running == 0: + if self.tcfg.continuous: + if subtest.rval == 0: + self.rcf.apply(1.0) + else: + self.rcf.apply(0.0) + self.update_stats() + elif self.nsubtests_running == 0: if self.rval == 1: self.rval = 0 ED2.breakLoop() diff --git a/lib/spinor.py b/lib/spinor.py new file mode 100644 index 0000000..8edaa48 --- /dev/null +++ b/lib/spinor.py @@ -0,0 +1,15 @@ +class spinor(object): + i = 0 + busy_states = '-\|/' + idle_states = '.oOo' + idle = False + + def tick(self): + if self.idle: + st = self.idle_states + else: + st = self.busy_states + ri = self.i % len(st) + rv = st[ri] + self.i += 1 + return rv diff --git a/lib/test_config.py b/lib/test_config.py index 9fc66c0..e8e3c15 100644 --- a/lib/test_config.py +++ b/lib/test_config.py @@ -5,6 +5,7 @@ import os from sippy.SdpOrigin import SdpOrigin +from sippy.Time.MonoTime import MonoTime from .GenIPs import genIP from .PortRange import PortRange @@ -85,8 +86,12 @@ class test_config(object): nh_address6 = ('[::1]', 5060) acfg = None bcfg = None + continuous = False + cps = None + ntime = None def gen_tccfg(self, atype, done_cb, cli = None): + self.ntime = MonoTime() if atype == 'IP4': nh_address = self.nh_address4 else: