module RemsimBankd_Tests { /* Integration Tests for osmo-remsim-bankd * (C) 2019 by Harald Welte * All rights reserved. * * Released under the terms of GNU General Public License, Version 2 or * (at your option) any later version. * * SPDX-License-Identifier: GPL-2.0-or-later * * This test suite tests osmo-remsim-bankd by attaching to the external interfaces * such as RSPRO for simulated clients + server. */ import from Osmocom_Types all; import from IPA_Emulation all; import from Misc_Helpers all; import from VPCD_Types all; import from VPCD_CodecPort all; import from VPCD_Adapter all; import from RSPRO all; import from RSRES all; import from RSPRO_Types all; import from RSPRO_Server all; import from REMSIM_Tests all; modulepar { integer mp_bank_id := 1; integer mp_num_slots := 8; } /* We implement a RSPRO server to simulate the remsim-server and a RSPRO client to simulate a remsim-client connecting to bankd */ type component bankd_test_CT extends rspro_server_CT, rspro_client_CT, VPCD_Adapter_CT { } private function f_init(boolean start_client := false) runs on bankd_test_CT { var ComponentIdentity srv_comp_id := valueof(ts_CompId(remsimServer, "ttcn-server")); f_rspro_srv_init(0, mp_server_ip, mp_server_port, srv_comp_id); if (start_client) { f_init_client(0); } } private function f_init_client(integer i := 0) runs on rspro_client_CT { var ComponentIdentity clnt_comp_id := valueof(ts_CompId(remsimClient, "ttcn-client")); f_rspro_init(rspro[0], mp_bankd_ip, mp_bankd_port, clnt_comp_id, 0); rspro[0].rspro_client_slot := { clientId := 23+i, slotNr := 0 }; } /* Test if the bankd disconnects the TCP/IPA session if we don't respond to connectBankReq */ testcase TC_connectBankReq_timeout() runs on bankd_test_CT { timer T := 20.0; f_init(); f_rspro_srv_exp(tr_RSPRO_ConnectBankReq(?, ?, ?)); T.start; alt { [] RSPRO_SRV[0].receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_DOWN)) { setverdict(pass); } [] RSPRO_SRV[0].receive { repeat; } [] T.timeout { setverdict(fail, "Timeout waiting for disconnect"); } } } /* accept an inbound connection from bankd to simulated server */ testcase TC_connectBankReq() runs on bankd_test_CT { f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* attempt to create a mapping */ testcase TC_createMapping() runs on bankd_test_CT { f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; var ClientSlot cs := { clientId := 23, slotNr := 42 }; f_rspro_srv_create_slotmap(cs, bs); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* attempt to create a mapping for a slot that already has a mapping */ testcase TC_createMapping_busySlot() runs on bankd_test_CT { f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; var ClientSlot cs := { clientId := 23, slotNr := 42 }; /* create the mapping the first time */ f_rspro_srv_create_slotmap(cs, bs); /* re-create the mapping a second time */ f_rspro_srv_create_slotmap(cs, bs); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* attempt to create a mapping for an out-of-range slot number */ testcase TC_createMapping_invalidSlot() runs on bankd_test_CT { f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); var BankSlot bs := { bankId := mp_bank_id, slotNr := 200 }; var ClientSlot cs := { clientId := 23, slotNr := 42 }; f_rspro_srv_create_slotmap(cs, bs, exp_res := illegalSlotId); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* attempt to create a mapping for an invalid bankID */ testcase TC_createMapping_invalidBank() runs on bankd_test_CT { f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); var BankSlot bs := { bankId := 200, slotNr := 0 }; var ClientSlot cs := { clientId := 23, slotNr := 42 }; f_rspro_srv_create_slotmap(cs, bs, exp_res := illegalBankId); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* attempt to remove a non-existant mapping */ testcase TC_removeMapping_unknownMap() runs on bankd_test_CT { f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; var ClientSlot cs := { clientId := 23, slotNr := 42 }; f_rspro_srv_remove_slotmap(cs, bs, exp_res := unknownSlotmap); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* add and then remove a mapping, expect both to be successful */ testcase TC_removeMapping() runs on bankd_test_CT { f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; var ClientSlot cs := { clientId := 23, slotNr := 42 }; f_rspro_srv_create_slotmap(cs, bs); f_rspro_srv_remove_slotmap(cs, bs); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* connect from client to bankd without specifying a clientId */ testcase TC_clientConnect_missingSlot() runs on bankd_test_CT { f_init_client(0); RSPRO[0].send(ts_RSPRO_ConnectClientReq(rspro[0].rspro_id, omit)); f_rspro_exp(tr_RSPRO_ConnectClientRes(?, ResultCode:illegalClientId), 0); f_rspro_exp_disconnect(0); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* connect from client to bankd using a clientId for which bankd has no map */ testcase TC_clientConnect_unknown() runs on bankd_test_CT { f_init_client(0); f_rspro_connect_client(0, tr_Status_ok_or_nocard); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* connect from client to bankd using a clientSlot for which bankd has no map */ /* first connect client, then later add matching mapping from server */ testcase TC_clientConnect_createMapping() runs on bankd_test_CT { f_init_client(0); f_rspro_connect_client(0, tr_Status_ok_or_nocard); f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs); f_sleep(10.0); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* first add mapping, then connect matching client */ testcase TC_createMapping_clientConnect() runs on bankd_test_CT { /* FIXME: this would only be done in f_init_client(), but we need it before */ rspro[0].rspro_client_slot := { clientId := 23+0, slotNr := 0 }; f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs); f_sleep(1.0); f_init_client(0); f_rspro_connect_client(0, tr_Status_ok_or_nocard); /* FIXME: how to determine that bank correctly mapped us */ Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* first add mapping, then connect client, then change mapping (expect disconnect) */ testcase TC_createMapping_connectClient_changeMapping() runs on bankd_test_CT { /* FIXME: this would only be done in f_init_client(), but we need it before */ rspro[0].rspro_client_slot := { clientId := 23+0, slotNr := 0 }; f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); /* create slotmap */ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs); f_sleep(1.0); /* connect client */ f_init_client(0); f_rspro_connect_client(0, tr_Status_ok_or_nocard); /* FIXME: how to determine that bank correctly mapped us */ /* create another mapping for the same bank-slot */ var ClientSlot cs := { clientId := 987, slotNr := 654 }; f_rspro_srv_create_slotmap(cs, bs); /* expect client to be disconnected */ timer T := 5.0; T.start; alt { [] RSPRO[0].receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_DOWN)) { setverdict(pass); } [] RSPRO[0].receive { setverdict(fail, "Unexpected RSPRO on client connection"); } [] T.timeout { setverdict(fail, "Timeout waiting for client being disconnected"); } } Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* first add mapping, then connect client, then re-crete mapping (expect no disconnect) */ testcase TC_createMapping_connectClient_recreateMapping() runs on bankd_test_CT { /* FIXME: this would only be done in f_init_client(), but we need it before */ rspro[0].rspro_client_slot := { clientId := 23+0, slotNr := 0 }; f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); /* create slotmap */ var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs); f_sleep(1.0); /* connect client */ f_init_client(0); f_rspro_connect_client(0, tr_Status_ok_or_nocard); /* FIXME: how to determine that bank correctly mapped us */ /* re-create same mapping for the same bank-slot */ f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs); /* expect client not to be disconnected */ timer T := 5.0; T.start; alt { [] RSPRO[0].receive(tr_ASP_IPA_EV(ASP_IPA_EVENT_DOWN)) { setverdict(fail, "Unexpected client disconnect"); } [] RSPRO[0].receive { repeat; } [] T.timeout { setverdict(pass); } } Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* add mapping, connect matching client, disconnect + reconnect */ testcase TC_createMapping_clientReconnect() runs on bankd_test_CT { /* FIXME: this would only be done in f_init_client(), but we need it before */ rspro[0].rspro_client_slot := { clientId := 23+0, slotNr := 0 }; f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs); f_sleep(1.0); f_init_client(0); f_rspro_connect_client(0, tr_Status_ok_or_nocard); /* TODO: works only with empty slot, as setAtrReq isn't handled */ /* FIXME: how to determine that bank correctly mapped us */ f_sleep(5.0); f_rspro_fini(rspro[0], 0); f_init_client(0); f_rspro_connect_client(0, tr_Status_ok_or_nocard); /* FIXME: how to determine that bank correctly mapped us */ f_sleep(5.0); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } /* remove mapping while client is connected */ testcase TC_removeMapping_connected() runs on bankd_test_CT { f_init_client(0); f_rspro_connect_client(0, tr_Status_ok_or_nocard); /* TODO: works only with empty slot, as setAtrReq isn't handled */ f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs); /* FIXME: how to determine that bank correctly mapped us */ f_sleep(5.0); f_rspro_srv_remove_slotmap(rspro[0].rspro_client_slot, bs); f_rspro_exp_disconnect(0); Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } private altstep as_vpcd() runs on VPCD_Adapter_CT { [] VPCD.receive(tr_VPCD_Recv(g_vpcd_conn_id, tr_VPCD_CTRL_ATR)) { f_vpcd_send(ts_VPCD_DATA('3B9F96801FC78031A073BE21136743200718000001A5'O)); repeat; } [] VPCD.receive(tr_VPCD_Recv(g_vpcd_conn_id, tr_VPCD_CTRL_OFF)) { repeat; } [] VPCD.receive(tr_VPCD_Recv(g_vpcd_conn_id, tr_VPCD_CTRL_ON)) { repeat; } } /* transceive a TPDU from modem to card (and back) */ function f_rspro_xceive_mdm2card_vpcd(integer idx, BankSlot bs, template (value) octetstring data, template (value) TpduFlags flags, template (value) octetstring res) runs on bankd_test_CT return octetstring { var RsproPDU rx; RSPRO[idx].send(ts_RSPRO_TpduModemToCard(rspro[idx].rspro_client_slot, bs, flags, data)); f_vpcd_exp(tr_VPCD_DATA(data)); f_vpcd_send(ts_VPCD_DATA(res)); rx := f_rspro_exp(tr_RSPRO_TpduCardToModem(bs, rspro[idx].rspro_client_slot, ?, ?)); if (rx.msg.tpduCardToModem.data != valueof(res)) { setverdict(fail, "Expected ", res, " from card, but got ", rx.msg.tpduCardToModem.data); } return rx.msg.tpduCardToModem.data; } /* first add mapping, then connect matching client and exchange some TPDUs */ testcase TC_createMapping_exchangeTPDU() runs on bankd_test_CT { /* FIXME: this would only be done in f_init_client(), but we need it before */ rspro[0].rspro_client_slot := { clientId := 23+0, slotNr := 0 }; VPCD_Adapter.f_connect(); activate(as_vpcd()); f_sleep(2.0); f_init(); as_connectBankReq(bid := mp_bank_id, nslots := mp_num_slots); f_rspro_srv_reset_state(ok); var BankSlot bs := { bankId := mp_bank_id, slotNr := 0 }; f_rspro_srv_create_slotmap(rspro[0].rspro_client_slot, bs); f_sleep(1.0); f_init_client(0); f_rspro_connect_client(0, ok); /* FIXME: how to determine that bank correctly mapped us */ f_rspro_exp(tr_RSPRO_SetAtrReq(rspro[0].rspro_client_slot, ?)); var TpduFlags f := {tpduHeaderPresent:=true, finalPart:=true, procByteContinueTx:=false, procByteContinueRx:=false}; for (var integer i := 0; i < 10; i:=i+1) { f_rspro_xceive_mdm2card_vpcd(0, bs, 'A0A40000023F00'O, f, '9000'O); } Misc_Helpers.f_shutdown(__BFILE__, __LINE__, pass); } control { execute( TC_connectBankReq_timeout() ); execute( TC_connectBankReq() ); execute( TC_createMapping() ); execute( TC_createMapping_busySlot() ); execute( TC_createMapping_invalidSlot() ); execute( TC_createMapping_invalidBank() ); execute( TC_removeMapping_unknownMap() ); execute( TC_removeMapping() ); execute( TC_clientConnect_missingSlot() ); execute( TC_clientConnect_unknown() ); execute( TC_clientConnect_createMapping() ); execute( TC_createMapping_clientConnect() ); execute( TC_createMapping_clientReconnect() ); execute( TC_removeMapping_connected() ); execute( TC_createMapping_connectClient_changeMapping() ); execute( TC_createMapping_connectClient_recreateMapping() ); execute( TC_createMapping_exchangeTPDU() ); } }