Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: CI

on:
push:
branches: [ main]
branches: [ main ]

pull_request:
branches: [ main ]
Expand Down
120 changes: 120 additions & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: Integration Test

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
integration-test:
runs-on: ubuntu-latest
timeout-minutes: 60

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Free up disk space
run: |
sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc
sudo apt-get clean
df -h

- name: Build UE and gNB plugins
run: |
docker run --rm \
-v "${{ github.workspace }}:/workspace" \
osrf/ros:humble-desktop \
/bin/bash -c '
set -e
apt-get update && apt-get install -y \
python3-colcon-common-extensions \
wget lsb-release gnupg2

# Add Gazebo Ignition key and repo (Fortress)
echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" \
> /etc/apt/sources.list.d/gazebo-stable.list
wget https://packages.osrfoundation.org/gazebo.key -O - | apt-key add -
apt-get update && apt-get install -y ignition-fortress

cd /workspace/phine-plugins
source /opt/ros/humble/setup.bash
colcon build --event-handlers console_direct+

# Verify plugins were built
ls -la /workspace/phine-plugins/install/phine_plugins/lib/phine_plugins/
'

- name: Copy built plugins to simulation image
run: |
PLUGIN_SRC=phine-plugins/install/phine_plugins/lib/phine_plugins
PLUGIN_DST=demo_containerized/images/simulation/gazebo_launch/src/ign_turtlebot/phine_plugins

cp "${PLUGIN_SRC}/libgNB_plugin.so" "${PLUGIN_DST}/libgNB_plugin.so"
cp "${PLUGIN_SRC}/libUE_plugin.so" "${PLUGIN_DST}/libUE_plugin.so"

echo "Plugins copied:"
ls -la "${PLUGIN_DST}/"

- name: Build simulation Docker image
run: |
cd demo_containerized/images/simulation
docker build -t simulation:test .

- name: Start OAI Core Network
run: |
cd demo_containerized/oai_setup
docker compose up -d
./wait_for_core_network.sh

- name: Create ros_gz_net network
run: |
docker network create \
--driver bridge \
--subnet=192.168.73.128/26 \
--opt com.docker.network.bridge.name="ros_gz_net" \
ros_gz_net

- name: Start simulation container (headless)
run: |
cd tests/integration
DEMO_PROJECT_PATH="${{ github.workspace }}/demo_containerized" \
docker compose -f docker-compose-test-simulation.yml up -d

- name: Check gNB and UE registration
run: |
chmod +x tests/integration/check_registration.sh
tests/integration/check_registration.sh 900

- name: Collect logs on failure
if: failure()
run: |
echo "=== AMF logs ==="
docker logs oai-amf 2>&1 | tail -100 || true
echo ""
echo "=== Simulation container logs ==="
docker logs simulation 2>&1 | tail -100 || true
echo ""
echo "=== gNB container logs ==="
docker logs oai-gNB1 2>&1 | tail -50 || true
echo ""
echo "=== UE container logs ==="
docker logs ue_turtlebot 2>&1 | tail -50 || true
echo ""
echo "=== All running containers ==="
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" || true
echo ""
echo "=== Docker networks ==="
docker network ls || true

- name: Cleanup
if: always()
run: |
cd tests/integration
docker compose -f docker-compose-test-simulation.yml down || true
docker stop ue_turtlebot oai-gNB1 2>/dev/null || true
docker rm ue_turtlebot oai-gNB1 2>/dev/null || true
cd "${{ github.workspace }}/demo_containerized/oai_setup"
docker compose down || true
docker network rm ros_gz_net 2>/dev/null || true
135 changes: 135 additions & 0 deletions tests/integration/check_registration.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/bin/bash
# Check OAI AMF logs for gNB and UE registration
# Usage: ./check_registration.sh [timeout_seconds]

set -e

TIMEOUT=${1:-600}
INTERVAL=15
ELAPSED=0

GNB_CONTAINER_UP=false
UE_CONTAINER_UP=false
GNB_REGISTERED=false
UE_REGISTERED=false

echo "============================================"
echo "Integration Test: gNB and UE Registration"
echo "Timeout: ${TIMEOUT}s"
echo "============================================"

# Phase 1: Wait for gNB and UE containers to be started by the plugins
echo ""
echo "Phase 1: Waiting for gNB and UE containers to appear..."
echo "(The UE plugin builds a Docker image first, this can take several minutes)"
echo ""

while [ $ELAPSED -lt $TIMEOUT ]; do
# Check if gNB container exists
if [ "$GNB_CONTAINER_UP" = false ]; then
if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "oai-gNB"; then
echo "[OK] gNB container is running (elapsed: ${ELAPSED}s)"
GNB_CONTAINER_UP=true
fi
fi

# Check if UE container exists (ue_turtlebot from the SDF config)
if [ "$UE_CONTAINER_UP" = false ]; then
if docker ps --format '{{.Names}}' 2>/dev/null | grep -qE "(ue_turtlebot|robot_1)"; then
echo "[OK] UE container is running (elapsed: ${ELAPSED}s)"
UE_CONTAINER_UP=true
fi
fi

# Both containers running, move to phase 2
if [ "$GNB_CONTAINER_UP" = true ] && [ "$UE_CONTAINER_UP" = true ]; then
echo ""
echo "Phase 2: Checking AMF logs for registration..."
echo ""
break
fi

# Show what's happening
if [ $((ELAPSED % 60)) -eq 0 ] && [ $ELAPSED -gt 0 ]; then
echo " Still waiting... (elapsed: ${ELAPSED}s)"
echo " Running containers:"
docker ps --format " {{.Names}} ({{.Status}})" 2>/dev/null || true
else
echo " Waiting... gNB_container=${GNB_CONTAINER_UP} UE_container=${UE_CONTAINER_UP} (elapsed: ${ELAPSED}s)"
fi

sleep $INTERVAL
ELAPSED=$((ELAPSED + INTERVAL))
done

if [ "$GNB_CONTAINER_UP" = false ] || [ "$UE_CONTAINER_UP" = false ]; then
echo "============================================"
echo "[FAILURE] Timeout waiting for containers to start"
echo " gNB container running: ${GNB_CONTAINER_UP}"
echo " UE container running: ${UE_CONTAINER_UP}"
echo "============================================"
echo ""
echo "--- Running containers ---"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" 2>/dev/null || true
echo ""
echo "--- Simulation container logs (last 50 lines) ---"
docker logs simulation 2>&1 | tail -50 || true
exit 1
fi

# Phase 2: Check AMF logs for registration
while [ $ELAPSED -lt $TIMEOUT ]; do
AMF_LOGS=$(docker logs oai-amf 2>&1 || echo "")

# Check for gNB registration
if echo "$AMF_LOGS" | grep -qiE "(gNB.*associated|NG_SETUP.*SUCCESS|Connected.*gNB|gNodeB.*context)"; then
if [ "$GNB_REGISTERED" = false ]; then
echo "[PASS] gNB registered with AMF (elapsed: ${ELAPSED}s)"
GNB_REGISTERED=true
fi
fi

# Check for UE registration
if echo "$AMF_LOGS" | grep -qiE "(registration.*accept|5GMM-REGISTERED|UE.*registered|InitialUEMessage|REGISTRATION_COMPLETE)"; then
if [ "$UE_REGISTERED" = false ]; then
echo "[PASS] UE registered with AMF (elapsed: ${ELAPSED}s)"
UE_REGISTERED=true
fi
fi

# Both registered - success
if [ "$GNB_REGISTERED" = true ] && [ "$UE_REGISTERED" = true ]; then
echo ""
echo "============================================"
echo "[SUCCESS] Both gNB and UE are registered with the AMF"
echo "============================================"
echo ""
echo "--- Relevant AMF log lines ---"
docker logs oai-amf 2>&1 | grep -iE "(gNB|UE|registration|associated|connected|NGAP)" | tail -20 || true
exit 0
fi

echo " Checking AMF... gNB=${GNB_REGISTERED} UE=${UE_REGISTERED} (elapsed: ${ELAPSED}s)"
sleep $INTERVAL
ELAPSED=$((ELAPSED + INTERVAL))
done

echo ""
echo "============================================"
echo "[FAILURE] Timeout after ${TIMEOUT}s"
echo " gNB registered: ${GNB_REGISTERED}"
echo " UE registered: ${UE_REGISTERED}"
echo "============================================"
echo ""
echo "--- AMF logs (last 80 lines) ---"
docker logs oai-amf 2>&1 | tail -80 || true
echo ""
echo "--- gNB container logs ---"
docker logs oai-gNB1 2>&1 | tail -30 || true
echo ""
echo "--- UE container logs ---"
docker logs ue_turtlebot 2>&1 | tail -30 || true
echo ""
echo "--- Running containers ---"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" 2>/dev/null || true
exit 1
44 changes: 44 additions & 0 deletions tests/integration/docker-compose-test-simulation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
version: '3.8'
services:
simulation:
image: simulation:test
privileged: true
container_name: simulation
environment:
- PROJECT_PATH=/app
- DEMO_PROJECT_PATH=${DEMO_PROJECT_PATH}
- ROS_DISCOVERY_SERVER=192.168.70.159:11811
- IGN_IP=192.168.73.151
- ROS_DOMAIN_ID=10
- IGN_PARTITION=domain1
volumes:
- /var/run/docker.sock:/var/run/docker.sock
stdin_open: true
tty: true
networks:
public_net:
ipv4_address: 192.168.70.152
ros_gz_net:
ipv4_address: 192.168.73.151
entrypoint:
- /bin/bash
- -c
- |
source /opt/ros/humble/setup.bash
source /app/gazebo_launch/install/setup.bash
export FASTRTPS_DEFAULT_PROFILES_FILE=/app/dds.xml
PACKAGE_PATH=$$(ros2 pkg prefix ign_turtlebot --share)
export IGN_GAZEBO_RESOURCE_PATH=$${PACKAGE_PATH}/models:/opt/ros/humble/share
export IGN_GAZEBO_SYSTEM_PLUGIN_PATH=$${PACKAGE_PATH}/phine_plugins:/usr/lib/x86_64-linux-gnu/ign-gazebo-6/plugins:/opt/ros/humble/lib
export IGN_GUI_PLUGIN_PATH=$${PACKAGE_PATH}/gui_plugins
ip route add 10.0.0.0/16 via 192.168.70.134 dev eth0 || true
echo "Starting Gazebo in headless (server-only) mode..."
ign gazebo -s -r -v 3 $${PACKAGE_PATH}/worlds/world_only.sdf &
tail -f /dev/null
networks:
public_net:
external:
name: phine-net
ros_gz_net:
external:
name: ros_gz_net
Loading