Change in osmo-bsc[master]: handover_test: implement as VTY shell

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

neels gerrit-no-reply at lists.osmocom.org
Wed Dec 23 01:59:08 UTC 2020


neels has submitted this change. ( https://gerrit.osmocom.org/c/osmo-bsc/+/21642 )

Change subject: handover_test: implement as VTY shell
......................................................................

handover_test: implement as VTY shell

Drop the string arrays, and move the 32 handover tests to separate
script files. Instead of the peculiar implementation and instead of
cryptic commands, implement the handover test scripts as a VTY.

handover_test.c now sets up a VTY with handover testing VTY commands. It
also features the complete and unabridged VTY configuration nodes of
osmo-bsc itself. That allows dropping various ho script commands.

Before:

  static char *test_case_14[] = {
      "Handover to congested cell, if RX level is below minimum\n\n"
      "The better neighbor cell is congested, so no handover is performed.\n"
      "If the RX level of the current cell drops below minimum acceptable\n"
      "level, the handover is performed.\n",

      "create-n-bts", "2",
      "create-ms", "0", "TCH/F", "AMR",
      "expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
      "set-min-free", "1", "TCH/F", "4",
      "set-min-free", "1", "TCH/H", "4",
      "meas-rep", "0","0","1","0", "10","0", "1","0","30",
      "expect-no-chan",
      "meas-rep", "0","0","1","0", "9","0", "1","0","30",
      "expect-chan", "1", "1",
      "ack-chan",
      "expect-ho", "0", "1",
      "ho-complete",
      "expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
      "expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
  }

After:

  # Handover to congested cell, if RX level is below minimum
  # The better neighbor cell is congested, so no handover is performed.
  # If the RX level of the current cell drops below minimum acceptable
  # level, the handover is performed.

  create-n-bts 2
  set-ts-use trx 0 0 states * TCH/F - - - - - -
  network
   bts 1
    handover2 min-free-slots tch/f 4
    handover2 min-free-slots tch/h 4
  meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 30
  expect-no-chan
  meas-rep lchan 0 0 1 0 rxlev 9 rxqual 0 ta 0 neighbors 30
  expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
  expect-ts-use trx 0 0 states * - - - - - - -
  expect-ts-use trx 1 0 states * TCH/F - - - - - -

Note how osmo-bsc's stock vty config nodes seamlessly integrate in the
test steps: just enter a configuration node, modify some values, and
indenting trivially takes care of exiting nodes correctly.

Running a test manually:
	./handover_test test_0123.ho_vty

Instead of calling each test separately in testsuite.at, have a
handover_tests.sh script that picks up new tests just by presence of
files named test*.ho_vty.

Rationale:

It was considered to move handover tests to the TTCN suite, but there is
an advantage in having these C tests: they run super fast and catch bugs
even in the gerrit verification job, potentially saving a lot of time.

It is a reality that I need more of these tests, for dynamic timeslots
and TCH/F <-> TCH/H switches. The way the handover tests are written, as
arrays of strings containing cryptic fixed-argument script commands, has
been a pain to work with from the start, and now I am no longer willing
to endure that pain.

Change-Id: Ie238ebe41039d3fa44c9699937589e000883e052
---
M tests/handover/Makefile.am
M tests/handover/handover_test.c
D tests/handover/handover_test.ok
A tests/handover/handover_tests.ok
A tests/handover/handover_tests.sh
A tests/handover/test0000.ho_vty
A tests/handover/test0001.ho_vty
A tests/handover/test0002.ho_vty
A tests/handover/test0003.ho_vty
A tests/handover/test0004.ho_vty
A tests/handover/test0005.ho_vty
A tests/handover/test0006.ho_vty
A tests/handover/test0007.ho_vty
A tests/handover/test0008.ho_vty
A tests/handover/test0009.ho_vty
A tests/handover/test0010.ho_vty
A tests/handover/test0011.ho_vty
A tests/handover/test0012.ho_vty
A tests/handover/test0013.ho_vty
A tests/handover/test0014.ho_vty
A tests/handover/test0015.ho_vty
A tests/handover/test0016.ho_vty
A tests/handover/test0017.ho_vty
A tests/handover/test0018.ho_vty
A tests/handover/test0019.ho_vty
A tests/handover/test0020.ho_vty
A tests/handover/test0021.ho_vty
A tests/handover/test0022.ho_vty
A tests/handover/test0023.ho_vty
A tests/handover/test0024.ho_vty
A tests/handover/test0025.ho_vty
A tests/handover/test0026.ho_vty
A tests/handover/test0027.ho_vty
A tests/handover/test0028.ho_vty
A tests/handover/test0029.ho_vty
A tests/handover/test0030.ho_vty
A tests/handover/test0031.ho_vty
A tests/handover/test0032.ho_vty
M tests/testsuite.at
39 files changed, 1,387 insertions(+), 1,714 deletions(-)

Approvals:
  laforge: Looks good to me, but someone else must approve
  neels: Looks good to me, approved
  pespin: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am
index eb2a26b..3951624 100644
--- a/tests/handover/Makefile.am
+++ b/tests/handover/Makefile.am
@@ -21,7 +21,9 @@
 	$(NULL)
 
 EXTRA_DIST = \
-	handover_test.ok \
+	handover_tests.sh \
+	handover_tests.ok \
+	$(srcdir)/test*.ho_vty \
 	neighbor_ident_test.ok \
 	neighbor_ident_test.err \
 	$(NULL)
@@ -130,3 +132,4 @@
 .PHONY: update_exp
 update_exp:
 	$(builddir)/neighbor_ident_test >$(srcdir)/neighbor_ident_test.ok 2>$(srcdir)/neighbor_ident_test.err
+	$(srcdir)/handover_tests.sh $(srcdir) $(builddir) -u
diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c
index 28d87db..33e5ff0 100644
--- a/tests/handover/handover_test.c
+++ b/tests/handover/handover_test.c
@@ -26,6 +26,7 @@
 #include <osmocom/core/application.h>
 #include <osmocom/core/select.h>
 #include <osmocom/core/talloc.h>
+#include <osmocom/vty/vty.h>
 
 #include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
 
@@ -49,6 +50,9 @@
 #include <osmocom/bsc/bsc_msc_data.h>
 #include <osmocom/bsc/bts.h>
 #include <osmocom/bsc/paging.h>
+#include <osmocom/bsc/vty.h>
+
+#include "../../bscconfig.h"
 
 void *ctx;
 
@@ -77,13 +81,21 @@
 /* measurement report */
 
 uint8_t meas_rep_ba = 0, meas_rep_valid = 1, meas_valid = 1, meas_multi_rep = 0;
-uint8_t meas_dl_rxlev = 0, meas_dl_rxqual = 0;
 uint8_t meas_ul_rxlev = 0, meas_ul_rxqual = 0;
-uint8_t meas_tx_power_ms = 0, meas_tx_power_bs = 0, meas_ta_ms = 0;
+uint8_t meas_tx_power_ms = 0, meas_tx_power_bs = 0;
 uint8_t meas_dtx_ms = 0, meas_dtx_bs = 0, meas_nr = 0;
-uint8_t meas_num_nc = 0, meas_rxlev_nc[6], meas_bsic_nc[6], meas_bcch_f_nc[6];
+char *codec_tch_f = NULL;
+char *codec_tch_h = NULL;
 
-static void gen_meas_rep(struct gsm_lchan *lchan)
+struct neighbor_meas {
+	uint8_t rxlev;
+	uint8_t bsic;
+	uint8_t bcch_f;
+};
+
+static void gen_meas_rep(struct gsm_lchan *lchan,
+			 uint8_t rxlev, uint8_t rxqual, uint8_t ta,
+			 int neighbors_count, struct neighbor_meas *neighbors)
 {
 	struct msgb *msg = msgb_alloc_headroom(256, 64, "RSL");
 	struct abis_rsl_dchan_hdr *dh;
@@ -108,7 +120,7 @@
 	msgb_tv_put(msg, RSL_IE_BS_POWER, meas_tx_power_bs);
 
 	l1i[0] = 0;
-	l1i[1] = meas_ta_ms;
+	l1i[1] = ta;
 	msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, sizeof(l1i), l1i);
 
 	buf = msgb_put(msg, 3);
@@ -123,49 +135,44 @@
 	gh->msg_type = GSM48_MT_RR_MEAS_REP;
 
 	/* measurement results */
-	mr->rxlev_full = meas_dl_rxlev;
-	mr->rxlev_sub = meas_dl_rxlev;
-	mr->rxqual_full = meas_dl_rxqual;
-	mr->rxqual_sub = meas_dl_rxqual;
+	mr->rxlev_full = rxlev;
+	mr->rxlev_sub = rxlev;
+	mr->rxqual_full = rxqual;
+	mr->rxqual_sub = rxqual;
 	mr->dtx_used = meas_dtx_ms;
 	mr->ba_used = meas_rep_ba;
-	mr->meas_valid = !meas_valid; /* 0 = valid */
-	if (meas_rep_valid) {
-		mr->no_nc_n_hi = meas_num_nc >> 2;
-		mr->no_nc_n_lo = meas_num_nc & 3;
-	} else {
-		/* no results for serving cells */
-		mr->no_nc_n_hi = 1;
-		mr->no_nc_n_lo = 3;
-	}
-	mr->rxlev_nc1 = meas_rxlev_nc[0];
-	mr->rxlev_nc2_hi = meas_rxlev_nc[1] >> 1;
-	mr->rxlev_nc2_lo = meas_rxlev_nc[1] & 1;
-	mr->rxlev_nc3_hi = meas_rxlev_nc[2] >> 2;
-	mr->rxlev_nc3_lo = meas_rxlev_nc[2] & 3;
-	mr->rxlev_nc4_hi = meas_rxlev_nc[3] >> 3;
-	mr->rxlev_nc4_lo = meas_rxlev_nc[3] & 7;
-	mr->rxlev_nc5_hi = meas_rxlev_nc[4] >> 4;
-	mr->rxlev_nc5_lo = meas_rxlev_nc[4] & 15;
-	mr->rxlev_nc6_hi = meas_rxlev_nc[5] >> 5;
-	mr->rxlev_nc6_lo = meas_rxlev_nc[5] & 31;
-	mr->bsic_nc1_hi = meas_bsic_nc[0] >> 3;
-	mr->bsic_nc1_lo = meas_bsic_nc[0] & 7;
-	mr->bsic_nc2_hi = meas_bsic_nc[1] >> 4;
-	mr->bsic_nc2_lo = meas_bsic_nc[1] & 15;
-	mr->bsic_nc3_hi = meas_bsic_nc[2] >> 5;
-	mr->bsic_nc3_lo = meas_bsic_nc[2] & 31;
-	mr->bsic_nc4 = meas_bsic_nc[3];
-	mr->bsic_nc5 = meas_bsic_nc[4];
-	mr->bsic_nc6 = meas_bsic_nc[5];
-	mr->bcch_f_nc1 = meas_bcch_f_nc[0];
-	mr->bcch_f_nc2 = meas_bcch_f_nc[1];
-	mr->bcch_f_nc3 = meas_bcch_f_nc[2];
-	mr->bcch_f_nc4 = meas_bcch_f_nc[3];
-	mr->bcch_f_nc5_hi = meas_bcch_f_nc[4] >> 1;
-	mr->bcch_f_nc5_lo = meas_bcch_f_nc[4] & 1;
-	mr->bcch_f_nc6_hi = meas_bcch_f_nc[5] >> 2;
-	mr->bcch_f_nc6_lo = meas_bcch_f_nc[5] & 3;
+	mr->meas_valid = 0; /* 0 = valid */
+	mr->no_nc_n_hi = neighbors_count >> 2;
+	mr->no_nc_n_lo = neighbors_count & 3;
+
+	mr->rxlev_nc1 = neighbors[0].rxlev;
+	mr->rxlev_nc2_hi = neighbors[1].rxlev >> 1;
+	mr->rxlev_nc2_lo = neighbors[1].rxlev & 1;
+	mr->rxlev_nc3_hi = neighbors[2].rxlev >> 2;
+	mr->rxlev_nc3_lo = neighbors[2].rxlev & 3;
+	mr->rxlev_nc4_hi = neighbors[3].rxlev >> 3;
+	mr->rxlev_nc4_lo = neighbors[3].rxlev & 7;
+	mr->rxlev_nc5_hi = neighbors[4].rxlev >> 4;
+	mr->rxlev_nc5_lo = neighbors[4].rxlev & 15;
+	mr->rxlev_nc6_hi = neighbors[5].rxlev >> 5;
+	mr->rxlev_nc6_lo = neighbors[5].rxlev & 31;
+	mr->bsic_nc1_hi = neighbors[0].bsic >> 3;
+	mr->bsic_nc1_lo = neighbors[0].bsic & 7;
+	mr->bsic_nc2_hi = neighbors[1].bsic >> 4;
+	mr->bsic_nc2_lo = neighbors[1].bsic & 15;
+	mr->bsic_nc3_hi = neighbors[2].bsic >> 5;
+	mr->bsic_nc3_lo = neighbors[2].bsic & 31;
+	mr->bsic_nc4 = neighbors[3].bsic;
+	mr->bsic_nc5 = neighbors[4].bsic;
+	mr->bsic_nc6 = neighbors[5].bsic;
+	mr->bcch_f_nc1 = neighbors[0].bcch_f;
+	mr->bcch_f_nc2 = neighbors[1].bcch_f;
+	mr->bcch_f_nc3 = neighbors[2].bcch_f;
+	mr->bcch_f_nc4 = neighbors[3].bcch_f;
+	mr->bcch_f_nc5_hi = neighbors[4].bcch_f >> 1;
+	mr->bcch_f_nc5_lo = neighbors[4].bcch_f & 1;
+	mr->bcch_f_nc6_hi = neighbors[5].bcch_f >> 2;
+	mr->bcch_f_nc6_lo = neighbors[5].bcch_f & 3;
 
 	msg->dst = lchan->ts->trx->bts->c0->rsl_link;
 	msg->l2h = (unsigned char *)dh;
@@ -195,7 +202,7 @@
 	"c+s4", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "TCH/H", "TCH/H", "-",
 };
 
-static struct gsm_bts *create_bts(int num_trx, const char * const *ts_args)
+static struct gsm_bts *_create_bts(int num_trx, const char * const *ts_args, int ts_args_count)
 {
 	static int arfcn = 870;
 	struct gsm_bts *bts;
@@ -205,15 +212,10 @@
 	struct gsm_bts_trx *trx;
 
 	fprintf(stderr, "- Creating BTS %d, %d TRX\n", bsc_gsmnet->num_bts, num_trx);
-	for (trx_i = 0; trx_i < num_trx; trx_i++) {
-		for (i = 0; i < 8; i++)
-			fprintf(stderr, "\t%s", ts_args[8*trx_i + i]);
-		fprintf(stderr, "\n");
-	}
 
 	bts = bsc_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_UNKNOWN, 0x3f);
 	if (!bts) {
-		printf("No resource for bts1\n");
+		fprintf(stderr, "No resource for bts1\n");
 		return NULL;
 	}
 
@@ -241,13 +243,21 @@
 
 		/* 4 full rate and 4 half rate channels */
 		for (i = 0; i < 8; i++) {
-			trx->ts[i].pchan_from_config = pchan_from_str(ts_args[trx_i * 8 + i]);
+			int arg_i = trx_i * 8 + i;
+			const char *ts_arg;
+			if (arg_i >= ts_args_count)
+				ts_arg = bts_default_ts[i];
+			else
+				ts_arg = ts_args[arg_i];
+			fprintf(stderr, "\t%s", ts_arg);
+			trx->ts[i].pchan_from_config = pchan_from_str(ts_arg);
 			if (trx->ts[i].pchan_from_config == GSM_PCHAN_NONE)
 				continue;
 			trx->ts[i].mo.nm_state.operational = NM_OPSTATE_ENABLED;
 			trx->ts[i].mo.nm_state.availability = NM_AVSTATE_OK;
 			trx->ts[i].mo.nm_state.administrative = NM_STATE_UNLOCKED;
 		}
+		fprintf(stderr, "\n");
 
 		for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
 			/* make sure ts->lchans[] get initialized */
@@ -303,22 +313,15 @@
 	}
 }
 
-bool expect_ts_use(int bts_nr, int trx_nr, const char * const *ts_use)
+bool _expect_ts_use(struct gsm_bts *bts, struct gsm_bts_trx *trx, const char * const *ts_use)
 {
-	struct gsm_bts *bts;
-	struct gsm_bts_trx *trx;
 	int i;
 	int mismatching_ts = -1;
-	bts = gsm_bts_num(bsc_gsmnet, bts_nr);
-	OSMO_ASSERT(bts);
-	trx = gsm_bts_trx_num(bts, trx_nr);
-	OSMO_ASSERT(trx);
 
-	fprintf(stderr, "Expect TS use:");
+	fprintf(stderr, "bts %d trx %d: expect:", bts->nr, trx->nr);
 	for (i = 0; i < 8; i++)
 		fprintf(stderr, "\t%s", ts_use[i]);
-	fprintf(stderr, "\n");
-	fprintf(stderr, "   Got TS use:");
+	fprintf(stderr, "\nbts %d trx %d:    got:", bts->nr, trx->nr);
 
 	for (i = 0; i < 8; i++) {
 		struct gsm_bts_trx_ts *ts = &trx->ts[i];
@@ -328,14 +331,14 @@
 
 		if (!strcmp(ts_use[i], "*"))
 			continue;
-		if (strcmp(ts_use[i], use) && mismatching_ts < 0)
+		if (strcasecmp(ts_use[i], use) && mismatching_ts < 0)
 			mismatching_ts = i;
 	}
 	fprintf(stderr, "\n");
 
 	if (mismatching_ts >= 0) {
 		fprintf(stderr, "Test failed: mismatching TS use in bts %d trx %d ts %d\n",
-		       bts_nr, trx_nr, mismatching_ts);
+		       bts->nr, trx->nr, mismatching_ts);
 		return false;
 	}
 	return true;
@@ -399,7 +402,7 @@
 		lchan->tch_mode = GSM48_CMODE_SPEECH_AMR;
 		lchan->activate.info.s15_s0 = 0x0002;
 	} else {
-		printf("Given codec unknown\n");
+		fprintf(stderr, "Given codec unknown\n");
 		exit(EXIT_FAILURE);
 	}
 
@@ -423,7 +426,7 @@
 
 	lchan = lchan_select_by_type(bts, (full_rate) ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H);
 	if (!lchan) {
-		printf("No resource for lchan\n");
+		fprintf(stderr, "No resource for lchan\n");
 		exit(EXIT_FAILURE);
 	}
 
@@ -445,15 +448,9 @@
 	}
 }
 
-bool set_ts_use(int bts_nr, int trx_nr, const char * const *ts_use)
+bool _set_ts_use(struct gsm_bts *bts, struct gsm_bts_trx *trx, const char * const *ts_use)
 {
-	struct gsm_bts *bts;
-	struct gsm_bts_trx *trx;
 	int i;
-	bts = gsm_bts_num(bsc_gsmnet, bts_nr);
-	OSMO_ASSERT(bts);
-	trx = gsm_bts_trx_num(bts, trx_nr);
-	OSMO_ASSERT(trx);
 
 	fprintf(stderr, "Setting TS use:");
 	for (i = 0; i < 8; i++)
@@ -469,44 +466,44 @@
 			continue;
 
 		/* If it is already as desired, don't change anything */
-		if (!strcmp(want_use, is_use))
+		if (!strcasecmp(want_use, is_use))
 			continue;
 
-		if (!strcmp(want_use, "TCH/F")) {
+		if (!strcasecmp(want_use, "tch/f")) {
 			if (!ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_F)) {
-				printf("Error: bts %d trx %d ts %d cannot be used as TCH/F\n",
-				       bts_nr, trx_nr, i);
+				fprintf(stderr, "Error: bts %d trx %d ts %d cannot be used as TCH/F\n",
+				       bts->nr, trx->nr, i);
 				return false;
 			}
 			ts_clear(ts);
 
-			lchan_act(&ts->lchan[0], true, "AMR");
-		} else if (!strcmp(want_use, "TCH/H-")
-			   || !strcmp(want_use, "TCH/HH")
-			   || !strcmp(want_use, "TCH/-H")) {
+			lchan_act(&ts->lchan[0], true, codec_tch_f ? : "AMR");
+		} else if (!strcasecmp(want_use, "tch/h-")
+			   || !strcasecmp(want_use, "tch/hh")
+			   || !strcasecmp(want_use, "tch/-h")) {
 			bool act[2];
 			int j;
 
 			if (!ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_H)) {
-				printf("Error: bts %d trx %d ts %d cannot be used as TCH/H\n",
-				       bts_nr, trx_nr, i);
+				fprintf(stderr, "Error: bts %d trx %d ts %d cannot be used as TCH/H\n",
+				       bts->nr, trx->nr, i);
 				return false;
 			}
 
 			if (ts->pchan_is != GSM_PCHAN_TCH_H)
 				ts_clear(ts);
 
-			act[0] = want_use[4] == 'H';
-			act[1] = want_use[5] == 'H';
+			act[0] = (want_use[4] == 'h' || want_use[4] == 'H');
+			act[1] = (want_use[5] == 'h' || want_use[5] == 'H');
 
 			for (j = 0; j < 2; j++) {
 				if (lchan_state_is(&ts->lchan[j], LCHAN_ST_UNUSED)) {
 					if (act[j])
-						lchan_act(&ts->lchan[j], false, "AMR");
+						lchan_act(&ts->lchan[j], false, codec_tch_h ? : "AMR");
 				} else if (!act[j])
 					lchan_clear(&ts->lchan[j]);
 			}
-		} else if (!strcmp(want_use, "-") || !strcmp(want_use, "PDCH")) {
+		} else if (!strcmp(want_use, "-") || !strcasecmp(want_use, "PDCH")) {
 			ts_clear(ts);
 		}
 	}
@@ -592,6 +589,8 @@
 	struct abis_rsl_rll_hdr *rh;
 	uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
 
+	fprintf(stderr, "- Send EST IND for %s\n", gsm_lchan_name(lchan));
+
 	rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));
 	rh->c.msg_discr = ABIS_RSL_MDISC_RLL;
 	rh->c.msg_type = RSL_MT_EST_IND;
@@ -646,6 +645,16 @@
 	abis_rsl_rcvmsg(msg);
 }
 
+static void lchan_release_ack(struct gsm_lchan *lchan)
+{
+	if (lchan->fi && lchan->fi->state == LCHAN_ST_WAIT_BEFORE_RF_RELEASE) {
+		/* don't wait before release */
+		osmo_fsm_inst_state_chg(lchan->fi, LCHAN_ST_WAIT_RF_RELEASE_ACK, 0, 0);
+		/* ack the release */
+		osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RSL_RF_CHAN_REL_ACK, 0);
+	}
+}
+
 /* override, requires '-Wl,--wrap=abis_rsl_sendmsg'.
  * Catch RSL messages sent towards the BTS. */
 int __real_abis_rsl_sendmsg(struct msgb *msg);
@@ -658,7 +667,7 @@
 	struct gsm_lchan *other_lchan;
 
 	if (rc) {
-		printf("rsl_lchan_lookup() failed\n");
+		fprintf(stderr, "rsl_lchan_lookup() failed\n");
 		exit(1);
 	}
 
@@ -707,1105 +716,381 @@
 	case RSL_MT_DEACTIVATE_SACCH:
 		break;
 	default:
-		printf("unknown rsl message=0x%x\n", dh->c.msg_type);
+		fprintf(stderr, "unknown rsl message=0x%x\n", dh->c.msg_type);
 	}
 	return 0;
 }
 
-/* test cases */
+struct gsm_bts *bts_by_num_str(const char *num_str)
+{
+	struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, atoi(num_str));
+	OSMO_ASSERT(bts);
+	return bts;
+}
 
-static char *test_case_0[] = {
-	"2",
+struct gsm_bts_trx *trx_by_num_str(struct gsm_bts *bts, const char *num_str)
+{
+	struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, atoi(num_str));
+	OSMO_ASSERT(trx);
+	return trx;
+}
 
-	"Stay in better cell\n\n"
-	"There are many neighbor cells, but only the current cell is the best\n"
-	"cell, so no handover is performed\n",
+#define LCHAN_ARGS "lchan <0-255> <0-255> <0-7> <0-7>"
+#define LCHAN_ARGS_DOC "identify an lchan\nBTS nr\nTRX nr\nTimeslot nr\nSubslot nr\n"
 
-	"create-n-bts", "7",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0",
-		"30","0",
-		"6", "0","20", "1","21", "2","18", "3","20", "4","23", "5","19",
-	"expect-no-chan",
-	NULL
-};
+static struct gsm_lchan *parse_lchan_args(const char **argv)
+{
+	struct gsm_bts *bts = bts_by_num_str(argv[0]);
+	struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]);
+	struct gsm_bts_trx_ts *ts = &trx->ts[atoi(argv[2])];
+	return &ts->lchan[atoi(argv[3])];
+}
 
-static char *test_case_1[] = {
-	"2",
+#define TS_USE " (TCH/F|TCH/H-|TCH/-H|TCH/HH|PDCH" \
+	       "|tch/f|tch/h-|tch/-h|tch/hh|pdch" \
+	       "|-|*)"
+#define TS_USE_DOC "'TCH/F': one FR call\n" \
+		   "'TCH/H-': HR TS with first subslot used as TCH/H, other subslot unused\n" \
+		   "'TCH/HH': HR TS with both subslots used as TCH/H\n" \
+		   "'TCH/-H': HR TS with only second subslot used as TCH/H\n" \
+		   "'PDCH': TS used for PDCH (e.g. unused dynamic TS)\n" \
+		   "'tch/f': one FR call\n" \
+		   "'tch/h-': HR TS with first subslot used as TCH/H, other subslot unused\n" \
+		   "'tch/hh': HR TS with both subslots used as TCH/H\n" \
+		   "'tch/-h': HR TS with only second subslot used as TCH/H\n" \
+		   "'pdch': TS used for PDCH (e.g. unused dynamic TS)\n" \
+		   "'-': TS unused\n" \
+		   "'*': TS allowed to be in any state\n"
 
-	"Handover to best better cell\n\n"
-	"The best neighbor cell is selected\n",
+DEFUN(create_n_bts, create_n_bts_cmd,
+      "create-n-bts <1-255>",
+      "Create a number of BTS with four TCH/F and four TCH/H timeslots\n"
+      "Number of BTS to create\n")
+{
+	int i;
+	int n = atoi(argv[0]);
+	for (i = 0; i < n; i++)
+		_create_bts(1, NULL, 0);
+	return CMD_SUCCESS;
+}
 
-	"create-n-bts", "7",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0",
-		"10","0",
-		"6", "0","20", "1","21", "2","18", "3","20", "4","23", "5","19",
-	"expect-chan", "5", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "5", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
+DEFUN(create_bts, create_bts_cmd,
+      "create-bts trx-count <1-255> timeslots .TS_CFG",
+      "Create a new BTS with specific timeslot configuration\n"
+      "Create N TRX in the new BTS\n"
+      "TRX count\n"
+      "Timeslot config\n"
+      "Timeslot types for 8 * trx-count, each being one of CCCH+SDCCH4|SDCCH8|TCH/F|TCH/H|TCH/F_TCH/H_PDCH|...;"
+      " shorthands: cs+4 = CCCH+SDCCH4; dyn = TCH/F_TCH/H_PDCH\n")
+{
+	int num_trx = atoi(argv[0]);
+	_create_bts(num_trx, argv + 1, argc - 1);
+	return CMD_SUCCESS;
+}
 
-static char *test_case_2[] = {
-	"2",
+DEFUN(create_ms, create_ms_cmd,
+      "create-ms bts <0-999> (TCH/F|TCH/H) (AMR|HR|EFR)",
+      "Create an MS using the next free matching lchan on a given BTS\n"
+      "BTS index to subscribe on\n"
+      "lchan type to select\n"
+      "codec\n")
+{
+	const char *bts_nr_str = argv[0];
+	const char *tch_type = argv[1];
+	const char *codec = argv[2];
+	struct gsm_lchan *lchan;
+	fprintf(stderr, "- Creating mobile at BTS %s on "
+		"%s with %s codec\n", bts_nr_str, tch_type, codec);
+	lchan = create_lchan(bts_by_num_str(bts_nr_str),
+			     !strcmp(tch_type, "TCH/F"), codec);
+	if (!lchan) {
+		fprintf(stderr, "Failed to create lchan!\n");
+		return CMD_WARNING;
+	}
+	fprintf(stderr, " * New MS is at %s\n", gsm_lchan_name(lchan));
+	return CMD_SUCCESS;
+}
 
-	"Handover and Assignment must be enabled\n\n"
-	"This test will start with disabled assignment and handover.  A\n"
-	"better neighbor cell (assignment enabled) will not be selected and \n"
-	"also no assignment from TCH/H to TCH/F to improve quality. There\n"
-	"will be no handover nor assignment. After enabling assignment on the\n"
-	"current cell, the MS will assign to TCH/F. After enabling handover\n"
-	"in the current cell, but disabling in the neighbor cell, handover\n"
-	"will not be performed, until it is enabled in the neighbor cell too.\n",
+DEFUN(meas_rep, meas_rep_cmd,
+      "meas-rep " LCHAN_ARGS " rxlev <0-255> rxqual <0-7> ta <0-255>"
+	" [neighbors] [<0-255>] [<0-255>] [<0-255>] [<0-255>] [<0-255>] [<0-255>]",
+      "Send measurement report\n"
+      LCHAN_ARGS_DOC
+      "rxlev\nrxlev\n"
+      "rxqual\nrxqual\n"
+      "timing advance\ntiming advance\n"
+      "neighbors list of rxlev reported by each neighbor cell\n"
+      "neighbor 0 rxlev\n"
+      "neighbor 1 rxlev\n"
+      "neighbor 2 rxlev\n"
+      "neighbor 3 rxlev\n"
+      "neighbor 4 rxlev\n"
+      "neighbor 5 rxlev\n"
+     )
+{
+	struct gsm_lchan *lc;
+	uint8_t rxlev;
+	uint8_t rxqual;
+	uint8_t ta;
+	int i;
+	struct neighbor_meas nm[6] = {};
 
-	"create-n-bts", "2",
-	"afs-rxlev-improve", "0", "5",
-	"create-ms", "0", "TCH/H", "AMR",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "TCH/H-", "-", "-",
-	"as-enable", "0", "0",
-	"ho-enable", "0", "0",
-	"meas-rep", "0","0","5","0", "0","0", "1","0","30",
-	"expect-no-chan",
-	"as-enable", "0", "1",
-	"meas-rep", "0","0","5","0", "0","0", "1","0","30",
-	"expect-chan", "0", "1",
-	"ack-chan",
-	"expect-ho", "0", "5",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"ho-enable", "0", "1",
-	"ho-enable", "1", "0",
-	"meas-rep", "0","0","1","0", "0","0", "1","0","30",
-	"expect-no-chan",
-	"ho-enable", "1", "1",
-	"meas-rep", "0","0","1","0", "0","0", "1","0","30",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
+	lc = parse_lchan_args(argv);
+	argv += 4;
+	argc -= 4;
 
-static char *test_case_3[] = {
-	"2",
+	rxlev = atoi(argv[0]);
+	rxqual = atoi(argv[1]);
+	ta = atoi(argv[2]);
+	argv += 3;
+	argc -= 3;
 
-	"Penalty timer must not run\n\n"
-	"The MS will try to handover to a better cell, but this will fail.\n"
-	"Even though the cell is still better, handover will not be performed\n"
-	"due to penalty timer after handover failure\n",
+	if (!lchan_state_is(lc, LCHAN_ST_ESTABLISHED)) {
+		fprintf(stderr, "Error: sending measurement report for %s which is in state %s\n",
+		       gsm_lchan_name(lc), lchan_state_name(lc));
+		exit(1);
+	}
 
-	"create-n-bts", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-failed",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-no-chan",
-	NULL
-};
+	/* skip the optional [neighbors] keyword */
+	if (argc) {
+		argv++;
+		argc--;
+	}
 
-static char *test_case_4[] = {
-	"2",
+	fprintf(stderr, "- Sending measurement report from %s: rxlev=%u rxqual=%u ta=%u (%d neighbors)\n",
+		gsm_lchan_name(lc), rxlev, rxqual, ta, argc);
 
-	"TCH/H keeping with HR codec\n\n"
-	"The MS is using half rate V1 codec, but the better cell is congested\n"
-	"at TCH/H slots. As the congestion is removed, the handover takes\n"
-	"place.\n",
+	for (i = 0; i < 6; i++) {
+		int neighbor_bts_nr = i;
+		/* since our bts is not in the list of neighbor cells, we need to shift */
+		if (neighbor_bts_nr >= lc->ts->trx->bts->nr)
+			neighbor_bts_nr++;
+		nm[i] = (struct neighbor_meas){
+			.rxlev = argc > i ? atoi(argv[i]) : 0,
+			.bsic = 0x3f,
+			.bcch_f = i,
+		};
+		if (i < argc)
+			fprintf(stderr, " * Neighbor cell #%d, actual BTS %d: rxlev=%d\n", i, neighbor_bts_nr,
+				nm[i].rxlev);
+	}
+	got_chan_req = 0;
+	gen_meas_rep(lc, rxlev, rxqual, ta, argc, nm);
+	return CMD_SUCCESS;
+}
 
-	"create-n-bts", "2",
-	"set-min-free", "1", "TCH/H", "4",
-	"create-ms", "0", "TCH/H", "HR",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "TCH/H-", "-", "-",
-	"meas-rep", "0","0","5","0", "20","0", "1","0","30",
-	"expect-no-chan",
-	"set-min-free", "1", "TCH/H", "3",
-	"meas-rep", "0","0","5","0", "20","0", "1","0","30",
-	"expect-chan", "1", "5",
-	"ack-chan",
-	"expect-ho", "0", "5",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "-", "-", "-", "-", "TCH/H-", "-", "-",
-	NULL
-};
+DEFUN(congestion_check, congestion_check_cmd,
+      "congestion-check",
+      "Trigger a congestion check\n")
+{
+	fprintf(stderr, "- Triggering congestion check\n");
+	got_chan_req = 0;
+	hodec2_congestion_check(bsc_gsmnet);
+	return CMD_SUCCESS;
+}
 
-static char *test_case_5[] = {
-	"2",
+DEFUN(expect_no_chan, expect_no_chan_cmd,
+      "expect-no-chan",
+      "Expect that no channel request was sent from BSC to any cell\n")
+{
+	fprintf(stderr, "- Expecting no channel request\n");
+	if (got_chan_req) {
+		fprintf(stderr, " * Got channel request at %s\n", gsm_lchan_name(chan_req_lchan));
+		fprintf(stderr, "Test failed, because channel was requested\n");
+		exit(1);
+	}
+	fprintf(stderr, " * Got no channel request\n");
+	return CMD_SUCCESS;
+}
 
-	"TCH/F keeping with FR codec\n\n"
-	"The MS is using full rate V1 codec, but the better cell is congested\n"
-	"at TCH/F slots. As the congestion is removed, the handover takes\n"
-	"place.\n",
+static void _expect_chan_activ(struct gsm_lchan *lchan)
+{
+	fprintf(stderr, "- Expecting channel request at %s\n",
+		gsm_lchan_name(lchan));
+	if (!got_chan_req) {
+		fprintf(stderr, "Test failed, because no channel was requested\n");
+		exit(1);
+	}
+	fprintf(stderr, " * Got channel request at %s\n", gsm_lchan_name(chan_req_lchan));
+	if (lchan != chan_req_lchan) {
+		fprintf(stderr, "Test failed, because channel was requested on a different lchan than expected\n"
+		       "expected: %s  got: %s\n",
+		       gsm_lchan_name(lchan), gsm_lchan_name(chan_req_lchan));
+		exit(1);
+	}
+}
 
-	"create-n-bts", "2",
-	"set-min-free", "1", "TCH/F", "4",
-	"create-ms", "0", "TCH/F", "FR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-no-chan",
-	"set-min-free", "1", "TCH/F", "3",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
+static void _ack_chan_activ(struct gsm_lchan *lchan)
+{
+	fprintf(stderr, "- Acknowledging channel request on %s\n", gsm_lchan_name(lchan));
+	got_ho_req = 0;
+	send_chan_act_ack(lchan, 1);
+}
 
-static char *test_case_6[] = {
-	"2",
+static void _expect_ho_req(struct gsm_lchan *lchan)
+{
+	fprintf(stderr, "- Expecting handover/assignment request at %s\n",
+		gsm_lchan_name(lchan));
 
-	"TCH/F keeping with EFR codec\n\n"
-	"The MS is using full rate V2 codec, but the better cell is congested\n"
-	"at TCH/F slots. As the congestion is removed, the handover takes\n"
-	"place.\n",
+	if (!got_ho_req) {
+		fprintf(stderr, "Test failed, because no handover was requested\n");
+		exit(1);
+	}
+	fprintf(stderr, " * Got handover/assignment request at %s\n", gsm_lchan_name(ho_req_lchan));
+	if (ho_req_lchan != lchan) {
+		fprintf(stderr, "Test failed, because handover/assignment was not commanded on the expected lchan\n");
+		exit(1);
+	}
+}
 
-	"create-n-bts", "2",
-	"set-min-free", "1", "TCH/F", "4",
-	"create-ms", "0", "TCH/F", "EFR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-no-chan",
-	"set-min-free", "1", "TCH/F", "3",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
+DEFUN(expect_chan, expect_chan_cmd,
+      "expect-chan " LCHAN_ARGS,
+      "Expect a channel request from BSC to a cell for a specific lchan\n"
+      LCHAN_ARGS_DOC)
+{
+	_expect_chan_activ(parse_lchan_args(argv));
+	return CMD_SUCCESS;
+}
 
-static char *test_case_7[] = {
-	"2",
+DEFUN(ack_chan, ack_chan_cmd,
+      "ack-chan",
+      "ACK a previous Channel Request\n")
+{
+	OSMO_ASSERT(got_chan_req);
+	_ack_chan_activ(chan_req_lchan);
+	return CMD_SUCCESS;
+}
 
-	"TCH/F to TCH/H changing with AMR codec\n\n"
-	"The MS is using AMR V3 codec, the better cell is congested at TCH/F\n"
-	"slots. The handover is performed to non-congested TCH/H slots.\n",
+DEFUN(expect_ho_req, expect_ho_req_cmd,
+      "expect-ho-req " LCHAN_ARGS,
+      "Expect a handover of a given lchan\n"
+      LCHAN_ARGS_DOC)
+{
+	_expect_ho_req(parse_lchan_args(argv));
+	return CMD_SUCCESS;
+}
 
-	"create-n-bts", "2",
-	"set-min-free", "1", "TCH/F", "4",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-chan", "1", "5",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "-", "-", "-", "-", "TCH/H-", "-", "-",
-	NULL
-};
+DEFUN(ho_complete, ho_complete_cmd,
+      "ho-complete",
+      "Send Handover Complete for the most recent HO target lchan\n")
+{
+	if (!got_chan_req) {
+		fprintf(stderr, "Cannot ack handover/assignment, because no chan request\n");
+		exit(1);
+	}
+	if (!got_ho_req) {
+		fprintf(stderr, "Cannot ack handover/assignment, because no ho request\n");
+		exit(1);
+	}
+	send_ho_complete(chan_req_lchan, true);
+	lchan_release_ack(ho_req_lchan);
+	return CMD_SUCCESS;
+}
 
-static char *test_case_8[] = {
-	"2",
+DEFUN(expect_ho, expect_ho_cmd,
+      "expect-ho from " LCHAN_ARGS " to " LCHAN_ARGS,
+      "Expect a handover of a specific lchan to a specific target lchan;"
+      " shorthand for expect-chan, ack-chan, expect-ho, ho-complete.\n"
+      "lchan to handover from\n" LCHAN_ARGS_DOC
+      "lchan that to handover to\n" LCHAN_ARGS_DOC)
+{
+	struct gsm_lchan *from = parse_lchan_args(argv);
+	struct gsm_lchan *to = parse_lchan_args(argv+4);
 
-	"No handover to a cell with no slots available\n\n"
-	"If no slot is available, no handover is performed\n",
+	_expect_chan_activ(to);
+	_ack_chan_activ(to);
+	_expect_ho_req(from);
+	send_ho_complete(to, true);
+	lchan_release_ack(from);
+	return CMD_SUCCESS;
+}
 
-	"create-n-bts", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"create-ms", "1", "TCH/F", "AMR",
-	"create-ms", "1", "TCH/F", "AMR",
-	"create-ms", "1", "TCH/F", "AMR",
-	"create-ms", "1", "TCH/F", "AMR",
-	"create-ms", "1", "TCH/H", "AMR",
-	"create-ms", "1", "TCH/H", "AMR",
-	"create-ms", "1", "TCH/H", "AMR",
-	"create-ms", "1", "TCH/H", "AMR",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "TCH/HH", "TCH/HH", "-",
-	"meas-rep", "0","0","1","0", "0","0", "1","0","30",
-	"expect-no-chan",
-	NULL
-};
+DEFUN(ho_failed, ho_failed_cmd,
+      "ho-failed",
+      "Fail the most recent handover request\n")
+{
+	if (!got_chan_req) {
+		fprintf(stderr, "Cannot fail handover, because no chan request\n");
+		exit(1);
+	}
+	got_chan_req = 0;
+	got_ho_req = 0;
+	send_ho_complete(ho_req_lchan, false);
+	lchan_release_ack(chan_req_lchan);
+	return CMD_SUCCESS;
+}
 
-static char *test_case_9[] = {
-	"2",
+DEFUN(expect_ts_use, expect_ts_use_cmd,
+	"expect-ts-use trx <0-255> <0-255> states" TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE,
+	"Expect timeslots of a BTS' TRX to be in a specific state\n"
+	"Indicate a BTS and TRX\n" "BTS nr\n" "TRX nr\n"
+	"List of 8 expected TS states\n"
+	TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC)
+{
+	struct gsm_bts *bts = bts_by_num_str(argv[0]);
+	struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]);
+	argv += 2;
+	argc -= 2;
+	if (!_expect_ts_use(bts, trx, argv))
+		exit(1);
+	return CMD_SUCCESS;
+}
 
-	"No more parallel handovers, if max_unsync_ho is defined\n\n"
-	"There are tree mobiles that want to handover, but only two can do\n"
-	"it at a time, because the maximum number is limited to two.\n",
+DEFUN(codec_f, codec_f_cmd,
+	"codec tch/f (AMR|EFR|FR)",
+	"Define which codec should be used for new TCH/F lchans (for set-ts-use)\n"
+	"Configure the TCH/F codec to use\nAMR\nEFR\nFR\n")
+{
+	osmo_talloc_replace_string(ctx, &codec_tch_f, argv[0]);
+	return CMD_SUCCESS;
+}
 
-	"create-n-bts", "2",
-	"set-max-ho", "1", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "0","0", "1","0","30",
-	"expect-chan", "1", "1",
-	"meas-rep", "0","0","2","0", "0","0", "1","0","30",
-	"expect-chan", "1", "2",
-	"meas-rep", "0","0","3","0", "0","0", "1","0","30",
-	"expect-no-chan",
-	NULL
-};
+DEFUN(codec_h, codec_h_cmd,
+	"codec tch/h (AMR|HR)",
+	"Define which codec should be used for new TCH/H lchans (for set-ts-use)\n"
+	"Configure the TCH/H codec to use\nAMR\nHR\n")
+{
+	osmo_talloc_replace_string(ctx, &codec_tch_h, argv[0]);
+	return CMD_SUCCESS;
+}
 
-static char *test_case_10[] = {
-	"2",
+DEFUN(set_ts_use, set_ts_use_cmd,
+	"set-ts-use trx <0-255> <0-255> states" TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE TS_USE,
+	"Put timeslots of a BTS' TRX into a specific state\n"
+	"Indicate a BTS and TRX\n" "BTS nr\n" "TRX nr\n"
+	"List of 8 TS states to apply\n"
+	TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC TS_USE_DOC)
+{
+	struct gsm_bts *bts = bts_by_num_str(argv[0]);
+	struct gsm_bts_trx *trx = trx_by_num_str(bts, argv[1]);
+	argv += 2;
+	argc -= 2;
+	if (!_set_ts_use(bts, trx, argv))
+		exit(1);
+	if (!_expect_ts_use(bts, trx, argv))
+		exit(1);
+	return CMD_SUCCESS;
+}
 
-	"Hysteresis\n\n"
-	"If neighbor cell is better, handover is only performed if the\n"
-	"amount of improvement is greater or equal hyteresis\n",
-
-	"create-n-bts", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "27","0", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "26","0", "1","0","30",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_11[] = {
-	"2",
-
-	"No Hysteresis and minimum RX level\n\n"
-	"If current cell's RX level is below mimium level, handover must be\n"
-	"performed, no matter of the hysteresis. First do not perform\n"
-	"handover to better neighbor cell, because the hysteresis is not\n"
-	"met. Second do not perform handover because better neighbor cell is\n"
-	"below minimum RX level. Third perform handover because current cell\n"
-	"is below minimum RX level, even if the better neighbor cell (minimum\n"
-	"RX level reached) does not meet the hysteresis.\n",
-
-	"create-n-bts", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "10","0", "1","0","11",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "8","0", "1","0","9",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "9","0", "1","0","10",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_12[] = {
-	"2",
-
-	"No handover to congested cell\n\n"
-	"The better neighbor cell is congested, so no handover is performed.\n"
-	"After the congestion is over, handover will be performed.\n",
-
-	"create-n-bts", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"set-min-free", "1", "TCH/F", "4",
-	"set-min-free", "1", "TCH/H", "4",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-no-chan",
-	"set-min-free", "1", "TCH/F", "3",
-	"set-min-free", "1", "TCH/H", "3",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_13[] = {
-	"2",
-
-	"Handover to balance congestion\n\n"
-	"The current and the better cell are congested, so no handover is\n"
-	"performed. This is because handover would congest the neighbor cell\n"
-	"more. After congestion raises in the current cell, the handover is\n"
-	"performed to balance congestion\n",
-
-	"create-n-bts", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"set-min-free", "0", "TCH/F", "4",
-	"set-min-free", "0", "TCH/H", "4",
-	"set-min-free", "1", "TCH/F", "4",
-	"set-min-free", "1", "TCH/H", "4",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-no-chan",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "20","0", "1","0","30",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "TCH/F", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_14[] = {
-	"2",
-
-	"Handover to congested cell, if RX level is below minimum\n\n"
-	"The better neighbor cell is congested, so no handover is performed.\n"
-	"If the RX level of the current cell drops below minimum acceptable\n"
-	"level, the handover is performed.\n",
-
-	"create-n-bts", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"set-min-free", "1", "TCH/F", "4",
-	"set-min-free", "1", "TCH/H", "4",
-	"meas-rep", "0","0","1","0", "10","0", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "9","0", "1","0","30",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_15[] = {
-	"2",
-
-	"Handover to cell with worse RXLEV, if RXQUAL is below minimum\n\n"
-	"The neighbor cell has worse RXLEV, so no handover is performed.\n"
-	"If the RXQUAL of the current cell drops below minimum acceptable\n"
-	"level, the handover is performed. It is also required that 10\n"
-	"reports are received, before RXQUAL is checked.\n",
-	/* (See also test 28, which tests for RXQUAL triggering HO to congested cell.) */
-	/* TODO: bad RXQUAL may want to prefer assignment within the same cell to avoid interference.
-	 * See Performance Enhancements in a Frequency Hopping GSM Network (Nielsen Wigard 2002), Chapter
-	 * 2.1.1, "Interference" in the list of triggers on p.157. */
-
-	"create-n-bts", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "40","6", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "40","6", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "40","6", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "40","6", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "40","6", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "40","6", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "40","6", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "40","6", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "40","6", "1","0","30",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "40","6", "1","0","30",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_16[] = {
-	"2",
-
-	"Handover due to maximum TA exceeded\n\n"
-	"The MS in the current (best) cell has reached maximum allowed timing\n"
-	"advance. No handover is performed until the timing advance exceeds\n"
-	"it. The originating cell is still the best, but no handover is\n"
-	"performed back to that cell, because the penalty timer (due to\n"
-	"maximum allowed timing advance) is running.\n",
-
-	"create-n-bts", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"set-max-ta", "0", "5", /* of cell */
-	"set-ta", "0", "5", /* of ms */
-	"meas-rep", "0","0","1","0", "30","0", "1","0","20",
-	"expect-no-chan",
-	"set-ta", "0", "6", /* of ms */
-	"meas-rep", "0","0","1","0", "30","0", "1","0","20",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "1","0","1","0", "20","0", "1","0","30",
-	"expect-no-chan",
-	NULL
-};
-
-static char *test_case_17[] = {
-	"2",
-
-	"Congestion check: No congestion\n\n"
-	"Three cells have different number of used slots, but there is no\n"
-	"congestion in any of these cells. No handover is performed.\n",
-
-	"create-n-bts", "3",
-	"set-min-free", "0", "TCH/F", "2",
-	"set-min-free", "0", "TCH/H", "2",
-	"set-min-free", "1", "TCH/F", "2",
-	"set-min-free", "1", "TCH/H", "2",
-	"set-min-free", "2", "TCH/F", "2",
-	"set-min-free", "2", "TCH/H", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/H", "AMR",
-	"create-ms", "0", "TCH/H", "AMR",
-	"create-ms", "1", "TCH/F", "AMR",
-	"create-ms", "1", "TCH/H", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "-", "-", "TCH/HH", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "TCH/H-", "-", "-",
-	"meas-rep", "0","0","1","0", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"meas-rep", "0","0","2","0", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"meas-rep", "0","0","5","0", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"meas-rep", "0","0","5","1", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"meas-rep", "1","0","1","0", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"meas-rep", "1","0","5","0", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"congestion-check",
-	"expect-no-chan",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "-", "-", "TCH/HH", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "TCH/H-", "-", "-",
-	NULL
-};
-
-static char *test_case_18[] = {
-	"2",
-
-	"Congestion check: One out of three cells is congested\n\n"
-	"Three cells have different number of used slots, but there is\n"
-	"congestion at TCH/F in the first cell. Handover is performed with\n"
-	"the best candidate.\n",
-
-	"create-n-bts", "3",
-	"set-min-free", "0", "TCH/F", "2",
-	"set-min-free", "0", "TCH/H", "2",
-	"set-min-free", "1", "TCH/F", "2",
-	"set-min-free", "1", "TCH/H", "2",
-	"set-min-free", "2", "TCH/F", "2",
-	"set-min-free", "2", "TCH/H", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/H", "AMR",
-	"create-ms", "0", "TCH/H", "AMR",
-	"create-ms", "1", "TCH/F", "AMR",
-	"create-ms", "1", "TCH/H", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "-", "TCH/HH", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "TCH/H-", "-", "-",
-	"meas-rep", "0","0","1","0", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"meas-rep", "0","0","2","0", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"meas-rep", "0","0","3","0", "30","0", "2","0","21","1","20",
-	"expect-no-chan",
-	"meas-rep", "0","0","5","0", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"meas-rep", "0","0","5","1", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"meas-rep", "1","0","1","0", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"meas-rep", "1","0","5","0", "30","0", "2","0","20","1","20",
-	"expect-no-chan",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "-", "TCH/HH", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "TCH/H-", "-", "-",
-	"congestion-check",
-	"expect-chan", "1", "2",
-	"ack-chan",
-	"expect-ho", "0", "3", /* best candidate is MS 2 at BTS 0, TS 3 */
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "-", "-", "TCH/HH", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "TCH/F", "-", "-", "TCH/H-", "-", "-",
-	NULL
-};
-
-static char *test_case_19[] = {
-	"2",
-
-	"Congestion check: Balancing over congested cells\n\n"
-	"Two cells are congested, but the second cell is less congested.\n"
-	"Handover is performed to solve the congestion.\n",
-
-	"create-n-bts", "2",
-	"set-min-free", "0", "TCH/F", "4",
-	"set-min-free", "1", "TCH/F", "4",
-	"create-ms", "0", "TCH/F", "FR",
-	"create-ms", "0", "TCH/F", "FR",
-	"create-ms", "0", "TCH/F", "FR",
-	"create-ms", "1", "TCH/F", "FR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "30","0", "1","0","20",
-	"expect-no-chan",
-	"meas-rep", "0","0","2","0", "30","0", "1","0","21",
-	"expect-no-chan",
-	"meas-rep", "0","0","3","0", "30","0", "1","0","20",
-	"expect-no-chan",
-	"meas-rep", "1","0","1","0", "30","0", "1","0","20",
-	"expect-no-chan",
-	"congestion-check",
-	"expect-chan", "1", "2",
-	"ack-chan",
-	"expect-ho", "0", "2", /* best candidate is MS 1 at BTS 0, TS 2 */
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "TCH/F", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "TCH/F", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_20[] = {
-	"2",
-
-	"Congestion check: Solving congestion by handover TCH/F -> TCH/H\n\n"
-	"Two BTS, one MS in the first congested BTS must handover to\n"
-	"non-congested TCH/H of second BTS, in order to solve congestion\n",
-	"create-n-bts", "2",
-	"set-min-free", "0", "TCH/F", "4",
-	"set-min-free", "0", "TCH/H", "4",
-	"set-min-free", "1", "TCH/F", "4",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "30","0", "1","0","30",
-	"expect-no-chan",
-	"congestion-check",
-	"expect-chan", "1", "5",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "-", "-", "-", "-", "TCH/H-", "-", "-",
-	NULL
-};
-
-static char *test_case_21[] = {
-	"2",
-
-	"Congestion check: Balancing congestion by handover TCH/F -> TCH/H\n\n"
-	"Two BTS, one MS in the first congested BTS must handover to\n"
-	"less-congested TCH/H of second BTS, in order to balance congestion\n",
-	"create-n-bts", "2",
-	"set-min-free", "0", "TCH/F", "4",
-	"set-min-free", "0", "TCH/H", "4",
-	"set-min-free", "1", "TCH/F", "4",
-	"set-min-free", "1", "TCH/H", "4",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/H", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "-", "-", "TCH/H-", "-", "-",
-	"meas-rep", "0","0","1","0", "30","0", "1","0","30",
-	"expect-no-chan",
-	"congestion-check",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "TCH/F", "-", "-", "TCH/H-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_22[] = {
-	"2",
-
-	"Congestion check: Upgrading worst candidate from TCH/H -> TCH/F\n\n"
-	"There is only one BTS. The TCH/H slots are congested. Since\n"
-	"assignment is performed to less-congested TCH/F, the candidate with\n"
-	"the worst RX level is chosen.\n",
-
-	"create-n-bts", "1",
-	"set-min-free", "0", "TCH/F", "4",
-	"set-min-free", "0", "TCH/H", "4",
-	"create-ms", "0", "TCH/H", "AMR",
-	"create-ms", "0", "TCH/H", "AMR",
-	"create-ms", "0", "TCH/H", "AMR",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "TCH/HH", "TCH/H-", "-",
-	"meas-rep", "0","0","5","0", "30","0", "0",
-	"meas-rep", "0","0","5","1", "34","0", "0",
-	"meas-rep", "0","0","6","0", "20","0", "0",
-	"expect-no-chan",
-	"congestion-check",
-	"expect-chan", "0", "1",
-	"ack-chan",
-	"expect-ho", "0", "6",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "TCH/HH", "-", "-",
-	NULL
-};
-
-static char *test_case_23[] = {
-	"2",
-
-	"Story: 'A neighbor is your friend'\n",
-
-	"create-n-bts", "3",
-
-	"print",
-	"Andreas is driving along the coast, on a sunny june afternoon.\n"
-	"Suddenly he is getting a call from his friend and neighbor Axel.\n"
-	"\n"
-	"What happens: Two MS are created, #0 for Axel, #1 for Andreas.",
-	/* Axel */
-	"create-ms", "2", "TCH/F", "AMR",
-	/* andreas */
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "2", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "40","0", "1","0","30",
-	"expect-no-chan",
-
-	"print",
-	"Axel asks Andreas if he would like to join them for a barbecue.\n"
-	"Axel's house is right in the neighborhood and the weather is fine.\n"
-	"Andreas agrees, so he drives to a close store to buy some barbecue\n"
-	"skewers.\n"
-	"\n"
-	"What happens: While driving, a different cell (mounted atop the\n"
-	"store) becomes better.",
-	/* drive to bts 1 */
-	"meas-rep", "0","0","1","0", "20","0", "1","0","35",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "2", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-
-	"print",
-	"While Andreas is walking into the store, Axel asks, if he could also\n"
-	"bring some beer. Andreas has problems understanding him: \"I have a\n"
-	"bad reception here. The cell tower is right atop the store, but poor\n"
-	"coverage inside. Can you repeat please?\"\n"
-	"\n"
-	"What happens: Inside the store the close cell is so bad, that\n"
-	"handover back to the previous cell is required.",
-	/* bts 1 becomes bad, so bts 0 helps out */
-	"meas-rep", "1","0","1","0", "5","0", "1","0","20",
-	"expect-chan", "0", "1",
-	"ack-chan",
-	"expect-ho", "1", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "2", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-
-	"print",
-	"After Andreas bought skewers and beer, he leaves the store.\n"
-	"\n"
-	"What happens: Outside the store the close cell is better again, so\n"
-	"handover back to the that cell is performed.",
-	/* bts 1 becomes better again */
-	"meas-rep", "0","0","1","0", "20","0", "1","0","35",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "2", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-
-	"print",
-	/* bts 2 becomes better */
-	"Andreas drives down to the lake where Axel's house is.\n"
-	"\n"
-	"What happens: There is a small cell at Axel's house, which becomes\n"
-	"better, because the current cell has no good comverage at the lake.",
-	"meas-rep", "1","0","1","0", "14","0", "2","0","2","1","63",
-	"expect-chan", "2", "2",
-	"ack-chan",
-	"expect-ho", "1", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "2", "0", "*", "TCH/F", "TCH/F", "-", "-", "-", "-", "-",
-
-	"print",
-	"Andreas wonders why he still has good radio coverage: \"Last time it\n"
-	"was so bad\". Axel says: \"I installed a pico cell in my house,\n"
-	"now we can use our mobile phones down here at the lake.\"",
-
-	NULL
-};
-
-static char *test_case_24[] = {
-	"2",
-	"No (or not enough) measurements for handover\n\n"
-	"Do not solve congestion in cell, because there is no measurement.\n"
-	"As soon as enough measurements available (1 in our case), perform\n"
-	"handover. Afterwards the old cell becomes congested and the new\n"
-	"cell is not. Do not perform handover until new measurements are\n"
-	"received.\n",
-
-	/* two cells, first in congested, but no handover */
-	"create-n-bts", "2",
-	"set-min-free", "0", "TCH/F", "4",
-	"set-min-free", "0", "TCH/H", "4",
-	"create-ms", "0", "TCH/F", "AMR",
-	"congestion-check",
-	"expect-no-chan",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-
-	/* send measurement and trigger congestion check */
-	"meas-rep", "0","0","1","0", "20","0", "1","0","20",
-	"expect-no-chan",
-	"congestion-check",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-
-	/* congest the first cell and remove congestion from second cell */
-	"set-min-free", "0", "TCH/F", "0",
-	"set-min-free", "0", "TCH/H", "0",
-	"set-min-free", "1", "TCH/F", "4",
-	"set-min-free", "1", "TCH/H", "4",
-
-	/* no handover until measurements applied */
-	"congestion-check",
-	"expect-no-chan",
-	"meas-rep", "1","0","1","0", "20","0", "1","0","20",
-	"expect-no-chan",
-	"congestion-check",
-	"expect-chan", "0", "1",
-	"ack-chan",
-	"expect-ho", "1", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_25[] = {
-	"1",
-
-	"Stay in better cell\n\n"
-	"There are many neighbor cells, but only the current cell is the best\n"
-	"cell, so no handover is performed\n",
-
-	"create-n-bts", "7",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "30","0",
-		"6","0","20","1","21","2","18","3","20","4","23","5","19",
-	"expect-no-chan",
-	NULL
-};
-
-static char *test_case_26[] = {
-	"1",
-
-	"Handover to best better cell\n\n"
-	"The best neighbor cell is selected\n",
-
-	"create-n-bts", "7",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"meas-rep", "0","0","1","0", "10","0",
-		"6","0","20","1","21","2","18","3","20","4","23","5","19",
-	"expect-chan", "5", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "5", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_27[] = {
-	"2",
-
-	"Congestion check: Upgrading worst candidate from TCH/H -> TCH/F\n\n"
-	"There is only one BTS. The TCH/H slots are congested. Since\n"
-	"assignment is performed to less-congested TCH/F, the candidate with\n"
-	"the worst RX level is chosen. (So far like test 22.)\n"
-	"After that, trigger more congestion checks to ensure stability.\n",
-
-	"create-n-bts", "1",
-	"set-min-free", "0", "TCH/F", "2",
-	"set-min-free", "0", "TCH/H", "4",
-	"create-ms", "0", "TCH/H", "AMR",
-	"create-ms", "0", "TCH/H", "AMR",
-	"create-ms", "0", "TCH/H", "AMR",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "TCH/HH", "TCH/H-", "-",
-	"meas-rep", "0","0","5","0", "30","0", "0",
-	"meas-rep", "0","0","5","1", "34","0", "0",
-	"meas-rep", "0","0","6","0", "20","0", "0",
-	"expect-no-chan",
-	"congestion-check",
-	"expect-chan", "0", "1",
-	"ack-chan",
-	"expect-ho", "0", "6",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "TCH/HH", "-", "-",
-	"congestion-check",
-	"expect-chan", "0", "2",
-	"ack-chan",
-	"expect-ho", "0", "5",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "-", "-", "TCH/-H", "-", "-",
-	"congestion-check",
-	"expect-no-chan",
-	"congestion-check",
-	"expect-no-chan",
-	NULL
-};
-
-static char *test_case_28[] = {
-	"2",
-
-	"Handover to congested cell, if RX quality is below minimum\n\n"
-	"The better neighbor cell is congested, so no handover is performed.\n"
-	"If the RX quality of the current cell drops below minimum acceptable\n"
-	"level, the handover is performed. It is also required that 10\n"
-	"resports are received, before RX quality is checked.\n",
-
-	"create-n-bts", "2",
-	"create-ms", "0", "TCH/F", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	"set-min-free", "1", "TCH/F", "4",
-	"set-min-free", "1", "TCH/H", "4",
-	"meas-rep", "0","0","1","0", "30","6", "1","0","40",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "30","6", "1","0","40",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "30","6", "1","0","40",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "30","6", "1","0","40",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "30","6", "1","0","40",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "30","6", "1","0","40",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "30","6", "1","0","40",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "30","6", "1","0","40",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "30","6", "1","0","40",
-	"expect-no-chan",
-	"meas-rep", "0","0","1","0", "30","6", "1","0","40",
-	"expect-chan", "1", "1",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "-", "-", "-", "-", "-",
-	"expect-ts-use", "1", "0", "*", "TCH/F", "-", "-", "-", "-", "-", "-",
-	NULL
-};
-
-static char *test_case_29[] = {
-	"2",
-
-	"Congestion check: Balancing congestion by handover TCH/F -> TCH/H\n\n"
-	"One BTS, and TCH/F are considered congested, TCH/H are not.\n"
-	,
-	"create-n-bts", "1",
-	"set-min-free", "0", "TCH/F", "3",
-	"set-min-free", "0", "TCH/H", "0",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/F", "AMR",
-	"create-ms", "0", "TCH/H", "AMR",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "-", "-", "TCH/H-", "-", "-",
-	"meas-rep", "0","0","1","0", "30","0", "1","0","30",
-	"expect-no-chan",
-	"congestion-check",
-	"expect-chan", "0", "5",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "TCH/F", "-", "-", "TCH/HH", "-", "-",
-	NULL
-};
-
-static char *test_case_30[] = {
-	"2",
-
-	"Congestion check: Balancing congestion by handover TCH/F -> TCH/H\n\n"
-	"With dynamic timeslots.\n"
-	"As soon as only one TCH/F is left, there should be HO to a dyn TS.\n"
-	,
-	"create-bts", "1", "c+s4", "TCH/F", "TCH/F", "TCH/F", "dyn", "dyn", "dyn", "PDCH",
-	"set-min-free", "0", "TCH/F", "2",
-	"set-min-free", "0", "TCH/H", "0",
-	"as-enable", "0", "1",
-	"set-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "PDCH", "PDCH", "PDCH",
-	"meas-rep", "0","0","1","0", "40","0", "1", "0","30",
-	"meas-rep", "0","0","2","0", "40","0", "1", "0","30",
-	"meas-rep", "0","0","3","0", "40","0", "1", "0","30",
-	"meas-rep", "0","0","4","0", "40","0", "1", "0","30",
-	"congestion-check",
-	"expect-no-chan",
-	"create-ms", "0", "TCH/F", "AMR",
-	"meas-rep", "0","0","5","0", "40","0", "1", "0","30",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "PDCH", "PDCH",
-	"congestion-check",
-	"expect-chan", "0", "6",
-	"ack-chan",
-	"expect-ho", "0", "5",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "PDCH", "TCH/H-", "PDCH",
-	"congestion-check",
-	"expect-chan", "0", "6",
-	"ack-chan",
-	"expect-ho", "0", "4",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "PDCH", "PDCH", "TCH/HH", "PDCH",
-	"congestion-check",
-	"expect-no-chan",
-	"create-ms", "0", "TCH/F", "AMR",
-	"meas-rep", "0","0","4","0", "40","0", "1", "0","30",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "PDCH", "TCH/HH", "PDCH",
-	"congestion-check",
-	"expect-chan", "0", "5",
-	"ack-chan",
-	"expect-ho", "0", "4",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "PDCH", "TCH/H-", "TCH/HH", "PDCH",
-	"congestion-check",
-	"expect-chan", "0", "5",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "TCH/F", "TCH/F", "PDCH", "TCH/HH", "TCH/HH", "PDCH",
-	"congestion-check",
-	"expect-no-chan",
-	"create-ms", "0", "TCH/F", "AMR",
-	"meas-rep", "0","0","1","0", "40","0", "1", "0","30",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "PDCH", "TCH/HH", "TCH/HH", "PDCH",
-	"congestion-check",
-	"expect-chan", "0", "4",
-	"ack-chan",
-	"expect-ho", "0", "1",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "TCH/F", "TCH/F", "TCH/H-", "TCH/HH", "TCH/HH", "PDCH",
-	"congestion-check",
-	"expect-chan", "0", "4",
-	"ack-chan",
-	"expect-ho", "0", "2",
-	"ho-complete",
-	"expect-ts-use", "0", "0", "*", "-", "-", "TCH/F", "TCH/HH", "TCH/HH", "TCH/HH", "PDCH",
-	"congestion-check",
-	"expect-no-chan",
-	"create-ms", "0", "TCH/F", "AMR",
-	"meas-rep", "0","0","1","0", "40","0", "1", "0","30",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "-", "TCH/F", "TCH/HH", "TCH/HH", "TCH/HH", "PDCH",
-	"congestion-check",
-	"expect-no-chan",
-	"create-ms", "0", "TCH/F", "AMR",
-	"meas-rep", "0","0","2","0", "40","0", "1", "0","30",
-	"expect-ts-use", "0", "0", "*", "TCH/F", "TCH/F", "TCH/F", "TCH/HH", "TCH/HH", "TCH/HH", "PDCH",
-	"congestion-check",
-	"expect-no-chan",
-	NULL
-};
-
-static char *test_case_31[] = {
-	"2",
-
-	"Congestion check: re-use half used TCH/H to avoid switching more dyn TS to TCH/H\n"
-	,
-	"create-bts", "1", "c+s4", "TCH/F", "TCH/F", "TCH/F", "dyn", "dyn", "dyn", "PDCH",
-	"set-ts-use", "0", "0",  "*", "-", "-", "-", "PDCH", "TCH/H-", "PDCH", "PDCH",
-	"create-ms", "0", "TCH/H", "AMR",
-	"expect-ts-use", "0", "0",  "*", "-", "-", "-", "PDCH", "TCH/HH", "PDCH", "PDCH",
-	NULL
-};
-
-static char *test_case_32[] = {
-	"2",
-
-	"Congestion check: favor moving a TCH/H that frees a half-used dyn TS completely\n"
-	,
-	"create-bts", "1", "c+s4", "dyn", "dyn", "dyn", "dyn", "dyn", "-", "-",
-	"set-ts-use", "0", "0",  "*", "PDCH", "TCH/HH", "TCH/H-", "TCH/HH", "PDCH", "-", "-",
-	"meas-rep", "0","0","2","1", "30","0", "0",
-	"meas-rep", "0","0","3","0", "30","0", "0",
-	"meas-rep", "0","0","4","0", "30","0", "0",
-	"meas-rep", "0","0","4","1", "30","0", "0",
-	/* pick one to move */
-	"set-min-free", "0", "TCH/H", "6",
-	"congestion-check",
-	"expect-chan", "0", "1",
-	"ack-chan",
-	"expect-ho", "0", "3",
-	"ho-complete",
-	"expect-ts-use", "0", "0",  "*", "TCH/F", "TCH/HH", "PDCH", "TCH/HH", "PDCH", "-", "-",
-	NULL
-};
-
-static char **test_cases[] =  {
-	test_case_0,
-	test_case_1,
-	test_case_2,
-	test_case_3,
-	test_case_4,
-	test_case_5,
-	test_case_6,
-	test_case_7,
-	test_case_8,
-	test_case_9,
-	test_case_10,
-	test_case_11,
-	test_case_12,
-	test_case_13,
-	test_case_14,
-	test_case_15,
-	test_case_16,
-	test_case_17,
-	test_case_18,
-	test_case_19,
-	test_case_20,
-	test_case_21,
-	test_case_22,
-	test_case_23,
-	test_case_24,
-	test_case_25,
-	test_case_26,
-	test_case_27,
-	test_case_28,
-	test_case_29,
-	test_case_30,
-	test_case_31,
-	test_case_32,
-};
+static void ho_test_vty_init()
+{
+	install_element(CONFIG_NODE, &create_n_bts_cmd);
+	install_element(CONFIG_NODE, &create_bts_cmd);
+	install_element(CONFIG_NODE, &create_ms_cmd);
+	install_element(CONFIG_NODE, &meas_rep_cmd);
+	install_element(CONFIG_NODE, &congestion_check_cmd);
+	install_element(CONFIG_NODE, &expect_no_chan_cmd);
+	install_element(CONFIG_NODE, &expect_chan_cmd);
+	install_element(CONFIG_NODE, &ack_chan_cmd);
+	install_element(CONFIG_NODE, &expect_ho_req_cmd);
+	install_element(CONFIG_NODE, &ho_complete_cmd);
+	install_element(CONFIG_NODE, &expect_ho_cmd);
+	install_element(CONFIG_NODE, &ho_failed_cmd);
+	install_element(CONFIG_NODE, &expect_ts_use_cmd);
+	install_element(CONFIG_NODE, &codec_f_cmd);
+	install_element(CONFIG_NODE, &codec_h_cmd);
+	install_element(CONFIG_NODE, &set_ts_use_cmd);
+}
 
 static const struct log_info_cat log_categories[] = {
 	[DHO] = {
@@ -1878,35 +1163,43 @@
 	.num_cat = ARRAY_SIZE(log_categories),
 };
 
-struct gsm_bts *bts_by_num_str(const char *num_str)
-{
-	struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, atoi(num_str));
-	OSMO_ASSERT(bts);
-	return bts;
-}
+static struct vty_app_info vty_info = {
+	.name = "ho_test",
+	.copyright =
+	"Copyright (C) 2020 sysmocom - s.f.m.c. GmbH\r\n"
+	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
+	"This is free software: you are free to change and redistribute it.\r\n"
+	"There is NO WARRANTY, to the extent permitted by law.\r\n",
+	.version	= PACKAGE_VERSION,
+	.usr_attr_desc	= {
+		[BSC_VTY_ATTR_RESTART_ABIS_OML_LINK] = \
+			"This command applies on A-bis OML link (re)establishment",
+		[BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK] = \
+			"This command applies on A-bis RSL link (re)establishment",
+		[BSC_VTY_ATTR_NEW_LCHAN] = \
+			"This command applies for newly created lchans",
+	},
+	.usr_attr_letters = {
+		[BSC_VTY_ATTR_RESTART_ABIS_OML_LINK]	= 'o',
+		[BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK]	= 'r',
+		[BSC_VTY_ATTR_NEW_LCHAN]		= 'l',
+	},
+};
 
 int main(int argc, char **argv)
 {
-	char **test_case;
-	int i;
-	int algorithm;
-	int test_case_i;
-	int last_test_i;
+	char *test_file = NULL;
+	int rc;
+
+	if (argc < 2) {
+		fprintf(stderr, "Pass a handover test script as argument\n");
+		exit(1);
+	}
+	test_file = argv[1];
 
 	ctx = talloc_named_const(NULL, 0, "handover_test");
 	msgb_talloc_ctx_init(ctx, 0);
-
-	test_case_i = argc > 1? atoi(argv[1]) : -1;
-	last_test_i = ARRAY_SIZE(test_cases) - 1;
-
-	if (test_case_i < 0 || test_case_i > last_test_i) {
-		for (i = 0; i <= last_test_i; i++) {
-			printf("Test #%d (algorithm %s):\n%s\n", i,
-				test_cases[i][0], test_cases[i][1]);
-		}
-		printf("\nPlease specify test case number 0..%d\n", last_test_i);
-		return EXIT_FAILURE;
-	}
+	vty_info.tall_ctx = ctx;
 
 	osmo_init_logging2(ctx, &log_info);
 
@@ -1919,6 +1212,10 @@
 	if (!bsc_gsmnet)
 		exit(1);
 
+	vty_init(&vty_info);
+	bsc_vty_init(bsc_gsmnet);
+	ho_test_vty_init();
+
 	ts_fsm_init();
 	lchan_fsm_init();
 	bsc_subscr_conn_fsm_init();
@@ -1945,355 +1242,24 @@
 	/* We don't really need any specific model here */
 	bts_model_unknown_init();
 
-	test_case = test_cases[test_case_i];
-
-	fprintf(stderr, "--------------------\n");
-	fprintf(stderr, "Performing the following test %d (algorithm %s):\n%s",
-		test_case_i, test_case[0], test_case[1]);
-	algorithm = atoi(test_case[0]);
-	test_case += 2;
-	fprintf(stderr, "--------------------\n");
-
 	/* Disable the congestion check timer, we will trigger manually. */
 	bsc_gsmnet->hodec2.congestion_check_interval_s = 0;
 
 	handover_decision_1_init();
 	hodec2_init(bsc_gsmnet);
 
-	while (*test_case) {
-		if (!strcmp(*test_case, "create-n-bts")) {
-			int n = atoi(test_case[1]);
-			for (i = 0; i < n; i++)
-				create_bts(1, bts_default_ts);
-			test_case += 2;
-		} else
-		if (!strcmp(*test_case, "create-bts")) {
-			/* new BTS with one TRX:
-			 * "create-bts", "1", "CCCH+SDCCH4", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "TCH/H", "TCH/H", "PDCH",
-			 *
-			 * new BTS with two TRX:
-			 * "create-bts", "2", "CCCH+SDCCH4", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "TCH/H", "TCH/H", "PDCH",
-			 *                    "SDCCH8", "TCH/F", "TCH/F", "TCH/F", "TCH/F", "TCH/H", "TCH/H", "PDCH",
-			 */
-			int num_trx = atoi(test_case[1]);
-			const char * const * ts_cfg = (void*)&test_case[2];
-			create_bts(num_trx, ts_cfg);
-			test_case += 2 + 8 * num_trx;
-		} else
-		if (!strcmp(*test_case, "as-enable")) {
-			fprintf(stderr, "- Set assignment enable state at "
-				"BTS %s to %s\n", test_case[1], test_case[2]);
-			ho_set_hodec2_as_active(bts_by_num_str(test_case[1])->ho, atoi(test_case[2]));
-			test_case += 3;
-		} else
-		if (!strcmp(*test_case, "ho-enable")) {
-			fprintf(stderr, "- Set handover enable state at "
-				"BTS %s to %s\n", test_case[1], test_case[2]);
-			ho_set_ho_active(bts_by_num_str(test_case[1])->ho, atoi(test_case[2]));
-			test_case += 3;
-		} else
-		if (!strcmp(*test_case, "afs-rxlev-improve")) {
-			fprintf(stderr, "- Set afs RX level improvement at "
-				"BTS %s to %s\n", test_case[1], test_case[2]);
-			ho_set_hodec2_afs_bias_rxlev(bts_by_num_str(test_case[1])->ho, atoi(test_case[2]));
-			test_case += 3;
-		} else
-		if (!strcmp(*test_case, "afs-rxqual-improve")) {
-			fprintf(stderr, "- Set afs RX quality improvement at "
-				"BTS %s to %s\n", test_case[1], test_case[2]);
-			ho_set_hodec2_afs_bias_rxqual(bts_by_num_str(test_case[1])->ho, atoi(test_case[2]));
-			test_case += 3;
-		} else
-		if (!strcmp(*test_case, "set-min-free")) {
-			fprintf(stderr, "- Setting minimum required free %s "
-				"slots at BTS %s to %s\n", test_case[2],
-				test_case[1], test_case[3]);
-			if (!strcmp(test_case[2], "TCH/F"))
-				ho_set_hodec2_tchf_min_slots(bts_by_num_str(test_case[1])->ho, atoi(test_case[3]));
-			else
-				ho_set_hodec2_tchh_min_slots(bts_by_num_str(test_case[1])->ho, atoi(test_case[3]));
-			test_case += 4;
-		} else
-		if (!strcmp(*test_case, "set-max-ho")) {
-			fprintf(stderr, "- Setting maximum parallel handovers "
-				"at BTS %s to %s\n", test_case[1],
-				test_case[2]);
-			ho_set_hodec2_ho_max( bts_by_num_str(test_case[1])->ho, atoi(test_case[2]));
-			test_case += 3;
-		} else
-		if (!strcmp(*test_case, "set-max-ta")) {
-			fprintf(stderr, "- Setting maximum timing advance "
-				"at BTS %s to %s\n", test_case[1],
-				test_case[2]);
-			ho_set_hodec2_max_distance(bts_by_num_str(test_case[1])->ho, atoi(test_case[2]));
-			test_case += 3;
-		} else
-		if (!strcmp(*test_case, "create-ms")) {
-			const char *bts_nr_str = test_case[1];
-			const char *tch_type = test_case[2];
-			const char *codec = test_case[3];
-			struct gsm_lchan *lchan;
-			fprintf(stderr, "- Creating mobile at BTS %s on "
-				"%s with %s codec\n", bts_nr_str, tch_type, codec);
-			lchan = create_lchan(bts_by_num_str(bts_nr_str),
-				!strcmp(tch_type, "TCH/F"), codec);
-			if (!lchan) {
-				printf("Failed to create lchan!\n");
-				return EXIT_FAILURE;
-			}
-			fprintf(stderr, " * New MS is at BTS %d TS %d\n",
-				lchan->ts->trx->bts->nr,
-				lchan->ts->nr);
-			test_case += 4;
-		} else
-		if (!strcmp(*test_case, "set-ta")) {
-			fprintf(stderr, "- Setting maximum timing advance "
-				"at MS %s to %s\n", test_case[1],
-				test_case[2]);
-			meas_ta_ms = atoi(test_case[2]);
-			test_case += 3;
-		} else
-		if (!strcmp(*test_case, "meas-rep")) {
-			/* meas-rep <bts-nr> <trx-nr> <ts-nr> <lchan-nr> <rxlev> <rxqual> <nr-of-neighbors> [<cell-idx> <rxlev> [...]] */
-			int n;
-			struct gsm_bts *bts;
-			struct gsm_bts_trx *trx;
-			struct gsm_bts_trx_ts *ts;
-			struct gsm_lchan *lc;
-			int bts_nr = atoi(test_case[1]);
-			int trx_nr = atoi(test_case[2]);
-			int ts_nr = atoi(test_case[3]);
-			int ss_nr = atoi(test_case[4]);
-			meas_dl_rxlev = atoi(test_case[5]);
-			meas_dl_rxqual = atoi(test_case[6]);
-			n = atoi(test_case[7]);
-
-			bts = gsm_bts_num(bsc_gsmnet, bts_nr);
-			trx = gsm_bts_trx_num(bts, trx_nr);
-			ts = &trx->ts[ts_nr];
-			lc = &ts->lchan[ss_nr];
-
-			if (!lchan_state_is(lc, LCHAN_ST_ESTABLISHED)) {
-				printf("Error: sending measurement report for %d-%d-%d-%d which is in state %s\n",
-				       bts_nr, trx_nr, ts_nr, ss_nr,
-				       lchan_state_name(lc));
-				exit(1);
-			}
-
-			fprintf(stderr, "- Sending measurement report from "
-				"%d-%d-%d-%d (rxlev=%d, rxqual=%d)\n",
-				bts_nr, trx_nr, ts_nr, ss_nr,
-				meas_dl_rxlev, meas_dl_rxqual);
-			meas_num_nc = n;
-			test_case += 8;
-			for (i = 0; i < n; i++) {
-				int nr = atoi(test_case[0]);
-				/* since our bts is not in the list of neighbor
-				 * cells, we need to shift */
-				if (nr >= lc->ts->trx->bts->nr)
-					nr++;
-				fprintf(stderr, " * Neighbor cell #%s, actual "
-					"BTS %d (rxlev=%s)\n", test_case[0], nr,
-					test_case[1]);
-				meas_bcch_f_nc[i] = atoi(test_case[0]);
-					/* bts number, not counting our own */
-				meas_rxlev_nc[i] = atoi(test_case[1]);
-				meas_bsic_nc[i] = 0x3f;
-				test_case += 2;
-			}
-			got_chan_req = 0;
-			gen_meas_rep(lc);
-		} else
-		if (!strcmp(*test_case, "congestion-check")) {
-			fprintf(stderr, "- Triggering congestion check\n");
-			got_chan_req = 0;
-			if (algorithm == 2)
-				hodec2_congestion_check(bsc_gsmnet);
-			test_case += 1;
-		} else
-		if (!strcmp(*test_case, "expect-chan")) {
-			fprintf(stderr, "- Expecting channel request at BTS %s "
-				"TS %s\n", test_case[1], test_case[2]);
-			if (!got_chan_req) {
-				printf("Test failed, because no channel was "
-					"requested\n");
-				return EXIT_FAILURE;
-			}
-			fprintf(stderr, " * Got channel request at BTS %d "
-				"TS %d\n", chan_req_lchan->ts->trx->bts->nr,
-				chan_req_lchan->ts->nr);
-			if (chan_req_lchan->ts->trx->bts->nr
-						!= atoi(test_case[1])) {
-				printf("Test failed, because channel was not "
-					"requested on expected BTS\n");
-				return EXIT_FAILURE;
-			}
-			if (chan_req_lchan->ts->nr != atoi(test_case[2])) {
-				printf("Test failed, because channel was not "
-					"requested on expected TS\n");
-				return EXIT_FAILURE;
-			}
-			test_case += 3;
-		} else
-		if (!strcmp(*test_case, "expect-no-chan")) {
-			fprintf(stderr, "- Expecting no channel request\n");
-			if (got_chan_req) {
-				fprintf(stderr, " * Got channel request at "
-					"BTS %d TS %d\n",
-					chan_req_lchan->ts->trx->bts->nr,
-					chan_req_lchan->ts->nr);
-				printf("Test failed, because channel was "
-					"requested\n");
-				return EXIT_FAILURE;
-			}
-			fprintf(stderr, " * Got no channel request\n");
-			test_case += 1;
-		} else
-		if (!strcmp(*test_case, "expect-ho")) {
-			fprintf(stderr, "- Expecting handover/assignment "
-				"request at BTS %s TS %s\n", test_case[1],
-				test_case[2]);
-			if (!got_ho_req) {
-				printf("Test failed, because no handover was "
-					"requested\n");
-				return EXIT_FAILURE;
-			}
-			fprintf(stderr, " * Got handover/assignment request at "
-				"BTS %d TS %d\n",
-				ho_req_lchan->ts->trx->bts->nr,
-				ho_req_lchan->ts->nr);
-			if (ho_req_lchan->ts->trx->bts->nr
-							!= atoi(test_case[1])) {
-				printf("Test failed, because "
-					"handover/assignment was not commanded "
-					"at the expected BTS\n");
-				return EXIT_FAILURE;
-			}
-			if (ho_req_lchan->ts->nr != atoi(test_case[2])) {
-				printf("Test failed, because "
-					"handover/assignment was not commanded "
-					"at the expected TS\n");
-				return EXIT_FAILURE;
-			}
-			test_case += 3;
-		} else
-		if (!strcmp(*test_case, "ack-chan")) {
-			fprintf(stderr, "- Acknowledging channel request\n");
-			if (!got_chan_req) {
-				printf("Cannot ack channel, because no "
-					"request\n");
-				return EXIT_FAILURE;
-			}
-			test_case += 1;
-			got_ho_req = 0;
-			send_chan_act_ack(chan_req_lchan, 1);
-		} else
-		if (!strcmp(*test_case, "ho-complete")) {
-			fprintf(stderr, "- Acknowledging handover/assignment "
-				"request\n");
-			if (!got_chan_req) {
-				printf("Cannot ack handover/assignment, "
-					"because no chan request\n");
-				return EXIT_FAILURE;
-			}
-			if (!got_ho_req) {
-				printf("Cannot ack handover/assignment, "
-					"because no ho request\n");
-				return EXIT_FAILURE;
-			}
-			test_case += 1;
-			got_chan_req = 0;
-			got_ho_req = 0;
-			send_ho_complete(chan_req_lchan, true);
-		} else
-		if (!strcmp(*test_case, "ho-failed")) {
-			fprintf(stderr, "- Making handover fail\n");
-			if (!got_chan_req) {
-				printf("Cannot fail handover, because no chan "
-					"request\n");
-				return EXIT_FAILURE;
-			}
-			test_case += 1;
-			got_chan_req = 0;
-			got_ho_req = 0;
-			send_ho_complete(ho_req_lchan, false);
-		} else
-		if (!strcmp(*test_case, "expect-ts-use")) {
-			/* expect-ts-use <bts-nr> <trx-nr> 8x<ts-use>
-			 * e.g.
-			 * expect-ts-use 0 0  - TCH/F - - TCH/H- TCH/HH TCH/-H PDCH
-			 * TCH/F: one FR call.
-			 * TCH/H-: HR TS with first subslot used as TCH/H, other subslot unused.
-			 * TCH/HH: HR TS with both subslots used as TCH/H
-			 * TCH/-H: HR TS with only second subslot used as TCH/H
-			 * PDCH: TS used for PDCH (e.g. unused dynamic TS)
-			 */
-			int bts_nr = atoi(test_case[1]);
-			int trx_nr = atoi(test_case[2]);
-			const char * const * ts_use = (void*)&test_case[3];
-			if (!expect_ts_use(bts_nr, trx_nr, ts_use))
-				return EXIT_FAILURE;
-			test_case += 1 + 2 + 8;
-		} else
-		if (!strcmp(*test_case, "set-ts-use")) {
-			/* set-ts-use <bts-nr> <trx-nr> 8x<ts-use>
-			 * e.g.
-			 * set-ts-use 0 0  * TCH/F - - TCH/H- TCH/HH TCH/-H PDCH
-			 * '*': keep as is
-			 * TCH/F: one FR call.
-			 * TCH/H-: HR TS with first subslot used as TCH/H, other subslot unused.
-			 * TCH/HH: HR TS with both subslots used as TCH/H
-			 * TCH/-H: HR TS with only second subslot used as TCH/H
-			 * PDCH: TS used for PDCH (e.g. unused dynamic TS)
-			 */
-			int bts_nr = atoi(test_case[1]);
-			int trx_nr = atoi(test_case[2]);
-			const char * const * ts_use = (void*)&test_case[3];
-			if (!set_ts_use(bts_nr, trx_nr, ts_use))
-				return EXIT_FAILURE;
-			if (!expect_ts_use(bts_nr, trx_nr, ts_use))
-				return EXIT_FAILURE;
-			test_case += 1 + 2 + 8;
-		} else
-		if (!strcmp(*test_case, "print")) {
-			fprintf(stderr, "\n%s\n\n", test_case[1]);
-			test_case += 2;
-		} else {
-			printf("Unknown test command '%s', please fix!\n",
-				*test_case);
-			return EXIT_FAILURE;
-		}
-
-		{
-			/* Help the lchan out of releasing states */
-			struct gsm_bts *bts;
-			llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
-				struct gsm_bts_trx *trx;
-				llist_for_each_entry(trx, &bts->trx_list, list) {
-					int ts_nr;
-					for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) {
-						struct gsm_lchan *lchan;
-						ts_for_each_lchan(lchan, &trx->ts[ts_nr]) {
-
-							if (lchan->fi && lchan->fi->state == LCHAN_ST_WAIT_BEFORE_RF_RELEASE) {
-								osmo_fsm_inst_state_chg(lchan->fi, LCHAN_ST_WAIT_RF_RELEASE_ACK, 0, 0);
-								osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RSL_RF_CHAN_REL_ACK, 0);
-							}
-						}
-					}
-				}
-			}
-		}
+	rc = vty_read_config_file(test_file, NULL);
+	if (rc < 0) {
+		fprintf(stderr, "Failed to parse the test file: '%s'\n", test_file);
 	}
 
-	fprintf(stderr, "--------------------\n");
-
-	printf("Test OK\n");
-
-	fprintf(stderr, "--------------------\n");
-
 	talloc_free(ctx);
-	return EXIT_SUCCESS;
+	fprintf(stderr,"-------------------\n");
+	if (!rc)
+		fprintf(stderr, "pass\n");
+	else
+		fprintf(stderr, "FAIL\n");
+	return rc;
 }
 
 void rtp_socket_free() {}
diff --git a/tests/handover/handover_test.ok b/tests/handover/handover_test.ok
deleted file mode 100644
index 678f9a3..0000000
--- a/tests/handover/handover_test.ok
+++ /dev/null
@@ -1 +0,0 @@
-Test OK
diff --git a/tests/handover/handover_tests.ok b/tests/handover/handover_tests.ok
new file mode 100644
index 0000000..5ec8dc2
--- /dev/null
+++ b/tests/handover/handover_tests.ok
@@ -0,0 +1,33 @@
+pass test0000.ho_vty
+pass test0001.ho_vty
+pass test0002.ho_vty
+pass test0003.ho_vty
+pass test0004.ho_vty
+pass test0005.ho_vty
+pass test0006.ho_vty
+pass test0007.ho_vty
+pass test0008.ho_vty
+pass test0009.ho_vty
+pass test0010.ho_vty
+pass test0011.ho_vty
+pass test0012.ho_vty
+pass test0013.ho_vty
+pass test0014.ho_vty
+pass test0015.ho_vty
+pass test0016.ho_vty
+pass test0017.ho_vty
+pass test0018.ho_vty
+pass test0019.ho_vty
+pass test0020.ho_vty
+pass test0021.ho_vty
+pass test0022.ho_vty
+pass test0023.ho_vty
+pass test0024.ho_vty
+pass test0025.ho_vty
+pass test0026.ho_vty
+pass test0027.ho_vty
+pass test0028.ho_vty
+pass test0029.ho_vty
+pass test0030.ho_vty
+pass test0031.ho_vty
+pass test0032.ho_vty
diff --git a/tests/handover/handover_tests.sh b/tests/handover/handover_tests.sh
new file mode 100755
index 0000000..68a8e2d
--- /dev/null
+++ b/tests/handover/handover_tests.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+set -e
+tests_dir="${1:-.}"
+build_dir="${2:-.}"
+update="$3"
+test -d "$tests_dir"
+test -d "$build_dir"
+
+if [ -n "$update" -a "x$update" != "x-u" ]; then
+	echo "unknown argument: $update"
+	exit 1
+fi
+
+one_test() {
+	test_path="$1"
+	test_name="$(basename "$test_path")"
+	got_out="$(mktemp "tmp.$test_name.stdout.XXXXX")"
+	got_err="$(mktemp "tmp.$test_name.stderr.XXXXX")"
+	set +e
+	"$build_dir"/handover_test "$test_path" > "$got_out" 2> "$got_err"
+	rc=$?
+	if [ "x$rc" = "x0" ]; then
+		expect_out="$test_path.ok"
+		expect_err="$test_path.err"
+		if [ "x$update" = "x-u" ]; then
+			cp "$got_out" "$expect_out"
+			cp "$got_err" "$expect_err"
+		else
+			if [ -f "$expect_out" ]; then
+				diff -u "$expect_out" "$got_out"
+				rc=$?
+			fi
+			if [ -f "$expect_err" ]; then
+				diff -u "$expect_err" "$got_err"
+				rc2=$?
+			fi
+			if [ "x$rc" = "x0" ]; then
+				rc=$rc2
+			fi
+		fi
+	fi
+	rm "$got_out"
+	rm "$got_err"
+	set -e
+	return $rc
+}
+
+results="$(mktemp "tmp.handover_test_results.XXXXX")"
+for test_path in "$tests_dir"/test*.ho_vty ; do
+	test_name="$(basename "$test_path")"
+	if one_test "$test_path"; then
+		echo "pass $test_name" >> "$results"
+	else
+		echo "FAIL $test_name" >> "$results"
+	fi
+done
+set +e
+cat "$results"
+failed="$(grep FAIL "$results")"
+if [ -z "$failed" -a "x$update" = "x-u" ]; then
+	cp "$results" "$tests_dir"/handover_tests.ok
+fi
+rm "$results"
+if [ -n "$failed" ]; then
+	echo "tests failed"
+	exit 1
+fi
+exit 0
diff --git a/tests/handover/test0000.ho_vty b/tests/handover/test0000.ho_vty
new file mode 100644
index 0000000..00e0e1a
--- /dev/null
+++ b/tests/handover/test0000.ho_vty
@@ -0,0 +1,6 @@
+# Stay in better cell
+# There are many neighbor cells, but only the current cell is the best cell, so no handover is performed
+create-n-bts 7
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20 21 18 20 23 19
+expect-no-chan
diff --git a/tests/handover/test0001.ho_vty b/tests/handover/test0001.ho_vty
new file mode 100644
index 0000000..afa0a88
--- /dev/null
+++ b/tests/handover/test0001.ho_vty
@@ -0,0 +1,8 @@
+# Handover to best better cell
+# The best neighbor cell is selected
+create-n-bts 7
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 20 21 18 20 23 19
+expect-ho from lchan 0 0 1 0 to lchan 5 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 5 0 states * TCH/F - - - - - -
diff --git a/tests/handover/test0002.ho_vty b/tests/handover/test0002.ho_vty
new file mode 100644
index 0000000..ffd0311
--- /dev/null
+++ b/tests/handover/test0002.ho_vty
@@ -0,0 +1,36 @@
+# Handover and Assignment must be enabled
+# This test will start with disabled assignment and handover.  A
+# better neighbor cell (assignment enabled) will not be selected and
+# also no assignment from TCH/H to TCH/F to improve quality. There
+# will be no handover nor assignment. After enabling assignment on the
+# current cell, the MS will assign to TCH/F. After enabling handover
+# in the current cell, but disabling in the neighbor cell, handover
+# will not be performed, until it is enabled in the neighbor cell too.
+network
+ handover 0
+ handover2 afs-bias rxlev 5
+ handover2 assignment 0
+
+create-n-bts 2
+set-ts-use trx 0 0 states * - - - - TCH/H- - -
+meas-rep lchan 0 0 5 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 0
+  handover2 assignment 1
+meas-rep lchan 0 0 5 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 5 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ bts 0
+  handover 1
+meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 1
+  handover 1
+meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0003.ho_vty b/tests/handover/test0003.ho_vty
new file mode 100644
index 0000000..be25c20
--- /dev/null
+++ b/tests/handover/test0003.ho_vty
@@ -0,0 +1,16 @@
+# Penalty timer must not run
+# The MS will try to handover to a better cell, but this will fail.
+# Even though the cell is still better, handover will not be performed
+# due to penalty timer after handover failure
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-chan lchan 1 0 1 0
+ack-chan
+expect-ho-req lchan 0 0 1 0
+ho-failed
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
diff --git a/tests/handover/test0004.ho_vty b/tests/handover/test0004.ho_vty
new file mode 100644
index 0000000..2f514bf
--- /dev/null
+++ b/tests/handover/test0004.ho_vty
@@ -0,0 +1,20 @@
+# TCH/H keeping with HR codec
+# The MS is using half rate V1 codec, but the better cell is congested
+# at TCH/H slots. As the congestion is removed, the handover takes
+# place.
+
+create-n-bts 2
+network
+ bts 1
+  handover2 min-free-slots tch/h 4
+codec tch/h HR
+set-ts-use trx 0 0 states * - - - - TCH/H- - -
+meas-rep lchan 0 0 5 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 1
+  handover2 min-free-slots tch/h 3
+meas-rep lchan 0 0 5 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 5 0 to lchan 1 0 5 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * - - - - TCH/H- - -
diff --git a/tests/handover/test0005.ho_vty b/tests/handover/test0005.ho_vty
new file mode 100644
index 0000000..4729d25
--- /dev/null
+++ b/tests/handover/test0005.ho_vty
@@ -0,0 +1,21 @@
+# TCH/F keeping with FR codec
+# The MS is using full rate V1 codec, but the better cell is congested
+# at TCH/F slots. As the congestion is removed, the handover takes
+# place.
+
+create-n-bts 2
+network
+ bts 1
+  handover2 min-free-slots tch/f 4
+codec tch/f FR
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 1
+  handover2 min-free-slots tch/f 3
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0006.ho_vty b/tests/handover/test0006.ho_vty
new file mode 100644
index 0000000..f1a9b40
--- /dev/null
+++ b/tests/handover/test0006.ho_vty
@@ -0,0 +1,21 @@
+# TCH/F keeping with EFR codec
+# The MS is using full rate V2 codec, but the better cell is congested
+# at TCH/F slots. As the congestion is removed, the handover takes
+# place.
+
+create-n-bts 2
+network
+ bts 1
+  handover2 min-free-slots tch/f 4
+codec tch/f EFR
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 1
+  handover2 min-free-slots tch/f 3
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0007.ho_vty b/tests/handover/test0007.ho_vty
new file mode 100644
index 0000000..22c5e87
--- /dev/null
+++ b/tests/handover/test0007.ho_vty
@@ -0,0 +1,14 @@
+# TCH/F to TCH/H changing with AMR codec
+# The MS is using AMR V3 codec, the better cell is congested at TCH/F
+# slots. The handover is performed to non-congested TCH/H slots.
+
+create-n-bts 2
+network
+ bts 1
+  handover2 min-free-slots tch/f 4
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * - - - - TCH/H- - -
+
diff --git a/tests/handover/test0008.ho_vty b/tests/handover/test0008.ho_vty
new file mode 100644
index 0000000..3b06d3d
--- /dev/null
+++ b/tests/handover/test0008.ho_vty
@@ -0,0 +1,9 @@
+# No handover to a cell with no slots available
+# If no slot is available, no handover is performed
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+set-ts-use trx 1 0 states * TCH/F TCH/F TCH/F TCH/F TCH/HH TCH/HH -
+meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
diff --git a/tests/handover/test0009.ho_vty b/tests/handover/test0009.ho_vty
new file mode 100644
index 0000000..31482d2
--- /dev/null
+++ b/tests/handover/test0009.ho_vty
@@ -0,0 +1,16 @@
+# No more parallel handovers, if max_unsync_ho is defined
+# There are three mobiles that want to handover, but only two can do
+# it at a time, because the maximum number is limited to two.
+
+create-n-bts 2
+network
+ bts 1
+  handover2 max-handovers 2
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - - - -
+meas-rep lchan 0 0 1 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-chan lchan 1 0 1 0
+meas-rep lchan 0 0 2 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-chan lchan 1 0 2 0
+meas-rep lchan 0 0 3 0 rxlev 0 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
diff --git a/tests/handover/test0010.ho_vty b/tests/handover/test0010.ho_vty
new file mode 100644
index 0000000..5c06a36
--- /dev/null
+++ b/tests/handover/test0010.ho_vty
@@ -0,0 +1,13 @@
+# Hysteresis
+# If neighbor cell is better, handover is only performed if the
+# amount of improvement is greater or equal hyteresis
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 27 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 26 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0011.ho_vty b/tests/handover/test0011.ho_vty
new file mode 100644
index 0000000..e034fad
--- /dev/null
+++ b/tests/handover/test0011.ho_vty
@@ -0,0 +1,20 @@
+# No Hysteresis and minimum RX level
+# If current cell's RX level is below mimium level, handover must be
+# performed, no matter of the hysteresis. First do not perform
+# handover to better neighbor cell, because the hysteresis is not
+# met. Second do not perform handover because better neighbor cell is
+# below minimum RX level. Third perform handover because current cell
+# is below minimum RX level, even if the better neighbor cell (minimum
+# RX level reached) does not meet the hysteresis.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 11
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 8 rxqual 0 ta 0 neighbors 9
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 9 rxqual 0 ta 0 neighbors 10
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0012.ho_vty b/tests/handover/test0012.ho_vty
new file mode 100644
index 0000000..b2319d4
--- /dev/null
+++ b/tests/handover/test0012.ho_vty
@@ -0,0 +1,21 @@
+# No handover to congested cell
+# The better neighbor cell is congested, so no handover is performed.
+# After the congestion is over, handover will be performed.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ bts 1
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+network
+ bts 1
+  handover2 min-free-slots tch/f 3
+  handover2 min-free-slots tch/h 3
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0013.ho_vty b/tests/handover/test0013.ho_vty
new file mode 100644
index 0000000..e8304a1
--- /dev/null
+++ b/tests/handover/test0013.ho_vty
@@ -0,0 +1,24 @@
+# Handover to balance congestion
+# The current and the better cell are congested, so no handover is
+# performed. This is because handover would congest the neighbor cell
+# more. After congestion rises in the current cell, the handover is
+# performed to balance congestion
+
+create-n-bts 2
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ bts 0
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+ bts 1
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+set-ts-use trx 0 0 states * TCH/F TCH/F - - - - -
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - TCH/F - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0014.ho_vty b/tests/handover/test0014.ho_vty
new file mode 100644
index 0000000..d43b89c
--- /dev/null
+++ b/tests/handover/test0014.ho_vty
@@ -0,0 +1,18 @@
+# Handover to congested cell, if RX level is below minimum
+# The better neighbor cell is congested, so no handover is performed.
+# If the RX level of the current cell drops below minimum acceptable
+# level, the handover is performed.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ bts 1
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 9 rxqual 0 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0015.ho_vty b/tests/handover/test0015.ho_vty
new file mode 100644
index 0000000..2b80d01
--- /dev/null
+++ b/tests/handover/test0015.ho_vty
@@ -0,0 +1,37 @@
+# Handover to cell with worse RXLEV, if RXQUAL is below minimum
+# The neighbor cell has worse RXLEV, so no handover is performed.
+# If the RXQUAL of the current cell drops below minimum acceptable
+# level, the handover is performed. It is also required that 10
+# reports are received, before RXQUAL is checked.
+#
+# (See also test 28, which tests for RXQUAL triggering HO to congested cell.)
+#
+# TODO: bad RXQUAL may want to prefer assignment within the same cell to avoid interference.
+# See Performance Enhancements in a Frequency Hopping GSM Network (Nielsen Wigard 2002), Chapter
+# 2.1.1, "Interference" in the list of triggers on p.157.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 6 ta 0 neighbors 30
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0016.ho_vty b/tests/handover/test0016.ho_vty
new file mode 100644
index 0000000..05c4a76
--- /dev/null
+++ b/tests/handover/test0016.ho_vty
@@ -0,0 +1,21 @@
+# Handover due to maximum TA exceeded
+# The MS in the current (best) cell has reached maximum allowed timing
+# advance. No handover is performed until the timing advance exceeds
+# it. The originating cell is still the best, but no handover is
+# performed back to that cell, because the penalty timer (due to
+# maximum allowed timing advance) is running.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ bts 0
+  handover2 maximum distance 5
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 5 neighbors 20
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 6 neighbors 20
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+meas-rep lchan 1 0 1 0 rxlev 20 rxqual 0 ta 6 neighbors 30
+expect-no-chan
+
diff --git a/tests/handover/test0017.ho_vty b/tests/handover/test0017.ho_vty
new file mode 100644
index 0000000..824d1d3
--- /dev/null
+++ b/tests/handover/test0017.ho_vty
@@ -0,0 +1,34 @@
+# Congestion check: No congestion
+# Three cells have different number of used slots, but there is no
+# congestion in any of these cells. No handover is performed.
+
+create-n-bts 3
+network
+ bts 0
+  handover2 min-free-slots tch/f 2
+  handover2 min-free-slots tch/h 2
+ bts 1
+  handover2 min-free-slots tch/f 2
+  handover2 min-free-slots tch/h 2
+ bts 2
+  handover2 min-free-slots tch/f 2
+  handover2 min-free-slots tch/h 2
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - -
+set-ts-use trx 1 0 states * TCH/F -     - - TCH/H- - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+meas-rep lchan 0 0 2 0 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+meas-rep lchan 0 0 5 1 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+meas-rep lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+meas-rep lchan 1 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+congestion-check
+expect-no-chan
+expect-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - -
+expect-ts-use trx 1 0 states * TCH/F -     - - TCH/H- - -
+
diff --git a/tests/handover/test0018.ho_vty b/tests/handover/test0018.ho_vty
new file mode 100644
index 0000000..4919d20
--- /dev/null
+++ b/tests/handover/test0018.ho_vty
@@ -0,0 +1,39 @@
+# Congestion check: One out of three cells is congested
+# Three cells have different number of used slots, but there is
+# congestion at TCH/F in the first cell. Handover is performed with
+# the best candidate.
+
+create-n-bts 3
+network
+ bts 0
+  handover2 min-free-slots tch/f 2
+  handover2 min-free-slots tch/h 2
+ bts 1
+  handover2 min-free-slots tch/f 2
+  handover2 min-free-slots tch/h 2
+ bts 2
+  handover2 min-free-slots tch/f 2
+  handover2 min-free-slots tch/h 2
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - TCH/HH - -
+set-ts-use trx 1 0 states * TCH/F -     -     - TCH/H- - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+meas-rep lchan 0 0 2 0 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+meas-rep lchan 0 0 3 0 rxlev 30 rxqual 0 ta 0 neighbors 21 1 20
+expect-no-chan
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+meas-rep lchan 0 0 5 1 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+meas-rep lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+meas-rep lchan 1 0 5 0 rxlev 30 rxqual 0 ta 0 neighbors 20 1 20
+expect-no-chan
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - TCH/HH - -
+expect-ts-use trx 1 0 states * TCH/F -     -     - TCH/H- - -
+congestion-check
+expect-ho from lchan 0 0 3 0 to lchan 1 0 2 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/HH - -
+expect-ts-use trx 1 0 states * TCH/F TCH/F - - TCH/H- - -
+
diff --git a/tests/handover/test0019.ho_vty b/tests/handover/test0019.ho_vty
new file mode 100644
index 0000000..2e7fa5a
--- /dev/null
+++ b/tests/handover/test0019.ho_vty
@@ -0,0 +1,26 @@
+# Congestion check: Balancing over congested cells
+# Two cells are congested, but the second cell is less congested.
+# Handover is performed to solve the congestion.
+
+create-n-bts 2
+network
+ bts 0
+  handover2 min-free-slots tch/f 4
+ bts 1
+  handover2 min-free-slots tch/f 4
+codec tch/f FR
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F - - - -
+set-ts-use trx 1 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+meas-rep lchan 0 0 2 0 rxlev 30 rxqual 0 ta 0 neighbors 21
+expect-no-chan
+meas-rep lchan 0 0 3 0 rxlev 30 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+meas-rep lchan 1 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 2 0 to lchan 1 0 2 0
+expect-ts-use trx 0 0 states * TCH/F -     TCH/F - - - -
+expect-ts-use trx 1 0 states * TCH/F TCH/F -     - - - -
+
diff --git a/tests/handover/test0020.ho_vty b/tests/handover/test0020.ho_vty
new file mode 100644
index 0000000..5c93494
--- /dev/null
+++ b/tests/handover/test0020.ho_vty
@@ -0,0 +1,19 @@
+# Congestion check: Solving congestion by handover TCH/F -> TCH/H
+# Two BTS, one MS in the first congested BTS must handover to
+# non-congested TCH/H of second BTS, in order to solve congestion
+
+create-n-bts 2
+network
+ bts 0
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+ bts 1
+  handover2 min-free-slots tch/f 4
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 1 0 5 0
+expect-ts-use trx 0 0 states * - - - - -      - -
+expect-ts-use trx 1 0 states * - - - - TCH/H- - -
+
diff --git a/tests/handover/test0021.ho_vty b/tests/handover/test0021.ho_vty
new file mode 100644
index 0000000..79a3084
--- /dev/null
+++ b/tests/handover/test0021.ho_vty
@@ -0,0 +1,20 @@
+# Congestion check: Balancing congestion by handover TCH/F -> TCH/H
+# Two BTS, one MS in the first congested BTS must handover to
+# less-congested TCH/H of second BTS, in order to balance congestion
+
+create-n-bts 2
+network
+ bts 0
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+ bts 1
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/H- - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * -     TCH/F - - TCH/H- - -
+expect-ts-use trx 1 0 states * TCH/F -     - - -      - -
+
diff --git a/tests/handover/test0022.ho_vty b/tests/handover/test0022.ho_vty
new file mode 100644
index 0000000..d6515fc
--- /dev/null
+++ b/tests/handover/test0022.ho_vty
@@ -0,0 +1,19 @@
+# Congestion check: Upgrading worst candidate from TCH/H -> TCH/F
+# There is only one BTS. The TCH/H slots are congested. Since
+# assignment is performed to less-congested TCH/F, the candidate with
+# the worst RX level is chosen.
+
+create-n-bts 1
+network
+ bts 0
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+set-ts-use trx 0 0 states * - - - - TCH/HH TCH/H- -
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0
+meas-rep lchan 0 0 5 1 rxlev 34 rxqual 0 ta 0
+meas-rep lchan 0 0 6 0 rxlev 20 rxqual 0 ta 0
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 6 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - TCH/HH - -
+
diff --git a/tests/handover/test0023.ho_vty b/tests/handover/test0023.ho_vty
new file mode 100644
index 0000000..4e82761
--- /dev/null
+++ b/tests/handover/test0023.ho_vty
@@ -0,0 +1,72 @@
+# Story: 'A neighbor is your friend'
+
+create-n-bts 3
+
+# Andreas is driving along the coast, on a sunny june afternoon.
+# Suddenly he is getting a call from his friend and neighbor Axel.
+#
+# What happens: Two MS are created, #0 for Axel, #1 for Andreas.
+# Axel:
+create-ms bts 2 TCH/F AMR
+# andreas:
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+expect-ts-use trx 2 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+
+# Axel asks Andreas if he would like to join them for a barbecue.
+# Axel's house is right in the neighborhood and the weather is fine.
+# Andreas agrees, so he drives to a close store to buy some barbecue
+# skewers.
+#
+# What happens: While driving, a different cell (mounted atop the
+# store) becomes better.
+# drive to bts 1:
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 35
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+expect-ts-use trx 2 0 states * TCH/F - - - - - -
+
+# While Andreas is walking into the store, Axel asks, if he could also
+# bring some beer. Andreas has problems understanding him: "I have a
+# bad reception here. The cell tower is right atop the store, but poor
+# coverage inside. Can you repeat please?"
+#
+# What happens: Inside the store the close cell is so bad, that
+# handover back to the previous cell is required.
+# bts 1 becomes bad, so bts 0 helps out:
+meas-rep lchan 1 0 1 0 rxlev 5 rxqual 0 ta 0 neighbors 20
+expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+expect-ts-use trx 2 0 states * TCH/F - - - - - -
+
+# After Andreas bought skewers and beer, he leaves the store.
+#
+# What happens: Outside the store the close cell is better again, so
+# handover back to the that cell is performed.
+# bts 1 becomes better again:
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 35
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+expect-ts-use trx 2 0 states * TCH/F - - - - - -
+
+# bts 2 becomes better:
+# Andreas drives down to the lake where Axel's house is.
+#
+# What happens: There is a small cell at Axel's house, which becomes
+# better, because the current cell has no good comverage at the lake.
+meas-rep lchan 1 0 1 0 rxlev 14 rxqual 0 ta 0 neighbors 2 63
+expect-ho from lchan 1 0 1 0 to lchan 2 0 2 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+expect-ts-use trx 2 0 states * TCH/F TCH/F - - - - -
+
+# Andreas wonders why he still has good radio coverage: "Last time it
+# was so bad". Axel says: "I installed a pico cell in my house,
+# now we can use our mobile phones down here at the lake."
+
diff --git a/tests/handover/test0024.ho_vty b/tests/handover/test0024.ho_vty
new file mode 100644
index 0000000..b67a248
--- /dev/null
+++ b/tests/handover/test0024.ho_vty
@@ -0,0 +1,46 @@
+# No (or not enough) measurements for handover
+# Do not solve congestion in cell, because there is no measurement.
+# As soon as enough measurements available (1 in our case), perform
+# handover. Afterwards the old cell becomes congested and the new
+# cell is not. Do not perform handover until new measurements are
+# received.
+#
+# two cells, first in congested, but no handover:
+
+create-n-bts 2
+network
+ bts 0
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+congestion-check
+expect-no-chan
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+
+# send measurement and trigger congestion check:
+meas-rep lchan 0 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
+# congest the first cell and remove congestion from second cell:
+network
+ bts 0
+  handover2 min-free-slots tch/f 0
+  handover2 min-free-slots tch/h 0
+ bts 1
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+
+# no handover until measurements applied:
+congestion-check
+expect-no-chan
+meas-rep lchan 1 0 1 0 rxlev 20 rxqual 0 ta 0 neighbors 20
+expect-no-chan
+congestion-check
+expect-ho from lchan 1 0 1 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+expect-ts-use trx 1 0 states * - - - - - - -
+
diff --git a/tests/handover/test0025.ho_vty b/tests/handover/test0025.ho_vty
new file mode 100644
index 0000000..b3e76f8
--- /dev/null
+++ b/tests/handover/test0025.ho_vty
@@ -0,0 +1,10 @@
+# Stay in better cell
+# There are many neighbor cells, but only the current cell is the best
+# cell, so no handover is performed
+
+create-n-bts 7
+create-ms bts 0 TCH/F AMR
+expect-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 20 21 18 20 23 19
+expect-no-chan
+
diff --git a/tests/handover/test0026.ho_vty b/tests/handover/test0026.ho_vty
new file mode 100644
index 0000000..c3f5544
--- /dev/null
+++ b/tests/handover/test0026.ho_vty
@@ -0,0 +1,10 @@
+# Handover to best better cell
+# The best neighbor cell is selected
+
+create-n-bts 7
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+meas-rep lchan 0 0 1 0 rxlev 10 rxqual 0 ta 0 neighbors 20 21 18 20 23 19
+expect-ho from lchan 0 0 1 0 to lchan 5 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 5 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0027.ho_vty b/tests/handover/test0027.ho_vty
new file mode 100644
index 0000000..e7778d0
--- /dev/null
+++ b/tests/handover/test0027.ho_vty
@@ -0,0 +1,27 @@
+# Congestion check: Upgrading worst candidate from TCH/H -> TCH/F
+# There is only one BTS. The TCH/H slots are congested. Since
+# assignment is performed to less-congested TCH/F, the candidate with
+# the worst RX level is chosen. (So far like test 22.)
+# After that, trigger more congestion checks to ensure stability.
+
+create-n-bts 1
+network
+ bts 0
+  handover2 min-free-slots tch/f 2
+  handover2 min-free-slots tch/h 4
+set-ts-use trx 0 0 states * - - - - TCH/HH TCH/H- -
+meas-rep lchan 0 0 5 0 rxlev 30 rxqual 0 ta 0
+meas-rep lchan 0 0 5 1 rxlev 34 rxqual 0 ta 0
+meas-rep lchan 0 0 6 0 rxlev 20 rxqual 0 ta 0
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 6 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0 states * TCH/F - - - TCH/HH - -
+congestion-check
+expect-ho from lchan 0 0 5 0 to lchan 0 0 2 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/-H - -
+congestion-check
+expect-no-chan
+congestion-check
+expect-no-chan
+
diff --git a/tests/handover/test0028.ho_vty b/tests/handover/test0028.ho_vty
new file mode 100644
index 0000000..1e81293
--- /dev/null
+++ b/tests/handover/test0028.ho_vty
@@ -0,0 +1,35 @@
+# Handover to congested cell, if RX quality is below minimum
+# The better neighbor cell is congested, so no handover is performed.
+# If the RX quality of the current cell drops below minimum acceptable
+# level, the handover is performed. It is also required that 10
+# resports are received, before RX quality is checked.
+
+create-n-bts 2
+set-ts-use trx 0 0 states * TCH/F - - - - - -
+network
+ bts 1
+  handover2 min-free-slots tch/f 4
+  handover2 min-free-slots tch/h 4
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-no-chan
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 6 ta 0 neighbors 40
+expect-ho from lchan 0 0 1 0 to lchan 1 0 1 0
+expect-ts-use trx 0 0 states * - - - - - - -
+expect-ts-use trx 1 0 states * TCH/F - - - - - -
+
diff --git a/tests/handover/test0029.ho_vty b/tests/handover/test0029.ho_vty
new file mode 100644
index 0000000..ffa3614
--- /dev/null
+++ b/tests/handover/test0029.ho_vty
@@ -0,0 +1,15 @@
+# Congestion check: Balancing congestion by handover TCH/F -> TCH/H
+# One BTS, and TCH/F are considered congested, TCH/H are not.
+
+create-n-bts 1
+network
+ bts 0
+  handover2 min-free-slots tch/f 3
+  handover2 min-free-slots tch/h 0
+set-ts-use trx 0 0 states * TCH/F TCH/F - - TCH/H- - -
+meas-rep lchan 0 0 1 0 rxlev 30 rxqual 0 ta 0 neighbors 30
+expect-no-chan
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 0 0 5 1
+expect-ts-use trx 0 0 states * - TCH/F - - TCH/HH - -
+
diff --git a/tests/handover/test0030.ho_vty b/tests/handover/test0030.ho_vty
new file mode 100644
index 0000000..3ae5997
--- /dev/null
+++ b/tests/handover/test0030.ho_vty
@@ -0,0 +1,81 @@
+# Congestion check: Balancing congestion by handover TCH/F -> TCH/H
+# With dynamic timeslots.
+# As soon as only one TCH/F is left, there should be HO to a dyn TS.
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn dyn dyn PDCH
+
+network
+ bts 0
+  handover2 min-free-slots tch/f 2
+  handover2 min-free-slots tch/h 0
+  handover2 assignment 1
+set-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F pdch pdch pdch
+
+# (there must be at leas one measurement report on each lchan for congestion check to work)
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+meas-rep lchan 0 0 2 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+meas-rep lchan 0 0 3 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+meas-rep lchan 0 0 4 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+
+congestion-check
+expect-no-chan
+
+create-ms bts 0 TCH/F AMR
+meas-rep lchan 0 0 5 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F TCH/F pdch pdch
+
+congestion-check
+expect-ho from lchan 0 0 5 0 to lchan 0 0 6 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F pdch TCH/H- pdch
+
+congestion-check
+expect-ho from lchan 0 0 4 0 to lchan 0 0 6 1
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F pdch pdch TCH/HH pdch
+
+congestion-check
+expect-no-chan
+
+create-ms bts 0 TCH/F AMR
+meas-rep lchan 0 0 4 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/F pdch TCH/HH pdch
+
+congestion-check
+expect-ho from lchan 0 0 4 0 to lchan 0 0 5 0
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F pdch TCH/H- TCH/HH pdch
+
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 0 0 5 1
+expect-ts-use trx 0 0 states * - TCH/F TCH/F pdch TCH/HH TCH/HH pdch
+
+congestion-check
+expect-no-chan
+
+create-ms bts 0 TCH/F AMR
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F pdch TCH/HH TCH/HH pdch
+
+congestion-check
+expect-ho from lchan 0 0 1 0 to lchan 0 0 4 0
+expect-ts-use trx 0 0 states * - TCH/F TCH/F TCH/H- TCH/HH TCH/HH pdch
+
+congestion-check
+expect-ho from lchan 0 0 2 0 to lchan 0 0 4 1
+expect-ts-use trx 0 0 states * - - TCH/F TCH/HH TCH/HH TCH/HH pdch
+
+congestion-check
+expect-no-chan
+
+create-ms bts 0 TCH/F AMR
+meas-rep lchan 0 0 1 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-ts-use trx 0 0 states * TCH/F - TCH/F TCH/HH TCH/HH TCH/HH pdch
+
+congestion-check
+expect-no-chan
+
+create-ms bts 0 TCH/F AMR
+meas-rep lchan 0 0 2 0 rxlev 40 rxqual 0 ta 0 neighbors 30
+expect-ts-use trx 0 0 states * TCH/F TCH/F TCH/F TCH/HH TCH/HH TCH/HH pdch
+
+congestion-check
+expect-no-chan
+
diff --git a/tests/handover/test0031.ho_vty b/tests/handover/test0031.ho_vty
new file mode 100644
index 0000000..c648c27
--- /dev/null
+++ b/tests/handover/test0031.ho_vty
@@ -0,0 +1,7 @@
+# assign new MS: re-use half used TCH/H to avoid switching more dyn TS to TCH/H
+
+create-bts trx-count 1 timeslots c+s4 TCH/F TCH/F TCH/F dyn  dyn    dyn  PDCH
+set-ts-use    trx 0 0     states *    -     -     -     pdch TCH/H- pdch pdch
+create-ms bts 0 TCH/H AMR
+expect-ts-use trx 0 0     states *    -     -     -     pdch TCH/HH pdch pdch
+
diff --git a/tests/handover/test0032.ho_vty b/tests/handover/test0032.ho_vty
new file mode 100644
index 0000000..b90236d
--- /dev/null
+++ b/tests/handover/test0032.ho_vty
@@ -0,0 +1,15 @@
+# Congestion check: favor moving a TCH/H that frees a half-used dyn TS completely
+
+create-bts trx-count 1 timeslots c+s4 dyn   dyn    dyn    dyn    dyn  - -
+set-ts-use    trx 0 0     states *    pdch  TCH/HH TCH/H- TCH/HH pdch - -
+meas-rep lchan 0 0 2 1 rxlev 30 rxqual 0 ta 0
+meas-rep lchan 0 0 3 0 rxlev 30 rxqual 0 ta 0
+meas-rep lchan 0 0 4 0 rxlev 30 rxqual 0 ta 0
+meas-rep lchan 0 0 4 1 rxlev 30 rxqual 0 ta 0
+# pick one to move:
+network
+ bts 0
+  handover2 min-free-slots tch/h 6
+congestion-check
+expect-ho from lchan 0 0 3 0 to lchan 0 0 1 0
+expect-ts-use trx 0 0     states *    TCH/F TCH/HH pdch TCH/HH pdch - -
diff --git a/tests/testsuite.at b/tests/testsuite.at
index afff9c5..17153cc 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -51,200 +51,8 @@
 AT_CHECK([$abs_top_builddir/tests/handover/neighbor_ident_test], [], [expout], [experr])
 AT_CLEANUP
 
-AT_SETUP([handover test 0])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 0], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 1])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 1], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 2])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 2], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 3])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 3], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 4])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 4], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 5])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 5], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 6])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 6], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 7])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 7], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 8])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 8], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 9])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 9], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 10])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 10], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 11])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 11], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 12])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 12], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 13])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 13], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 14])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 14], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 15])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 15], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 16])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 16], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 17])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 17], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 18])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 18], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 19])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 19], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 20])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 20], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 21])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 21], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 22])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 22], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 23])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 23], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 24])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 24], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 25])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 25], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 26])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 26], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 27])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 27], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 28])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 28], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 29])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 29], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 30])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 30], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 31])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 31], [], [expout], [ignore])
-AT_CLEANUP
-
-AT_SETUP([handover test 32])
-AT_KEYWORDS([handover])
-cat $abs_srcdir/handover/handover_test.ok > expout
-AT_CHECK([$abs_top_builddir/tests/handover/handover_test 32], [], [expout], [ignore])
+AT_SETUP([handover_tests])
+AT_KEYWORDS([handover_tests])
+cat $abs_srcdir/handover/handover_tests.ok > expout
+AT_CHECK([$abs_srcdir/handover/handover_tests.sh $abs_srcdir/handover $abs_builddir/handover], [], [expout], [ignore])
 AT_CLEANUP

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-bsc/+/21642
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bsc
Gerrit-Branch: master
Gerrit-Change-Id: Ie238ebe41039d3fa44c9699937589e000883e052
Gerrit-Change-Number: 21642
Gerrit-PatchSet: 6
Gerrit-Owner: neels <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: neels <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20201223/ec1041a0/attachment.htm>


More information about the gerrit-log mailing list