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
43 changes: 39 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,25 @@ jobs:
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex'
- ROBOT_MODEL: 'ur7e'
URSIM_VERSION: '10.13.0'
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex'
PROGRAM_FOLDER: 'tests/resources/dockerursim/programs/polyscopex/10.13.0/ur7e'
POLYSCOPE_X_WITH_REMOTE_CONTROL: 'true'
CITADEL_DB_FOLDER: 'tests/resources/dockerursim/citadelDB'
Comment thread
urfeex marked this conversation as resolved.

steps:
- uses: actions/checkout@v7
- name: start ursim
run: |
scripts/start_ursim.sh -m $ROBOT_MODEL -v $URSIM_VERSION -p $PROGRAM_FOLDER -d -f DISABLED
CITADEL_DB_ARG=""
if [ -n "$CITADEL_DB_FOLDER" ]; then
CITADEL_DB_ARG="-c $CITADEL_DB_FOLDER"
fi
scripts/start_ursim.sh -m $ROBOT_MODEL -v $URSIM_VERSION -p $PROGRAM_FOLDER $CITADEL_DB_ARG -d -f DISABLED
env:
DOCKER_RUN_OPTS: --network ursim_net
ROBOT_MODEL: ${{matrix.env.ROBOT_MODEL}}
URSIM_VERSION: ${{matrix.env.URSIM_VERSION}}
PROGRAM_FOLDER: ${{matrix.env.PROGRAM_FOLDER}}
CITADEL_DB_FOLDER: ${{matrix.env.CITADEL_DB_FOLDER || ''}}
- id: check_polyscopex
run: |
if [[ "${{matrix.env.URSIM_VERSION}}" == "10."* ]]; then
Expand All @@ -103,18 +110,27 @@ jobs:
- name: Extract CMake build directory
run: tar -xzf build.tar.gz
- name: Create folder for test artifacts
run: mkdir -p test_artifacts
run: mkdir -p test_artifacts/screenshots
- name: Access PolyScope
if: ${{ steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
run: chrome --no-sandbox --disable-settuid-sandbox --headless=new 192.168.56.101 &
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install Python dependencies
run: sudo apt-get update && sudo apt-get install -y python3-pandas python3-lxml
run: pip install pandas lxml
- name: Install Playwright for PolyScope X screenshots
if: ${{ steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
run: pip install playwright && playwright install chromium
- name: Generate rtde outputs lists
run: python3 tests/resources/generate_rtde_outputs.py
- name: test
run: cd build && ctest --output-on-failure --output-junit junit.xml
env:
URSIM_VERSION: ${{matrix.env.URSIM_VERSION}}
POLYSCOPE_X_TESTS_WITH_REMOTE_CONTROL: ${{matrix.env.POLYSCOPE_X_WITH_REMOTE_CONTROL || ''}}
POLYSCOPE_X_SCREENSHOT_DIR: ${{github.workspace}}/test_artifacts/screenshots
- name: Upload test results to Codecov
uses: codecov/codecov-action@v7
with:
Expand Down Expand Up @@ -168,6 +184,25 @@ jobs:
path: test_artifacts
if-no-files-found: error
retention-days: 10
- name: Capture PolyScope X URSim logs
if: ${{ always() && steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
run: docker logs ursim > test_artifacts/ursim_docker.log 2>&1
- name: Upload PolyScope X URSim logs
uses: actions/upload-artifact@v7
if: ${{ always() && steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
with:
name: ${{matrix.env.ROBOT_MODEL}}_${{matrix.env.URSIM_VERSION}}_URSim_Logs
path: test_artifacts/ursim_docker.log
if-no-files-found: error
retention-days: 10
- name: Upload PolyScope X screenshots
uses: actions/upload-artifact@v7
if: ${{ failure() && steps.check_polyscopex.outputs.is_polyscopex == 'true' }}
with:
name: ${{matrix.env.ROBOT_MODEL}}_${{matrix.env.URSIM_VERSION}}_screenshots
path: test_artifacts/screenshots
if-no-files-found: ignore
retention-days: 10
- name: Upload generated scripts
uses: actions/upload-artifact@v6
if: ${{ always() }}
Expand Down
69 changes: 40 additions & 29 deletions scripts/start_ursim.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ help()
for available versions. Defaults to 'latest'"
echo " -p <folder> Location from which programs are read / to which programs are written.
If not specified, will fallback to ${PERSISTENT_BASE}/${ROBOT_SERIES}/${ROBOT_MODEL}/programs"
echo " -c <folder> Location of the citadelDB to mount into the URSim container.
Only used for PolyScope X. Enables persisting settings such as
remote-control mode across container restarts.
If not specified, no citadelDB volume is mounted."
echo " -u <folder> Location from which URCaps are read / to which URCaps are written.
If not specified, will fallback to ${PERSISTENT_BASE}/${ROBOT_SERIES}/urcaps"
echo " -n Name of the docker container. Defaults to '$CONTAINER_NAME'"
Expand All @@ -64,6 +68,7 @@ ROBOT_SERIES=""
URSIM_VERSION=latest
PORT_FORWARDING=""
PROGRAM_STORAGE_ARG=""
CITADEL_DB_STORAGE_ARG=""
URCAP_STORAGE_ARG=""
DETACHED=false

Expand Down Expand Up @@ -351,7 +356,7 @@ post_setup_polyscopex()
}

parse_arguments(){
while getopts ":hm:v:p:u:i:f:n:dt" option; do
while getopts ":hm:v:p:c:u:i:f:n:dt" option; do
case $option in
h) # display Help
help
Expand All @@ -365,6 +370,9 @@ parse_arguments(){
p) # program_folder
PROGRAM_STORAGE_ARG=${OPTARG}
;;
c) # citadel_db_folder
CITADEL_DB_STORAGE_ARG=${OPTARG}
;;
u) # urcaps_folder
URCAP_STORAGE_ARG=${OPTARG}
;;
Expand Down Expand Up @@ -469,12 +477,6 @@ main() {
PORT_FORWARDING=""
fi

DOCKER_ARGS=""

if [ "$ROBOT_SERIES" == "polyscopex" ]; then
DOCKER_ARGS="$DOCKER_ARGS --privileged"
fi

if [ -n "$PROGRAM_STORAGE_ARG" ]; then
PROGRAM_STORAGE="$PROGRAM_STORAGE_ARG"
fi
Expand All @@ -492,18 +494,22 @@ main() {
mkdir -p "${PROGRAM_STORAGE}"
PROGRAM_STORAGE=$(realpath "$PROGRAM_STORAGE")

ROBOT_MODEL_CONTROLLER_FLAG=""
verlte "10.7.0" "$URSIM_VERSION" && verlte "$URSIM_VERSION" "10.8.0" && ROBOT_MODEL_CONTROLLER_FLAG="-e ROBOT_TYPE_CONTROLLER=${ROBOT_MODEL}"

docker_cmd="docker run --rm -d \
--net ursim_net --ip $IP_ADDRESS \
-v ${PROGRAM_STORAGE}:/ur/bin/backend/applications \
-e ROBOT_TYPE=${ROBOT_MODEL} \
$ROBOT_MODEL_CONTROLLER_FLAG \
$PORT_FORWARDING \
$DOCKER_ARGS \
--name $CONTAINER_NAME \
universalrobots/ursim_${ROBOT_SERIES}:$URSIM_VERSION"
docker_args=(docker run --rm -d
--net ursim_net --ip "$IP_ADDRESS"
--privileged
-v "${PROGRAM_STORAGE}:/ur/bin/backend/applications"
-e "ROBOT_TYPE=${ROBOT_MODEL}"
)

if [ -n "$CITADEL_DB_STORAGE_ARG" ]; then
mkdir -p "$CITADEL_DB_STORAGE_ARG"
CITADEL_DB_STORAGE=$(realpath "$CITADEL_DB_STORAGE_ARG")
docker_args+=(-v "${CITADEL_DB_STORAGE}:/citadelDB")
fi

verlte "10.7.0" "$URSIM_VERSION" && verlte "$URSIM_VERSION" "10.8.0" && \
docker_args+=(-e "ROBOT_TYPE_CONTROLLER=${ROBOT_MODEL}")

else
# Create local storage for programs and URCaps
mkdir -p "${URCAP_STORAGE}"
Expand Down Expand Up @@ -538,21 +544,26 @@ main() {
fi
fi
fi
docker_cmd="docker run --rm -d --net ursim_net --ip $IP_ADDRESS\
-v ${URCAP_STORAGE}:/urcaps \
-v ${PROGRAM_STORAGE}:/ursim/programs \
-v ${POLYSCOPE_STORAGE}:/ursim/.polyscope \
-e ROBOT_MODEL=${ROBOT_MODEL} \
$PORT_FORWARDING \
--name $CONTAINER_NAME \
universalrobots/ursim_${ROBOT_SERIES}:$URSIM_VERSION"

docker_args=(docker run --rm -d
--net ursim_net --ip "$IP_ADDRESS"
-v "${URCAP_STORAGE}:/urcaps"
-v "${PROGRAM_STORAGE}:/ursim/programs"
-v "${POLYSCOPE_STORAGE}:/ursim/.polyscope"
-e "ROBOT_MODEL=${ROBOT_MODEL}"
)
fi

# PORT_FORWARDING is a space-separated list of -p flags; word-splitting is intentional
# shellcheck disable=SC2206
[[ -n "$PORT_FORWARDING" ]] && docker_args+=($PORT_FORWARDING)
docker_args+=(--name "$CONTAINER_NAME" "universalrobots/ursim_${ROBOT_SERIES}:$URSIM_VERSION")

if [ "$TEST_RUN" = true ]; then
echo "$docker_cmd" | tr -s ' '
echo "${docker_args[*]}" | tr -s ' '
exit 0
fi
$docker_cmd || exit 2
"${docker_args[@]}" || exit 2

# Stop container when interrupted
TRAP_CMD="
Expand Down
5 changes: 0 additions & 5 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ target_link_libraries(fake_primary_server PRIVATE ur_client_library::urcl)
include(GoogleTest)

option(INTEGRATION_TESTS "Build the integration tests that require a running robot / URSim" OFF)
option(POLYSCOPE_X_TESTS_WITH_REMOTE_CONTROL "Run Polyscope X tests that require remote control" OFF)
option(CHECK_RTDE_DOCS_RECIPE "Fetch the RTDE documentation to auto-generate a recipe containing all output fields and check that with the RTDE client. Requires python3 and pandas and an internet connection." OFF)
set(INTEGRATION_TESTS_ROBOT_IP "" CACHE STRING "If set, override the default robot IP (192.168.56.101) compiled into each integration test main() by passing --robot_ip <ip> to every ctest-registered integration test.")
# Build Tests
Expand All @@ -36,10 +35,6 @@ if (INTEGRATION_TESTS)
set(INTEGRATION_TESTS_ROBOT_IP_ARG --robot_ip ${INTEGRATION_TESTS_ROBOT_IP})
endif()

if(POLYSCOPE_X_TESTS_WITH_REMOTE_CONTROL)
add_compile_definitions(POLYSCOPE_X_TESTS_WITH_REMOTE_CONTROL=1 )
endif()

add_executable(rtde_tests test_rtde_client.cpp fake_rtde_server.cpp)
target_link_libraries(rtde_tests PRIVATE ur_client_library::urcl GTest::gmock_main)
gtest_add_tests(TARGET rtde_tests
Expand Down
Binary file added tests/resources/dockerursim/citadelDB/000001.sst
Binary file not shown.
Binary file added tests/resources/dockerursim/citadelDB/000001.vlog
Binary file not shown.
Binary file added tests/resources/dockerursim/citadelDB/DISCARD
Binary file not shown.
1 change: 1 addition & 0 deletions tests/resources/dockerursim/citadelDB/KEYREGISTRY
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
¼ò%¬{.AØGé„ۃzHello Badger
Binary file added tests/resources/dockerursim/citadelDB/MANIFEST
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"application": 0,
"program": 0
}
Binary file not shown.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Binary file not shown.
28 changes: 28 additions & 0 deletions tests/resources/polyscopex_screenshot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
"""Take a screenshot of the PolyScope X UI with a real post-load delay for SPA rendering.

Usage: polyscopex_screenshot.py <url> <output_file> <delay_ms>
"""
import sys

from playwright.sync_api import sync_playwright


def main():
if len(sys.argv) != 4:
print(f"Usage: {sys.argv[0]} <url> <output_file> <delay_ms>")
sys.exit(1)

url, output_file, delay_ms = sys.argv[1], sys.argv[2], int(sys.argv[3])

with sync_playwright() as p:
browser = p.chromium.launch(args=["--no-sandbox", "--disable-setuid-sandbox"])
page = browser.new_page(viewport={"width": 1920, "height": 1080})
page.goto(url, wait_until="load")
page.wait_for_timeout(delay_ms)
page.screenshot(path=output_file)
browser.close()


if __name__ == "__main__":
main()
74 changes: 68 additions & 6 deletions tests/test_dashboard_client_x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@
#include <ur_client_library/exceptions.h>
#include <algorithm>
#include <chrono>
#include <cstdlib>
#include <filesystem>
#include <thread>
#include <vector>
#ifndef _WIN32
# include <fcntl.h>
# include <sys/wait.h>
# include <unistd.h>
#endif
#include "gtest/gtest.h"
#include "test_utils.h"
#include "ur_client_library/comm/tcp_socket.h"
Expand Down Expand Up @@ -64,11 +72,14 @@ class DashboardClientTestX : public ::testing::Test
protected:
void SetUp()
{
#ifdef POLYSCOPE_X_TESTS_WITH_REMOTE_CONTROL
# if POLYSCOPE_X_TESTS_WITH_REMOTE_CONTROL == 1
skip_remote_control_tests = false;
# endif
#endif
if (std::getenv("POLYSCOPE_X_TESTS_WITH_REMOTE_CONTROL") != nullptr)
{
std::string env_var = std::getenv("POLYSCOPE_X_TESTS_WITH_REMOTE_CONTROL");
if (env_var != "" && parseBoolean(env_var))
{
skip_remote_control_tests = false;
}
}
urcl::comm::INotifier notifier;
primary_client_.reset(new urcl::primary_interface::PrimaryClient(g_ROBOT_IP, notifier));
primary_client_->start();
Expand Down Expand Up @@ -151,6 +162,8 @@ TEST_F(DashboardClientTestX, unlock_protective_stop)
GTEST_SKIP_("Skipping test that would require remote control to be enabled on robot");
}
ASSERT_TRUE(dashboard_client_->connect());
dashboard_client_->commandPowerOff();
ASSERT_NO_THROW(waitForRobotMode(RobotMode::POWER_OFF));
dashboard_client_->commandPowerOn();
Comment thread
urfeex marked this conversation as resolved.
ASSERT_NO_THROW(waitForRobotMode(RobotMode::IDLE));
DashboardResponse response;
Expand Down Expand Up @@ -204,7 +217,7 @@ TEST_F(DashboardClientTestX, program_interaction)
auto resp = dashboard_client_->commandGetLoadedProgram();
return std::get<std::string>(resp.data["program_name"]) == "wait_program";
},
std::chrono::milliseconds(1000));
std::chrono::milliseconds(5000));
}
response = dashboard_client_->commandPowerOn();
ASSERT_TRUE(response.ok);
Expand Down Expand Up @@ -507,6 +520,53 @@ TEST_F(DashboardClientTestX, microsecond_receive_timeout_makes_connect_fail)
EXPECT_FALSE(dashboard_client_->connect());
}

class PolyScopeScreenshotListener : public ::testing::EmptyTestEventListener
{
public:
void OnTestEnd(const ::testing::TestInfo& test_info) override
{
if (!test_info.result()->Failed())
{
return;
}

const char* dir_env = std::getenv("POLYSCOPE_X_SCREENSHOT_DIR");
std::filesystem::path screenshot_dir = dir_env ? dir_env : "test_artifacts/screenshots";
std::filesystem::create_directories(screenshot_dir);

std::string filename =
(screenshot_dir / (std::string(test_info.test_suite_name()) + "." + test_info.name() + ".png")).string();
std::string url = "http://" + g_ROBOT_IP;
std::string script = "../tests/resources/polyscopex_screenshot.py";
std::string delay = "5000";

#ifndef _WIN32
// Build argv as a proper array — no shell involved, so spaces and metacharacters
// in url, filename, or script path are passed through safely.
std::vector<char*> args = { const_cast<char*>("python3"), const_cast<char*>(script.c_str()),
const_cast<char*>(url.c_str()), const_cast<char*>(filename.c_str()),
const_cast<char*>(delay.c_str()), nullptr };

pid_t pid = fork();
if (pid == 0)
{
int devnull = open("/dev/null", O_WRONLY);
if (devnull >= 0)
{
dup2(devnull, STDERR_FILENO);
close(devnull);
}
execvp("python3", args.data());
_exit(1);
}
else if (pid > 0)
{
waitpid(pid, nullptr, 0);
}
#endif
}
};

int main(int argc, char* argv[])
{
::testing::InitGoogleTest(&argc, argv);
Expand All @@ -520,5 +580,7 @@ int main(int argc, char* argv[])
}
}

::testing::UnitTest::GetInstance()->listeners().Append(new PolyScopeScreenshotListener());

return RUN_ALL_TESTS();
}
Loading
Loading