From 1a59f2d5ba8f954187a102abfd03e5b2a76862df Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 4 Mar 2021 17:55:52 +0100 Subject: [PATCH] rlc_am_test: extend header reconstruction test this patch adds various new test cases generated from rlc_stress_test to test header reconstruction. --- lib/test/upper/rlc_am_test.cc | 1832 +++++++++++++++++++++++++++++---- 1 file changed, 1618 insertions(+), 214 deletions(-) diff --git a/lib/test/upper/rlc_am_test.cc b/lib/test/upper/rlc_am_test.cc index fa3f1d32e..6235b1f9b 100644 --- a/lib/test/upper/rlc_am_test.cc +++ b/lib/test/upper/rlc_am_test.cc @@ -40,17 +40,13 @@ bool rx_is_tx(const rlc_bearer_metrics_t& rlc1_metrics, const rlc_bearer_metrics class rlc_am_tester : public pdcp_interface_rlc, public rrc_interface_rlc { public: - rlc_am_tester(rlc_pcap* pcap_ = NULL) - { - n_sdus = 0; - pcap = pcap_; - } + rlc_am_tester(rlc_pcap* pcap_ = NULL) : pcap(pcap_) {} // PDCP interface void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu) { assert(lcid == 1); - sdus[n_sdus++] = std::move(sdu); + sdus.push_back(std::move(sdu)); } void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {} void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {} @@ -73,12 +69,13 @@ public: } // RRC interface - void max_retx_attempted() {} + void max_retx_attempted() { max_retx_triggered = true; } + std::string get_rb_name(uint32_t lcid) { return std::string(""); } - unique_byte_buffer_t sdus[10]; - int n_sdus; - rlc_pcap* pcap; + std::vector sdus; + rlc_pcap* pcap = nullptr; + bool max_retx_triggered = false; std::map notified_counts; // Map of PDCP SNs to number of notifications }; @@ -197,7 +194,7 @@ int basic_test() // Check PDCP notifications TESTASSERT(tester.notified_counts.size() == 5); - for (uint16_t i = 0; i < tester.n_sdus; i++) { + for (uint16_t i = 0; i < tester.sdus.size(); i++) { TESTASSERT(tester.sdus[i]->N_bytes == 1); TESTASSERT(*(tester.sdus[i]->msg) == i); TESTASSERT(tester.notified_counts[i] == 1); @@ -263,15 +260,15 @@ int concat_test() // Write status PDU to RLC1 rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); - TESTASSERT(tester.n_sdus == 5); - for (int i = 0; i < tester.n_sdus; i++) { + TESTASSERT(tester.sdus.size() == 5); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { TESTASSERT(tester.sdus[i]->N_bytes == 1); TESTASSERT(*(tester.sdus[i]->msg) == i); } // Check PDCP notifications TESTASSERT(tester.notified_counts.size() == 5); - for (uint16_t i = 0; i < tester.n_sdus; i++) { + for (uint32_t i = 0; i < tester.sdus.size(); i++) { TESTASSERT(tester.sdus[i]->N_bytes == 1); TESTASSERT(*(tester.sdus[i]->msg) == i); TESTASSERT(tester.notified_counts[i] == 1); @@ -364,11 +361,12 @@ int segment_test(bool in_seq_rx) TESTASSERT(0 == rlc2.get_buffer_state()); - TESTASSERT(tester.n_sdus == 5); - for (int i = 0; i < tester.n_sdus; i++) { + TESTASSERT(tester.sdus.size() == 5); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { TESTASSERT(tester.sdus[i]->N_bytes == 10); - for (int j = 0; j < 10; j++) + for (int j = 0; j < 10; j++) { TESTASSERT(tester.sdus[i]->msg[j] == j); + } } // Check statistics @@ -417,8 +415,9 @@ int retx_test() // Write PDUs into RLC2 (skip SN 1) for (int i = 0; i < NBUFS; i++) { - if (i != 1) + if (i != 1) { rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } } // check buffered bytes at receiver, 3 PDUs with one 1 B each (SN=0 has been delivered already) @@ -473,8 +472,8 @@ int retx_test() // Write the retx PDU to RLC2 rlc2.write_pdu(retx.msg, retx.N_bytes); - TESTASSERT(tester.n_sdus == 5); - for (int i = 0; i < tester.n_sdus; i++) { + TESTASSERT(tester.sdus.size() == 5); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { if (tester.sdus[i]->N_bytes != 1) return -1; if (*(tester.sdus[i]->msg) != i) @@ -693,8 +692,8 @@ int segment_retx_test() TESTASSERT(tester.notified_counts.size() == 1); TESTASSERT(tester.notified_counts.find(0) != tester.notified_counts.end() && tester.notified_counts[0] == 1); - TESTASSERT(tester.n_sdus == nof_sdus); - for (int i = 0; i < tester.n_sdus; i++) { + TESTASSERT(tester.sdus.size() == nof_sdus); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { if (tester.sdus[i]->N_bytes != 10) { return SRSLTE_ERROR; } @@ -826,8 +825,8 @@ int resegment_test_1() // Write the retx PDU to RLC2 rlc2.write_pdu(retx2.msg, retx2.N_bytes); - TESTASSERT(tester.n_sdus == 5); - for (int i = 0; i < tester.n_sdus; i++) { + TESTASSERT(tester.sdus.size() == 5); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { if (tester.sdus[i]->N_bytes != 10) return -1; for (int j = 0; j < 10; j++) @@ -953,7 +952,7 @@ int resegment_test_2() // Write the retx PDU to RLC2 rlc2.write_pdu(retx1.msg, retx1.N_bytes); - TESTASSERT(18 == rlc1.get_buffer_state()); + TESTASSERT(16 == rlc1.get_buffer_state()); // Read the remaining segment byte_buffer_t retx2; @@ -962,8 +961,8 @@ int resegment_test_2() // Write the retx PDU to RLC2 rlc2.write_pdu(retx2.msg, retx2.N_bytes); - TESTASSERT(tester.n_sdus == 5); - for (int i = 0; i < tester.n_sdus; i++) { + TESTASSERT(tester.sdus.size() == 5); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { if (tester.sdus[i]->N_bytes != 10) return -1; for (int j = 0; j < 10; j++) @@ -1091,8 +1090,8 @@ int resegment_test_3() // Write the retx PDU to RLC2 rlc2.write_pdu(retx2.msg, retx2.N_bytes); - TESTASSERT(tester.n_sdus == 5); - for (int i = 0; i < tester.n_sdus; i++) { + TESTASSERT(tester.sdus.size() == 5); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { if (tester.sdus[i]->N_bytes != 10) return -1; for (int j = 0; j < 10; j++) @@ -1214,17 +1213,17 @@ int resegment_test_4() // Write the retx PDU to RLC2 rlc2.write_pdu(retx1.msg, retx1.N_bytes); - TESTASSERT(23 == rlc1.get_buffer_state()); + TESTASSERT(21 == rlc1.get_buffer_state()); // Read the remaining segment byte_buffer_t retx2; - retx2.N_bytes = rlc1.read_pdu(retx2.msg, 23); // 6 byte header + 18 data + retx2.N_bytes = rlc1.read_pdu(retx2.msg, 21); // Write the retx PDU to RLC2 rlc2.write_pdu(retx2.msg, retx2.N_bytes); - TESTASSERT(tester.n_sdus == 5); - for (int i = 0; i < tester.n_sdus; i++) { + TESTASSERT(tester.sdus.size() == 5); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { if (tester.sdus[i]->N_bytes != 10) return -1; for (int j = 0; j < 10; j++) @@ -1282,8 +1281,9 @@ int resegment_test_5() unique_byte_buffer_t sdu_bufs[NBUFS]; for (int i = 0; i < NBUFS; i++) { sdu_bufs[i] = srslte::make_byte_buffer(); - for (int j = 0; j < 10; j++) - sdu_bufs[i]->msg[j] = j; + for (int j = 0; j < 10; j++) { + sdu_bufs[i]->msg[j] = i; + } sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes sdu_bufs[i]->md.pdcp_sn = i; rlc1.write_sdu(std::move(sdu_bufs[i])); @@ -1339,22 +1339,21 @@ int resegment_test_5() // Write the retx PDU to RLC2 rlc2.write_pdu(retx1.msg, retx1.N_bytes); - TESTASSERT(31 == rlc1.get_buffer_state()); + TESTASSERT(32 == rlc1.get_buffer_state()); // Read the remaining segment byte_buffer_t retx2; - retx2.N_bytes = rlc1.read_pdu(retx2.msg, 34); // 7 byte header + 24 data + retx2.N_bytes = rlc1.read_pdu(retx2.msg, 40); // Write the retx PDU to RLC2 rlc2.write_pdu(retx2.msg, retx2.N_bytes); - TESTASSERT(tester.n_sdus == 5); - for (int i = 0; i < tester.n_sdus; i++) { - if (tester.sdus[i]->N_bytes != 10) - return -1; - for (int j = 0; j < 10; j++) - if (tester.sdus[i]->msg[j] != j) - return -1; + TESTASSERT(tester.sdus.size() == 5); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { + TESTASSERT(tester.sdus[i]->N_bytes == 10); + for (int j = 0; j < 10; j++) { + TESTASSERT(tester.sdus[i]->msg[j] == i); + } } // Get status from RLC 2 @@ -1489,30 +1488,29 @@ int resegment_test_6() // Write the retx PDU to RLC2 rlc2.write_pdu(retx1.msg, retx1.N_bytes); - TESTASSERT(159 == rlc1.get_buffer_state()); + TESTASSERT(169 == rlc1.get_buffer_state()); // Read the remaining segment byte_buffer_t retx2; - len = rlc1.read_pdu(retx2.msg, 162); + len = rlc1.read_pdu(retx2.msg, 169); retx2.N_bytes = len; // Write the retx PDU to RLC2 rlc2.write_pdu(retx2.msg, retx2.N_bytes); - TESTASSERT(tester.n_sdus == 9); + TESTASSERT(tester.sdus.size() == 9); for (int i = 0; i < 3; i++) { TESTASSERT(tester.sdus[i]->N_bytes == 10); for (int j = 0; j < 10; j++) TESTASSERT(tester.sdus[i]->msg[j] == j); } - for (int i = 3; i < 9; i++) { - if (i >= tester.n_sdus) - return -1; - if (tester.sdus[i]->N_bytes != 54) - return -1; + for (uint32_t i = 3; i < 9; i++) { + if (i >= tester.sdus.size()) { + return SRSLTE_ERROR; + } + TESTASSERT(tester.sdus[i]->N_bytes == 54); for (int j = 0; j < 54; j++) { - if (tester.sdus[i]->msg[j] != j) - return -1; + TESTASSERT(tester.sdus[i]->msg[j] == j); } } @@ -1557,7 +1555,7 @@ int resegment_test_7() #if HAVE_PCAP rlc_pcap pcap; - pcap.open("rlc_am_test7.pcap", 0); + pcap.open("rlc_am_test7.pcap", rlc_config_t::default_rlc_am_config()); rlc_am_tester tester(&pcap); #else rlc_am_tester tester(NULL); @@ -1610,7 +1608,7 @@ int resegment_test_7() if (i != 2) { rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); #if HAVE_PCAP - pcap.write_dl_am_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); #endif } } @@ -1635,7 +1633,7 @@ int resegment_test_7() if (i > 1) { rlc2.write_pdu(retx[i].msg, retx[i].N_bytes); #if HAVE_PCAP - pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); + pcap.write_dl_ccch(retx[i].msg, retx[i].N_bytes); #endif } } @@ -1655,7 +1653,7 @@ int resegment_test_7() // Write status PDU to RLC1 rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); #if HAVE_PCAP - pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes); + pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes); #endif TESTASSERT(15 == rlc1.get_buffer_state()); @@ -1672,7 +1670,7 @@ int resegment_test_7() rlc2.write_pdu(retx2[i].msg, retx2[i].N_bytes); #if HAVE_PCAP - pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); + pcap.write_dl_ccch(retx[i].msg, retx[i].N_bytes); #endif } @@ -1698,7 +1696,7 @@ int resegment_test_7() // Write status PDU to RLC1 rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); #if HAVE_PCAP - pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes); + pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes); #endif // check status again @@ -1706,13 +1704,11 @@ int resegment_test_7() TESTASSERT(0 == rlc2.get_buffer_state()); // Check number of SDUs and their content - TESTASSERT(tester.n_sdus == N_SDU_BUFS); - for (int i = 0; i < tester.n_sdus; i++) { - if (tester.sdus[i]->N_bytes != sdu_size) - return -1; + TESTASSERT(tester.sdus.size() == N_SDU_BUFS); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { + TESTASSERT(tester.sdus[i]->N_bytes == sdu_size); for (uint32_t j = 0; j < N_SDU_BUFS; j++) { - if (tester.sdus[i]->msg[j] != i) - return -1; + TESTASSERT(tester.sdus[i]->msg[j] == i); } } @@ -1744,7 +1740,7 @@ int resegment_test_8() #if HAVE_PCAP rlc_pcap pcap; - pcap.open("rlc_am_test8.pcap", 0); + pcap.open("rlc_am_test8.pcap", rlc_config_t::default_rlc_am_config()); rlc_am_tester tester(&pcap); #else rlc_am_tester tester(NULL); @@ -1790,7 +1786,7 @@ int resegment_test_8() if (i < 1 || i > 2) { rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); #if HAVE_PCAP - pcap.write_dl_am_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); #endif } } @@ -1815,7 +1811,7 @@ int resegment_test_8() if (i > 1) { rlc2.write_pdu(retx[i].msg, retx[i].N_bytes); #if HAVE_PCAP - pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); + pcap.write_dl_ccch(retx[i].msg, retx[i].N_bytes); #endif } } @@ -1834,7 +1830,7 @@ int resegment_test_8() // Write status PDU to RLC1 rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); #if HAVE_PCAP - pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes); + pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes); #endif TESTASSERT(15 == rlc1.get_buffer_state()); @@ -1847,7 +1843,7 @@ int resegment_test_8() TESTASSERT(retx2[i].N_bytes != 0); rlc2.write_pdu(retx2[i].msg, retx2[i].N_bytes); #if HAVE_PCAP - pcap.write_dl_am_ccch(retx[i].msg, retx[i].N_bytes); + pcap.write_dl_ccch(retx[i].msg, retx[i].N_bytes); #endif } @@ -1857,7 +1853,7 @@ int resegment_test_8() // Write status PDU to RLC1 rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); #if HAVE_PCAP - pcap.write_ul_am_ccch(status_buf.msg, status_buf.N_bytes); + pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes); #endif // check buffer states @@ -1869,8 +1865,8 @@ int resegment_test_8() }; // Check number of SDUs and their content - TESTASSERT(tester.n_sdus == N_SDU_BUFS); - for (int i = 0; i < tester.n_sdus; i++) { + TESTASSERT(tester.sdus.size() == N_SDU_BUFS); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { if (tester.sdus[i]->N_bytes != sdu_size) return -1; for (uint32_t j = 0; j < N_SDU_BUFS; j++) { @@ -1886,158 +1882,207 @@ int resegment_test_8() return 0; } -bool reset_test() +// Resegmentation with 1 B segments +int resegment_test_9() { - rlc_am_tester tester; + // SDUs: | 10 | 10 | 10 | + // PDUs: | 9 | x | + // Retx PDU segments: |2| 9 | + + const rlc_config_t config = rlc_config_t::default_rlc_am_config(); +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_resegment_test_9.pcap", config); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif srslte::timer_handler timers(8); - int len = 0; rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { - return -1; + if (not rlc1.configure(config)) { + return SRSLTE_ERROR; } - // Push 1 SDU of size 10 into RLC1 - unique_byte_buffer_t sdu_buf = srslte::make_byte_buffer(); - sdu_buf->N_bytes = 100; - std::fill(sdu_buf->msg, sdu_buf->msg + sdu_buf->N_bytes, 0); - sdu_buf->msg[0] = 1; // Write the index into the buffer - rlc1.write_sdu(std::move(sdu_buf)); - - // read 1 PDU from RLC1 and force segmentation - byte_buffer_t pdu_bufs; - len = rlc1.read_pdu(pdu_bufs.msg, 4); - pdu_bufs.N_bytes = len; + if (not rlc2.configure(config)) { + return SRSLTE_ERROR; + } - // reset RLC1 - rlc1.stop(); + // Push 3 SDUs into RLC1 + const uint32_t n_bufs = 3; + unique_byte_buffer_t sdu_bufs[n_bufs]; + for (uint32_t i = 0; i < n_bufs; i++) { + sdu_bufs[i] = srslte::make_byte_buffer(); + for (uint32_t j = 0; j < 10; j++) { + sdu_bufs[i]->msg[j] = i; + } + sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes + sdu_bufs[i]->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu_bufs[i])); + } - // read another PDU segment from RLC1 - len = rlc1.read_pdu(pdu_bufs.msg, 4); - pdu_bufs.N_bytes = len; + // Read 5 PDUs from RLC1 (2 bytes, 3 bytes, 40 bytes, 3 bytes, 2 bytes) + byte_buffer_t pdu_bufs[n_bufs]; + pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 11); // 2 byte header + 9 byte payload + pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 15); // 4 byte header + 11 byte payload + pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 12); // 2 byte header + 10 byte payload - // now empty RLC buffer - len = rlc1.read_pdu(pdu_bufs.msg, 100); - pdu_bufs.N_bytes = len; + TESTASSERT(0 == rlc1.get_buffer_state()); - if (0 != rlc1.get_buffer_state()) { - return -1; + // Write PDUs into RLC2 (skip SN 0) + for (uint32_t i = 0; i < n_bufs; i++) { + if (i != 1) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); +#endif + } } - return 0; -} - -bool resume_test() -{ - rlc_am_tester tester; - srslte::timer_handler timers(8); - int len = 0; + // Step timers until reordering timeout expires + int cnt = 5; + while (cnt--) { + timers.step_all(); + } - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + // Read status PDU from RLC2 + byte_buffer_t status_buf; + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status - if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { - return -1; - } + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); +#if HAVE_PCAP + pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes); +#endif - // Push 1 SDU of size 10 into RLC1 - unique_byte_buffer_t sdu_buf = srslte::make_byte_buffer(); - sdu_buf->N_bytes = 100; - std::fill(sdu_buf->msg, sdu_buf->msg + sdu_buf->N_bytes, 0); - sdu_buf->msg[0] = 1; // Write the index into the buffer - rlc1.write_sdu(std::move(sdu_buf)); + // Read the retx PDU from RLC1 and force resegmentation + byte_buffer_t retx1; + byte_buffer_t retx2; + retx1.N_bytes = rlc1.read_pdu(retx1.msg, 8); // 6 byte header + 2 data - // read 1 PDU from RLC1 and force segmentation - byte_buffer_t pdu_bufs; - len = rlc1.read_pdu(pdu_bufs.msg, 4); - pdu_bufs.N_bytes = len; + // Write the retx PDU to RLC2 + rlc2.write_pdu(retx1.msg, retx1.N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(retx1.msg, retx1.N_bytes); +#endif - // reestablish RLC1 - rlc1.reestablish(); + // Read 2nd with a big enough grant to fit remaining content + retx2.N_bytes = rlc1.read_pdu(retx2.msg, 40); - // resume RLC1 - rlc1.resume(); + // Write the retx PDU to RLC2 + rlc2.write_pdu(retx2.msg, retx2.N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(retx2.msg, retx2.N_bytes); +#endif + // goto exit; - // Buffer should be zero - if (0 != rlc1.get_buffer_state()) { - return -1; + TESTASSERT(tester.sdus.size() == n_bufs); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { + TESTASSERT(tester.sdus[i]->N_bytes == 10); + for (int j = 0; j < 10; j++) { + TESTASSERT(tester.sdus[i]->msg[j] == i); + } } - // Do basic test - byte_buffer_t pdu_bufs_tx[NBUFS]; - basic_test_tx(&rlc1, pdu_bufs_tx); - - return 0; -} + // Get status from RLC 2 + for (int i = 0; i < 5; i++) { + timers.step_all(); + } -bool stop_test() -{ - rlc_am_tester tester; - srslte::timer_handler timers(8); + // Read status PDU from RLC2 + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); - if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { - return -1; + // Check final notifications + TESTASSERT(tester.notified_counts.size() == n_bufs); + for (uint32_t i = 0; i < n_bufs; i++) { + auto it = tester.notified_counts.find(i); + TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1); } - // start thread reading - ul_writer writer(&rlc1); - writer.start(-2); - - // let writer thread block on tx_queue - usleep(1e6); + // exit: - // stop RLC1 - rlc1.stop(); +#if HAVE_PCAP + pcap.close(); +#endif return 0; } -// This test checks if status PDUs are generated even though the grant size may not -// be enough to fit all SNs that would need to be NACKed -bool status_pdu_test() +// Retransmission of segment Resegmentation with 1 B segments +int resegment_test_10() { - rlc_am_tester tester; + /// 21:35:17.369012 [RLC_1] [I] DRB1 Tx PDU SN=520 (20 B) + /// 0000: 9e 08 80 40 0a 34 34 34 34 35 35 35 35 35 35 35 + /// 0010: 35 35 35 36 + /// 21:35:17.369016 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=520, LSF=0, SO=0, N_li=2 (4, 10, )] + + /// 21:35:17.369703 [RLC_1] [I] DRB1 Retx PDU segment SN=520 [so=0] (10 B) (attempt 2/16) + /// 0000: fe 08 00 00 00 40 34 34 34 34 + /// 21:35:17.369712 [RLC_2] [I] DRB1 Rx data PDU segment of SN=520 (4 B), SO=0, N_li=1 + /// 0000: 34 34 34 34 + /// 21:35:17.369718 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=520, LSF=0, SO=0, N_li=1 (4, )] + + // SDUs: | 10 | 10 | 10 | 10 | + // PDUs: | 6 | 25(x) | 9 | + // Retx PDU segments: |4| 50 | + + const rlc_config_t config = rlc_config_t::default_rlc_am_config(); +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_resegment_test_10.pcap", config); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif srslte::timer_handler timers(8); - int len = 0; rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); - if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { - return -1; + if (not rlc1.configure(config)) { + return SRSLTE_ERROR; } - if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) { - return -1; + if (not rlc2.configure(config)) { + return SRSLTE_ERROR; } - // Push 5 SDUs into RLC1 - unique_byte_buffer_t sdu_bufs[NBUFS]; - for (int i = 0; i < NBUFS; i++) { - sdu_bufs[i] = srslte::make_byte_buffer(); - sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte - sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + // Push 3 SDUs into RLC1 + const uint32_t n_sdus = 4; + unique_byte_buffer_t sdu_bufs[n_sdus]; + for (uint32_t i = 0; i < n_sdus; i++) { + sdu_bufs[i] = srslte::make_byte_buffer(); + for (uint32_t j = 0; j < 10; j++) { + sdu_bufs[i]->msg[j] = i; + } + sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes + sdu_bufs[i]->md.pdcp_sn = i; rlc1.write_sdu(std::move(sdu_bufs[i])); } - TESTASSERT(13 == rlc1.get_buffer_state()); - - // Read 5 PDUs from RLC1 (1 byte each) - byte_buffer_t pdu_bufs[NBUFS]; - for (int i = 0; i < NBUFS; i++) { - len = rlc1.read_pdu(pdu_bufs[i].msg, 4); // 2 byte header + 1 byte payload - pdu_bufs[i].N_bytes = len; - } + // Read 5 PDUs from RLC1 (2 bytes, 3 bytes, 40 bytes, 3 bytes, 2 bytes) + const uint32_t n_pdus = 3; + byte_buffer_t pdu_bufs[n_pdus]; + pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 8); // 2 byte header + 6 byte payload + pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 32); // 4 byte header + 25 byte payload + pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 11); // 2 byte header + 9 byte payload TESTASSERT(0 == rlc1.get_buffer_state()); - // Only pass last PDUs to RLC2 - for (int i = 0; i < NBUFS; i++) { - if (i == 4) { + // Write PDUs into RLC2 (skip SN 0) + for (uint32_t i = 0; i < n_pdus; i++) { + if (i != 1) { rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); } +#if HAVE_PCAP + // write to PCAP even if its lost in the TC + pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); +#endif } // Step timers until reordering timeout expires @@ -2046,44 +2091,1200 @@ bool status_pdu_test() timers.step_all(); } - uint32_t buffer_state = rlc2.get_buffer_state(); - TESTASSERT(8 == buffer_state); - // Read status PDU from RLC2 byte_buffer_t status_buf; - len = rlc2.read_pdu(status_buf.msg, 5); // provide only small grant - status_buf.N_bytes = len; - - TESTASSERT(status_buf.N_bytes != 0); - - // check status PDU doesn't contain ACK_SN in NACK list - rlc_status_pdu_t status_pdu = {}; - rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_pdu); - if (rlc_am_is_valid_status_pdu(status_pdu) == false) { - return -1; - } + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status // Write status PDU to RLC1 rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); +#if HAVE_PCAP + pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes); +#endif - TESTASSERT(3 == rlc1.get_buffer_state()); // 2 byte header + 1 byte payload - - // Read the retx PDU from RLC1 - byte_buffer_t retx; - len = rlc1.read_pdu(retx.msg, 10); - retx.N_bytes = len; + // Read the retx PDU from RLC1 and force resegmentation + byte_buffer_t retx1; + byte_buffer_t retx2; + retx1.N_bytes = rlc1.read_pdu(retx1.msg, 13); // 6 byte header + 4 data ( +2 B MAC) // Write the retx PDU to RLC2 - rlc2.write_pdu(retx.msg, retx.N_bytes); + rlc2.write_pdu(retx1.msg, retx1.N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(retx1.msg, retx1.N_bytes); +#endif - // Step timers until reordering timeout expires - cnt = 5; - while (cnt--) { - timers.step_all(); + // Read 2nd with a big enough grant to fit remaining content + retx2.N_bytes = rlc1.read_pdu(retx2.msg, 32); + rlc2.write_pdu(retx2.msg, retx2.N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(retx2.msg, retx2.N_bytes); +#endif + + TESTASSERT(tester.sdus.size() == n_sdus); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { + TESTASSERT(tester.sdus[i]->N_bytes == 10); + for (int j = 0; j < 10; j++) { + TESTASSERT(tester.sdus[i]->msg[j] == i); + } } - // get buffer state and status PDU again - status_buf.clear(); + // Get status from RLC 2 + for (int i = 0; i < 5; i++) { + timers.step_all(); + } + + // Read status PDU from RLC2 + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check final notifications + TESTASSERT(tester.notified_counts.size() == n_sdus); + for (uint32_t i = 0; i < n_sdus; i++) { + auto it = tester.notified_counts.find(i); + TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1); + } + + return SRSLTE_SUCCESS; +} + +// Custom resegmentation test of a orignal PDU with N_li=2 +// Because the provided MAC grant is relativly small, the retx segment +// can only accomodate 2 B of the original PDU. +// The test verifies the correct PDU packing, specifically the LI value +int resegment_test_11() +{ + /// Original PDU: + /// 11:29:16.065008 [RLC_1] [I] DRB1 Tx PDU SN=419 (21 B) + /// 0000: bd a3 80 50 0a aa aa aa aa aa ab ab ab ab ab ab + /// 0010: ab ab ab ab ac + /// 11:29:16.065013 [RLC_1] [D] [Data PDU, RF=0, P=1, FI=1, SN=419, LSF=0, SO=0, N_li=2 (5, 10, )] + + /// Log messages with the restoration bug: + /// 11:29:16.065688 [RLC_1] [D] MAC opportunity - 10 bytes + /// 11:29:16.065695 [RLC_1] [D] DRB1 build_retx_pdu - resegmentation required + /// 11:29:16.065702 [RLC_1] [D] retx.so_start=2, retx.so_end=6 + /// 11:29:16.065703 [RLC_1] [D] new_header head_len=4 + /// 11:29:16.065706 [RLC_1] [D] old_header.li[0], head_len=6, pdu_space=4 + /// 11:29:16.065710 [RLC_1] [D] new_header head_len=6 + /// 11:29:16.065713 [RLC_1] [D] old_header.li[1], head_len=8, pdu_space=2 + /// 11:29:16.065716 [RLC_1] [D] DRB1 vt_a = 419, vt_ms = 931, vt_s = 426, poll_sn = 424 + /// 11:29:16.065718 [RLC_1] [I] DRB1 Retx PDU segment SN=419 [so=2] (8 B) (attempt 2/16) + /// 0000: dd a3 00 02 00 30 aa aa + /// 11:29:16.065723 [RLC_2] [I] DRB1 Rx data PDU segment of SN=419 (2 B), SO=2, N_li=1 + /// 0000: aa aa + /// 11:29:16.065730 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=419, LSF=0, SO=2, N_li=1 (3, )] + /// NOTE: this segment is malformed, it has 2 B data and a larger LI field of 3 B + + // SDUs: | 10 | 10 | 10 | 10 | + // PDUs: | 15 | 16(x) | 9 | + // Retx PDU segments: |4| 50 | + + const rlc_config_t config = rlc_config_t::default_rlc_am_config(); +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_resegment_test_11.pcap", config); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + srslte::timer_handler timers(8); + + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + if (not rlc1.configure(config)) { + return SRSLTE_ERROR; + } + + if (not rlc2.configure(config)) { + return SRSLTE_ERROR; + } + + // Push 4 SDUs into RLC1 + const uint32_t n_sdus = 4; + unique_byte_buffer_t sdu_bufs[n_sdus]; + for (uint32_t i = 0; i < n_sdus; i++) { + sdu_bufs[i] = srslte::make_byte_buffer(); + for (uint32_t j = 0; j < 10; j++) { + sdu_bufs[i]->msg[j] = i; + } + sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes + sdu_bufs[i]->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + // Read 3 PDUs from RLC1 (MAC opportunities are taken from logs) + const uint32_t n_pdus = 3; + byte_buffer_t pdu_bufs[n_pdus]; + pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 19); + pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 21); + pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 12); + + TESTASSERT(0 == rlc1.get_buffer_state()); + + // Write PDUs into RLC2 (skip SN 1) + for (uint32_t i = 0; i < n_pdus; i++) { + if (i != 1) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } +#if HAVE_PCAP + // write to PCAP even if its lost in the TC + pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); +#endif + } + + // Step timers until reordering timeout expires + int cnt = 5; + while (cnt--) { + timers.step_all(); + } + + // Read status PDU from RLC2 + byte_buffer_t status_buf; + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); +#if HAVE_PCAP + pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes); +#endif + + // Read the retx PDU from RLC1 and force resegmentation + byte_buffer_t retx1; + retx1.N_bytes = rlc1.read_pdu(retx1.msg, 8); + rlc2.write_pdu(retx1.msg, retx1.N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(retx1.msg, retx1.N_bytes); +#endif + + // Read 2nd with a small grant to trigger the original segmentation bug + byte_buffer_t retx2; + retx2.N_bytes = rlc1.read_pdu(retx2.msg, 10); + + // Write the retx PDU to RLC2 + rlc2.write_pdu(retx2.msg, retx2.N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(retx2.msg, retx2.N_bytes); +#endif + + // Read 3nd with a big enough grant to fit remaining content + byte_buffer_t retx3; + retx3.N_bytes = rlc1.read_pdu(retx3.msg, 20); + rlc2.write_pdu(retx3.msg, retx3.N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(retx3.msg, retx3.N_bytes); +#endif + + TESTASSERT(tester.sdus.size() == n_sdus); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { + TESTASSERT(tester.sdus[i]->N_bytes == 10); + for (int j = 0; j < 10; j++) { + TESTASSERT(tester.sdus[i]->msg[j] == i); + } + } + + // Get status from RLC 2 + for (int i = 0; i < 5; i++) { + timers.step_all(); + } + + // Read status PDU from RLC2 + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSLTE_SUCCESS; +} + +// Custom resegmentation test of a orignal PDU with N_li=2 +// The test triggered a bug in the packing and was creating a too large +// PDU +int resegment_test_12() +{ + /// Original PDU: + /// 17:19:51.296653 [RLC_1] [I] DRB1 Tx PDU SN=728 (21 B) + /// 0000: be d8 80 10 0a d1 d2 d2 d2 d2 d2 d2 d2 d2 d2 d2 + /// 0010: d3 d3 d3 d3 d3 + /// 17:19:51.296659 [RLC_1] [D] [Data PDU, RF=0, P=1, FI=1, SN=728, LSF=0, SO=0, N_li=2 (1, 10, )] + + /// Log messages with the segmentation bug: + /// 17:19:51.297485 [RLC_1] [D] MAC opportunity - 18 bytes + /// 17:19:51.297487 [RLC_1] [D] tx_window size - 2 PDUs + /// 17:19:51.297489 [RLC_1] [D] DRB1 build_retx_pdu - resegmentation required + /// 17:19:51.297498 [RLC_1] [I] DRB1 pdu_without_poll: 4 + /// 17:19:51.297499 [RLC_1] [I] DRB1 byte_without_poll: 67 + /// 17:19:51.297501 [RLC_1] [D] retx.so_start=0, retx.so_end=12 + /// 17:19:51.297502 [RLC_1] [D] new_header head_len=4 + /// 17:19:51.297504 [RLC_1] [D] old_header.li[0], head_len=4, pdu_space=14 + /// 17:19:51.297505 [RLC_1] [D] new_header head_len=6 + /// 17:19:51.297506 [RLC_1] [D] old_header.li[1], head_len=6, pdu_space=12 + /// 17:19:51.297509 [RLC_1] [D] DRB1 vt_a = 724, vt_ms = 212, vt_s = 736, poll_sn = 733 + /// 17:19:51.297513 [RLC_1] [E] DRB1 Retx PDU segment length error. Available: 18, Used: 19 + /// 17:19:51.297522 [RLC_1] [D] DRB1 Retx PDU segment length error. Header len: 7, Payload len: 12, N_li: 2 + /// 17:19:51.297527 [RLC_1] [I] DRB1 Retx PDU segment SN=728 [so=0] (19 B) (attempt 2/16) + /// 0000: de d8 00 00 80 10 0a d1 d2 d2 d2 d2 d2 d2 d2 d2 + /// 0010: d2 d2 d3 + /// 17:19:51.297531 [RLC_2] [I] DRB1 Rx data PDU segment of SN=728 (12 B), SO=0, N_li=2 + /// 0000: d1 d2 d2 d2 d2 d2 d2 d2 d2 d2 d2 d3 + /// 17:19:51.297538 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=728, LSF=0, SO=0, N_li=2 (1, 10, )] + + // SDUs: | 10 | 10 | 10 | 10 | + // PDUs: | 9 | 16(x) | 9 | + // Retx PDU segments: |4| 50 | + + const rlc_config_t config = rlc_config_t::default_rlc_am_config(); +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_resegment_test_12.pcap", config); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + srslte::timer_handler timers(8); + + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + if (not rlc1.configure(config)) { + return SRSLTE_ERROR; + } + + if (not rlc2.configure(config)) { + return SRSLTE_ERROR; + } + + // Push 4 SDUs into RLC1 + const uint32_t n_sdus = 4; + unique_byte_buffer_t sdu_bufs[n_sdus]; + for (uint32_t i = 0; i < n_sdus; i++) { + sdu_bufs[i] = srslte::make_byte_buffer(); + for (uint32_t j = 0; j < 10; j++) { + sdu_bufs[i]->msg[j] = i; + } + sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes + sdu_bufs[i]->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + // Read 3 PDUs from RLC1 (MAC opportunities are taken from logs) + const uint32_t n_pdus = 3; + byte_buffer_t pdu_bufs[n_pdus]; + pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 11); + pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 21); + pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 19); + + TESTASSERT(0 == rlc1.get_buffer_state()); + + // Write PDUs into RLC2 (skip SN 1) + for (uint32_t i = 0; i < n_pdus; i++) { + if (i != 1) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } +#if HAVE_PCAP + // write to PCAP even if its lost in the TC + pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); +#endif + } + + // Step timers until reordering timeout expires + int cnt = 5; + while (cnt--) { + timers.step_all(); + } + + // Read status PDU from RLC2 + byte_buffer_t status_buf; + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); +#if HAVE_PCAP + pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes); +#endif + + // Read the retx PDU from RLC1 and force resegmentation + byte_buffer_t retx1; + retx1.N_bytes = rlc1.read_pdu(retx1.msg, 18); + rlc2.write_pdu(retx1.msg, retx1.N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(retx1.msg, retx1.N_bytes); +#endif + + // Read 2nd to trigger the original segmentation bug + byte_buffer_t retx2; + retx2.N_bytes = rlc1.read_pdu(retx2.msg, 18); + rlc2.write_pdu(retx2.msg, retx2.N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(retx2.msg, retx2.N_bytes); +#endif + + // Read 3nd with a big enough grant to fit remaining content + byte_buffer_t retx3; + retx3.N_bytes = rlc1.read_pdu(retx3.msg, 20); + rlc2.write_pdu(retx3.msg, retx3.N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(retx3.msg, retx3.N_bytes); +#endif + + TESTASSERT(tester.sdus.size() == n_sdus); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { + TESTASSERT(tester.sdus[i]->N_bytes == 10); + for (int j = 0; j < 10; j++) { + TESTASSERT(tester.sdus[i]->msg[j] == i); + } + } + + // Get status from RLC 2 + for (int i = 0; i < 5; i++) { + timers.step_all(); + } + + // Read status PDU from RLC2 + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSLTE_SUCCESS; +} + +// Series of header reconstruction tests that all used canned TV generated with the rlc_stress_test +// In this particular case, check correct reconstruction of headers after 2 segment retx +int header_reconstruction_test(srslte::log_sink_message_spy& spy) +{ + /// Original SN=277 with 3 segments, including full SDU with 24 + /// 13:35:16.337011 [RLC_1] [I] DRB1 Tx PDU SN=277 (20 B) + /// 0000: 9d 15 80 20 0a 23 23 24 24 24 24 24 24 24 24 24 + /// 0010: 24 25 25 25 + /// 13:35:16.337016 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=277, LSF=0, SO=0, N_li=2 (2, 10, )] + + // 2nd retransmission with SO=9 + std::array tv2 = {0xdd, 0x15, 0x80, 0x09, 0x00, 0x30, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25}; + + // 3rd retransmission with S0=0 + std::array tv3 = { + 0xdd, 0x15, 0x00, 0x00, 0x00, 0x20, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24}; + + byte_buffer_t pdu_tv2; + memcpy(pdu_tv2.msg, tv2.data(), tv2.size()); + pdu_tv2.N_bytes = tv2.size(); + + byte_buffer_t pdu_tv3; + memcpy(pdu_tv3.msg, tv3.data(), tv3.size()); + pdu_tv3.N_bytes = tv3.size(); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_header_reconstruction_test.pcap", rlc_config_t::default_rlc_am_config()); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + srslte::timer_handler timers(8); + + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + +#if HAVE_PCAP + pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes); + pcap.write_dl_ccch(pdu_tv3.msg, pdu_tv3.N_bytes); +#endif + + rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes); + rlc1.write_pdu(pdu_tv3.msg, pdu_tv3.N_bytes); + + // Check RLC re-assembled message header + TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=277, LSF=0, SO=0, N_li=2 (2, 10, )]")); + +#if HAVE_PCAP + pcap.close(); +#endif + + return 0; +} + +// Check correct reconstruction of headers after 3 segment retx +int header_reconstruction_test2(srslte::log_sink_message_spy& spy) +{ + /// Original SN=199 with 3 segments, including full SDU with d4 + /// 15:19:19.148272 [RLC_1] [I] DRB1 Tx PDU SN=199 (19 B) + /// 0000: 9c c7 80 30 0a d3 d3 d3 d4 d4 d4 d4 d4 d4 d4 d4 + /// 0010: d4 d4 d5 + /// 15:19:19.148278 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=199, LSF=0, SO=0, N_li=2 (3, 10, )] + + // 2nd retransmission with SO=0 + std::array tv1 = {0xd8, 0xc7, 0x00, 0x00, 0xd3, 0xd3}; + + // 3rd retransmission with S0=2 + std::array tv2 = { + 0xdc, 0xc7, 0x00, 0x02, 0x00, 0x10, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4}; + + std::array tv3 = {0xdc, 0xc7, 0x80, 0x0c, 0x00, 0x10, 0xd4, 0xd5}; + + byte_buffer_t pdu_tv1; + memcpy(pdu_tv1.msg, tv1.data(), tv1.size()); + pdu_tv1.N_bytes = tv1.size(); + + byte_buffer_t pdu_tv2; + memcpy(pdu_tv2.msg, tv2.data(), tv2.size()); + pdu_tv2.N_bytes = tv2.size(); + + byte_buffer_t pdu_tv3; + memcpy(pdu_tv3.msg, tv3.data(), tv3.size()); + pdu_tv3.N_bytes = tv3.size(); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_header_reconstruction_test2.pcap", rlc_config_t::default_rlc_am_config()); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + srslte::timer_handler timers(8); + + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + +#if HAVE_PCAP + pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes); + pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes); + pcap.write_dl_ccch(pdu_tv3.msg, pdu_tv3.N_bytes); +#endif + + rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes); + rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes); + rlc1.write_pdu(pdu_tv3.msg, pdu_tv3.N_bytes); + + // Check RLC re-assembled message header + TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=199, LSF=0, SO=0, N_li=2 (3, 10, )]")); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSLTE_SUCCESS; +} + +// TC with 3 segment retx +int header_reconstruction_test3(srslte::log_sink_message_spy& spy) +{ + // Original PDU + // 11:13:25.994566 [RLC_1] [I] DRB1 Tx PDU SN=206 (18 B) + // 0000: 8c ce 00 a0 db db db db db db db db db db dc dc + // 0010: dc dc + // 11:13:25.994571 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=206, LSF=0, SO=0, N_li=1 (10, )] + + // 11:13:25.995744 [RLC_1] [I] DRB1 Retx PDU segment SN=206 [so=8] (12 B) (attempt 2/16) + // 0000: dc ce 80 08 00 20 db db dc dc dc dc + // 11:13:25.995752 [RLC_2] [I] DRB1 Rx data PDU segment of SN=206 (6 B), SO=8, N_li=1 + // 0000: db db dc dc dc dc + std::array tv0 = {0xdc, 0xce, 0x80, 0x08, 0x00, 0x20, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc}; + + // 11:13:25.996267 [RLC_1] [I] DRB1 Retx PDU segment SN=206 [so=0] (14 B) (attempt 3/16) + // 0000: c0 ce 00 00 db db db db db db db db db db + // 11:13:25.996272 [RLC_2] [I] DRB1 Rx data PDU segment of SN=206 (10 B), SO=0, N_li=0 + // 0000: db db db db db db db db db db + std::array tv1 = {0xc0, 0xce, 0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb}; + + byte_buffer_t pdu_tv0; + memcpy(pdu_tv0.msg, tv0.data(), tv0.size()); + pdu_tv0.N_bytes = tv0.size(); + + byte_buffer_t pdu_tv1; + memcpy(pdu_tv1.msg, tv1.data(), tv1.size()); + pdu_tv1.N_bytes = tv1.size(); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_header_reconstruction_test3.pcap", rlc_config_t::default_rlc_am_config()); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + srslte::timer_handler timers(8); + + // configure RLC + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + +#if HAVE_PCAP + pcap.write_dl_ccch(pdu_tv0.msg, pdu_tv0.N_bytes); + pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes); +#endif + + rlc1.write_pdu(pdu_tv0.msg, pdu_tv0.N_bytes); + rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes); + + // Check RLC re-assembled message header + TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=206, LSF=0, SO=0, N_li=1 (10, )]")); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSLTE_SUCCESS; +} + +int header_reconstruction_test4(srslte::log_sink_message_spy& spy) +{ + // Original PDU + // 15:32:20.667043 [RLC_1] [I] DRB1 Tx PDU SN=172 (22 B) + // 0000: 9c ac 80 10 0a af b0 b0 b0 b0 b0 b0 b0 b0 b0 b0 + // 0010: b1 b1 b1 b1 b1 b1 + // 15:32:20.667048 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=172, LSF=0, SO=0, N_li=2 (1, 10, )] + + // 15:32:20.668094 [RLC_1] [I] DRB1 Retx PDU segment SN=172 [so=0] (14 B) (attempt 2/16) + // 0000: dc ac 00 00 00 10 af b0 b0 b0 b0 b0 b0 b0 + // 15:32:20.668100 [RLC_2] [I] DRB1 Rx data PDU segment of SN=172 (8 B), SO=0, N_li=1 + // 0000: af b0 b0 b0 b0 b0 b0 b0 + // 15:32:20.668105 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=172, LSF=0, SO=0, N_li=1 (1, )] + std::array tv1 = {0xdc, 0xac, 0x00, 0x00, 0x00, 0x10, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0}; + + // 15:32:20.668497 [RLC_1] [I] DRB1 Retx PDU segment SN=172 [so=0] (12 B) (attempt 3/16) + // 0000: fc ac 00 00 00 10 af b0 b0 b0 b0 b0 + // 15:32:20.668502 [RLC_2] [I] DRB1 Rx data PDU segment of SN=172 (6 B), SO=0, N_li=1 + // 0000: af b0 b0 b0 b0 b0 + // 15:32:20.668507 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=172, LSF=0, SO=0, N_li=1 (1, )] + std::array tv2 = {0xfc, 0xac, 0x00, 0x00, 0x00, 0x10, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0}; + + // 15:32:20.668575 [RLC_1] [I] DRB1 Retx PDU segment SN=172 [so=6] (7 B) (attempt 3/16) + // 0000: d8 ac 00 06 b0 b0 b0 + // 15:32:20.668581 [RLC_1] [I] DRB1 Tx SDU (10 B, tx_sdu_queue_len=33) + // 0000: d8 d8 d8 d8 d8 d8 d8 d8 d8 d8 + // 15:32:20.668582 [RLC_2] [I] DRB1 Rx data PDU segment of SN=172 (3 B), SO=6, N_li=0 + // 0000: b0 b0 b0 + std::array tv3 = {0xd8, 0xac, 0x00, 0x06, 0xb0, 0xb0, 0xb0}; + + // 15:32:20.668665 [RLC_1] [I] DRB1 Retx PDU segment SN=172 [so=9] (14 B) (attempt 3/16) + // 0000: dc ac 80 09 00 20 b0 b0 b1 b1 b1 b1 b1 b1 + // 15:32:20.668671 [RLC_2] [I] DRB1 Rx data PDU segment of SN=172 (8 B), SO=9, N_li=1 + // 0000: b0 b0 b1 b1 b1 b1 b1 b1 + // 15:32:20.668675 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=172, LSF=1, SO=9, N_li=1 (2, )] + std::array tv4 = {0xdc, 0xac, 0x80, 0x09, 0x00, 0x20, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1}; + + byte_buffer_t pdu_tv1; + memcpy(pdu_tv1.msg, tv1.data(), tv1.size()); + pdu_tv1.N_bytes = tv1.size(); + + byte_buffer_t pdu_tv2; + memcpy(pdu_tv2.msg, tv2.data(), tv2.size()); + pdu_tv2.N_bytes = tv2.size(); + + byte_buffer_t pdu_tv3; + memcpy(pdu_tv3.msg, tv3.data(), tv3.size()); + pdu_tv3.N_bytes = tv3.size(); + + byte_buffer_t pdu_tv4; + memcpy(pdu_tv4.msg, tv4.data(), tv4.size()); + pdu_tv4.N_bytes = tv4.size(); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_header_reconstruction_test4.pcap", rlc_config_t::default_rlc_am_config()); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + srslte::timer_handler timers(8); + + // configure RLC + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + +#if HAVE_PCAP + pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes); + pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes); + pcap.write_dl_ccch(pdu_tv3.msg, pdu_tv3.N_bytes); + pcap.write_dl_ccch(pdu_tv4.msg, pdu_tv4.N_bytes); +#endif + + rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes); + rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes); + rlc1.write_pdu(pdu_tv3.msg, pdu_tv3.N_bytes); + rlc1.write_pdu(pdu_tv4.msg, pdu_tv4.N_bytes); + + // Check RLC re-assembled message header + TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=172, LSF=0, SO=0, N_li=2 (1, 10, )]")); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSLTE_SUCCESS; +} + +int header_reconstruction_test5(srslte::log_sink_message_spy& spy) +{ + // Original PDU: + // 18:46:22.372858 [RLC_1] [I] DRB1 Tx PDU SN=222 (22 B) + // 0000: bc de 80 30 0a ee ee ee ef ef ef ef ef ef ef ef + // 0010: ef ef f0 f0 f0 f0 + // 18:46:22.372863 [RLC_1] [D] [Data PDU, RF=0, P=1, FI=1, SN=222, LSF=0, SO=0, N_li=2 (3, 10, )] + + // 18:46:22.373623 [RLC_1] [I] DRB1 Retx PDU segment SN=222 [so=0] (7 B) (attempt 2/16) + // 0000: d0 de 00 00 ee ee ee + // 18:46:22.373629 [RLC_2] [I] DRB1 Rx data PDU segment of SN=222 (3 B), SO=0, N_li=0 + // 0000: ee ee ee + std::array tv0 = {0xd0, 0xde, 0x00, 0x00, 0xee, 0xee, 0xee}; + + // 18:46:22.373707 [RLC_1] [I] DRB1 Retx PDU segment SN=222 [so=3] (19 B) (attempt 2/16) + // 0000: cc de 00 03 00 a0 ef ef ef ef ef ef ef ef ef ef + // 0010: f0 f0 f0 + // 18:46:22.373714 [RLC_2] [I] DRB1 Rx data PDU segment of SN=222 (13 B), SO=3, N_li=1 + // 0000: ef ef ef ef ef ef ef ef ef ef f0 f0 f0 + std::array tv1 = { + 0xcc, 0xde, 0x00, 0x03, 0x00, 0xa0, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0}; + + // 18:46:22.373793 [RLC_1] [I] DRB1 Retx PDU segment SN=222 [so=16] (5 B) (attempt 2/16) + // 0000: d8 de 80 10 f0 + // 18:46:22.373798 [RLC_2] [I] DRB1 Rx data PDU segment of SN=222 (1 B), SO=16, N_li=0 + // 0000: f0 + std::array tv2 = {0xd8, 0xde, 0x80, 0x10, 0xf0}; + + byte_buffer_t pdu_tv0; + memcpy(pdu_tv0.msg, tv0.data(), tv0.size()); + pdu_tv0.N_bytes = tv0.size(); + + byte_buffer_t pdu_tv1; + memcpy(pdu_tv1.msg, tv1.data(), tv1.size()); + pdu_tv1.N_bytes = tv1.size(); + + byte_buffer_t pdu_tv2; + memcpy(pdu_tv2.msg, tv2.data(), tv2.size()); + pdu_tv2.N_bytes = tv2.size(); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_header_reconstruction_test5.pcap", rlc_config_t::default_rlc_am_config()); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + srslte::timer_handler timers(8); + + // configure RLC + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + +#if HAVE_PCAP + pcap.write_dl_ccch(pdu_tv0.msg, pdu_tv0.N_bytes); + pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes); + pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes); +#endif + + // don't write original PDU + rlc1.write_pdu(pdu_tv0.msg, pdu_tv0.N_bytes); + rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes); + rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes); + + // Check RLC re-assembled message header + TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=222, LSF=0, SO=0, N_li=2 (3, 10, )]")); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSLTE_SUCCESS; +} + +int header_reconstruction_test6(srslte::log_sink_message_spy& spy) +{ + // Original PDU: + // 21:50:12.709646 [RLC_1] [I] DRB1 Tx PDU SN=509 (20 B) + // 0000: 9d fd 80 40 0a b1 b1 b1 b1 b2 b2 b2 b2 b2 b2 b2 + // 0010: b2 b2 b2 b3 + // 21:50:12.709653 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=509, LSF=0, SO=0, N_li=2 (4, 10, )]] + + // 21:50:12.711022 [RLC_1] [I] DRB1 Retx PDU segment SN=509 [so=0] (5 B) (attempt 3/16) + // 0000: d9 fd 00 00 b1 + // 21:50:12.711029 [RLC_2] [I] DRB1 Rx data PDU segment of SN=509 (1 B), SO=0, N_li=0 + // 0000: b1 + // 21:50:12.711034 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=509, LSF=0, SO=0, N_li=0] + std::array tv0 = {0xd9, 0xfd, 0x00, 0x00, 0xb1}; + + // 21:50:12.711104 [RLC_1] [I] DRB1 Retx PDU segment SN=509 [so=1] (7 B) (attempt 3/16) + // 0000: d1 fd 00 01 b1 b1 b1 + // 21:50:12.711110 [RLC_2] [I] DRB1 Rx data PDU segment of SN=509 (3 B), SO=1, N_li=0 + // 0000: b1 b1 b1 + // 21:50:12.711115 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=509, LSF=0, SO=1, N_li=0] + std::array tv1 = {0xd1, 0xfd, 0x00, 0x01, 0xb1, 0xb1, 0xb1}; + + // 21:50:12.711201 [RLC_1] [I] DRB1 Retx PDU segment SN=509 [so=4] (17 B) (attempt 3/16) + // 0000: ed fd 80 04 00 a0 b2 b2 b2 b2 b2 b2 b2 b2 b2 b2 + // 0010: b3 + // 21:50:12.711210 [RLC_2] [I] DRB1 Rx data PDU segment of SN=509 (11 B), SO=4, N_li=1 + // 0000: b2 b2 b2 b2 b2 b2 b2 b2 b2 b2 b3 + // 21:50:12.711216 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=509, LSF=1, SO=4, N_li=1 (10, )] + std::array tv2 = { + 0xed, 0xfd, 0x80, 0x04, 0x00, 0xa0, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3}; + + byte_buffer_t pdu_tv0; + memcpy(pdu_tv0.msg, tv0.data(), tv0.size()); + pdu_tv0.N_bytes = tv0.size(); + + byte_buffer_t pdu_tv1; + memcpy(pdu_tv1.msg, tv1.data(), tv1.size()); + pdu_tv1.N_bytes = tv1.size(); + + byte_buffer_t pdu_tv2; + memcpy(pdu_tv2.msg, tv2.data(), tv2.size()); + pdu_tv2.N_bytes = tv2.size(); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_header_reconstruction_test6.pcap", rlc_config_t::default_rlc_am_config()); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + srslte::timer_handler timers(8); + + // configure RLC + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + +#if HAVE_PCAP + pcap.write_dl_ccch(pdu_tv0.msg, pdu_tv0.N_bytes); + pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes); + pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes); +#endif + + // don't write original PDU + rlc1.write_pdu(pdu_tv0.msg, pdu_tv0.N_bytes); + rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes); + rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes); + + // Check RLC re-assembled message header + TESTASSERT(spy.has_message("[Data PDU, RF=0, P=1, FI=1, SN=509, LSF=0, SO=0, N_li=2 (4, 10, )]")); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSLTE_SUCCESS; +} + +int header_reconstruction_test7(srslte::log_sink_message_spy& spy) +{ + // Original PDU: + // 22:14:54.646530 [RLC_1] [I] DRB1 Tx PDU SN=282 (19 B) + // 0000: 9d 1a 80 10 0a 28 29 29 29 29 29 29 29 29 29 29 + // 0010: 2a 2a 2a + // 22:14:54.646535 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=282, LSF=0, SO=0, N_li=2 (1, 10, )] + + // 22:14:54.648484 [RLC_1] [I] DRB1 Retx PDU segment SN=282 [so=2] (6 B) (attempt 2/16) + // 0000: f9 1a 00 02 29 29 + // 22:14:54.648490 [RLC_2] [I] DRB1 Rx data PDU segment of SN=282 (2 B), SO=2, N_li=0 + // 0000: 29 29 + // 22:14:54.648495 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=282, LSF=0, SO=2, N_li=0] + std::array tv0 = {0xf9, 0x1a, 0x00, 0x02, 0x29, 0x29}; + + // 22:14:54.648576 [RLC_1] [I] DRB1 Retx PDU segment SN=282 [so=4] (11 B) (attempt 2/16) + // 0000: d1 1a 00 04 29 29 29 29 29 29 29 + // 22:14:54.648583 [RLC_2] [I] DRB1 Rx data PDU segment of SN=282 (7 B), SO=4, N_li=0 + // 0000: 29 29 29 29 29 29 29 + // 22:14:54.648588 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=282, LSF=0, SO=4, N_li=0] + std::array tv1 = {0xd1, 0x1a, 0x00, 0x04, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29}; + + // 22:14:54.648701 [RLC_1] [I] DRB1 Retx PDU segment SN=282 [so=11] (7 B) (attempt 2/16) + // 0000: d9 1a 80 0b 2a 2a 2a + // 22:14:54.648707 [RLC_2] [I] DRB1 Rx data PDU segment of SN=282 (3 B), SO=11, N_li=0 + // 0000: 2a 2a 2a + // 22:14:54.648713 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=282, LSF=1, SO=11, N_li=0] + std::array tv2 = {0xd9, 0x1a, 0x80, 0x0b, 0x2a, 0x2a, 0x2a}; + + // 22:14:54.648860 [RLC_1] [I] DRB1 Retx PDU segment SN=282 [so=0] (5 B) (attempt 3/16) + // 0000: d1 1a 00 00 28 + // 22:14:54.648866 [RLC_2] [I] DRB1 Rx data PDU segment of SN=282 (1 B), SO=0, N_li=0 + // 0000: 28 + // 22:14:54.648871 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=282, LSF=0, SO=0, N_li=0] + std::array tv3 = {0xd1, 0x1a, 0x00, 0x00, 0x28}; + + // 22:14:54.648948 [RLC_1] [I] DRB1 Retx PDU segment SN=282 [so=1] (8 B) (attempt 3/16) + // 0000: c9 1a 00 01 29 29 29 29 + // 22:14:54.648957 [RLC_2] [I] DRB1 Rx data PDU segment of SN=282 (4 B), SO=1, N_li=0 + // 0000: 29 29 29 29 + // 22:14:54.648962 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=282, LSF=0, SO=1, N_li=0] + std::array tv4 = {0xc9, 0x1a, 0x00, 0x01, 0x29, 0x29, 0x29, 0x29}; + + byte_buffer_t pdu_tv0; + memcpy(pdu_tv0.msg, tv0.data(), tv0.size()); + pdu_tv0.N_bytes = tv0.size(); + + byte_buffer_t pdu_tv1; + memcpy(pdu_tv1.msg, tv1.data(), tv1.size()); + pdu_tv1.N_bytes = tv1.size(); + + byte_buffer_t pdu_tv2; + memcpy(pdu_tv2.msg, tv2.data(), tv2.size()); + pdu_tv2.N_bytes = tv2.size(); + + byte_buffer_t pdu_tv3; + memcpy(pdu_tv3.msg, tv3.data(), tv3.size()); + pdu_tv3.N_bytes = tv3.size(); + + byte_buffer_t pdu_tv4; + memcpy(pdu_tv4.msg, tv4.data(), tv4.size()); + pdu_tv4.N_bytes = tv4.size(); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_header_reconstruction_test7.pcap", rlc_config_t::default_rlc_am_config()); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + srslte::timer_handler timers(8); + + // configure RLC + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + +#if HAVE_PCAP + pcap.write_dl_ccch(pdu_tv0.msg, pdu_tv0.N_bytes); + pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes); + pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes); + pcap.write_dl_ccch(pdu_tv3.msg, pdu_tv3.N_bytes); + pcap.write_dl_ccch(pdu_tv4.msg, pdu_tv4.N_bytes); +#endif + + // don't write original PDU + rlc1.write_pdu(pdu_tv0.msg, pdu_tv0.N_bytes); + rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes); + rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes); + rlc1.write_pdu(pdu_tv3.msg, pdu_tv3.N_bytes); + rlc1.write_pdu(pdu_tv4.msg, pdu_tv4.N_bytes); + + // Check RLC re-assembled message header + TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=282, LSF=0, SO=0, N_li=2 (1, 10, )]")); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSLTE_SUCCESS; +} + +int header_reconstruction_test8(srslte::log_sink_message_spy& spy) +{ + // Original PDU: + // 21:23:34.407718 [RLC_1] [I] DRB1 Tx PDU SN=423 (40 B) + // 0000: b5 a7 80 38 0a 00 a0 77 77 77 78 78 78 78 78 78 + // 0010: 78 78 78 78 79 79 79 79 79 79 79 79 79 79 7a 7a + // 0020: 7a 7a 7a 7a 7a 7a 7a 7a + // 21:23:34.407724 [RLC_1] [D] [Data PDU, RF=0, P=1, FI=1, SN=423, LSF=0, SO=0, N_li=3 (3, 10, 10, )] + + // 21:23:34.408815 [RLC_1] [I] DRB1 Retx PDU segment SN=423 [so=0] (18 B) (attempt 2/8) + // 0000: fd a7 00 00 00 30 77 77 77 78 78 78 78 78 78 78 + // 0010: 78 78 + // 21:23:34.408822 [RLC_2] [I] DRB1 Rx data PDU segment of SN=423 (12 B), SO=0, N_li=1 + // 0000: 77 77 77 78 78 78 78 78 78 78 78 78 + // 21:23:34.408828 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=423, LSF=0, SO=0, N_li=1 (3, )] + std::array tv0 = { + 0xfd, 0xa7, 0x00, 0x00, 0x00, 0x30, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78}; + + // 21:23:34.408913 [RLC_1] [I] DRB1 Retx PDU segment SN=423 [so=12] (17 B) (attempt 2/8) + // 0000: f5 a7 00 0c 00 10 78 79 79 79 79 79 79 79 79 79 + // 0010: 79 + // 21:23:34.408919 [RLC_2] [I] DRB1 Rx data PDU segment of SN=423 (11 B), SO=12, N_li=1 + // 0000: 78 79 79 79 79 79 79 79 79 79 79 + // 21:23:34.408925 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=423, LSF=0, SO=12, N_li=1 (1, )] + std::array tv1 = { + 0xf5, 0xa7, 0x00, 0x0c, 0x00, 0x10, 0x78, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79}; + + // 21:23:34.409421 [RLC_1] [I] DRB1 Retx PDU segment SN=423 [so=0] (19 B) (attempt 3/8) + // 0000: f5 a7 00 00 00 30 77 77 77 78 78 78 78 78 78 78 + // 0010: 78 78 78 + // 21:23:34.409433 [RLC_2] [I] DRB1 Rx data PDU segment of SN=423 (13 B), SO=0, N_li=1 + // 0000: 77 77 77 78 78 78 78 78 78 78 78 78 78 + // 21:23:34.409440 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=423, LSF=0, SO=0, N_li=1 (3, )] + std::array tv2 = { + 0xf5, 0xa7, 0x00, 0x00, 0x00, 0x30, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78}; + + // 21:23:34.409524 [RLC_1] [I] DRB1 Retx PDU segment SN=423 [so=13] (26 B) (attempt 3/8) + // 0000: e5 a7 80 0d 00 a0 79 79 79 79 79 79 79 79 79 79 + // 0010: 7a 7a 7a 7a 7a 7a 7a 7a 7a 7a + // 21:23:34.409531 [RLC_2] [I] DRB1 Rx data PDU segment of SN=423 (20 B), SO=13, N_li=1 + // 0000: 79 79 79 79 79 79 79 79 79 79 7a 7a 7a 7a 7a 7a + // 0010: 7a 7a 7a 7a + // 21:23:34.409537 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=0, SN=423, LSF=1, SO=13, N_li=1 (10, )] + std::array tv3 = {0xe5, 0xa7, 0x80, 0x0d, 0x00, 0xa0, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, + 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a}; + + byte_buffer_t pdu_tv0; + memcpy(pdu_tv0.msg, tv0.data(), tv0.size()); + pdu_tv0.N_bytes = tv0.size(); + + byte_buffer_t pdu_tv1; + memcpy(pdu_tv1.msg, tv1.data(), tv1.size()); + pdu_tv1.N_bytes = tv1.size(); + + byte_buffer_t pdu_tv2; + memcpy(pdu_tv2.msg, tv2.data(), tv2.size()); + pdu_tv2.N_bytes = tv2.size(); + + byte_buffer_t pdu_tv3; + memcpy(pdu_tv3.msg, tv3.data(), tv3.size()); + pdu_tv3.N_bytes = tv3.size(); + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_header_reconstruction_test8.pcap", rlc_config_t::default_rlc_am_config()); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + srslte::timer_handler timers(8); + + // configure RLC + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + +#if HAVE_PCAP + pcap.write_dl_ccch(pdu_tv0.msg, pdu_tv0.N_bytes); + pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes); + pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes); + pcap.write_dl_ccch(pdu_tv3.msg, pdu_tv3.N_bytes); + pcap.close(); +#endif + + // don't write original PDU + rlc1.write_pdu(pdu_tv0.msg, pdu_tv0.N_bytes); + rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes); + rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes); + rlc1.write_pdu(pdu_tv3.msg, pdu_tv3.N_bytes); + + // Check RLC re-assembled message header + TESTASSERT(spy.has_message("[Data PDU, RF=0, P=1, FI=1, SN=423, LSF=0, SO=0, N_li=3 (3, 10, 10, )]")); + + return SRSLTE_SUCCESS; +} + +bool reset_test() +{ + rlc_am_tester tester; + srslte::timer_handler timers(8); + int len = 0; + + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + + // Push 1 SDU of size 10 into RLC1 + unique_byte_buffer_t sdu_buf = srslte::make_byte_buffer(); + sdu_buf->N_bytes = 100; + std::fill(sdu_buf->msg, sdu_buf->msg + sdu_buf->N_bytes, 0); + sdu_buf->msg[0] = 1; // Write the index into the buffer + rlc1.write_sdu(std::move(sdu_buf)); + + // read 1 PDU from RLC1 and force segmentation + byte_buffer_t pdu_bufs; + len = rlc1.read_pdu(pdu_bufs.msg, 4); + pdu_bufs.N_bytes = len; + + // reset RLC1 + rlc1.stop(); + + // read another PDU segment from RLC1 + len = rlc1.read_pdu(pdu_bufs.msg, 4); + pdu_bufs.N_bytes = len; + + // now empty RLC buffer + len = rlc1.read_pdu(pdu_bufs.msg, 100); + pdu_bufs.N_bytes = len; + + if (0 != rlc1.get_buffer_state()) { + return -1; + } + + return 0; +} + +bool resume_test() +{ + rlc_am_tester tester; + srslte::timer_handler timers(8); + int len = 0; + + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + + // Push 1 SDU of size 10 into RLC1 + unique_byte_buffer_t sdu_buf = srslte::make_byte_buffer(); + sdu_buf->N_bytes = 100; + std::fill(sdu_buf->msg, sdu_buf->msg + sdu_buf->N_bytes, 0); + sdu_buf->msg[0] = 1; // Write the index into the buffer + rlc1.write_sdu(std::move(sdu_buf)); + + // read 1 PDU from RLC1 and force segmentation + byte_buffer_t pdu_bufs; + len = rlc1.read_pdu(pdu_bufs.msg, 4); + pdu_bufs.N_bytes = len; + + // reestablish RLC1 + rlc1.reestablish(); + + // resume RLC1 + rlc1.resume(); + + // Buffer should be zero + if (0 != rlc1.get_buffer_state()) { + return -1; + } + + // Do basic test + byte_buffer_t pdu_bufs_tx[NBUFS]; + basic_test_tx(&rlc1, pdu_bufs_tx); + + return 0; +} + +bool stop_test() +{ + rlc_am_tester tester; + srslte::timer_handler timers(8); + + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + + // start thread reading + ul_writer writer(&rlc1); + writer.start(-2); + + // let writer thread block on tx_queue + usleep(1e6); + + // stop RLC1 + rlc1.stop(); + + return 0; +} + +// This test checks if status PDUs are generated even though the grant size may not +// be enough to fit all SNs that would need to be NACKed +bool status_pdu_test() +{ + rlc_am_tester tester; + srslte::timer_handler timers(8); + int len = 0; + + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + + if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + + // Push 5 SDUs into RLC1 + unique_byte_buffer_t sdu_bufs[NBUFS]; + for (int i = 0; i < NBUFS; i++) { + sdu_bufs[i] = srslte::make_byte_buffer(); + sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + TESTASSERT(13 == rlc1.get_buffer_state()); + + // Read 5 PDUs from RLC1 (1 byte each) + byte_buffer_t pdu_bufs[NBUFS]; + for (int i = 0; i < NBUFS; i++) { + len = rlc1.read_pdu(pdu_bufs[i].msg, 4); // 2 byte header + 1 byte payload + pdu_bufs[i].N_bytes = len; + } + + TESTASSERT(0 == rlc1.get_buffer_state()); + + // Only pass last PDUs to RLC2 + for (int i = 0; i < NBUFS; i++) { + if (i == 4) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } + } + + // Step timers until reordering timeout expires + int cnt = 5; + while (cnt--) { + timers.step_all(); + } + + uint32_t buffer_state = rlc2.get_buffer_state(); + TESTASSERT(8 == buffer_state); + + // Read status PDU from RLC2 + byte_buffer_t status_buf; + len = rlc2.read_pdu(status_buf.msg, 5); // provide only small grant + status_buf.N_bytes = len; + + TESTASSERT(status_buf.N_bytes != 0); + + // check status PDU doesn't contain ACK_SN in NACK list + rlc_status_pdu_t status_pdu = {}; + rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_pdu); + if (rlc_am_is_valid_status_pdu(status_pdu) == false) { + return -1; + } + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + TESTASSERT(3 == rlc1.get_buffer_state()); // 2 byte header + 1 byte payload + + // Read the retx PDU from RLC1 + byte_buffer_t retx; + len = rlc1.read_pdu(retx.msg, 10); + retx.N_bytes = len; + + // Write the retx PDU to RLC2 + rlc2.write_pdu(retx.msg, retx.N_bytes); + + // Step timers until reordering timeout expires + cnt = 5; + while (cnt--) { + timers.step_all(); + } + + // get buffer state and status PDU again + status_buf.clear(); len = rlc2.read_pdu(status_buf.msg, 10); // big enough grant to fit full status PDU status_buf.N_bytes = len; TESTASSERT(status_buf.N_bytes != 0); @@ -2101,8 +3302,8 @@ bool status_pdu_test() rlc2.write_pdu(retx.msg, retx.N_bytes); } - TESTASSERT(tester.n_sdus == NBUFS); - for (int i = 0; i < tester.n_sdus; i++) { + TESTASSERT(tester.sdus.size() == NBUFS); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { if (tester.sdus[i]->N_bytes != 1) return -1; if (*(tester.sdus[i]->msg) != i) @@ -2112,16 +3313,153 @@ bool status_pdu_test() return 0; } +// This test checks the correct functioning of RLC reestablishment +// after maxRetx attempt. +bool reestablish_test() +{ + const rlc_config_t config = rlc_config_t::default_rlc_am_config(); +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_reestablish_test.pcap", config); + rlc_am_tester tester(&pcap); +#else + rlc_am_tester tester(NULL); +#endif + + srslte::timer_handler timers(8); + + rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + + if (not rlc1.configure(config)) { + return -1; + } + + if (not rlc2.configure(config)) { + return -1; + } + + bool reetablished_once = false; + + // Generate 40 SDUs/PDUs + const uint32_t total_num_tx_pdus = config.am.max_retx_thresh * 10; + uint32_t num_tx_pdus = 0; + + // Create a few SDUs and write to RLC1 to make sure buffers aren't empty after tx one PDU + for (uint32_t i = num_tx_pdus; i < 5; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srslte::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 5; // Give each buffer a size of 1 byte + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = i; // Write the index into the buffer + } + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + + for (uint32_t i = num_tx_pdus; i < total_num_tx_pdus; i++) { + // Write SDU + unique_byte_buffer_t sdu = srslte::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 5; // Give each buffer a size of 1 byte + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = i; // Write the index into the buffer + } + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + + // Read PDU + unique_byte_buffer_t pdu = srslte::make_byte_buffer(); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 7); // 2 byte header + 5 byte payload; + + // Find SN=0 PDU + bool is_data_pdu_sn0 = false; + if (not rlc_am_is_control_pdu(pdu->msg)) { + // After reestablishment after maxretx, also SN=0 is delivered + if (not reetablished_once) { + rlc_amd_pdu_header_t header = {}; + rlc_am_read_data_pdu_header(pdu.get(), &header); + if (header.sn == 0) { + is_data_pdu_sn0 = true; + } + } + } + + // Deliver all PDUs but SN=0 to RLC2 + if (not is_data_pdu_sn0) { + rlc2.write_pdu(pdu->msg, pdu->N_bytes); +#if HAVE_PCAP + pcap.write_dl_ccch(pdu->msg, pdu->N_bytes); +#endif + } + + // Check if RLC2 has something to send + if (rlc2.get_buffer_state() > 0) { + byte_buffer_t status_buf; + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 5); // provide only small grant + TESTASSERT(status_buf.N_bytes != 0); + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); +#if HAVE_PCAP + pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes); +#endif + } + + // each interation is one TTI + timers.step_all(); + + // Reestablish if max retx have been reached + if (tester.max_retx_triggered and !reetablished_once) { + rlc1.reestablish(); + rlc2.reestablish(); + // make sure we only reesablish once + reetablished_once = true; + } + } + + TESTASSERT(tester.sdus.size() == 17); + + srslog::fetch_basic_logger("TEST").info("Received %zd SDUs", tester.sdus.size()); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSLTE_SUCCESS; +} + int main(int argc, char** argv) { - srslog::init(); + srslte::logmap::set_default_log_level(srslte::LOG_LEVEL_DEBUG); + srslte::logmap::set_default_hex_limit(4096); + TESTASSERT(srslte::logmap::get("RLC_AM_1")->get_level() == srslte::LOG_LEVEL_DEBUG); - auto& logger_rrc1 = srslog::fetch_basic_logger("RLC_AM_1", false); - logger_rrc1.set_level(srslog::basic_levels::debug); - logger_rrc1.set_hex_dump_max_size(-1); - auto& logger_rrc2 = srslog::fetch_basic_logger("RLC_AM_2", false); - logger_rrc2.set_level(srslog::basic_levels::debug); - logger_rrc2.set_hex_dump_max_size(-1); + // Setup the log message spy to intercept error and warning log entries from RLC + if (!srslog::install_custom_sink(srslte::log_sink_message_spy::name(), + std::unique_ptr( + new srslte::log_sink_message_spy(srslog::get_default_log_formatter())))) { + return SRSLTE_ERROR; + } + + auto* spy = static_cast(srslog::find_sink(srslte::log_sink_message_spy::name())); + if (!spy) { + return SRSLTE_ERROR; + } + srslog::set_default_sink(*spy); + + auto& logger_rrc1 = srslog::fetch_basic_logger("RLC_AM_1", *spy, false); + auto& logger_rrc2 = srslog::fetch_basic_logger("RLC_AM_2", *spy, false); + logger_rrc1.set_hex_dump_max_size(100); + logger_rrc2.set_hex_dump_max_size(100); + + // start log backend + srslog::init(); if (basic_test()) { printf("basic_test failed\n"); @@ -2153,6 +3491,11 @@ int main(int argc, char** argv) exit(-1); }; + if (reestablish_test()) { + printf("reestablish_test failed\n"); + exit(-1); + }; + if (segment_retx_test()) { printf("segment_retx_test failed\n"); exit(-1); @@ -2202,6 +3545,67 @@ int main(int argc, char** argv) logger_rrc1.set_hex_dump_max_size(-1); logger_rrc2.set_hex_dump_max_size(-1); + if (resegment_test_9()) { + printf("resegment_test_9 failed\n"); + exit(-1); + }; + + if (resegment_test_10()) { + printf("resegment_test_10 failed\n"); + exit(-1); + }; + + if (resegment_test_11()) { + printf("resegment_test_11 failed\n"); + exit(-1); + }; + + if (resegment_test_12()) { + printf("resegment_test_12 failed\n"); + exit(-1); + }; + + // Set of unique header reconstruction tests using the logspy + if (header_reconstruction_test(*spy)) { + printf("header_reconstruction_test failed\n"); + exit(-1); + } + + if (header_reconstruction_test2(*spy)) { + printf("header_reconstruction_test2 failed\n"); + exit(-1); + } + + if (header_reconstruction_test3(*spy)) { + printf("header_reconstruction_test3 failed\n"); + exit(-1); + } + + if (header_reconstruction_test4(*spy)) { + printf("header_reconstruction_test4 failed\n"); + exit(-1); + } + + if (header_reconstruction_test5(*spy)) { + printf("header_reconstruction_test5 failed\n"); + exit(-1); + } + + if (header_reconstruction_test6(*spy)) { + printf("header_reconstruction_test6 failed\n"); + exit(-1); + } + + if (header_reconstruction_test7(*spy)) { + printf("header_reconstruction_test7 failed\n"); + exit(-1); + } + + if (header_reconstruction_test8(*spy)) { + printf("header_reconstruction_test8 failed\n"); + exit(-1); + } + if (reset_test()) { printf("reset_test failed\n"); exit(-1); @@ -2222,5 +3626,5 @@ int main(int argc, char** argv) exit(-1); }; - return 0; -} + return SRSLTE_SUCCESS; +} \ No newline at end of file