The CI fails sometimes, because some processes/threads are created in unpredictable order and if the client connects before the server is ready some tests fail, as seen in the following log: [UTIL] client/server iq-samples: launch sub-processes [UTIL] threadCreate() for time client: creating thread (no affinity, default priority) [UTIL] time manager configuration: [time source: server] [mode: client] [server IP: 127.0.0.1} [server port: 7374] [UTIL] time client: connection to 127.0.0.1:7374 failed, try again in 1s [UTIL] threadCreate() for time source iq samples: creating thread (no affinity, default priority) [UTIL] threadCreate() for time server: creating thread (no affinity, default priority) [UTIL] time manager configuration: [time source: iq_samples] [mode: server] [server IP: 127.0.0.1} [server port: 7374] [UTIL] client/server: rlc ticks: 1000 pdcp ticks: 1000 x2ap ticks: 1000 CMDLINE: "/oai-ran/build/common/utils/time_manager/tests/test_time_manager_auto" "--time_management.mode" "server" "--time_management.time_source" "iq_samples" [UTIL] client/server: rlc ticks: 0 pdcp ticks: 0 x2ap ticks: 0 CMDLINE: "/oai-ran/build/common/utils/time_manager/tests/test_time_manager_auto" "--time_management.mode" "client" [UTIL] client-server iq-samples: ERROR The client/server iq-samples test is started. Then we see this log: [UTIL] time manager configuration: [time source: server] [mode: client] [server IP: 127.0.0.1} [server port: 7374] Which means that the client is running. It tries to connect to the server (not running yet), but fails, so sleeps for 1 second before attempting another connection to the server: [UTIL] time client: connection to 127.0.0.1:7374 failed, try again in 1s The sleep is done by the time_client thread, the main thread of the test program continues and does a sleep(1). Then the server arrives: [UTIL] time manager configuration: [time source: iq_samples] [mode: server] [server IP: 127.0.0.1} [server port: 7374] It sends all the ticks (to no client, since the client is still sleeping). Then the test is over, the sleep(1) of the client finishes and the program exits. The client never connected to the server. We have this log: [UTIL] client/server: rlc ticks: 0 pdcp ticks: 0 x2ap ticks: 0 And the test fails. It's complicated to write an automatic test involving several uncoordinated programs. The operating system may create processes in any order. We use some sleeping to wait, but it may be too short or too long, leading to test failure. Let's see with more sleep if the CI fails less.
298 lines
8.0 KiB
C
298 lines
8.0 KiB
C
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.openairinterface.org/?page_id=698
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include "common/utils/time_manager/time_manager.h"
|
|
#include "common/utils/LOG/log.h"
|
|
#include "common/config/config_userapi.h"
|
|
|
|
configmodule_interface_t *uniqCfg = NULL;
|
|
|
|
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
|
|
{
|
|
if (s != NULL) {
|
|
printf("%s:%d %s() Exiting OAI softmodem: %s\n", file, line, function, s);
|
|
}
|
|
if (assert)
|
|
abort();
|
|
else
|
|
exit(1);
|
|
}
|
|
|
|
static int rlc_tick_count = 0;
|
|
static int pdcp_tick_count = 0;
|
|
static int x2ap_tick_count = 0;
|
|
|
|
void nr_rlc_ms_tick(void)
|
|
{
|
|
rlc_tick_count++;
|
|
}
|
|
|
|
void nr_pdcp_ms_tick(void)
|
|
{
|
|
pdcp_tick_count++;
|
|
}
|
|
|
|
void x2ap_ms_tick(void)
|
|
{
|
|
x2ap_tick_count++;
|
|
}
|
|
|
|
/* return 1 if ok, 0 if error */
|
|
static int standalone_realtime(void)
|
|
{
|
|
time_manager_tick_function_t tick_functions[] = {
|
|
nr_pdcp_ms_tick,
|
|
nr_rlc_ms_tick,
|
|
x2ap_ms_tick
|
|
};
|
|
int tick_functions_count = sizeofArray(tick_functions);
|
|
time_manager_start(tick_functions, tick_functions_count, TIME_SOURCE_REALTIME);
|
|
|
|
sleep(1);
|
|
|
|
time_manager_finish();
|
|
|
|
LOG_I(UTIL,
|
|
"standalone realtime: rlc ticks: %d pdcp ticks: %d x2ap ticks: %d\n",
|
|
rlc_tick_count,
|
|
pdcp_tick_count,
|
|
x2ap_tick_count);
|
|
|
|
/* let's accept 1% deviation from the 1000 counts we expect */
|
|
return abs(rlc_tick_count-1000) < 10
|
|
&& abs(pdcp_tick_count - 1000) < 10
|
|
&& abs(x2ap_tick_count - 1000) < 10;
|
|
}
|
|
|
|
/* return 1 if ok, 0 if error */
|
|
static int standalone_iq_samples(void)
|
|
{
|
|
time_manager_tick_function_t tick_functions[] = {
|
|
nr_pdcp_ms_tick,
|
|
nr_rlc_ms_tick,
|
|
x2ap_ms_tick
|
|
};
|
|
int tick_functions_count = sizeofArray(tick_functions);
|
|
time_manager_start(tick_functions, tick_functions_count, TIME_SOURCE_IQ_SAMPLES);
|
|
|
|
/* let's pretend we have 2000 samples per second */
|
|
for (int i = 0; i < 1000; i++) {
|
|
time_manager_iq_samples(1, 2000);
|
|
usleep(10);
|
|
time_manager_iq_samples(1, 2000);
|
|
usleep(10);
|
|
}
|
|
|
|
time_manager_finish();
|
|
|
|
LOG_I(UTIL,
|
|
"standalone iq-samples: rlc ticks: %d pdcp ticks: %d x2ap ticks: %d\n",
|
|
rlc_tick_count,
|
|
pdcp_tick_count,
|
|
x2ap_tick_count);
|
|
|
|
/* exact count is required */
|
|
return rlc_tick_count == 1000
|
|
&& pdcp_tick_count == 1000
|
|
&& x2ap_tick_count == 1000;
|
|
}
|
|
|
|
/* return 1 if ok, 0 if error */
|
|
static int client_server(char *program_name, bool iq_samples_time_source)
|
|
{
|
|
LOG_I(UTIL, "client/server %s: launch sub-processes\n", iq_samples_time_source ? "iq-samples" : "realtime");
|
|
|
|
pid_t server;
|
|
pid_t client;
|
|
|
|
/* start server */
|
|
server = fork();
|
|
DevAssert(server >= 0);
|
|
if (server == 0) {
|
|
/* run as server */
|
|
if (iq_samples_time_source)
|
|
execl(program_name, program_name, "--time_management.mode", "server", "--time_management.time_source", "iq_samples", NULL);
|
|
else
|
|
execl(program_name, program_name, "--time_management.mode", "server", NULL);
|
|
exit(1);
|
|
}
|
|
|
|
/* wait a little bit so that server is ready to accept clients
|
|
* (this is hackish)
|
|
*/
|
|
usleep(1000);
|
|
|
|
/* start client */
|
|
client = fork();
|
|
DevAssert(client >= 0);
|
|
if (client == 0) {
|
|
/* run as client */
|
|
execl(program_name, program_name, "--time_management.mode", "client", NULL);
|
|
exit(1);
|
|
}
|
|
|
|
/* wait for completion */
|
|
int ret = 1;
|
|
|
|
int status;
|
|
pid_t r = waitpid(server, &status, 0);
|
|
DevAssert(r == server);
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
|
ret = 0;
|
|
|
|
r = waitpid(client, &status, 0);
|
|
DevAssert(r == client);
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
|
|
ret = 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* return 1 if ok, 0 if error */
|
|
static int run_sub_client_server(bool iq_samples_time_source)
|
|
{
|
|
time_manager_tick_function_t tick_functions[] = {
|
|
nr_pdcp_ms_tick,
|
|
nr_rlc_ms_tick,
|
|
x2ap_ms_tick
|
|
};
|
|
int tick_functions_count = sizeofArray(tick_functions);
|
|
/* last argument doesn't matter, overwritten by args of execl in client_server() */
|
|
time_manager_start(tick_functions, tick_functions_count, TIME_SOURCE_REALTIME);
|
|
|
|
if (iq_samples_time_source) {
|
|
/* 'iq_samples_time_source' is true only for server
|
|
* wait a bit for client to be there otherwise server
|
|
* may finish too early
|
|
*/
|
|
usleep(100*1000);
|
|
|
|
/* let's pretend we have 2000 samples per second */
|
|
for (int i = 0; i < 1000; i++) {
|
|
time_manager_iq_samples(1, 2000);
|
|
usleep(10);
|
|
time_manager_iq_samples(1, 2000);
|
|
usleep(10);
|
|
}
|
|
} else {
|
|
sleep(1);
|
|
}
|
|
|
|
time_manager_finish();
|
|
|
|
LOG_I(UTIL, "client/server: rlc ticks: %d pdcp ticks: %d x2ap ticks: %d\n", rlc_tick_count, pdcp_tick_count, x2ap_tick_count);
|
|
|
|
if (iq_samples_time_source)
|
|
return rlc_tick_count == 1000
|
|
&& pdcp_tick_count == 1000
|
|
&& x2ap_tick_count == 1000;
|
|
else
|
|
/* let's accept 1% deviation from the 1000 counts we expect */
|
|
return abs(rlc_tick_count-1000) < 10
|
|
&& abs(pdcp_tick_count - 1000) < 10
|
|
&& abs(x2ap_tick_count - 1000) < 10;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == NULL) {
|
|
exit_fun("[SOFTMODEM] Error, configuration module init failed\n");
|
|
}
|
|
logInit();
|
|
|
|
/* for client/server tests, we use sub-processes with command line parameters */
|
|
if (argc > 1) {
|
|
/* if there are command line parameters then it's a sub-process */
|
|
rlc_tick_count = 0;
|
|
pdcp_tick_count = 0;
|
|
x2ap_tick_count = 0;
|
|
/* return value to the main test program
|
|
* let's do as for the shell: 0 means success and 1 means error
|
|
*/
|
|
/* we know we are in iq-samples time source mode if the last argument is "iq_samples" */
|
|
int success = run_sub_client_server(!strcmp(argv[argc - 1], "iq_samples")) == 1;
|
|
return 1 - success;
|
|
}
|
|
|
|
int err = 0;
|
|
|
|
/* test standalone realtime */
|
|
rlc_tick_count = 0;
|
|
pdcp_tick_count = 0;
|
|
x2ap_tick_count = 0;
|
|
int ret = standalone_realtime();
|
|
if (ret == 1)
|
|
LOG_I(UTIL, "standalone realtime: OK\n");
|
|
else {
|
|
LOG_E(UTIL, "standalone realtime: ERROR\n");
|
|
err = 1;
|
|
}
|
|
|
|
/* test standalone iq-samples */
|
|
rlc_tick_count = 0;
|
|
pdcp_tick_count = 0;
|
|
x2ap_tick_count = 0;
|
|
ret = standalone_iq_samples();
|
|
if (ret == 1)
|
|
LOG_I(UTIL, "standalone iq-samples: OK\n");
|
|
else {
|
|
LOG_E(UTIL, "standalone iq-samples: ERROR\n");
|
|
err = 1;
|
|
}
|
|
|
|
/* client/server tests are not written in a robust way
|
|
* they may fail depending on realtime on the machine
|
|
* de-activate if needed (if CI fails)
|
|
*/
|
|
/* test client/server realtime */
|
|
rlc_tick_count = 0;
|
|
pdcp_tick_count = 0;
|
|
x2ap_tick_count = 0;
|
|
ret = client_server(argv[0], false);
|
|
if (ret == 1)
|
|
LOG_I(UTIL, "client-server realtime: OK\n");
|
|
else {
|
|
LOG_E(UTIL, "client-server realtime: ERROR\n");
|
|
err = 1;
|
|
}
|
|
|
|
/* test client/server iq-samples */
|
|
rlc_tick_count = 0;
|
|
pdcp_tick_count = 0;
|
|
x2ap_tick_count = 0;
|
|
ret = client_server(argv[0], true);
|
|
if (ret == 1)
|
|
LOG_I(UTIL, "client-server iq-samples: OK\n");
|
|
else {
|
|
LOG_E(UTIL, "client-server iq-samples: ERROR\n");
|
|
err = 1;
|
|
}
|
|
|
|
logTerm();
|
|
|
|
return err;
|
|
}
|