tbf/test: Modify test to create a dangling TBF pointer

When new_tbf is freed before dl_tbf in test_tbf_final_ack, dl_tbf
still contains a pointer to it in m_new_tbf.

This patch changes the test to accept a test mode parameter and runs
it twice which a different order of tbf_free in each run. Consistency
checks are added, to check for a danglilng m_new_tbf pointer in both
cases.

Sponsored-by: On-Waves ehf
diff --git a/tests/tbf/TbfTest.cpp b/tests/tbf/TbfTest.cpp
index 0811520..5c41b53 100644
--- a/tests/tbf/TbfTest.cpp
+++ b/tests/tbf/TbfTest.cpp
@@ -85,7 +85,12 @@
 	return 0;
 }
 
-static void test_tbf_final_ack()
+enum test_tbf_final_ack_mode {
+	TEST_MODE_STANDARD,
+	TEST_MODE_REVERSE_FREE
+};
+
+static void test_tbf_final_ack(enum test_tbf_final_ack_mode test_mode)
 {
 	BTS the_bts;
 	gprs_rlcmac_bts *bts;
@@ -147,8 +152,18 @@
 	OSMO_ASSERT(new_tbf != dl_tbf);
 	OSMO_ASSERT(new_tbf->tfi() == 1);
 	dl_tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
-	tbf_free(dl_tbf);
-	tbf_free(new_tbf);
+	if (test_mode == TEST_MODE_REVERSE_FREE) {
+		tbf_free(new_tbf);
+		if (dl_tbf->m_new_tbf == new_tbf)
+			fprintf(stderr, "dangling m_new_tbf pointer in dl_tbf "
+				"(known bug)\n");
+		/* OSMO_ASSERT(dl_tbf->m_new_tbf != new_tbf); */
+		tbf_free(dl_tbf);
+	} else {
+		tbf_free(dl_tbf);
+		OSMO_ASSERT(new_tbf->m_new_tbf != dl_tbf);
+		tbf_free(new_tbf);
+	}
 }
 
 static const struct log_info_cat default_categories[] = {
@@ -188,7 +203,8 @@
 	log_set_print_filename(osmo_stderr_target, 0);
 
 	test_tbf_tlli_update();
-	test_tbf_final_ack();
+	test_tbf_final_ack(TEST_MODE_STANDARD);
+	test_tbf_final_ack(TEST_MODE_REVERSE_FREE);
 	return EXIT_SUCCESS;
 }
 
diff --git a/tests/tbf/TbfTest.err b/tests/tbf/TbfTest.err
index 054e93c..9d9e5f7 100644
--- a/tests/tbf/TbfTest.err
+++ b/tests/tbf/TbfTest.err
@@ -80,3 +80,69 @@
 TBF(TFI=1 TLLI=0x00000000 DIR=DL STATE=ASSIGN) free
 TBF(TFI=1 TLLI=0x00000000 DIR=DL STATE=ASSIGN) stopping timer 0.
 ********** TBF ends here **********
+Searching for first unallocated TFI: TRX=0 first TS=4
+ Found TFI=0.
+********** TBF starts here **********
+Allocating DL TBF: TFI=0 TRX=0 MS_CLASS=45
+Slot Allocation (Algorithm A) for class 45
+- Skipping TS 0, because not enabled
+- Skipping TS 1, because not enabled
+- Skipping TS 2, because not enabled
+- Skipping TS 3, because not enabled
+- Assign downlink TS=4
+- Setting Control TS 4
+TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL) changes state from NULL to FLOW
+TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW) append
+TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW) append
+TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW)  start Packet Downlink Assignment (PACCH)
++++++++++++++++++++++++++ TX : Packet Downlink Assignment +++++++++++++++++++++++++
+------------------------- TX : Packet Downlink Assignment -------------------------
+Scheduling control message at RTS for TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW) (TRX=0, TS=4)
+Sending data request: trx=0 ts=4 sapi=5 arfcn=0 fn=0 block=0 data=4f 08 20 00 44 02 00 02 08 04 00 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 
+Scheduling data message at RTS for DL TFI=0 (TRX=0, TS=4)
+TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW) downlink (V(A)==0 .. V(S)==0)
+- Sending new block at BSN 0
+-- Chunk with length 0 is less than remaining space (20): add length header to to delimit LLC frame
+Complete DL frame for TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW)len=0
+- Dequeue next LLC for TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW) (len=200)
+-- Chunk with length 200 larger than space (19) left in block: copy only remaining space, and we are done
+data block: 07 00 00 03 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 
+Sending data request: trx=0 ts=4 sapi=5 arfcn=0 fn=1 block=0 data=07 00 00 03 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 
+Scheduling data message at RTS for DL TFI=0 (TRX=0, TS=4)
+TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW) downlink (V(A)==0 .. V(S)==1)
+- Sending new block at BSN 1
+-- Chunk with length 181 larger than space (20) left in block: copy only remaining space, and we are done
+data block: 07 00 03 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 
+Sending data request: trx=0 ts=4 sapi=5 arfcn=0 fn=2 block=0 data=07 00 03 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 
+TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW) downlink acknowledge
+- Final ACK received.
+TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=FLOW) changes state from FLOW to WAIT RELEASE
+Searching for first unallocated TFI: TRX=0 first TS=4
+ Found TFI=1.
+********** TBF starts here **********
+Allocating DL TBF: TFI=1 TRX=0 MS_CLASS=45
+Slot Allocation (Algorithm A) for class 45
+- Skipping TS 0, because not enabled
+- Skipping TS 1, because not enabled
+- Skipping TS 2, because not enabled
+- Skipping TS 3, because not enabled
+- Assign downlink TS=4
+- Setting Control TS 4
+********** TBF update **********
+Slot Allocation (Algorithm A) for class 45
+- Skipping TS 0, because not enabled
+- Skipping TS 1, because not enabled
+- Skipping TS 2, because not enabled
+- Skipping TS 3, because not enabled
+- Assign downlink TS=4
+TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=WAIT RELEASE) Trigger dowlink assignment on PACCH, because another LLC PDU has arrived in between
+Send dowlink assignment on PACCH, because TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=WAIT RELEASE) exists
+TBF(TFI=1 TLLI=0x00000000 DIR=DL STATE=NULL) changes state from NULL to ASSIGN
+TBF(TFI=1 TLLI=0x00000000 DIR=DL STATE=ASSIGN) starting timer 0.
+TBF(TFI=1 TLLI=0x00000000 DIR=DL STATE=ASSIGN) free
+TBF(TFI=1 TLLI=0x00000000 DIR=DL STATE=ASSIGN) stopping timer 0.
+********** TBF ends here **********
+dangling m_new_tbf pointer in dl_tbf (known bug)
+DL packet loss of IMSI= / TLLI=0x00000000: 0%
+TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=WAIT RELEASE) free
+********** TBF ends here **********