Files
Cedric Roux a373a8da6b nr rlc: fix standalone tests
./run_tests.sh was not functional anymore because of the introduction
of AssertFatal() in nr_rlc_entity.c

Including the .h and providing exit_function() solves the problem.
2024-09-09 11:30:59 +02:00

501 lines
14 KiB
C

#include "../nr_rlc_entity.h"
#include "../nr_rlc_entity_am.h"
#include "../nr_rlc_entity_um.h"
#include "../nr_rlc_entity_tm.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <sys/wait.h>
#include <unistd.h>
#ifndef _STANDALONE_TESTING_
#include "common/utils/LOG/log.h"
#endif
/*
* GNB_AM <rx_maxsize> <tx_maxsize> <t_poll_retransmit> <t_reassembly>
* <t_status_prohibit> <poll_pdu> <poll_byte> <max_retx_threshold>
* <sn_field_length>
* create the gNB RLC AM entity with given parameters
*
* UE_AM <rx_maxsize> <tx_maxsize> <t_poll_retransmit> <t_reassembly>
* <t_status_prohibit> <poll_pdu> <poll_byte> <max_retx_threshold>
* <sn_field_length>
* create the UE RLC AM entity with given parameters
*
* GNB_UM <rx_maxsize> <tx_maxsize> <t_reassembly> <sn_field_length>
* create the gNB RLC UM entity with given parameters
*
* UE_UM <rx_maxsize> <tx_maxsize> <t_reassembly> <sn_field_length>
* create the UE RLC UM entity with given parameters
*
* GNB_TM <tx_maxsize>
* create the gNB RLC TM entity with given parameters
*
* UE_UM <tx_maxsize>
* create the UE RLC TM entity with given parameters
*
* TIME <time>
* following actions to be performed at time <time>
* <time> starts at 1
* You must end your test definition with a line 'TIME, -1'.
*
* GNB_SDU <id> <size>
* send an SDU to gNB with id <i> and size <size>
* the SDU is [00 01 ... ff 01 ...]
* (ie. start byte is 00 then we increment for each byte, loop if needed)
*
* UE_SDU <id> <size>
* same as GNB_SDU but the SDU is sent to the UE
*
* GNB_PDU <size> <'size' bytes>
* send a custom PDU from gNB to UE (gNB does not see this PDU at all)
*
* UE_PDU <size> <'size' bytes>
* send a custom PDU from UE to gNB (UE does not see this PDU at all)
*
* GNB_PDU_SIZE <size>
* set 'gnb_pdu_size'
*
* UE_PDU_SIZE <size>
* set 'ue_pdu_size'
*
* GNB_RECV_FAILS <fails>
* set the 'gnb_recv_fails' flag to <fails>
* (1: recv will fail, 0: recv will succeed)
*
* UE_RECV_FAILS <fails>
* same as GNB_RECV_FAILS but for 'ue_recv_fails'
*
* MUST_FAIL
* to be used as first command after the first TIME to indicate
* that the test must fail (ie. exit with non zero, crash not allowed)
*
* GNB_BUFFER_STATUS
* call buffer_status for gNB and print result
*
* UE_BUFFER_STATUS
* call buffer_status for UE and print result
*
* GNB_DISCARD_SDU <sdu ID>
* discards given SDU
*
* UE_DISCARD_SDU <sdu ID>
* discards given SDU
*
* RE_ESTABLISH
* re-establish both gNB and UE
*/
enum action {
GNB_AM, UE_AM,
GNB_UM, UE_UM,
GNB_TM, UE_TM,
TIME, GNB_SDU, UE_SDU, GNB_PDU, UE_PDU,
GNB_PDU_SIZE, UE_PDU_SIZE,
GNB_RECV_FAILS, UE_RECV_FAILS,
MUST_FAIL,
GNB_BUFFER_STATUS, UE_BUFFER_STATUS,
GNB_DISCARD_SDU, UE_DISCARD_SDU,
RE_ESTABLISH
};
int test[] = {
/* TEST is defined at compilation time */
#include TEST
};
void deliver_sdu_gnb_am(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: GNB: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void deliver_sdu_gnb_um(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
printf("TEST: GNB: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void deliver_sdu_gnb_tm(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
printf("TEST: GNB: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void successful_delivery_gnb(void *successful_delivery_data,
nr_rlc_entity_t *_entity, int sdu_id)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: GNB: %"PRIu64": SDU %d was successfully delivered.\n",
entity->t_current, sdu_id);
}
void max_retx_reached_gnb(void *max_retx_reached_data,
nr_rlc_entity_t *_entity)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: GNB: %"PRIu64": max RETX reached! radio link failure!\n",
entity->t_current);
exit(1);
}
void deliver_sdu_ue_am(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: UE: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void deliver_sdu_ue_um(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_um_t *entity = (nr_rlc_entity_um_t *)_entity;
printf("TEST: UE: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void deliver_sdu_ue_tm(void *deliver_sdu_data,
struct nr_rlc_entity_t *_entity,
char *buf, int size)
{
nr_rlc_entity_tm_t *entity = (nr_rlc_entity_tm_t *)_entity;
printf("TEST: UE: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void successful_delivery_ue(void *successful_delivery_data,
nr_rlc_entity_t *_entity, int sdu_id)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: UE: %"PRIu64": SDU %d was successfully delivered.\n",
entity->t_current, sdu_id);
}
void max_retx_reached_ue(void *max_retx_reached_data,
nr_rlc_entity_t *_entity)
{
nr_rlc_entity_am_t *entity = (nr_rlc_entity_am_t *)_entity;
printf("TEST: UE: %"PRIu64", max RETX reached! radio link failure!\n",
entity->t_current);
exit(1);
}
#ifdef _STANDALONE_TESTING_
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
{
exit(1);
}
#endif
int test_main(void)
{
nr_rlc_entity_t *gnb = NULL;
nr_rlc_entity_t *ue = NULL;
int i;
int k;
char *sdu;
char *pdu;
nr_rlc_entity_buffer_status_t buffer_status;
int gnb_do_buffer_status = 0;
int ue_do_buffer_status = 0;
int size;
int pos;
int next_byte_gnb = 0;
int next_byte_ue = 0;
int gnb_recv_fails = 0;
int ue_recv_fails = 0;
int gnb_pdu_size = 1000;
int ue_pdu_size = 1000;
#ifndef _STANDALONE_TESTING_
logInit();
#endif
printf("TEST: start\n");
sdu = malloc(16001);
pdu = malloc(3000);
if (sdu == NULL || pdu == NULL) {
printf("out of memory\n");
exit(1);
}
for (i = 0; i < 16001; i++)
sdu[i] = i & 255;
pos = 0;
if (test[pos] != TIME) {
printf("%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
for (i = 1; i < 1000; i++) {
if (i == test[pos+1]) {
pos += 2;
while (test[pos] != TIME)
switch (test[pos]) {
default: printf("fatal: unknown action\n"); exit(1);
case GNB_AM:
gnb = new_nr_rlc_entity_am(test[pos+1], test[pos+2],
deliver_sdu_gnb_am, NULL,
successful_delivery_gnb, NULL,
max_retx_reached_gnb, NULL,
test[pos+3], test[pos+4], test[pos+5],
test[pos+6], test[pos+7], test[pos+8],
test[pos+9]);
pos += 10;
break;
case UE_AM:
ue = new_nr_rlc_entity_am(test[pos+1], test[pos+2],
deliver_sdu_ue_am, NULL,
successful_delivery_ue, NULL,
max_retx_reached_ue, NULL,
test[pos+3], test[pos+4], test[pos+5],
test[pos+6], test[pos+7], test[pos+8],
test[pos+9]);
pos += 10;
break;
case GNB_UM:
gnb = new_nr_rlc_entity_um(test[pos+1], test[pos+2],
deliver_sdu_gnb_um, NULL,
test[pos+3], test[pos+4]);
pos += 5;
break;
case UE_UM:
ue = new_nr_rlc_entity_um(test[pos+1], test[pos+2],
deliver_sdu_ue_um, NULL,
test[pos+3], test[pos+4]);
pos += 5;
break;
case GNB_TM:
gnb = new_nr_rlc_entity_tm(test[pos+1], deliver_sdu_gnb_tm, NULL);
pos += 2;
break;
case UE_TM:
ue = new_nr_rlc_entity_tm(test[pos+1], deliver_sdu_ue_tm, NULL);
pos += 2;
break;
case GNB_SDU:
for (k = 0; k < test[pos+2]; k++, next_byte_gnb++)
sdu[k] = next_byte_gnb;
printf("TEST: GNB: %d: recv_sdu (id %d): size %d: [",
i, test[pos+1], test[pos+2]);
for (k = 0; k < test[pos+2]; k++)
printf(" %2.2x", (unsigned char)sdu[k]);
printf("]\n");
gnb->recv_sdu(gnb, sdu, test[pos+2], test[pos+1]);
pos += 3;
break;
case UE_SDU:
for (k = 0; k < test[pos+2]; k++, next_byte_ue++)
sdu[k] = next_byte_ue;
printf("TEST: UE: %d: recv_sdu (id %d): size %d: [",
i, test[pos+1], test[pos+2]);
for (k = 0; k < test[pos+2]; k++)
printf(" %2.2x", (unsigned char)sdu[k]);
printf("]\n");
ue->recv_sdu(ue, sdu, test[pos+2], test[pos+1]);
pos += 3;
break;
case GNB_PDU:
for (k = 0; k < test[pos+1]; k++)
pdu[k] = test[pos+2+k];
printf("TEST: GNB: %d: custom PDU: size %d: [", i, test[pos+1]);
for (k = 0; k < test[pos+1]; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!ue_recv_fails)
ue->recv_pdu(ue, pdu, test[pos+1]);
pos += 2 + test[pos+1];
break;
case UE_PDU:
for (k = 0; k < test[pos+1]; k++)
pdu[k] = test[pos+2+k];
printf("TEST: UE: %d: custom PDU: size %d: [", i, test[pos+1]);
for (k = 0; k < test[pos+1]; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!gnb_recv_fails)
gnb->recv_pdu(gnb, pdu, test[pos+1]);
pos += 2 + test[pos+1];
break;
case GNB_PDU_SIZE:
gnb_pdu_size = test[pos+1];
pos += 2;
break;
case UE_PDU_SIZE:
ue_pdu_size = test[pos+1];
pos += 2;
break;
case GNB_RECV_FAILS:
gnb_recv_fails = test[pos+1];
pos += 2;
break;
case UE_RECV_FAILS:
ue_recv_fails = test[pos+1];
pos += 2;
break;
case MUST_FAIL:
/* do nothing, only used by caller */
pos++;
break;
case GNB_BUFFER_STATUS:
gnb_do_buffer_status = 1;
pos++;
break;
case UE_BUFFER_STATUS:
ue_do_buffer_status = 1;
pos++;
break;
case GNB_DISCARD_SDU:
printf("TEST: GNB: %d: discard SDU %d\n", i, test[pos+1]);
gnb->discard_sdu(gnb, test[pos+1]);
pos += 2;
break;
case UE_DISCARD_SDU:
printf("TEST: UE: %d: discard SDU %d\n", i, test[pos+1]);
ue->discard_sdu(ue, test[pos+1]);
pos += 2;
break;
case RE_ESTABLISH:
printf("TEST: %d: re-establish gNB and UE\n", i);
gnb->reestablishment(gnb);
ue->reestablishment(ue);
pos++;
break;
}
}
gnb->set_time(gnb, i);
ue->set_time(ue, i);
if (gnb_do_buffer_status) {
gnb_do_buffer_status = 0;
buffer_status = gnb->buffer_status(gnb, gnb_pdu_size);
printf("TEST: GNB: %d: buffer_status: status_size %d tx_size %d retx_size %d\n",
i,
buffer_status.status_size,
buffer_status.tx_size,
buffer_status.retx_size);
}
size = gnb->generate_pdu(gnb, pdu, gnb_pdu_size);
if (size) {
printf("TEST: GNB: %d: generate_pdu: size %d: [", i, size);
for (k = 0; k < size; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!ue_recv_fails)
ue->recv_pdu(ue, pdu, size);
}
if (ue_do_buffer_status) {
ue_do_buffer_status = 0;
buffer_status = ue->buffer_status(ue, ue_pdu_size);
printf("TEST: UE: %d: buffer_status: status_size %d tx_size %d retx_size %d\n",
i,
buffer_status.status_size,
buffer_status.tx_size,
buffer_status.retx_size);
}
size = ue->generate_pdu(ue, pdu, ue_pdu_size);
if (size) {
printf("TEST: UE: %d: generate_pdu: size %d: [", i, size);
for (k = 0; k < size; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!gnb_recv_fails)
gnb->recv_pdu(gnb, pdu, size);
}
}
gnb->delete_entity(gnb);
ue->delete_entity(ue);
free(sdu);
free(pdu);
return 0;
}
void usage(void)
{
printf("options:\n");
printf(" -no-fork\n");
printf(" don't fork (to ease debugging with gdb)\n");
exit(0);
}
int main(int n, char **v)
{
int must_fail = 0;
int son;
int status;
int i;
int no_fork = 0;
for (i = 1; i < n; i++) {
if (!strcmp(v[i], "-no-fork")) { no_fork = 1; continue; }
usage();
}
if (test[2] == MUST_FAIL)
must_fail = 1;
if (no_fork) return test_main();
son = fork();
if (son == -1) {
perror("fork");
return 1;
}
if (son == 0)
return test_main();
if (wait(&status) == -1) {
perror("wait");
return 1;
}
/* child must quit properly */
if (!WIFEXITED(status))
return 1;
/* child must fail if expected to */
if (must_fail && WEXITSTATUS(status) == 0)
return 1;
/* child must not fail if not expected to */
if (!must_fail && WEXITSTATUS(status))
return 1;
return 0;
}