From 3531a192ae8eeb78c53342454f65327bce4fa57a Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Tue, 28 Mar 2017 14:30:28 +0200 Subject: core implementation code bomb implementing the bulk of the osmo-gsm-tester Change-Id: I53610becbf643ed51b90cfd9debc6992fe211ec9 --- selftest/Makefile | 12 ++ selftest/_prep.py | 17 ++ selftest/all_tests.py | 123 ++++++++++++ selftest/conf/paths.conf | 2 + selftest/conf/resources.conf | 133 +++++++++++++ selftest/config_test.err | 0 selftest/config_test.ok | 95 ++++++++++ selftest/config_test.py | 115 ++++++++++++ selftest/config_test/test.cfg | 59 ++++++ selftest/dbus_test/dbus_server.py | 44 +++++ selftest/dbus_test/ofono_client.py | 57 ++++++ selftest/dbus_test/ofono_client_one_thread.py | 71 +++++++ selftest/lock_test.err | 0 selftest/lock_test.ok | 8 + selftest/lock_test.sh | 10 + selftest/lock_test_help.py | 17 ++ selftest/log_test.err | 0 selftest/log_test.ok | 41 ++++ selftest/log_test.py | 161 ++++++++++++++++ selftest/misc.py | 7 + selftest/process_test.err | 0 selftest/process_test.ok | 33 ++++ selftest/process_test.ok.ign | 7 + selftest/process_test.py | 51 +++++ selftest/process_test/foo.py | 25 +++ selftest/py_import_test/invocation.py | 24 +++ selftest/py_import_test/subdir/script.py | 9 + selftest/py_import_test/support.py | 2 + selftest/real_suite/README.txt | 18 ++ selftest/real_suite/default.conf | 31 +++ selftest/real_suite/env | 4 + selftest/real_suite/paths.conf | 3 + selftest/real_suite/resources.conf | 139 ++++++++++++++ selftest/real_suite/scenarios/trx.conf | 3 + selftest/real_suite/suites/sms/mo_mt_sms.py | 26 +++ selftest/real_suite/suites/sms/suite.conf | 10 + selftest/resource_test.err | 0 selftest/resource_test.ok | 207 +++++++++++++++++++++ selftest/resource_test.py | 97 ++++++++++ selftest/suite_test.err | 0 selftest/suite_test.ok | 40 ++++ selftest/suite_test.py | 34 ++++ selftest/suite_test/empty_dir/.unrelated_file | 0 selftest/suite_test/test_suite/hello_world.py | 5 + selftest/suite_test/test_suite/mo_mt_sms.py | 18 ++ selftest/suite_test/test_suite/mo_sms.py | 20 ++ selftest/suite_test/test_suite/suite.conf | 10 + selftest/suite_test/test_suite/test_error.py | 5 + selftest/template_test.err | 0 selftest/template_test.ok | 149 +++++++++++++++ selftest/template_test.py | 76 ++++++++ selftest/template_test/osmo-nitb.cfg.tmpl | 87 +++++++++ selftest/trial_test.err | 0 selftest/trial_test.ok | 16 ++ selftest/trial_test.ok.ign | 2 + selftest/trial_test.py | 49 +++++ selftest/trial_test/invalid_checksum/checksums.md5 | 3 + selftest/trial_test/invalid_checksum/file1 | 1 + selftest/trial_test/invalid_checksum/file2 | 1 + selftest/trial_test/invalid_checksum/file3 | 1 + selftest/trial_test/missing_file/checksums.md5 | 3 + selftest/trial_test/missing_file/file1 | 1 + selftest/trial_test/missing_file/file3 | 1 + selftest/trial_test/valid_checksums/checksums.md5 | 3 + selftest/trial_test/valid_checksums/file1 | 1 + selftest/trial_test/valid_checksums/file2 | 1 + selftest/trial_test/valid_checksums/file3 | 1 + selftest/util_test.err | 0 selftest/util_test.ok | 5 + selftest/util_test.py | 12 ++ 70 files changed, 2206 insertions(+) create mode 100644 selftest/Makefile create mode 100644 selftest/_prep.py create mode 100755 selftest/all_tests.py create mode 100644 selftest/conf/paths.conf create mode 100644 selftest/conf/resources.conf create mode 100644 selftest/config_test.err create mode 100644 selftest/config_test.ok create mode 100755 selftest/config_test.py create mode 100644 selftest/config_test/test.cfg create mode 100755 selftest/dbus_test/dbus_server.py create mode 100755 selftest/dbus_test/ofono_client.py create mode 100644 selftest/dbus_test/ofono_client_one_thread.py create mode 100644 selftest/lock_test.err create mode 100644 selftest/lock_test.ok create mode 100755 selftest/lock_test.sh create mode 100644 selftest/lock_test_help.py create mode 100644 selftest/log_test.err create mode 100644 selftest/log_test.ok create mode 100755 selftest/log_test.py create mode 100755 selftest/misc.py create mode 100644 selftest/process_test.err create mode 100644 selftest/process_test.ok create mode 100644 selftest/process_test.ok.ign create mode 100755 selftest/process_test.py create mode 100755 selftest/process_test/foo.py create mode 100755 selftest/py_import_test/invocation.py create mode 100644 selftest/py_import_test/subdir/script.py create mode 100644 selftest/py_import_test/support.py create mode 100644 selftest/real_suite/README.txt create mode 100644 selftest/real_suite/default.conf create mode 100644 selftest/real_suite/env create mode 100644 selftest/real_suite/paths.conf create mode 100644 selftest/real_suite/resources.conf create mode 100644 selftest/real_suite/scenarios/trx.conf create mode 100755 selftest/real_suite/suites/sms/mo_mt_sms.py create mode 100644 selftest/real_suite/suites/sms/suite.conf create mode 100644 selftest/resource_test.err create mode 100644 selftest/resource_test.ok create mode 100755 selftest/resource_test.py create mode 100644 selftest/suite_test.err create mode 100644 selftest/suite_test.ok create mode 100755 selftest/suite_test.py create mode 100644 selftest/suite_test/empty_dir/.unrelated_file create mode 100644 selftest/suite_test/test_suite/hello_world.py create mode 100644 selftest/suite_test/test_suite/mo_mt_sms.py create mode 100644 selftest/suite_test/test_suite/mo_sms.py create mode 100644 selftest/suite_test/test_suite/suite.conf create mode 100755 selftest/suite_test/test_suite/test_error.py create mode 100644 selftest/template_test.err create mode 100644 selftest/template_test.ok create mode 100755 selftest/template_test.py create mode 100644 selftest/template_test/osmo-nitb.cfg.tmpl create mode 100644 selftest/trial_test.err create mode 100644 selftest/trial_test.ok create mode 100644 selftest/trial_test.ok.ign create mode 100755 selftest/trial_test.py create mode 100644 selftest/trial_test/invalid_checksum/checksums.md5 create mode 100644 selftest/trial_test/invalid_checksum/file1 create mode 100644 selftest/trial_test/invalid_checksum/file2 create mode 100644 selftest/trial_test/invalid_checksum/file3 create mode 100644 selftest/trial_test/missing_file/checksums.md5 create mode 100644 selftest/trial_test/missing_file/file1 create mode 100644 selftest/trial_test/missing_file/file3 create mode 100644 selftest/trial_test/valid_checksums/checksums.md5 create mode 100644 selftest/trial_test/valid_checksums/file1 create mode 100644 selftest/trial_test/valid_checksums/file2 create mode 100644 selftest/trial_test/valid_checksums/file3 create mode 100644 selftest/util_test.err create mode 100644 selftest/util_test.ok create mode 100755 selftest/util_test.py (limited to 'selftest') diff --git a/selftest/Makefile b/selftest/Makefile new file mode 100644 index 0000000..f0c8c69 --- /dev/null +++ b/selftest/Makefile @@ -0,0 +1,12 @@ +.PHONY: check update + +check: set_pythonpath + ./all_tests.py + +update: + ./all_tests.py -u + +set_pythonpath: + echo "export PYTHONPATH=\"$(PWD)/../src\"" > set_pythonpath + +# vim: noexpandtab tabstop=8 shiftwidth=8 diff --git a/selftest/_prep.py b/selftest/_prep.py new file mode 100644 index 0000000..e89c5a7 --- /dev/null +++ b/selftest/_prep.py @@ -0,0 +1,17 @@ +import sys, os + +script_dir = sys.path[0] +top_dir = os.path.join(script_dir, '..') +src_dir = os.path.join(top_dir, 'src') + +# to find the osmo_gsm_tester py module +sys.path.append(src_dir) + +from osmo_gsm_tester import log + +log.targets = [ log.TestsTarget() ] +log.set_all_levels(log.L_DBG) + +if '-v' in sys.argv: + log.style_change(trace=True) + diff --git a/selftest/all_tests.py b/selftest/all_tests.py new file mode 100755 index 0000000..5c1ce59 --- /dev/null +++ b/selftest/all_tests.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 + +import os +import sys +import subprocess +import time +import difflib +import argparse +import re + +parser = argparse.ArgumentParser() +parser.add_argument('testdir_or_test', nargs='*', + help='subdir name or test script name') +parser.add_argument('-u', '--update', action='store_true', + help='Update test expecations instead of verifying them') +args = parser.parse_args() + +def run_test(path): + print(path) + p = subprocess.Popen(path, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + o,e = p.communicate() + while True: + retval = p.poll() + if retval is not None: + break; + p.kill() + time.sleep(.1) + return retval, o.decode('utf-8'), e.decode('utf-8') + +def udiff(expect, got, expect_path): + expect = expect.splitlines(1) + got = got.splitlines(1) + for line in difflib.unified_diff(expect, got, + fromfile=expect_path, tofile='got'): + sys.stderr.write(line) + if not line.endswith('\n'): + sys.stderr.write('[no-newline]\n') + +def verify_output(got, expect_file, update=False): + if os.path.isfile(expect_file): + ign_file = expect_file + '.ign' + if os.path.isfile(ign_file): + with open(ign_file, 'r') as f: + ign_rules = f.readlines() + for ign_rule in ign_rules: + if not ign_rule: + continue + if '\t' in ign_rule: + ign_rule, repl = ign_rule.split('\t') + repl = repl.strip() + else: + repl = '*' + ir = re.compile(ign_rule) + got = repl.join(ir.split(got)) + + if update: + with open(expect_file, 'w') as f: + f.write(got) + return True + + with open(expect_file, 'r') as f: + expect = f.read() + + if expect != got: + udiff(expect, got, expect_file) + sys.stderr.write('output mismatch: %r\n' + % os.path.basename(expect_file)) + return False + return True + + +script_dir = sys.path[0] + +tests = [] +for f in os.listdir(script_dir): + file_path = os.path.join(script_dir, f) + if not os.path.isfile(file_path): + continue + + if not (file_path.endswith('_test.py') or file_path.endswith('_test.sh')): + continue + tests.append(file_path) + +ran = [] +errors = [] + +for test in sorted(tests): + + if args.testdir_or_test: + if not any([t in test for t in args.testdir_or_test]): + continue + + ran.append(test) + + success = True + + name, ext = os.path.splitext(test) + ok_file = name + '.ok' + err_file = name + '.err' + + rc, out, err = run_test(test) + + if rc != 0: + sys.stderr.write('%r: returned %d\n' % (os.path.basename(test), rc)) + success = False + + if not verify_output(out, ok_file, args.update): + success = False + if not verify_output(err, err_file, args.update): + success = False + + if not success: + sys.stderr.write('\nTest failed: %r\n\n' % os.path.basename(test)) + errors.append(test) + +if errors: + print('%d of %d TESTS FAILED:\n %s' % (len(errors), len(ran), '\n '.join(errors))) + exit(1) + +print('%d tests ok' % len(ran)) +exit(0) + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/selftest/conf/paths.conf b/selftest/conf/paths.conf new file mode 100644 index 0000000..0b2d035 --- /dev/null +++ b/selftest/conf/paths.conf @@ -0,0 +1,2 @@ +state_dir: ./test_work/state_dir +suites_dir: ./suite_test diff --git a/selftest/conf/resources.conf b/selftest/conf/resources.conf new file mode 100644 index 0000000..84c85d0 --- /dev/null +++ b/selftest/conf/resources.conf @@ -0,0 +1,133 @@ +# all hardware and interfaces available to this osmo-gsm-tester + +nitb_iface: +- addr: 10.42.42.1 +- addr: 10.42.42.2 +- addr: 10.42.42.3 + +bts: +- label: sysmoBTS 1002 + type: sysmo + unit_id: 1 + addr: 10.42.42.114 + band: GSM-1800 + +- label: octBTS 3000 + type: oct + unit_id: 5 + addr: 10.42.42.115 + band: GSM-1800 + trx: + - hwaddr: 00:0c:90:32:b5:8a + +- label: nanoBTS 1900 + type: nanobts + unit_id: 1902 + addr: 10.42.42.190 + band: GSM-1900 + trx: + - hwaddr: 00:02:95:00:41:b3 + +arfcn: + - arfcn: 512 + band: GSM-1800 + - arfcn: 514 + band: GSM-1800 + - arfcn: 516 + band: GSM-1800 + - arfcn: 518 + band: GSM-1800 + - arfcn: 520 + band: GSM-1800 + + - arfcn: 540 + band: GSM-1900 + - arfcn: 542 + band: GSM-1900 + - arfcn: 544 + band: GSM-1900 + - arfcn: 546 + band: GSM-1900 + - arfcn: 548 + band: GSM-1900 + +modem: +- label: m7801 + path: '/wavecom_0' + imsi: 901700000007801 + ki: D620F48487B1B782DA55DF6717F08FF9 + +- label: m7802 + path: '/wavecom_1' + imsi: 901700000007802 + ki: 47FDB2D55CE6A10A85ABDAD034A5B7B3 + +- label: m7803 + path: '/wavecom_2' + imsi: 901700000007803 + ki: ABBED4C91417DF710F60675B6EE2C8D2 + +- label: m7804 + path: '/wavecom_3' + imsi: 901700000007804 + ki: 8BA541179156F2BF0918CA3CFF9351B0 + +- label: m7805 + path: '/wavecom_4' + imsi: 901700000007805 + ki: 82BEC24B5B50C9FAA69D17DEC0883A23 + +- label: m7806 + path: '/wavecom_5' + imsi: 901700000007806 + ki: DAF6BD6A188F7A4F09866030BF0F723D + +- label: m7807 + path: '/wavecom_6' + imsi: 901700000007807 + ki: AEB411CFE39681A6352A1EAE4DDC9DBA + +- label: m7808 + path: '/wavecom_7' + imsi: 901700000007808 + ki: F5DEF8692B305D7A65C677CA9EEE09C4 + +- label: m7809 + path: '/wavecom_8' + imsi: 901700000007809 + ki: A644F4503E812FD75329B1C8D625DA44 + +- label: m7810 + path: '/wavecom_9' + imsi: 901700000007810 + ki: EF663BDF3477DCD18D3D2293A2BAED67 + +- label: m7811 + path: '/wavecom_10' + imsi: 901700000007811 + ki: E88F37F048A86A9BC4D652539228C039 + +- label: m7812 + path: '/wavecom_11' + imsi: 901700000007812 + ki: E8D940DD66FCF6F1CD2C0F8F8C45633D + +- label: m7813 + path: '/wavecom_12' + imsi: 901700000007813 + ki: DBF534700C10141C49F699B0419107E3 + +- label: m7814 + path: '/wavecom_13' + imsi: 901700000007814 + ki: B36021DEB90C4EA607E408A92F3B024D + +- label: m7815 + path: '/wavecom_14' + imsi: 901700000007815 + ki: 1E209F6F839F9195778C4F96BE281A24 + +- label: m7816 + path: '/wavecom_15' + imsi: 901700000007816 + ki: BF827D219E739DD189F6F59E60D6455C diff --git a/selftest/config_test.err b/selftest/config_test.err new file mode 100644 index 0000000..e69de29 diff --git a/selftest/config_test.ok b/selftest/config_test.ok new file mode 100644 index 0000000..40a5dcb --- /dev/null +++ b/selftest/config_test.ok @@ -0,0 +1,95 @@ +{'addr': ['0.0.0.0', + '255.255.255.255', + '10.11.12.13', + '10.0.99.1', + '192.168.0.14'], + 'bts': [{'addr': '10.42.42.114', + 'name': 'sysmoBTS 1002', + 'trx': [{'band': 'GSM-1800', + 'timeslots': ['CCCH+SDCCH4', + 'SDCCH8', + 'TCH/F_TCH/H_PDCH', + 'TCH/F_TCH/H_PDCH', + 'TCH/F_TCH/H_PDCH', + 'TCH/F_TCH/H_PDCH', + 'TCH/F_TCH/H_PDCH', + 'TCH/F_TCH/H_PDCH']}, + {'band': 'GSM-1900', + 'timeslots': ['SDCCH8', + 'PDCH', + 'PDCH', + 'PDCH', + 'PDCH', + 'PDCH', + 'PDCH', + 'PDCH']}], + 'type': 'sysmobts'}], + 'hwaddr': ['ca:ff:ee:ba:aa:be', + '00:00:00:00:00:00', + 'CA:FF:EE:BA:AA:BE', + 'cA:Ff:eE:Ba:aA:Be', + 'ff:ff:ff:ff:ff:ff'], + 'imsi': ['012345', '012345678', '012345678912345'], + 'ki': ['000102030405060708090a0b0c0d0e0f', '000102030405060708090a0b0c0d0e0f'], + 'modems': [{'dbus_path': '/sierra_0', + 'imsi': '901700000009001', + 'ki': 'D620F48487B1B782DA55DF6717F08FF9', + 'msisdn': '7801'}, + {'dbus_path': '/sierra_1', + 'imsi': '901700000009002', + 'ki': 'D620F48487B1B782DA55DF6717F08FF9', + 'msisdn': '7802'}]} +- expect validation success: +Validation: OK +- unknown item: +--- -: ERR: ValueError: config item not known: 'bts[].unknown_item' +Validation: Error +- wrong type modems[].imsi: +--- -: ERR: ValueError: config item is dict but should be a leaf node of type 'imsi': 'modems[].imsi' +Validation: Error +- invalid key with space: +--- -: ERR: ValueError: invalid config key: 'imsi ' +Validation: Error +- list instead of dict: +--- -: ERR: ValueError: config item not known: 'a_dict[]' +Validation: Error +- unknown band: +--- (item='bts[].trx[].band'): ERR: ValueError: Unknown GSM band: 'what' +Validation: Error +- invalid v4 addrs: +--- (item='addr[]'): ERR: ValueError: Invalid IPv4 address: '1.2.3' +Validation: Error +--- (item='addr[]'): ERR: ValueError: Invalid IPv4 address: '1.2.3 .4' +Validation: Error +--- (item='addr[]'): ERR: ValueError: Invalid IPv4 address: '91.2.3' +Validation: Error +--- (item='addr[]'): ERR: ValueError: Invalid IPv4 address: 'go away' +Validation: Error +--- (item='addr[]'): ERR: ValueError: Invalid IPv4 address: '' +Validation: Error +--- (item='addr[]'): ERR: ValueError: Invalid IPv4 address: None +Validation: Error +- invalid hw addrs: +--- (item='hwaddr[]'): ERR: ValueError: Invalid hardware address: '1.2.3' +Validation: Error +--- (item='hwaddr[]'): ERR: ValueError: Invalid hardware address: '0b:0c:0d:0e:0f:0g' +Validation: Error +--- (item='hwaddr[]'): ERR: ValueError: Invalid hardware address: '0b:0c:0d:0e : 0f:0f' +Validation: Error +--- (item='hwaddr[]'): ERR: ValueError: Invalid hardware address: 'go away' +Validation: Error +--- (item='hwaddr[]'): ERR: ValueError: Invalid hardware address: '' +Validation: Error +--- (item='hwaddr[]'): ERR: ValueError: Invalid hardware address: None +Validation: Error +- invalid imsis: +--- (item='imsi[]'): ERR: ValueError: Invalid IMSI: '99999999x9' +Validation: Error +--- (item='imsi[]'): ERR: ValueError: Invalid IMSI: '123 456 789 123' +Validation: Error +--- (item='imsi[]'): ERR: ValueError: Invalid IMSI: 'go away' +Validation: Error +--- (item='imsi[]'): ERR: ValueError: Invalid IMSI: '' +Validation: Error +--- (item='imsi[]'): ERR: ValueError: Invalid IMSI: None +Validation: Error diff --git a/selftest/config_test.py b/selftest/config_test.py new file mode 100755 index 0000000..ce4d939 --- /dev/null +++ b/selftest/config_test.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +import _prep + +import sys +import os +import io +import pprint +import copy + +from osmo_gsm_tester import config, log, schema + +example_config_file = 'test.cfg' +example_config = os.path.join(_prep.script_dir, 'config_test', example_config_file) +cfg = config.read(example_config) + +pprint.pprint(cfg) + +test_schema = { + 'modems[].dbus_path': schema.STR, + 'modems[].msisdn': schema.STR, + 'modems[].imsi': schema.IMSI, + 'modems[].ki': schema.STR, + 'bts[].name' : schema.STR, + 'bts[].type' : schema.STR, + 'bts[].addr' : schema.STR, + 'bts[].trx[].timeslots[]' : schema.STR, + 'bts[].trx[].band' : schema.BAND, + 'a_dict.foo' : schema.INT, + 'addr[]' : schema.IPV4, + 'hwaddr[]' : schema.HWADDR, + 'imsi[]' : schema.IMSI, + 'ki[]' : schema.KI, + } + +def val(which): + try: + schema.validate(which, test_schema) + print('Validation: OK') + except ValueError: + log.log_exn() + print('Validation: Error') + +print('- expect validation success:') +val(cfg) + +print('- unknown item:') +c = copy.deepcopy(cfg) +c['bts'][0]['unknown_item'] = 'no' +val(c) + +print('- wrong type modems[].imsi:') +c = copy.deepcopy(cfg) +c['modems'][0]['imsi'] = {'no':'no'} +val(c) + +print('- invalid key with space:') +c = copy.deepcopy(cfg) +c['modems'][0]['imsi '] = '12345' +val(c) + +print('- list instead of dict:') +c = copy.deepcopy(cfg) +c['a_dict'] = [ 1, 2, 3 ] +val(c) + +print('- unknown band:') +c = copy.deepcopy(cfg) +c['bts'][0]['trx'][0]['band'] = 'what' +val(c) + +print('- invalid v4 addrs:') +c = copy.deepcopy(cfg) +c['addr'][3] = '1.2.3' +val(c) +c['addr'][3] = '1.2.3 .4' +val(c) +c['addr'][3] = '91.2.3' +val(c) +c['addr'][3] = 'go away' +val(c) +c['addr'][3] = '' +val(c) +c['addr'][3] = None +val(c) + +print('- invalid hw addrs:') +c = copy.deepcopy(cfg) +c['hwaddr'][3] = '1.2.3' +val(c) +c['hwaddr'][3] = '0b:0c:0d:0e:0f:0g' +val(c) +c['hwaddr'][3] = '0b:0c:0d:0e : 0f:0f' +val(c) +c['hwaddr'][3] = 'go away' +val(c) +c['hwaddr'][3] = '' +val(c) +c['hwaddr'][3] = None +val(c) + +print('- invalid imsis:') +c = copy.deepcopy(cfg) +c['imsi'][2] = '99999999x9' +val(c) +c['imsi'][2] = '123 456 789 123' +val(c) +c['imsi'][2] = 'go away' +val(c) +c['imsi'][2] = '' +val(c) +c['imsi'][2] = None +val(c) + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/selftest/config_test/test.cfg b/selftest/config_test/test.cfg new file mode 100644 index 0000000..cc62182 --- /dev/null +++ b/selftest/config_test/test.cfg @@ -0,0 +1,59 @@ +modems: + +- dbus_path: /sierra_0 + msisdn: 7801 + imsi: 901700000009001 + ki: D620F48487B1B782DA55DF6717F08FF9 + +- dbus_path: /sierra_1 + msisdn: '7802' + imsi: '901700000009002' + ki: D620F48487B1B782DA55DF6717F08FF9 + +# comment +BTS: + +- name: sysmoBTS 1002 + TYPE: sysmobts + addr: 10.42.42.114 + trx: + - timeslots: + - CCCH+SDCCH4 + - SDCCH8 + - TCH/F_TCH/H_PDCH + - TCH/F_TCH/H_PDCH + - TCH/F_TCH/H_PDCH + - TCH/F_TCH/H_PDCH + - TCH/F_TCH/H_PDCH + - TCH/F_TCH/H_PDCH + band: GSM-1800 + - timeslots: + - SDCCH8 + - PDCH + - PDCH + - PDCH + - PDCH + - PDCH + - PDCH + - PDCH + band: GSM-1900 + +addr: +- 0.0.0.0 +- 255.255.255.255 +- 10.11.12.13 +- 10.0.99.1 +- 192.168.0.14 +hwaddr: +- ca:ff:ee:ba:aa:be +- 00:00:00:00:00:00 +- CA:FF:EE:BA:AA:BE +- cA:Ff:eE:Ba:aA:Be +- ff:ff:ff:ff:ff:ff +imsi: +- '012345' +- '012345678' +- '012345678912345' +ki: +- 000102030405060708090a0b0c0d0e0f +- 000102030405060708090a0b0c0d0e0f diff --git a/selftest/dbus_test/dbus_server.py b/selftest/dbus_test/dbus_server.py new file mode 100755 index 0000000..222b28b --- /dev/null +++ b/selftest/dbus_test/dbus_server.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +# Based on http://stackoverflow.com/questions/22390064/use-dbus-to-just-send-a-message-in-python + +# Python DBUS Test Server +# runs until the Quit() method is called via DBUS + +from gi.repository import GLib +from pydbus import SessionBus + +loop = GLib.MainLoop() + +class MyDBUSService(object): + """ + + + + + + + + + + + + + """ + + def Hello(self): + """returns the string 'Hello, World!'""" + return "Hello, World!" + + def EchoString(self, s): + """returns whatever is passed to it""" + return s + + def Quit(self): + """removes this object from the DBUS connection and exits""" + loop.quit() + +bus = SessionBus() +bus.publish("net.lew21.pydbus.ClientServerExample", MyDBUSService()) +loop.run() + diff --git a/selftest/dbus_test/ofono_client.py b/selftest/dbus_test/ofono_client.py new file mode 100755 index 0000000..6b60f98 --- /dev/null +++ b/selftest/dbus_test/ofono_client.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +''' +Power on and off some modem on ofono, while running the glib main loop in a +thread and receiving modem state changes by dbus signals. +''' + +from pydbus import SystemBus, Variant +import time +import threading +import pprint + +from gi.repository import GLib +loop = GLib.MainLoop() + +def propchanged(*args, **kwargs): + print('-> PROP CHANGED: %r %r' % (args, kwargs)) + +class GlibMainloop(threading.Thread): + def run(self): + loop.run() + +ml = GlibMainloop() +ml.start() + +try: + bus = SystemBus() + + print('\n- list modems') + root = bus.get("org.ofono", '/') + print(root.Introspect()) + modems = sorted(root.GetModems()) + pprint.pprint(modems) + + first_modem_path = modems[0][0] + print('\n- first modem %r' % first_modem_path) + modem = bus.get("org.ofono", first_modem_path) + modem.PropertyChanged.connect(propchanged) + + print(modem.Introspect()) + print(modem.GetProperties()) + + print('\n- set Powered = True') + modem.SetProperty('Powered', Variant('b', True)) + print('call returned') + print(modem.GetProperties()) + + time.sleep(1) + + print('\n- set Powered = False') + modem.SetProperty('Powered', Variant('b', False)) + print('call returned') + + print(modem.GetProperties()) +finally: + loop.quit() +ml.join() diff --git a/selftest/dbus_test/ofono_client_one_thread.py b/selftest/dbus_test/ofono_client_one_thread.py new file mode 100644 index 0000000..96d54bc --- /dev/null +++ b/selftest/dbus_test/ofono_client_one_thread.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +''' +Power on and off some modem on ofono, while running the glib main loop in a +thread and receiving modem state changes by dbus signals. +''' + +from pydbus import SystemBus, Variant +import time +import pprint + +from gi.repository import GLib +glib_main_loop = GLib.MainLoop() +glib_main_ctx = glib_main_loop.get_context() + +def propchanged(*args, **kwargs): + print('-> PROP CHANGED: %r %r' % (args, kwargs)) + + +def pump(): + global glib_main_ctx + print('pump?') + while glib_main_ctx.pending(): + print('* pump') + glib_main_ctx.iteration() + +def wait(condition): + pump() + while not condition(): + time.sleep(.1) + pump() + +bus = SystemBus() + +print('\n- list modems') +root = bus.get("org.ofono", '/') +print(root.Introspect()) +modems = sorted(root.GetModems()) +pprint.pprint(modems) +pump() + +first_modem_path = modems[0][0] +print('\n- first modem %r' % first_modem_path) +modem = bus.get("org.ofono", first_modem_path) +modem.PropertyChanged.connect(propchanged) + +print(modem.Introspect()) +print(modem.GetProperties()) + +print('\n- set Powered = True') +modem.SetProperty('Powered', Variant('b', True)) +print('call returned') +print('- pump dbus events') +pump() +pump() +print('sleep 1') +time.sleep(1) +pump() + + +print('- modem properties:') +print(modem.GetProperties()) + + +print('\n- set Powered = False') +modem.SetProperty('Powered', Variant('b', False)) +print('call returned') + +print(modem.GetProperties()) + +# vim: tabstop=4 shiftwidth=4 expandtab diff --git a/selftest/lock_test.err b/selftest/lock_test.err new file mode 100644 index 0000000..e69de29 diff --git a/selftest/lock_test.ok b/selftest/lock_test.ok new file mode 100644 index 0000000..2c0f31b --- /dev/null +++ b/selftest/lock_test.ok @@ -0,0 +1,8 @@ +acquired lock: 'long_name' +launched first, locked by: long_name +launched second, locked by: long_name +leaving lock: 'long_name' +acquired lock: 'shorter' +waited, locked by: shorter +leaving lock: 'shorter' +waited more, locked by: diff --git a/selftest/lock_test.sh b/selftest/lock_test.sh new file mode 100755 index 0000000..c82d141 --- /dev/null +++ b/selftest/lock_test.sh @@ -0,0 +1,10 @@ +#!/bin/sh +python3 ./lock_test_help.py long name & +sleep .2 +echo "launched first, locked by: $(cat /tmp/lock_test)" +python3 ./lock_test_help.py shorter & +echo "launched second, locked by: $(cat /tmp/lock_test)" +sleep .4 +echo "waited, locked by: $(cat /tmp/lock_test)" +sleep .5 +echo "waited more, locked by: $(cat /tmp/lock_test)" diff --git a/selftest/lock_test_help.py b/selftest/lock_test_help.py new file mode 100644 index 0000000..0549981 --- /dev/null +++ b/selftest/lock_test_help.py @@ -0,0 +1,17 @@ +import sys +import time + +import _prep + +from osmo_gsm_tester.util import FileLock + +fl = FileLock('/tmp/lock_test', '_'.join(sys.argv[1:])) + +with fl: + print('acquired lock: %r' % fl.owner) + sys.stdout.flush() + time.sleep(0.5) + print('leaving lock: %r' % fl.owner) + sys.stdout.flush() + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/selftest/log_test.err b/selftest/log_test.err new file mode 100644 index 0000000..e69de29 diff --git a/selftest/log_test.ok b/selftest/log_test.ok new file mode 100644 index 0000000..b2fdd69 --- /dev/null +++ b/selftest/log_test.ok @@ -0,0 +1,41 @@ +- Testing global log functions +01:02:03 tst : from log.log() +01:02:03 tst : DBG: from log.dbg() +01:02:03 tst : ERR: from log.err() +- Testing log.Origin functions +01:02:03 tst some-name(some='detail'): hello log +01:02:03 tst some-name(some='detail'): ERR: hello err +01:02:03 tst some-name(some='detail'): message {int=3, none=None, str='str\n', tuple=('foo', 42)} +01:02:03 tst some-name(some='detail'): DBG: hello dbg +- Testing log.style() +01:02:03: only time +tst: only category +DBG: only level +some-name(some='detail'): only origin +only src [log_test.py:70] +- Testing log.style_change() +no log format +01:02:03: add time +but no time format +01:02:03: DBG: add level +01:02:03 tst: DBG: add category +01:02:03 tst: DBG: add src [log_test.py:85] +01:02:03 tst some-name(some='detail'): DBG: add origin [log_test.py:87] +- Testing origin_width +01:02:03 tst shortname: origin str set to 23 chars [log_test.py:94] +01:02:03 tst very long name(and_some=(3, 'things', 'in a tuple'), some='details'): long origin str [log_test.py:96] +01:02:03 tst very long name(and_some=(3, 'things', 'in a tuple'), some='details'): DBG: long origin str dbg [log_test.py:97] +01:02:03 tst very long name(and_some=(3, 'things', 'in a tuple'), some='details'): ERR: long origin str err [log_test.py:98] +- Testing log.Origin with omitted info +01:02:03 tst LogTest: hello log, name implicit from class name [log_test.py:103] +01:02:03 --- explicit_name: hello log, no category set [log_test.py:107] +01:02:03 --- LogTest: hello log, no category nor name set [log_test.py:110] +01:02:03 --- LogTest: DBG: debug message, no category nor name set [log_test.py:113] +- Testing logging of Exceptions, tracing origins +Not throwing an exception in 'with:' works. +nested print just prints +01:02:03 tst level3: nested log() [level1↪level2↪level3] [log_test.py:145] +01:02:03 tst level2: nested l2 log() from within l3 scope [level1↪level2] [log_test.py:146] +01:02:03 tst level3: ERR: ValueError: bork [level1↪level2↪level3] [log_test.py:147: raise ValueError('bork')] +- Enter the same Origin context twice +01:02:03 tst level2: nested log [level1↪level2] [log_test.py:159] diff --git a/selftest/log_test.py b/selftest/log_test.py new file mode 100755 index 0000000..2ec8635 --- /dev/null +++ b/selftest/log_test.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 + +# osmo_gsm_tester: logging tests +# +# Copyright (C) 2016-2017 by sysmocom - s.f.m.c. GmbH +# +# Author: Neels Hofmeyr +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import _prep + +import sys +import os + +from osmo_gsm_tester import log + +#log.targets[0].get_time_str = lambda: '01:02:03' +fake_time = '01:02:03' +log.style_change(time=True, time_fmt=fake_time) +log.set_all_levels(None) + +print('- Testing global log functions') +log.log('', log.C_TST, 'from log.log()') +log.dbg('', log.C_TST, 'from log.dbg(), not seen') +log.set_level(log.C_TST, log.L_DBG) +log.dbg('', log.C_TST, 'from log.dbg()') +log.set_level(log.C_TST, log.L_LOG) +log.err('', log.C_TST, 'from log.err()') + +print('- Testing log.Origin functions') +class LogTest(log.Origin): + pass + +t = LogTest() +t.set_log_category(log.C_TST) +t.set_name('some', 'name', some="detail") + +t.log("hello log") +t.err("hello err") +t.dbg("hello dbg not visible") + +t.log("message", int=3, tuple=('foo', 42), none=None, str='str\n') + +log.set_level(log.C_TST, log.L_DBG) +t.dbg("hello dbg") + +print('- Testing log.style()') + +log.style(time=True, category=False, level=False, origin=False, src=False, time_fmt=fake_time) +t.dbg("only time") +log.style(time=False, category=True, level=False, origin=False, src=False, time_fmt=fake_time) +t.dbg("only category") +log.style(time=False, category=False, level=True, origin=False, src=False, time_fmt=fake_time) +t.dbg("only level") +log.style(time=False, category=False, level=False, origin=True, src=False, time_fmt=fake_time) +t.dbg("only origin") +log.style(time=False, category=False, level=False, origin=False, src=True, time_fmt=fake_time) +t.dbg("only src") + +print('- Testing log.style_change()') +log.style(time=False, category=False, level=False, origin=False, src=False, time_fmt=fake_time) +t.dbg("no log format") +log.style_change(time=True) +t.dbg("add time") +log.style_change(time=True, time_fmt=0) +t.dbg("but no time format") +log.style_change(time=True, time_fmt=fake_time) +log.style_change(level=True) +t.dbg("add level") +log.style_change(category=True) +t.dbg("add category") +log.style_change(src=True) +t.dbg("add src") +log.style_change(origin=True) +t.dbg("add origin") + +print('- Testing origin_width') +t = LogTest() +t.set_log_category(log.C_TST) +t.set_name('shortname') +log.style(origin_width=23, time_fmt=fake_time) +t.log("origin str set to 23 chars") +t.set_name('very long name', some='details', and_some=(3, 'things', 'in a tuple')) +t.log("long origin str") +t.dbg("long origin str dbg") +t.err("long origin str err") + +print('- Testing log.Origin with omitted info') +t = LogTest() +t.set_log_category(log.C_TST) +t.log("hello log, name implicit from class name") + +t = LogTest() +t.set_name('explicit_name') +t.log("hello log, no category set") + +t = LogTest() +t.log("hello log, no category nor name set") +t.dbg("hello log, no category nor name set, not seen") +log.set_level(log.C_DEFAULT, log.L_DBG) +t.dbg("debug message, no category nor name set") + +print('- Testing logging of Exceptions, tracing origins') +log.style(time_fmt=fake_time) + +class Thing(log.Origin): + def __init__(self, some_path): + self.set_log_category(log.C_TST) + self.set_name(some_path) + + def say(self, msg): + print(msg) + +#log.style_change(trace=True) + +with Thing('print_redirected'): + print("Not throwing an exception in 'with:' works.") + +def l1(): + level1 = Thing('level1') + with level1: + l2() + +def l2(): + level2 = Thing('level2') + with level2: + l3(level2) + +def l3(level2): + level3 = Thing('level3') + with level3: + print('nested print just prints') + level3.log('nested log()') + level2.log('nested l2 log() from within l3 scope') + raise ValueError('bork') + +try: + l1() +except Exception: + log.log_exn() + +print('- Enter the same Origin context twice') +with Thing('level1'): + l2 = Thing('level2') + with l2: + with l2: + l2.log('nested log') + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/selftest/misc.py b/selftest/misc.py new file mode 100755 index 0000000..e57a48c --- /dev/null +++ b/selftest/misc.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 + +msisdn = '0000' + +l = len(msisdn) +next_msisdn = ('%%0%dd' % l) % (int(msisdn) + 1) +print(next_msisdn) diff --git a/selftest/process_test.err b/selftest/process_test.err new file mode 100644 index 0000000..e69de29 diff --git a/selftest/process_test.ok b/selftest/process_test.ok new file mode 100644 index 0000000..4245eeb --- /dev/null +++ b/selftest/process_test.ok @@ -0,0 +1,33 @@ +run foo: DBG: cd '[TMP]'; PATH=[$PATH] foo.py arg1 arg2 [foo↪foo] +run foo: DBG: [TMP]/stdout [foo↪foo] +run foo: DBG: [TMP]/stderr [foo↪foo] +run foo(pid=[PID]): Launched [foo(pid=[PID])↪foo(pid=[PID])] +stdout: +(launched: [DATETIME]) +foo stdout +[[$0], 'arg1', 'arg2'] + +stderr: +(launched: [DATETIME]) +foo stderr + +run foo(pid=[PID]): Terminating (SIGINT) +run foo(pid=[PID]): DBG: Cleanup +run foo(pid=[PID]): Terminated {rc=1} +result: 1 +stdout: +(launched: [DATETIME]) +foo stdout +[[$0], 'arg1', 'arg2'] +Exiting (stdout) + +stderr: +(launched: [DATETIME]) +foo stderr +Traceback (most recent call last): + File [$0], line [LINE], in + time.sleep(1) +KeyboardInterrupt +Exiting (stderr) + +done. diff --git a/selftest/process_test.ok.ign b/selftest/process_test.ok.ign new file mode 100644 index 0000000..0abd7d5 --- /dev/null +++ b/selftest/process_test.ok.ign @@ -0,0 +1,7 @@ +PATH='[^']*' PATH=[$PATH] +/tmp/[^/ '"]* [TMP] +pid=[0-9]* pid=[PID] +....-..-.._..:..:.. [DATETIME] +'[^']*/selftest/process_test/foo.py' [$0] +"[^"]*/selftest/process_test/foo.py" [$0] +, line [0-9]* , line [LINE] diff --git a/selftest/process_test.py b/selftest/process_test.py new file mode 100755 index 0000000..9ad082b --- /dev/null +++ b/selftest/process_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +import _prep +import time +import os + +from osmo_gsm_tester import process, util, log + +tmpdir = util.Dir(util.get_tempdir()) + +dollar_path = '%s:%s' % ( + os.path.join(os.getcwd(), 'process_test'), + os.getenv('PATH')) + +p = process.Process('foo', tmpdir, ('foo.py', 'arg1', 'arg2'), + env={'PATH': dollar_path}) + +p.launch() +time.sleep(.5) +p.poll() +print('stdout:') +print(p.get_stdout()) +print('stderr:') +print(p.get_stderr()) + +assert not p.terminated() +p.terminate() +assert p.terminated() +print('result: %r' % p.result) + +print('stdout:') +print(p.get_stdout()) +print('stderr:') +print(p.get_stderr()) +print('done.') + +test_ssh = True +test_ssh = False +if test_ssh: + # this part of the test requires ability to ssh to localhost + p = process.RemoteProcess('localhost', '/tmp', 'ssh-test', tmpdir, + ('ls', '-al')) + p.launch() + p.wait() + assert p.terminated() + print('stdout:') + print(p.get_stdout()) + print('stderr:') + print(p.get_stderr()) + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/selftest/process_test/foo.py b/selftest/process_test/foo.py new file mode 100755 index 0000000..4abe887 --- /dev/null +++ b/selftest/process_test/foo.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 + +import sys +import atexit +import time + + +sys.stdout.write('foo stdout\n') +sys.stderr.write('foo stderr\n') + +print(repr(sys.argv)) +sys.stdout.flush() +sys.stderr.flush() + +def x(): + sys.stdout.write('Exiting (stdout)\n') + sys.stdout.flush() + sys.stderr.write('Exiting (stderr)\n') + sys.stderr.flush() +atexit.register(x) + +while True: + time.sleep(1) + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/selftest/py_import_test/invocation.py b/selftest/py_import_test/invocation.py new file mode 100755 index 0000000..ad58b80 --- /dev/null +++ b/selftest/py_import_test/invocation.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +import support +import importlib.util + +if hasattr(importlib.util, 'module_from_spec'): + def run_test(path): + print('py 3.5+') + spec = importlib.util.spec_from_file_location("tests.script", path) + spec.loader.exec_module( importlib.util.module_from_spec(spec) ) +else: + def run_test(path): + print('py 3.4-') + from importlib.machinery import SourceFileLoader + SourceFileLoader("tests.script", path).load_module() + +path = './subdir/script.py' + +support.config = 'specifics' +run_test(path) + +support.config = 'specifics2' +run_test(path) + diff --git a/selftest/py_import_test/subdir/script.py b/selftest/py_import_test/subdir/script.py new file mode 100644 index 0000000..1b57c20 --- /dev/null +++ b/selftest/py_import_test/subdir/script.py @@ -0,0 +1,9 @@ +from support import * + +print('hello') + +def run(what): + print(what) + print(what) + +run(config) diff --git a/selftest/py_import_test/support.py b/selftest/py_import_test/support.py new file mode 100644 index 0000000..aceedb8 --- /dev/null +++ b/selftest/py_import_test/support.py @@ -0,0 +1,2 @@ + +config = None diff --git a/selftest/real_suite/README.txt b/selftest/real_suite/README.txt new file mode 100644 index 0000000..f18840a --- /dev/null +++ b/selftest/real_suite/README.txt @@ -0,0 +1,18 @@ +This a real gsm test suite configured and ready to use. +The only thing missing is a trial dir containing binaries. + +If you have your trial with binary tar archives in ~/my_trial +you can run the suite for example like this: + + . ./env # point your environment at all the right places + run_once.py ~/my_trial -s sms:trx + +This combines the suites/sms test suite with the scenarios/trx choice of +osmo-bts-trx and runs all tests in the 'sms' suite. + +A ./state dir will be created to store the current osmo-gsm-tester state. If +you prefer not to write to this dir, set up an own configuration pointing at a +different path (see paths.conf: 'state_dir' and the env file). When there is +no OSMO_GSM_TESTER_CONF set (from ./env), osmo-gsm-tester will instead look for +conf files in several locations like ~/.config/osmo-gsm-tester, +/usr/local/etc/osmo-gsm-tester, /etc/osmo-gsm-tester diff --git a/selftest/real_suite/default.conf b/selftest/real_suite/default.conf new file mode 100644 index 0000000..b247722 --- /dev/null +++ b/selftest/real_suite/default.conf @@ -0,0 +1,31 @@ +nitb: + net: + mcc: 1 + mnc: 868 + short_name: osmo-gsm-tester + long_name: osmo-gsm-tester + auth_policy: closed + encryption: a5 0 + +nitb_bts: + location_area_code: 23 + base_station_id_code: 63 + stream_id: 255 + trx_list: + - max_power_red: 22 + arfcn: 868 + timeslot_list: + - phys_chan_config: CCCH+SDCCH4 + - phys_chan_config: SDCCH8 + - phys_chan_config: TCH_F/TCH_H/PDCH + - phys_chan_config: TCH_F/TCH_H/PDCH + - phys_chan_config: TCH_F/TCH_H/PDCH + - phys_chan_config: TCH_F/TCH_H/PDCH + - phys_chan_config: TCH_F/TCH_H/PDCH + - phys_chan_config: TCH_F/TCH_H/PDCH + +osmo_bts_sysmo: + ipa_unit_id: 1123 + +osmo_bts_trx: + ipa_unit_id: 1124 diff --git a/selftest/real_suite/env b/selftest/real_suite/env new file mode 100644 index 0000000..1d9cc0a --- /dev/null +++ b/selftest/real_suite/env @@ -0,0 +1,4 @@ +OSMO_GSM_TESTER_SRC="$(readlink -f ../../src)" +export PYTHONPATH="$OSMO_GSM_TESTER_SRC" +export PATH="$OSMO_GSM_TESTER_SRC:$PATH" +export OSMO_GSM_TESTER_CONF="$PWD" diff --git a/selftest/real_suite/paths.conf b/selftest/real_suite/paths.conf new file mode 100644 index 0000000..bb7316c --- /dev/null +++ b/selftest/real_suite/paths.conf @@ -0,0 +1,3 @@ +state_dir: './state' +suites_dir: './suites' +scenarios_dir: './scenarios' diff --git a/selftest/real_suite/resources.conf b/selftest/real_suite/resources.conf new file mode 100644 index 0000000..a6c396b --- /dev/null +++ b/selftest/real_suite/resources.conf @@ -0,0 +1,139 @@ +# all hardware and interfaces available to this osmo-gsm-tester + +nitb_iface: +- addr: 127.0.0.10 +- addr: 127.0.0.11 +- addr: 127.0.0.12 + +bts: +- label: sysmoBTS 1002 + type: sysmo + unit_id: 1 + addr: 10.42.42.114 + band: GSM-1800 + +- label: octBTS 3000 + type: oct + unit_id: 5 + addr: 10.42.42.115 + band: GSM-1800 + trx: + - hwaddr: 00:0c:90:32:b5:8a + +- label: Ettus B210 + type: osmotrx + unit_id: 6 + addr: 10.42.42.116 + band: GSM-1800 + +- label: nanoBTS 1900 + type: nanobts + unit_id: 1902 + addr: 10.42.42.190 + band: GSM-1900 + trx: + - hwaddr: 00:02:95:00:41:b3 + +arfcn: + - arfcn: 512 + band: GSM-1800 + - arfcn: 514 + band: GSM-1800 + - arfcn: 516 + band: GSM-1800 + - arfcn: 518 + band: GSM-1800 + - arfcn: 520 + band: GSM-1800 + + - arfcn: 540 + band: GSM-1900 + - arfcn: 542 + band: GSM-1900 + - arfcn: 544 + band: GSM-1900 + - arfcn: 546 + band: GSM-1900 + - arfcn: 548 + band: GSM-1900 + +modem: +- label: m7801 + path: '/wavecom_0' + imsi: 901700000007801 + ki: D620F48487B1B782DA55DF6717F08FF9 + +- label: m7802 + path: '/wavecom_1' + imsi: 901700000007802 + ki: 47FDB2D55CE6A10A85ABDAD034A5B7B3 + +- label: m7803 + path: '/wavecom_2' + imsi: 901700000007803 + ki: ABBED4C91417DF710F60675B6EE2C8D2 + +- label: m7804 + path: '/wavecom_3' + imsi: 901700000007804 + ki: 8BA541179156F2BF0918CA3CFF9351B0 + +- label: m7805 + path: '/wavecom_4' + imsi: 901700000007805 + ki: 82BEC24B5B50C9FAA69D17DEC0883A23 + +- label: m7806 + path: '/wavecom_5' + imsi: 901700000007806 + ki: DAF6BD6A188F7A4F09866030BF0F723D + +- label: m7807 + path: '/wavecom_6' + imsi: 901700000007807 + ki: AEB411CFE39681A6352A1EAE4DDC9DBA + +- label: m7808 + path: '/wavecom_7' + imsi: 901700000007808 + ki: F5DEF8692B305D7A65C677CA9EEE09C4 + +- label: m7809 + path: '/wavecom_8' + imsi: 901700000007809 + ki: A644F4503E812FD75329B1C8D625DA44 + +- label: m7810 + path: '/wavecom_9' + imsi: 901700000007810 + ki: EF663BDF3477DCD18D3D2293A2BAED67 + +- label: m7811 + path: '/wavecom_10' + imsi: 901700000007811 + ki: E88F37F048A86A9BC4D652539228C039 + +- label: m7812 + path: '/wavecom_11' + imsi: 901700000007812 + ki: E8D940DD66FCF6F1CD2C0F8F8C45633D + +- label: m7813 + path: '/wavecom_12' + imsi: 901700000007813 + ki: DBF534700C10141C49F699B0419107E3 + +- label: m7814 + path: '/wavecom_13' + imsi: 901700000007814 + ki: B36021DEB90C4EA607E408A92F3B024D + +- label: m7815 + path: '/wavecom_14' + imsi: 901700000007815 + ki: 1E209F6F839F9195778C4F96BE281A24 + +- label: m7816 + path: '/wavecom_15' + imsi: 901700000007816 + ki: BF827D219E739DD189F6F59E60D6455C diff --git a/selftest/real_suite/scenarios/trx.conf b/selftest/real_suite/scenarios/trx.conf new file mode 100644 index 0000000..98065aa --- /dev/null +++ b/selftest/real_suite/scenarios/trx.conf @@ -0,0 +1,3 @@ +resources: + bts: + - type: osmotrx diff --git a/selftest/real_suite/suites/sms/mo_mt_sms.py b/selftest/real_suite/suites/sms/mo_mt_sms.py new file mode 100755 index 0000000..05be48c --- /dev/null +++ b/selftest/real_suite/suites/sms/mo_mt_sms.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +from osmo_gsm_tester.test import * + +print('use resources...') +nitb = suite.nitb() +bts = suite.bts() +ms_mo = suite.modem() +ms_mt = suite.modem() + +print('start nitb and bts...') +nitb.add_bts(bts) +nitb.start() +sleep(.1) +assert nitb.running() +bts.start() + +nitb.add_subscriber(ms_mo) +nitb.add_subscriber(ms_mt) + +ms_mo.connect(nitb) +ms_mt.connect(nitb) +wait(nitb.subscriber_attached, ms_mo, ms_mt) + +sms = ms_mo.sms_send(ms_mt.msisdn) +sleep(3) +wait(nitb.sms_received, sms) diff --git a/selftest/real_suite/suites/sms/suite.conf b/selftest/real_suite/suites/sms/suite.conf new file mode 100644 index 0000000..4a03379 --- /dev/null +++ b/selftest/real_suite/suites/sms/suite.conf @@ -0,0 +1,10 @@ +resources: + nitb_iface: + - times: 1 + bts: + - times: 1 + modem: + - times: 2 + +defaults: + timeout: 60s diff --git a/selftest/resource_test.err b/selftest/resource_test.err new file mode 100644 index 0000000..e69de29 diff --git a/selftest/resource_test.ok b/selftest/resource_test.ok new file mode 100644 index 0000000..008c447 --- /dev/null +++ b/selftest/resource_test.ok @@ -0,0 +1,207 @@ +- expect solutions: +[0, 1, 2] +[0, 1, 2] +[1, 0, 2] +[1, 2, 0] +- expect failure to solve: +The requested resource requirements are not solvable [[0, 2], [2], [0, 2]] +- test removing a Resources list from itself +ok, caused exception: RuntimeError('Refusing to drop a list of resources from itself. This is probably a bug where a list of Resources() should have been copied but is passed as-is. use Resources.clear() instead.',) +- test removing a Resources list from one with the same list in it +- test resources config and state dir: +*** all resources: +{'arfcn': [{'_hash': 'e620569450f8259b3f0212ec19c285dd07df063c', + 'arfcn': '512', + 'band': 'GSM-1800'}, + {'_hash': '022621e513c5a5bf33b77430a1e9c886be676fa1', + 'arfcn': '514', + 'band': 'GSM-1800'}, + {'_hash': '3199abf375a1dd899e554e9d63a552e06d7f38bf', + 'arfcn': '516', + 'band': 'GSM-1800'}, + {'_hash': '57aa7bd1da62495f2857ae6b859193dd592a0a02', + 'arfcn': '518', + 'band': 'GSM-1800'}, + {'_hash': '53dd2e2682b736f427abd2ce59a9a50ca8130678', + 'arfcn': '520', + 'band': 'GSM-1800'}, + {'_hash': '31687a5e6d5140a4b3877606ca5f18244f11d706', + 'arfcn': '540', + 'band': 'GSM-1900'}, + {'_hash': '1def43a5c88a83cdb21279eacab0679ea08ffaf3', + 'arfcn': '542', + 'band': 'GSM-1900'}, + {'_hash': '1d6e3b08a3861fd4d748f111295ec5a93ecd3d23', + 'arfcn': '544', + 'band': 'GSM-1900'}, + {'_hash': '8fb36927de15466fcdbee01f7f65704c312cb36c', + 'arfcn': '546', + 'band': 'GSM-1900'}, + {'_hash': 'dc9ce027a257da087f31a5bc1ee6b4abd2637369', + 'arfcn': '548', + 'band': 'GSM-1900'}], + 'bts': [{'_hash': 'a7c6d2ebaeb139e8c2e7d45c3495d046d7439007', + 'addr': '10.42.42.114', + 'band': 'GSM-1800', + 'label': 'sysmoBTS 1002', + 'type': 'sysmo', + 'unit_id': '1'}, + {'_hash': '02540ab9eb556056a0b4d28443bc9f4793f6d549', + 'addr': '10.42.42.115', + 'band': 'GSM-1800', + 'label': 'octBTS 3000', + 'trx': [{'hwaddr': '00:0c:90:32:b5:8a'}], + 'type': 'oct', + 'unit_id': '5'}, + {'_hash': '556c954d475d12cf0dc622c0df5743cac5543fa0', + 'addr': '10.42.42.190', + 'band': 'GSM-1900', + 'label': 'nanoBTS 1900', + 'trx': [{'hwaddr': '00:02:95:00:41:b3'}], + 'type': 'nanobts', + 'unit_id': '1902'}], + 'modem': [{'_hash': '19c69e45aa090fb511446bd00797690aa82ff52f', + 'imsi': '901700000007801', + 'ki': 'D620F48487B1B782DA55DF6717F08FF9', + 'label': 'm7801', + 'path': '/wavecom_0'}, + {'_hash': 'e1a46516a1fb493b2617ab14fc1693a9a45ec254', + 'imsi': '901700000007802', + 'ki': '47FDB2D55CE6A10A85ABDAD034A5B7B3', + 'label': 'm7802', + 'path': '/wavecom_1'}, + {'_hash': '4fe91500a309782bb0fd8ac6fc827834089f8b00', + 'imsi': '901700000007803', + 'ki': 'ABBED4C91417DF710F60675B6EE2C8D2', + 'label': 'm7803', + 'path': '/wavecom_2'}, + {'_hash': 'c895badf0c2faaa4a997cd9f2313b5ebda7486e4', + 'imsi': '901700000007804', + 'ki': '8BA541179156F2BF0918CA3CFF9351B0', + 'label': 'm7804', + 'path': '/wavecom_3'}, + {'_hash': '60f182abed05adb530e3d06d88cc47703b65d7d8', + 'imsi': '901700000007805', + 'ki': '82BEC24B5B50C9FAA69D17DEC0883A23', + 'label': 'm7805', + 'path': '/wavecom_4'}, + {'_hash': 'd1f0fbf089a4bf32dd566af956d23b89e3d60821', + 'imsi': '901700000007806', + 'ki': 'DAF6BD6A188F7A4F09866030BF0F723D', + 'label': 'm7806', + 'path': '/wavecom_5'}, + {'_hash': '2445e3b5949d15f4351c0db1d3f3f593f9d73aa5', + 'imsi': '901700000007807', + 'ki': 'AEB411CFE39681A6352A1EAE4DDC9DBA', + 'label': 'm7807', + 'path': '/wavecom_6'}, + {'_hash': '80247388b2ca382382c4aec678102355b7922965', + 'imsi': '901700000007808', + 'ki': 'F5DEF8692B305D7A65C677CA9EEE09C4', + 'label': 'm7808', + 'path': '/wavecom_7'}, + {'_hash': '5b9e4e117a8889430542d22a9693e7b999362856', + 'imsi': '901700000007809', + 'ki': 'A644F4503E812FD75329B1C8D625DA44', + 'label': 'm7809', + 'path': '/wavecom_8'}, + {'_hash': '219a7abb057050eef3ce4b99c487f32bbaae9a41', + 'imsi': '901700000007810', + 'ki': 'EF663BDF3477DCD18D3D2293A2BAED67', + 'label': 'm7810', + 'path': '/wavecom_9'}, + {'_hash': '75d45c2d975b893da34c7cae827c25a2039cecd2', + 'imsi': '901700000007811', + 'ki': 'E88F37F048A86A9BC4D652539228C039', + 'label': 'm7811', + 'path': '/wavecom_10'}, + {'_hash': '1777362f556b249a5c1d6a83110704dbd037bc20', + 'imsi': '901700000007812', + 'ki': 'E8D940DD66FCF6F1CD2C0F8F8C45633D', + 'label': 'm7812', + 'path': '/wavecom_11'}, + {'_hash': '21d7eb4b0c782e004821a9f7f778891c93956924', + 'imsi': '901700000007813', + 'ki': 'DBF534700C10141C49F699B0419107E3', + 'label': 'm7813', + 'path': '/wavecom_12'}, + {'_hash': 'f53e4e79bdbc63eb2845de671007d4f733f28409', + 'imsi': '901700000007814', + 'ki': 'B36021DEB90C4EA607E408A92F3B024D', + 'label': 'm7814', + 'path': '/wavecom_13'}, + {'_hash': 'df1abec7704ebc89b2c062a69bd299cf3663ed9e', + 'imsi': '901700000007815', + 'ki': '1E209F6F839F9195778C4F96BE281A24', + 'label': 'm7815', + 'path': '/wavecom_14'}, + {'_hash': '11df1e4c7708157e5b89020c757763f58d6e610b', + 'imsi': '901700000007816', + 'ki': 'BF827D219E739DD189F6F59E60D6455C', + 'label': 'm7816', + 'path': '/wavecom_15'}], + 'nitb_iface': [{'_hash': 'cde1debf28f07f94f92c761b4b7c6bf35785ced4', + 'addr': '10.42.42.1'}, + {'_hash': 'fd103b22c7cf2480d609150e06f4bbd92ac78d8c', + 'addr': '10.42.42.2'}, + {'_hash': '1c614d6210c551d142aadca8f25e1534ebb2a70f', + 'addr': '10.42.42.3'}]} +*** end: all resources + +- request some resources +--- (want='nitb_iface'): DBG: Looking for 1 x nitb_iface , candidates: 3 +--- (want='arfcn'): DBG: Looking for 2 x arfcn , candidates: 10 +--- (want='bts'): DBG: Looking for 2 x bts , candidates: 3 +--- (want='modem'): DBG: Looking for 2 x modem , candidates: 16 +~~~ currently reserved: +arfcn: +- _hash: e620569450f8259b3f0212ec19c285dd07df063c + _reserved_by: testowner-123-1490837279 + arfcn: '512' + band: GSM-1800 +- _hash: 022621e513c5a5bf33b77430a1e9c886be676fa1 + _reserved_by: testowner-123-1490837279 + arfcn: '514' + band: GSM-1800 +bts: +- _hash: a7c6d2ebaeb139e8c2e7d45c3495d046d7439007 + _reserved_by: testowner-123-1490837279 + addr: 10.42.42.114 + band: GSM-1800 + label: sysmoBTS 1002 + type: sysmo + unit_id: '1' +- _hash: 02540ab9eb556056a0b4d28443bc9f4793f6d549 + _reserved_by: testowner-123-1490837279 + addr: 10.42.42.115 + band: GSM-1800 + label: octBTS 3000 + trx: + - hwaddr: 00:0c:90:32:b5:8a + type: oct + unit_id: '5' +modem: +- _hash: 19c69e45aa090fb511446bd00797690aa82ff52f + _reserved_by: testowner-123-1490837279 + imsi: '901700000007801' + ki: D620F48487B1B782DA55DF6717F08FF9 + label: m7801 + path: /wavecom_0 +- _hash: e1a46516a1fb493b2617ab14fc1693a9a45ec254 + _reserved_by: testowner-123-1490837279 + imsi: '901700000007802' + ki: 47FDB2D55CE6A10A85ABDAD034A5B7B3 + label: m7802 + path: /wavecom_1 +nitb_iface: +- _hash: cde1debf28f07f94f92c761b4b7c6bf35785ced4 + _reserved_by: testowner-123-1490837279 + addr: 10.42.42.1 + +~~~ end: currently reserved + +~~~ currently reserved: +{} + +~~~ end: currently reserved + diff --git a/selftest/resource_test.py b/selftest/resource_test.py new file mode 100755 index 0000000..2d0f880 --- /dev/null +++ b/selftest/resource_test.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 + +import tempfile +import os +import pprint +import shutil +import atexit +import _prep +from osmo_gsm_tester import config, log, resource, util + +workdir = util.get_tempdir() + +# override config locations to make sure we use only the test conf +config.ENV_CONF = './conf' + +log.get_process_id = lambda: '123-1490837279' + +print('- expect solutions:') +pprint.pprint( + resource.solve([ [0, 1, 2], + [0, 1, 2], + [0, 1, 2] ]) ) +pprint.pprint( + resource.solve([ [0, 1, 2], + [0, 1], + [0, 2] ]) ) # == [0, 1, 2] +pprint.pprint( + resource.solve([ [0, 1, 2], + [0], + [0, 2] ]) ) # == [1, 0, 2] +pprint.pprint( + resource.solve([ [0, 1, 2], + [2], + [0, 2] ]) ) # == [1, 2, 0] + +print('- expect failure to solve:') +try: + resource.solve([ [0, 2], + [2], + [0, 2] ]) + assert False +except resource.NoResourceExn as e: + print(e) + +print('- test removing a Resources list from itself') +try: + r = resource.Resources({ 'k': [ {'a': 1, 'b': 2}, {'a': 3, 'b': 4}, ], + 'i': [ {'c': 1, 'd': 2}, {'c': 3, 'd': 4}, ] }) + r.drop(r) + assert False +except RuntimeError as e: + print('ok, caused exception: %r' % e) + +print('- test removing a Resources list from one with the same list in it') +r = resource.Resources({ 'k': [ {'a': 1, 'b': 2}, {'a': 3, 'b': 4}, ], + 'i': [ {'c': 1, 'd': 2}, {'c': 3, 'd': 4}, ] }) +r.drop({ 'k': r.get('k'), 'i': r.get('i') }) +assert not r + +print('- test resources config and state dir:') +resources_conf = os.path.join(_prep.script_dir, 'resource_test', 'etc', + 'resources.conf') + +state_dir = config.get_state_dir() +rrfile = state_dir.child(resource.RESERVED_RESOURCES_FILE) + +pool = resource.ResourcesPool() + +print('*** all resources:') +pprint.pprint(pool.all_resources) +print('*** end: all resources\n') + +print('- request some resources') +want = { + 'nitb_iface': [ { 'times': 1 } ], + 'bts': [ { 'type': 'sysmo', 'times': 1 }, { 'type': 'oct', 'times': 1 } ], + 'arfcn': [ { 'band': 'GSM-1800', 'times': 2 } ], + 'modem': [ { 'times': 2 } ], + } + +origin = log.Origin('testowner') + +resources = pool.reserve(origin, want) + +print('~~~ currently reserved:') +with open(rrfile, 'r') as f: + print(f.read()) +print('~~~ end: currently reserved\n') + +resources.free() + +print('~~~ currently reserved:') +with open(rrfile, 'r') as f: + print(f.read()) +print('~~~ end: currently reserved\n') + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/selftest/suite_test.err b/selftest/suite_test.err new file mode 100644 index 0000000..e69de29 diff --git a/selftest/suite_test.ok b/selftest/suite_test.ok new file mode 100644 index 0000000..c0232dd --- /dev/null +++ b/selftest/suite_test.ok @@ -0,0 +1,40 @@ +- non-existing suite dir +--- -: ERR: RuntimeError: Suite not found: 'does_not_exist' in ./suite_test +- no suite.conf +cnf empty_dir: DBG: reading suite.conf [empty_dir↪empty_dir] +--- ./suite_test/empty_dir/suite.conf: ERR: FileNotFoundError: [Errno 2] No such file or directory: './suite_test/empty_dir/suite.conf' [empty_dir↪./suite_test/empty_dir/suite.conf] +- valid suite dir +cnf test_suite: DBG: reading suite.conf [test_suite↪test_suite] +defaults: + timeout: 60s +resources: + bts: + - times: '1' + modem: + - times: '2' + nitb_iface: + - times: '1' + +- run hello world test +tst test_suite: reserving resources... +--- (want='nitb_iface'): DBG: Looking for 1 x nitb_iface , candidates: 3 +--- (want='modem'): DBG: Looking for 2 x modem , candidates: 16 +--- (want='bts'): DBG: Looking for 1 x bts , candidates: 3 +tst hello_world.py: START [test_suite↪hello_world.py] +tst hello_world.py:3: hello world [test_suite↪hello_world.py:3] +tst hello_world.py:4: I am 'test_suite' / 'hello_world.py:4' [test_suite↪hello_world.py:4] +tst hello_world.py:5: one [test_suite↪hello_world.py:5] +tst hello_world.py:5: two [test_suite↪hello_world.py:5] +tst hello_world.py:5: three [test_suite↪hello_world.py:5] +tst hello_world.py: PASS [test_suite↪hello_world.py] +pass: all 1 tests passed. + +- a test with an error +tst test_error.py: START [test_suite↪test_error.py] [suite.py:96] +tst test_error.py:3: I am 'test_suite' / 'test_error.py:3' [test_suite↪test_error.py:3] [test_error.py:3] +tst test_error.py:5: FAIL [test_suite↪test_error.py:5] [suite.py:108] +tst test_error.py:5: ERR: AssertionError: [test_suite↪test_error.py:5] [test_error.py:5: assert False] +FAIL: 1 of 1 tests failed: + test_error.py + +- graceful exit. diff --git a/selftest/suite_test.py b/selftest/suite_test.py new file mode 100755 index 0000000..8c0e6e8 --- /dev/null +++ b/selftest/suite_test.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +import os +import _prep +from osmo_gsm_tester import log, suite, config + +config.ENV_CONF = os.path.join(os.getcwd(), 'conf') + +#log.style_change(trace=True) + +print('- non-existing suite dir') +assert(log.run_logging_exceptions(suite.load, 'does_not_exist') == None) + +print('- no suite.conf') +assert(log.run_logging_exceptions(suite.load, 'empty_dir') == None) + +print('- valid suite dir') +example_suite_dir = os.path.join('test_suite') +s_def = suite.load(example_suite_dir) +assert(isinstance(s_def, suite.SuiteDefinition)) +print(config.tostr(s_def.conf)) + +print('- run hello world test') +s = suite.SuiteRun(None, s_def) +results = s.run_tests('hello_world.py') +print(str(results)) + +log.style_change(src=True) +#log.style_change(trace=True) +print('\n- a test with an error') +results = s.run_tests('test_error.py') +print(str(results)) + +print('\n- graceful exit.') +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/selftest/suite_test/empty_dir/.unrelated_file b/selftest/suite_test/empty_dir/.unrelated_file new file mode 100644 index 0000000..e69de29 diff --git a/selftest/suite_test/test_suite/hello_world.py b/selftest/suite_test/test_suite/hello_world.py new file mode 100644 index 0000000..9f3bf4a --- /dev/null +++ b/selftest/suite_test/test_suite/hello_world.py @@ -0,0 +1,5 @@ +from osmo_gsm_tester.test import * + +print('hello world') +print('I am %r / %r' % (suite.name(), test.name())) +print('one\ntwo\nthree') diff --git a/selftest/suite_test/test_suite/mo_mt_sms.py b/selftest/suite_test/test_suite/mo_mt_sms.py new file mode 100644 index 0000000..cf44357 --- /dev/null +++ b/selftest/suite_test/test_suite/mo_mt_sms.py @@ -0,0 +1,18 @@ +nitb_iface = resources.nitb_iface() +nitb = resources.nitb() +bts = resources.bts() +ms_mo = resources.modem() +ms_mt = resources.modem() + +nitb.start(nitb_iface) +bts.start(nitb) + +nitb.add_subscriber(ms_mo, resources.msisdn()) +nitb.add_subscriber(ms_mt, resources.msisdn()) + +ms_mo.start() +ms_mt.start() +wait(nitb.subscriber_attached, ms_mo, ms_mt) + +sms = ms_mo.sms_send(ms_mt.msisdn) +wait(nitb.sms_received, sms) diff --git a/selftest/suite_test/test_suite/mo_sms.py b/selftest/suite_test/test_suite/mo_sms.py new file mode 100644 index 0000000..d9517dd --- /dev/null +++ b/selftest/suite_test/test_suite/mo_sms.py @@ -0,0 +1,20 @@ +nitb_iface = resources.nitb_iface() +nitb = resources.nitb() +bts = resources.bts() +ms_ext = resources.msisdn() +fake_ext = resources.msisdn() +ms = resources.modem() + +nitb.configure(nitb_iface, bts) +bts.configure(nitb) + +nitb.start() +bts.start() + +nitb.add_fake_ext(fake_ext) +nitb.add_subscriber(ms, ms_ext) + +ms.start() +wait(nitb.subscriber_attached, ms) +sms = ms.sms_send(fake_ext) +wait(nitb.sms_received, sms) diff --git a/selftest/suite_test/test_suite/suite.conf b/selftest/suite_test/test_suite/suite.conf new file mode 100644 index 0000000..4a03379 --- /dev/null +++ b/selftest/suite_test/test_suite/suite.conf @@ -0,0 +1,10 @@ +resources: + nitb_iface: + - times: 1 + bts: + - times: 1 + modem: + - times: 2 + +defaults: + timeout: 60s diff --git a/selftest/suite_test/test_suite/test_error.py b/selftest/suite_test/test_suite/test_error.py new file mode 100755 index 0000000..17b05f4 --- /dev/null +++ b/selftest/suite_test/test_suite/test_error.py @@ -0,0 +1,5 @@ +from osmo_gsm_tester.test import * + +print('I am %r / %r' % (suite.name(), test.name())) + +assert False diff --git a/selftest/template_test.err b/selftest/template_test.err new file mode 100644 index 0000000..e69de29 diff --git a/selftest/template_test.ok b/selftest/template_test.ok new file mode 100644 index 0000000..879907d --- /dev/null +++ b/selftest/template_test.ok @@ -0,0 +1,149 @@ +- Testing: fill a config file with values +cnf Templates: DBG: rendering osmo-nitb.cfg.tmpl [osmo-nitb.cfg.tmpl↪Templates] +! Configuration rendered by osmo-gsm-tester +password foo +! +log stderr + logging filter all 1 + logging color 0 + logging print category 0 + logging print extended-timestamp 1 + logging level all debug +! +line vty + no login + bind val_nitb_iface_addr +! +e1_input + e1_line 0 driver ipa + ipa bind val_nitb_iface_addr +network + network country code val_mcc + mobile network code val_mnc + short name val_short_name + long name val_long_name + auth policy val_auth_policy + location updating reject cause 13 + encryption val_encryption + neci 1 + rrlp mode none + mm info 1 + handover 0 + handover window rxlev averaging 10 + handover window rxqual averaging 1 + handover window rxlev neighbor averaging 10 + handover power budget interval 6 + handover power budget hysteresis 3 + handover maximum distance 9999 + timer t3101 10 + timer t3103 0 + timer t3105 0 + timer t3107 0 + timer t3109 4 + timer t3111 0 + timer t3113 60 + timer t3115 0 + timer t3117 0 + timer t3119 0 + timer t3141 0 + bts 0 + type val_type_bts0 + band val_band_bts0 + cell_identity 0 + location_area_code val_bts.location_area_code_bts0 + training_sequence_code 7 + base_station_id_code val_bts.base_station_id_code_bts0 + ms max power 15 + cell reselection hysteresis 4 + rxlev access min 0 + channel allocator ascending + rach tx integer 9 + rach max transmission 7 + ip.access unit_id val_bts.unit_id_bts0 0 + oml ip.access stream_id val_bts.stream_id_bts0 line 0 + gprs mode none + trx 0 + rf_locked 0 + arfcn val_trx_arfcn_trx0 + nominal power 23 + max_power_red val_trx_max_power_red_trx0 + rsl e1 tei 0 + timeslot 0 + phys_chan_config val_phys_chan_config_0 + timeslot 1 + phys_chan_config val_phys_chan_config_1 + timeslot 2 + phys_chan_config val_phys_chan_config_2 + timeslot 3 + phys_chan_config val_phys_chan_config_3 + trx 1 + rf_locked 0 + arfcn val_trx_arfcn_trx1 + nominal power 23 + max_power_red val_trx_max_power_red_trx1 + rsl e1 tei 0 + timeslot 0 + phys_chan_config val_phys_chan_config_0 + timeslot 1 + phys_chan_config val_phys_chan_config_1 + timeslot 2 + phys_chan_config val_phys_chan_config_2 + timeslot 3 + phys_chan_config val_phys_chan_config_3 + bts 1 + type val_type_bts1 + band val_band_bts1 + cell_identity 0 + location_area_code val_bts.location_area_code_bts1 + training_sequence_code 7 + base_station_id_code val_bts.base_station_id_code_bts1 + ms max power 15 + cell reselection hysteresis 4 + rxlev access min 0 + channel allocator ascending + rach tx integer 9 + rach max transmission 7 + ip.access unit_id val_bts.unit_id_bts1 0 + oml ip.access stream_id val_bts.stream_id_bts1 line 0 + gprs mode none + trx 0 + rf_locked 0 + arfcn val_trx_arfcn_trx0 + nominal power 23 + max_power_red val_trx_max_power_red_trx0 + rsl e1 tei 0 + timeslot 0 + phys_chan_config val_phys_chan_config_0 + timeslot 1 + phys_chan_config val_phys_chan_config_1 + timeslot 2 + phys_chan_config val_phys_chan_config_2 + timeslot 3 + phys_chan_config val_phys_chan_config_3 + trx 1 + rf_locked 0 + arfcn val_trx_arfcn_trx1 + nominal power 23 + max_power_red val_trx_max_power_red_trx1 + rsl e1 tei 0 + timeslot 0 + phys_chan_config val_phys_chan_config_0 + timeslot 1 + phys_chan_config val_phys_chan_config_1 + timeslot 2 + phys_chan_config val_phys_chan_config_2 + timeslot 3 + phys_chan_config val_phys_chan_config_3 +smpp + local-tcp-ip val_nitb_iface_addr 2775 + system-id test + policy closed + esme test + password test + default-route +ctrl + bind val_nitb_iface_addr + +- Testing: expect to fail on invalid templates dir +sucess: setting non-existing templates dir raised RuntimeError + diff --git a/selftest/template_test.py b/selftest/template_test.py new file mode 100755 index 0000000..2b44ae5 --- /dev/null +++ b/selftest/template_test.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +import _prep + +import sys +import os + +from osmo_gsm_tester import template, log + +log.set_level(log.C_CNF, log.L_DBG) + +print('- Testing: fill a config file with values') + +mock_timeslot_list=( + { 'phys_chan_config': 'val_phys_chan_config_0' }, + { 'phys_chan_config': 'val_phys_chan_config_1' }, + { 'phys_chan_config': 'val_phys_chan_config_2' }, + { 'phys_chan_config': 'val_phys_chan_config_3' }, + ) + +mock_bts = { + 'type': 'val_type', + 'band': 'val_band', + 'location_area_code': 'val_bts.location_area_code', + 'base_station_id_code': 'val_bts.base_station_id_code', + 'ipa_unit_id': 'val_bts.unit_id', + 'stream_id': 'val_bts.stream_id', + 'trx_list': ( + dict(arfcn='val_trx_arfcn_trx0', + max_power_red='val_trx_max_power_red_trx0', + timeslot_list=mock_timeslot_list), + dict(arfcn='val_trx_arfcn_trx1', + max_power_red='val_trx_max_power_red_trx1', + timeslot_list=mock_timeslot_list), + ) +} + +def clone_mod(d, val_ext): + c = dict(d) + for name in c.keys(): + if isinstance(c[name], str): + c[name] = c[name] + val_ext + elif isinstance(c[name], dict): + c[name] = clone_mod(c[name], val_ext) + return c + +mock_bts0 = clone_mod(mock_bts, '_bts0') +mock_bts1 = clone_mod(mock_bts, '_bts1') + +vals = dict(nitb=dict( + net=dict( + mcc='val_mcc', + mnc='val_mnc', + short_name='val_short_name', + long_name='val_long_name', + auth_policy='val_auth_policy', + encryption='val_encryption', + bts_list=(mock_bts0, mock_bts1) + ), + ), + nitb_iface=dict(addr='val_nitb_iface_addr'), + ) + +print(template.render('osmo-nitb.cfg', vals)) + +print('- Testing: expect to fail on invalid templates dir') +try: + template.set_templates_dir('non-existing dir') + sys.stderr.write('Error: setting non-existing templates dir should raise RuntimeError\n') + assert(False) +except RuntimeError: + # not logging exception to omit non-constant path name from expected output + print('sucess: setting non-existing templates dir raised RuntimeError\n') + pass + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/selftest/template_test/osmo-nitb.cfg.tmpl b/selftest/template_test/osmo-nitb.cfg.tmpl new file mode 100644 index 0000000..3404b7f --- /dev/null +++ b/selftest/template_test/osmo-nitb.cfg.tmpl @@ -0,0 +1,87 @@ +! +! OpenBSC configuration saved from vty +! +password foo +! +log stderr + logging filter all 1 + logging color 0 + logging print category 0 + logging print extended-timestamp 1 + logging level all debug +! +line vty + no login + bind ${vty_bind_ip} +! +e1_input + e1_line 0 driver ipa + ipa bind ${abis_bind_ip} +network + network country code ${mcc} + mobile network code ${mnc} + short name ${net_name_short} + long name ${net_name_long} + auth policy ${net_auth_policy} + location updating reject cause 13 + encryption a5 ${encryption} + neci 1 + rrlp mode none + mm info 1 + handover 0 + handover window rxlev averaging 10 + handover window rxqual averaging 1 + handover window rxlev neighbor averaging 10 + handover power budget interval 6 + handover power budget hysteresis 3 + handover maximum distance 9999 + timer t3101 10 + timer t3103 0 + timer t3105 0 + timer t3107 0 + timer t3109 4 + timer t3111 0 + timer t3113 60 + timer t3115 0 + timer t3117 0 + timer t3119 0 + timer t3141 0 +smpp + local-tcp-ip ${smpp_bind_ip} 2775 + system-id test + policy closed + esme test + password test + default-route +ctrl + bind ${ctrl_bind_ip} +%for bts in bts_list: + bts ${loop.index} + type ${bts.type} + band ${bts.band} + cell_identity 0 + location_area_code ${bts.location_area_code} + training_sequence_code 7 + base_station_id_code ${bts.base_station_id_code} + ms max power 15 + cell reselection hysteresis 4 + rxlev access min 0 + channel allocator ascending + rach tx integer 9 + rach max transmission 7 + ip.access unit_id ${bts.unit_id} 0 + oml ip.access stream_id ${bts.stream_id} line 0 + gprs mode none +% for trx in bts.trx_list: + trx ${loop.index} + rf_locked 0 + arfcn ${trx.arfcn} + nominal power 23 + max_power_red ${trx.max_power_red} + rsl e1 tei 0 +% for ts in trx.timeslot_list: + timeslot ${loop.index} + phys_chan_config ${ts.phys_chan_config} +% endfor +% endfor +%endfor diff --git a/selftest/trial_test.err b/selftest/trial_test.err new file mode 100644 index 0000000..e69de29 diff --git a/selftest/trial_test.ok b/selftest/trial_test.ok new file mode 100644 index 0000000..0b3e31a --- /dev/null +++ b/selftest/trial_test.ok @@ -0,0 +1,16 @@ +- make a few trials dirs +[TMP]/first +[TMP]/second +[TMP]/third +- fetch trial dirs in order +[TMP]/first +['taken'] +[TMP]/second +[TMP]/third +- no more trial dirs left +None +- test checksum verification +- detect wrong checksum +ok, got RuntimeError("Checksum mismatch for 'trial_test/invalid_checksum/file2' vs. 'trial_test/invalid_checksum/checksums.md5' line 2",) +- detect missing file +ok, got RuntimeError("File listed in checksums file but missing in trials dir: 'trial_test/missing_file/file2' vs. 'trial_test/missing_file/checksums.md5' line 2",) diff --git a/selftest/trial_test.ok.ign b/selftest/trial_test.ok.ign new file mode 100644 index 0000000..38a82ce --- /dev/null +++ b/selftest/trial_test.ok.ign @@ -0,0 +1,2 @@ +/tmp/[^/]* [TMP] +....-..-.._..-..-.. [TIMESTAMP] diff --git a/selftest/trial_test.py b/selftest/trial_test.py new file mode 100755 index 0000000..ba3f01b --- /dev/null +++ b/selftest/trial_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import time +import _prep +import os +from osmo_gsm_tester import util +from osmo_gsm_tester.trial import Trial + +workdir = util.get_tempdir() + +trials_dir = util.Dir(workdir) + +print('- make a few trials dirs') +print(trials_dir.mkdir('first')) +time.sleep(1) +print(trials_dir.mkdir('second')) +time.sleep(1) +print(trials_dir.mkdir('third')) + +print('- fetch trial dirs in order') +t = Trial.next(trials_dir) +print(t) +print(repr(sorted(t.dir.children()))) +print(Trial.next(trials_dir)) +print(Trial.next(trials_dir)) + +print('- no more trial dirs left') +print(repr(Trial.next(trials_dir))) + +print('- test checksum verification') +d = util.Dir('trial_test') +t = Trial(d.child('valid_checksums')) +t.verify() + +print('- detect wrong checksum') +t = Trial(d.child('invalid_checksum')) +try: + t.verify() +except RuntimeError as e: + print('ok, got %r' % e) + +print('- detect missing file') +t = Trial(d.child('missing_file')) +try: + t.verify() +except RuntimeError as e: + print('ok, got %r' % e) + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/selftest/trial_test/invalid_checksum/checksums.md5 b/selftest/trial_test/invalid_checksum/checksums.md5 new file mode 100644 index 0000000..90d3547 --- /dev/null +++ b/selftest/trial_test/invalid_checksum/checksums.md5 @@ -0,0 +1,3 @@ +5149d403009a139c7e085405ef762e1a file1 +3d709e89c8ce201e3c928eb917989aef file2 +60b91f1875424d3b4322b0fdd0529d5d file3 diff --git a/selftest/trial_test/invalid_checksum/file1 b/selftest/trial_test/invalid_checksum/file1 new file mode 100644 index 0000000..e212970 --- /dev/null +++ b/selftest/trial_test/invalid_checksum/file1 @@ -0,0 +1 @@ +file1 diff --git a/selftest/trial_test/invalid_checksum/file2 b/selftest/trial_test/invalid_checksum/file2 new file mode 100644 index 0000000..34ccdac --- /dev/null +++ b/selftest/trial_test/invalid_checksum/file2 @@ -0,0 +1 @@ +no no no diff --git a/selftest/trial_test/invalid_checksum/file3 b/selftest/trial_test/invalid_checksum/file3 new file mode 100644 index 0000000..7c8ac2f --- /dev/null +++ b/selftest/trial_test/invalid_checksum/file3 @@ -0,0 +1 @@ +file3 diff --git a/selftest/trial_test/missing_file/checksums.md5 b/selftest/trial_test/missing_file/checksums.md5 new file mode 100644 index 0000000..90d3547 --- /dev/null +++ b/selftest/trial_test/missing_file/checksums.md5 @@ -0,0 +1,3 @@ +5149d403009a139c7e085405ef762e1a file1 +3d709e89c8ce201e3c928eb917989aef file2 +60b91f1875424d3b4322b0fdd0529d5d file3 diff --git a/selftest/trial_test/missing_file/file1 b/selftest/trial_test/missing_file/file1 new file mode 100644 index 0000000..e212970 --- /dev/null +++ b/selftest/trial_test/missing_file/file1 @@ -0,0 +1 @@ +file1 diff --git a/selftest/trial_test/missing_file/file3 b/selftest/trial_test/missing_file/file3 new file mode 100644 index 0000000..7c8ac2f --- /dev/null +++ b/selftest/trial_test/missing_file/file3 @@ -0,0 +1 @@ +file3 diff --git a/selftest/trial_test/valid_checksums/checksums.md5 b/selftest/trial_test/valid_checksums/checksums.md5 new file mode 100644 index 0000000..90d3547 --- /dev/null +++ b/selftest/trial_test/valid_checksums/checksums.md5 @@ -0,0 +1,3 @@ +5149d403009a139c7e085405ef762e1a file1 +3d709e89c8ce201e3c928eb917989aef file2 +60b91f1875424d3b4322b0fdd0529d5d file3 diff --git a/selftest/trial_test/valid_checksums/file1 b/selftest/trial_test/valid_checksums/file1 new file mode 100644 index 0000000..e212970 --- /dev/null +++ b/selftest/trial_test/valid_checksums/file1 @@ -0,0 +1 @@ +file1 diff --git a/selftest/trial_test/valid_checksums/file2 b/selftest/trial_test/valid_checksums/file2 new file mode 100644 index 0000000..6c493ff --- /dev/null +++ b/selftest/trial_test/valid_checksums/file2 @@ -0,0 +1 @@ +file2 diff --git a/selftest/trial_test/valid_checksums/file3 b/selftest/trial_test/valid_checksums/file3 new file mode 100644 index 0000000..7c8ac2f --- /dev/null +++ b/selftest/trial_test/valid_checksums/file3 @@ -0,0 +1 @@ +file3 diff --git a/selftest/util_test.err b/selftest/util_test.err new file mode 100644 index 0000000..e69de29 diff --git a/selftest/util_test.ok b/selftest/util_test.ok new file mode 100644 index 0000000..c2c5f87 --- /dev/null +++ b/selftest/util_test.ok @@ -0,0 +1,5 @@ +- expect the same hashes on every test run +a9993e364706816aba3e25717850c26c9cd0d89d +356a192b7913b04c54574d18c28d46e6395428ab +40bd001563085fc35165329ea1ff5c5ecbdbbeef +c129b324aee662b04eccf68babba85851346dff9 diff --git a/selftest/util_test.py b/selftest/util_test.py new file mode 100755 index 0000000..c517655 --- /dev/null +++ b/selftest/util_test.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +import _prep + +from osmo_gsm_tester.util import hash_obj + +print('- expect the same hashes on every test run') +print(hash_obj('abc')) +print(hash_obj(1)) +print(hash_obj([1, 2, 3])) +print(hash_obj({ 'k': [ {'a': 1, 'b': 2}, {'a': 3, 'b': 4}, ], + 'i': [ {'c': 1, 'd': 2}, {'c': 3, 'd': 4}, ] })) + -- cgit v1.2.3