From 77c80ccadc4b8e976f4c5202aeda3afce95b863e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Thu, 16 Apr 2026 13:58:02 +0100 Subject: [PATCH 01/15] Adding a hello-world test to VRT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- vrt/CMakeLists.txt | 5 +++++ vrt/tests/CMakeLists.txt | 21 +++++++++++++++++++++ vrt/tests/hello_test.cpp | 6 ++++++ 3 files changed, 32 insertions(+) create mode 100644 vrt/tests/CMakeLists.txt create mode 100644 vrt/tests/hello_test.cpp diff --git a/vrt/CMakeLists.txt b/vrt/CMakeLists.txt index e0b1d230..63b30fa4 100644 --- a/vrt/CMakeLists.txt +++ b/vrt/CMakeLists.txt @@ -131,6 +131,11 @@ target_link_libraries(vrt PkgConfig::PC_ZMQ ) +# Testing +add_subdirectory(tests) + +# Installation + set_target_properties(vrt PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) install( diff --git a/vrt/tests/CMakeLists.txt b/vrt/tests/CMakeLists.txt new file mode 100644 index 00000000..00888034 --- /dev/null +++ b/vrt/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.17.0.zip +) +FetchContent_MakeAvailable(googletest) + +enable_testing() +add_executable( + hello_test + hello_test.cpp +) +target_link_libraries( + hello_test + PUBLIC + GTest::gtest_main + vrt::vrt +) + +include(GoogleTest) +gtest_discover_tests(hello_test) \ No newline at end of file diff --git a/vrt/tests/hello_test.cpp b/vrt/tests/hello_test.cpp new file mode 100644 index 00000000..d3508aa9 --- /dev/null +++ b/vrt/tests/hello_test.cpp @@ -0,0 +1,6 @@ +#include + +TEST(HelloTest, BasicAssertions) { + EXPECT_STRNE("hello", "world"); + EXPECT_EQ(7 * 6, 42); +} \ No newline at end of file From 2e5cfa9d72a44ff72ba5324f4b1e28254ade40e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Thu, 16 Apr 2026 14:03:55 +0100 Subject: [PATCH 02/15] Adding a simple workflow to run the VRT unit tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- .github/workflows/vrt-unit-test.yml | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/vrt-unit-test.yml diff --git a/.github/workflows/vrt-unit-test.yml b/.github/workflows/vrt-unit-test.yml new file mode 100644 index 00000000..cf2cd2b2 --- /dev/null +++ b/.github/workflows/vrt-unit-test.yml @@ -0,0 +1,55 @@ +# ################################################################################################## +# The MIT License (MIT) +# Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software +# and associated documentation files (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, publish, distribute, +# sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or +# substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +# NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# ################################################################################################## + +name: VRT unit testing + +on: + push: + branches: + - main + - dev + - feature/vrt-unit-testing + pull_request: + +jobs: + vrt_unit_tests: + runs-on: ubuntu-24.04 + permissions: { contents: read } + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Install dependencies + run: + - apt update + - apt upgrade -y + - apt install -y cmake pkg-config ninja-build \ + libxml2-dev libzmq3-dev libjsoncpp-dev zlib1g-dev \ + libsystemd-dev libinih-dev libcli11-dev + + - name: Build and run VRT unit tests + run: + - mkdir vrt/build + - cd vrt/build + - cmake -DVRT_INCLUDE_VRTD=1 -DVRTD_INCLUDE_LIBSLASH=1 .. + - make hello_test + - ./tests/hello_test + \ No newline at end of file From 4dd05fef97c2a316983dbd4ee21e33af1f1579ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Thu, 16 Apr 2026 14:06:44 +0100 Subject: [PATCH 03/15] Fixing the multi-line usage in the GitHub Actions workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- .github/workflows/vrt-unit-test.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/vrt-unit-test.yml b/.github/workflows/vrt-unit-test.yml index cf2cd2b2..47540942 100644 --- a/.github/workflows/vrt-unit-test.yml +++ b/.github/workflows/vrt-unit-test.yml @@ -38,18 +38,18 @@ jobs: uses: actions/checkout@v6 - name: Install dependencies - run: - - apt update - - apt upgrade -y - - apt install -y cmake pkg-config ninja-build \ - libxml2-dev libzmq3-dev libjsoncpp-dev zlib1g-dev \ - libsystemd-dev libinih-dev libcli11-dev + run: | + apt update + apt upgrade -y + apt install -y cmake pkg-config ninja-build \ + libxml2-dev libzmq3-dev libjsoncpp-dev zlib1g-dev \ + libsystemd-dev libinih-dev libcli11-dev - name: Build and run VRT unit tests - run: - - mkdir vrt/build - - cd vrt/build - - cmake -DVRT_INCLUDE_VRTD=1 -DVRTD_INCLUDE_LIBSLASH=1 .. - - make hello_test - - ./tests/hello_test + run: | + mkdir vrt/build + cd vrt/build + cmake -DVRT_INCLUDE_VRTD=1 -DVRTD_INCLUDE_LIBSLASH=1 .. + make hello_test + ./tests/hello_test \ No newline at end of file From 0040040cbb7911e5876590270fe39b1c231bae81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Thu, 16 Apr 2026 14:07:31 +0100 Subject: [PATCH 04/15] Using sudo to install packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- .github/workflows/vrt-unit-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vrt-unit-test.yml b/.github/workflows/vrt-unit-test.yml index 47540942..e908484e 100644 --- a/.github/workflows/vrt-unit-test.yml +++ b/.github/workflows/vrt-unit-test.yml @@ -39,9 +39,9 @@ jobs: - name: Install dependencies run: | - apt update - apt upgrade -y - apt install -y cmake pkg-config ninja-build \ + sudo apt update + sudo apt upgrade -y + sudo apt install -y cmake pkg-config ninja-build \ libxml2-dev libzmq3-dev libjsoncpp-dev zlib1g-dev \ libsystemd-dev libinih-dev libcli11-dev From b77214c308853ad0852364d8511e05771066e777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Thu, 16 Apr 2026 14:23:16 +0100 Subject: [PATCH 05/15] Also checking out submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- .github/workflows/vrt-unit-test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/vrt-unit-test.yml b/.github/workflows/vrt-unit-test.yml index e908484e..4e7dc50e 100644 --- a/.github/workflows/vrt-unit-test.yml +++ b/.github/workflows/vrt-unit-test.yml @@ -36,6 +36,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6 + with: + submodules: "true" - name: Install dependencies run: | From a5daf97bf827fa59f503dea752fb2da9e8b8e53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Mon, 20 Apr 2026 12:14:44 +0100 Subject: [PATCH 06/15] Generating test cases for units in VRT that don't need a VBIN file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- .github/workflows/vrt-unit-test.yml | 4 +- vrt/tests/CMakeLists.txt | 31 ++-- vrt/tests/filesystem_cache_test.cpp | 111 +++++++++++++ vrt/tests/kernel_test.cpp | 196 ++++++++++++++++++++++ vrt/tests/logger_test.cpp | 127 ++++++++++++++ vrt/tests/qdma_connection_test.cpp | 27 +++ vrt/tests/register_test.cpp | 48 ++++++ vrt/tests/test_helpers.hpp | 63 +++++++ vrt/tests/utilization_data_test.cpp | 57 +++++++ vrt/tests/utilization_parser_test.cpp | 143 ++++++++++++++++ vrt/tests/vrtbin_test.cpp | 56 +++++++ vrt/tests/xml_parser_test.cpp | 227 ++++++++++++++++++++++++++ 12 files changed, 1077 insertions(+), 13 deletions(-) create mode 100644 vrt/tests/filesystem_cache_test.cpp create mode 100644 vrt/tests/kernel_test.cpp create mode 100644 vrt/tests/logger_test.cpp create mode 100644 vrt/tests/qdma_connection_test.cpp create mode 100644 vrt/tests/register_test.cpp create mode 100644 vrt/tests/test_helpers.hpp create mode 100644 vrt/tests/utilization_data_test.cpp create mode 100644 vrt/tests/utilization_parser_test.cpp create mode 100644 vrt/tests/vrtbin_test.cpp create mode 100644 vrt/tests/xml_parser_test.cpp diff --git a/.github/workflows/vrt-unit-test.yml b/.github/workflows/vrt-unit-test.yml index 4e7dc50e..1700217b 100644 --- a/.github/workflows/vrt-unit-test.yml +++ b/.github/workflows/vrt-unit-test.yml @@ -52,6 +52,6 @@ jobs: mkdir vrt/build cd vrt/build cmake -DVRT_INCLUDE_VRTD=1 -DVRTD_INCLUDE_LIBSLASH=1 .. - make hello_test - ./tests/hello_test + make unit_tests -j$(nproc) + cd tests && ctest --output-on-failure \ No newline at end of file diff --git a/vrt/tests/CMakeLists.txt b/vrt/tests/CMakeLists.txt index 00888034..ee211c48 100644 --- a/vrt/tests/CMakeLists.txt +++ b/vrt/tests/CMakeLists.txt @@ -6,16 +6,25 @@ FetchContent_Declare( FetchContent_MakeAvailable(googletest) enable_testing() -add_executable( - hello_test - hello_test.cpp -) -target_link_libraries( - hello_test - PUBLIC - GTest::gtest_main - vrt::vrt -) include(GoogleTest) -gtest_discover_tests(hello_test) \ No newline at end of file + +add_custom_target(unit_tests) + +macro(add_vrt_test test_name test_source) + add_executable(${test_name} ${test_source}) + target_link_libraries(${test_name} PRIVATE GTest::gtest_main vrt::vrt) + gtest_discover_tests(${test_name}) + add_dependencies(unit_tests ${test_name}) +endmacro() + +add_vrt_test(hello_test hello_test.cpp) +add_vrt_test(register_test register_test.cpp) +add_vrt_test(qdma_connection_test qdma_connection_test.cpp) +add_vrt_test(logger_test logger_test.cpp) +add_vrt_test(utilization_data_test utilization_data_test.cpp) +add_vrt_test(filesystem_cache_test filesystem_cache_test.cpp) +add_vrt_test(xml_parser_test xml_parser_test.cpp) +add_vrt_test(utilization_parser_test utilization_parser_test.cpp) +add_vrt_test(kernel_test kernel_test.cpp) +add_vrt_test(vrtbin_test vrtbin_test.cpp) diff --git a/vrt/tests/filesystem_cache_test.cpp b/vrt/tests/filesystem_cache_test.cpp new file mode 100644 index 00000000..4381e5b4 --- /dev/null +++ b/vrt/tests/filesystem_cache_test.cpp @@ -0,0 +1,111 @@ +#include +#include + +#include +#include + +#include "test_helpers.hpp" + +class FilesystemCacheTest : public ::testing::Test { + protected: + std::filesystem::path tmpDir; + + ScopedEnv* envSlashCache = nullptr; + ScopedEnv* envXdgCache = nullptr; + ScopedEnv* envHome = nullptr; + ScopedEnv* envSlashRuntime = nullptr; + ScopedEnv* envXdgRuntime = nullptr; + + void SetUp() override { tmpDir = makeTempDir("fscache-test"); } + + void TearDown() override { + delete envSlashCache; + delete envXdgCache; + delete envHome; + delete envSlashRuntime; + delete envXdgRuntime; + std::filesystem::remove_all(tmpDir); + } + + void clearCacheEnvVars() { + envSlashCache = new ScopedEnv("SLASH_CACHE_PATH"); + envXdgCache = new ScopedEnv("XDG_CACHE_HOME"); + envHome = new ScopedEnv("HOME"); + } + + void clearRuntimeEnvVars() { + envSlashRuntime = new ScopedEnv("SLASH_RUNTIME_PATH"); + envXdgRuntime = new ScopedEnv("XDG_RUNTIME_DIR"); + } +}; + +TEST_F(FilesystemCacheTest, CachePathFromSlashCachePath) { + clearCacheEnvVars(); + std::string target = (tmpDir / "slash-cache").string(); + ScopedEnv env("SLASH_CACHE_PATH", target); + auto path = FilesystemCache::getCachePath(); + EXPECT_EQ(path, std::filesystem::path(target)); +} + +TEST_F(FilesystemCacheTest, CachePathFromXdgCacheHome) { + clearCacheEnvVars(); + std::string xdg = (tmpDir / "xdg-cache").string(); + ScopedEnv env("XDG_CACHE_HOME", xdg); + auto path = FilesystemCache::getCachePath(); + EXPECT_EQ(path, std::filesystem::path(xdg) / "SLASH" / "vrt"); +} + +TEST_F(FilesystemCacheTest, CachePathFromHome) { + clearCacheEnvVars(); + std::string home = (tmpDir / "home").string(); + ScopedEnv env("HOME", home); + auto path = FilesystemCache::getCachePath(); + EXPECT_EQ(path, std::filesystem::path(home) / ".cache" / "SLASH" / "vrt"); +} + +TEST_F(FilesystemCacheTest, CachePathFallback) { + clearCacheEnvVars(); + auto path = FilesystemCache::getCachePath(); + std::string expected = "/tmp/SLASH-cache-" + std::to_string(getuid()) + "/vrt"; + EXPECT_EQ(path, std::filesystem::path(expected)); +} + +TEST_F(FilesystemCacheTest, RuntimePathFromSlashRuntimePath) { + clearRuntimeEnvVars(); + std::string target = (tmpDir / "slash-runtime").string(); + ScopedEnv env("SLASH_RUNTIME_PATH", target); + auto path = FilesystemCache::getRuntimePath(); + EXPECT_EQ(path, std::filesystem::path(target)); +} + +TEST_F(FilesystemCacheTest, RuntimePathFromXdgRuntimeDir) { + clearRuntimeEnvVars(); + std::string xdg = (tmpDir / "xdg-runtime").string(); + ScopedEnv env("XDG_RUNTIME_DIR", xdg); + auto path = FilesystemCache::getRuntimePath(); + EXPECT_EQ(path, std::filesystem::path(xdg) / "SLASH" / "vrt"); +} + +TEST_F(FilesystemCacheTest, RuntimePathFallback) { + clearRuntimeEnvVars(); + ScopedEnv envHome("HOME"); + auto path = FilesystemCache::getRuntimePath(); + std::string expected = "/tmp/SLASH-run-" + std::to_string(getuid()) + "/vrt"; + EXPECT_EQ(path, std::filesystem::path(expected)); +} + +TEST_F(FilesystemCacheTest, CachePathCreatesDirectory) { + clearCacheEnvVars(); + std::string target = (tmpDir / "new-cache-dir").string(); + ScopedEnv env("SLASH_CACHE_PATH", target); + auto path = FilesystemCache::getCachePath(); + EXPECT_TRUE(std::filesystem::is_directory(path)); +} + +TEST_F(FilesystemCacheTest, RuntimePathCreatesDirectory) { + clearRuntimeEnvVars(); + std::string target = (tmpDir / "new-runtime-dir").string(); + ScopedEnv env("SLASH_RUNTIME_PATH", target); + auto path = FilesystemCache::getRuntimePath(); + EXPECT_TRUE(std::filesystem::is_directory(path)); +} diff --git a/vrt/tests/kernel_test.cpp b/vrt/tests/kernel_test.cpp new file mode 100644 index 00000000..c4dd32b2 --- /dev/null +++ b/vrt/tests/kernel_test.cpp @@ -0,0 +1,196 @@ +#include +#include +#include + +#include + +static vrt::FunctionalArg makeArg(uint32_t idx, const std::string& name, const std::string& type, + uint32_t offset, uint32_t range = 32, bool readable = false, + bool writable = true, const std::string& port = "") { + vrt::FunctionalArg a; + a.idx = idx; + a.name = name; + a.type = type; + a.offset = offset; + a.range = range; + a.readable = readable; + a.writable = writable; + a.port = port; + return a; +} + +static vrt::Kernel makeTestKernel(const std::vector& args = {}, + const std::string& name = "testKernel", + uint64_t baseAddr = 0x1000, uint64_t range = 0x100) { + std::vector regs; + return vrt::Kernel(name, baseAddr, range, regs, args); +} + +TEST(KernelConstructTest, FiveArgConstructor) { + auto k = makeTestKernel(); + EXPECT_EQ(k.getName(), "testKernel"); + EXPECT_EQ(k.getPhysAddr(), 0x1000u); +} + +TEST(KernelConstructTest, HasFunctionalArgsTrue) { + auto k = makeTestKernel({makeArg(0, "a", "int", 0x10)}); + EXPECT_TRUE(k.hasFunctionalArgs()); +} + +TEST(KernelConstructTest, HasFunctionalArgsFalse) { + auto k = makeTestKernel(); + EXPECT_FALSE(k.hasFunctionalArgs()); +} + +TEST(KernelConstructTest, FunctionalArgsSortedOnConstruction) { + auto k = makeTestKernel( + {makeArg(2, "c", "int", 0x20), makeArg(0, "a", "int", 0x10), makeArg(1, "b", "int", 0x18)}); + auto& args = k.getFunctionalArgs(); + ASSERT_EQ(args.size(), 3u); + EXPECT_EQ(args[0].idx, 0u); + EXPECT_EQ(args[1].idx, 1u); + EXPECT_EQ(args[2].idx, 2u); +} + +TEST(KernelConstructTest, SetAndGetFunctionalArgs) { + auto k = makeTestKernel(); + std::vector newArgs = {makeArg(0, "x", "int", 0x10)}; + k.setFunctionalArgs(newArgs); + EXPECT_TRUE(k.hasFunctionalArgs()); + EXPECT_EQ(k.getFunctionalArgs().size(), 1u); + EXPECT_EQ(k.getFunctionalArgs()[0].name, "x"); +} + +TEST(KernelArgLookupTest, SetArgByIdx) { + auto k = makeTestKernel({makeArg(0, "input", "scalar", 0x10, 32)}); + EXPECT_NO_THROW(k.setArg(0, 42)); +} + +TEST(KernelArgLookupTest, SetArgByName) { + auto k = makeTestKernel({makeArg(0, "input", "scalar", 0x10, 32)}); + EXPECT_NO_THROW(k.setArg("input", 42)); +} + +TEST(KernelArgLookupTest, SetArgByNameWithRSuffix) { + auto k = makeTestKernel({makeArg(0, "input_r", "buffer", 0x10, 64)}); + EXPECT_NO_THROW(k.setArg("input", static_cast(0xDEAD))); +} + +TEST(KernelArgLookupTest, SetArgEmptyNameThrows) { + auto k = makeTestKernel({makeArg(0, "input", "scalar", 0x10)}); + EXPECT_THROW(k.setArg("", 42), std::runtime_error); +} + +TEST(KernelArgLookupTest, SetArgNameNotFoundThrows) { + auto k = makeTestKernel({makeArg(0, "input", "scalar", 0x10)}); + EXPECT_THROW(k.setArg("nonexistent", 42), std::runtime_error); +} + +TEST(KernelArgLookupTest, SetArgIdxNotFoundThrows) { + auto k = makeTestKernel({makeArg(0, "input", "scalar", 0x10)}); + EXPECT_THROW(k.setArg(99, 42), std::runtime_error); +} + +TEST(KernelArgLookupTest, SetArgNegativeIndexThrows) { + auto k = makeTestKernel({makeArg(0, "input", "scalar", 0x10)}); + EXPECT_THROW(k.setArg(-1, 42), std::runtime_error); +} + +TEST(KernelArgLookupTest, SetArgNoMetadataThrows) { + auto k = makeTestKernel(); + EXPECT_THROW(k.setArg(0, 42), std::runtime_error); +} + +TEST(KernelArgValidationTest, EnsureSetArgValuesComplete) { + auto k = makeTestKernel( + {makeArg(0, "a", "scalar", 0x10, 32), makeArg(1, "b", "scalar", 0x14, 32)}); + k.setArg(0, 1); + k.setArg(1, 2); + // With no platform set (UNKNOWN), call() skips all branches — validation is + // only exercised inside platform-specific blocks, so this tests that setArg + // itself succeeds for complete argument sets. + EXPECT_NO_THROW(k.call()); +} + +TEST(KernelArgValidationTest, EnsureSetArgValuesMissingThrows) { + auto k = makeTestKernel( + {makeArg(0, "a", "scalar", 0x10, 32), makeArg(1, "b", "scalar", 0x14, 32)}); + k.setPlatform(vrt::Platform::HARDWARE); + k.setArg(0, 1); + EXPECT_THROW(k.call(), std::runtime_error); +} + +TEST(KernelArgValidationTest, ReadOnlyArgNotRequiredForLaunch) { + auto k = makeTestKernel({makeArg(0, "a", "scalar", 0x10, 32, true, true), + makeArg(1, "status", "scalar", 0x14, 32, true, false)}); + k.setPlatform(vrt::Platform::HARDWARE); + k.setArg(0, 1); + // status is read-only (writable=false), so only "a" needs to be set. + // call() should reach ensureSetArgValuesCompleteForLaunch, which skips + // read-only args, then try writeBatch which throws because no BAR is set. + // The key assertion: it does NOT throw about a missing "status" arg. + EXPECT_THROW(k.call(), std::runtime_error); + try { + k.call(); + } catch (const std::runtime_error& e) { + std::string msg = e.what(); + EXPECT_EQ(msg.find("status"), std::string::npos) << "Should not require read-only arg"; + EXPECT_NE(msg.find("BAR"), std::string::npos) << "Should fail at BAR access, not arg validation"; + } +} + +TEST(KernelMemoryConfigTest, PortMemoryConfigDDR) { + auto k = makeTestKernel({makeArg(0, "in", "buffer", 0x10, 64, false, true, "m_axi_gmem0")}); + k.setConnections({{"m_axi_gmem0", "DDR"}}); + auto cfg = k.portMemoryConfig("m_axi_gmem0"); + EXPECT_EQ(cfg.type, vrt::MemoryRangeType::DDR); + EXPECT_FALSE(cfg.hbmPort.has_value()); +} + +TEST(KernelMemoryConfigTest, PortMemoryConfigHBM) { + auto k = makeTestKernel({makeArg(0, "in", "buffer", 0x10, 64, false, true, "m_axi_gmem0")}); + k.setConnections({{"m_axi_gmem0", "HBM3"}}); + auto cfg = k.portMemoryConfig("m_axi_gmem0"); + EXPECT_EQ(cfg.type, vrt::MemoryRangeType::HBM); + ASSERT_TRUE(cfg.hbmPort.has_value()); + EXPECT_EQ(cfg.hbmPort.value(), 3u); +} + +TEST(KernelMemoryConfigTest, PortMemoryConfigHBMVnoc) { + auto k = makeTestKernel(); + k.setConnections({{"port0", "HBM"}}); + auto cfg = k.portMemoryConfig("port0"); + EXPECT_EQ(cfg.type, vrt::MemoryRangeType::HBM_VNOC); +} + +TEST(KernelMemoryConfigTest, PortMemoryConfigMEM) { + auto k = makeTestKernel(); + k.setConnections({{"port0", "MEM"}}); + auto cfg = k.portMemoryConfig("port0"); + EXPECT_EQ(cfg.type, vrt::MemoryRangeType::HBM_VNOC); +} + +TEST(KernelMemoryConfigTest, PortMemoryConfigUnknownTargetThrows) { + auto k = makeTestKernel(); + k.setConnections({{"port0", "INVALID"}}); + EXPECT_THROW(k.portMemoryConfig("port0"), std::runtime_error); +} + +TEST(KernelMemoryConfigTest, PortMemoryConfigNoConnectionThrows) { + auto k = makeTestKernel(); + k.setConnections({{"port0", "DDR"}}); + EXPECT_THROW(k.portMemoryConfig("nonexistent"), std::runtime_error); +} + +TEST(KernelMemoryConfigTest, ArgMemoryConfigByName) { + auto k = makeTestKernel({makeArg(0, "input", "buffer", 0x10, 64, false, true, "m_axi_gmem0")}); + k.setConnections({{"m_axi_gmem0", "DDR"}}); + auto cfg = k.argMemoryConfig("input"); + EXPECT_EQ(cfg.type, vrt::MemoryRangeType::DDR); +} + +TEST(KernelMemoryConfigTest, ArgMemoryConfigNoPortThrows) { + auto k = makeTestKernel({makeArg(0, "scalar_arg", "scalar", 0x10, 32, false, true, "")}); + k.setConnections({}); + EXPECT_THROW(k.argMemoryConfig("scalar_arg"), std::runtime_error); +} diff --git a/vrt/tests/logger_test.cpp b/vrt/tests/logger_test.cpp new file mode 100644 index 00000000..ecde2a82 --- /dev/null +++ b/vrt/tests/logger_test.cpp @@ -0,0 +1,127 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "test_helpers.hpp" + +class LoggerTest : public ::testing::Test { + protected: + std::filesystem::path tmpDir; + std::string logFile; + + void SetUp() override { + tmpDir = makeTempDir("logger-test"); + logFile = (tmpDir / "test.log").string(); + vrt::utils::Logger::setLogLevel(vrt::utils::LogLevel::DEBUG); + vrt::utils::Logger::setOutput(logFile); + } + + void TearDown() override { + vrt::utils::Logger::setLogLevel(vrt::utils::LogLevel::INFO); + std::filesystem::remove_all(tmpDir); + } + + std::string readLog() { + std::ifstream ifs(logFile); + return std::string((std::istreambuf_iterator(ifs)), + std::istreambuf_iterator()); + } +}; + +TEST_F(LoggerTest, GeneralPlaceholder) { + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "hello {}", "world"); + EXPECT_NE(readLog().find("hello world"), std::string::npos); +} + +TEST_F(LoggerTest, GeneralPlaceholderInt) { + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "value={}", 42); + EXPECT_NE(readLog().find("value=42"), std::string::npos); +} + +TEST_F(LoggerTest, HexPlaceholder) { + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "addr={x}", 255); + std::string log = readLog(); + EXPECT_NE(log.find("0xff"), std::string::npos); +} + +TEST_F(LoggerTest, BinaryPlaceholder) { + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "bits={b}", + static_cast(5)); + std::string log = readLog(); + EXPECT_NE(log.find("0b00000101"), std::string::npos); +} + +TEST_F(LoggerTest, OctalPlaceholder) { + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "oct={o}", 8); + std::string log = readLog(); + EXPECT_NE(log.find("0o"), std::string::npos); +} + +TEST_F(LoggerTest, MultiplePlaceholders) { + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "{} + {} = {}", 1, 2, 3); + EXPECT_NE(readLog().find("1 + 2 = 3"), std::string::npos); +} + +TEST_F(LoggerTest, NoPlaceholders) { + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "literal message"); + EXPECT_NE(readLog().find("literal message"), std::string::npos); +} + +TEST_F(LoggerTest, TooFewArgsThrows) { + EXPECT_THROW( + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "{} {}", "only_one"), + std::runtime_error); +} + +TEST_F(LoggerTest, TooManyArgsThrows) { + EXPECT_THROW(vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "{}", 1, 2), + std::runtime_error); +} + +TEST_F(LoggerTest, SetLogLevelFilters) { + vrt::utils::Logger::setLogLevel(vrt::utils::LogLevel::WARN); + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "should not appear"); + EXPECT_TRUE(readLog().empty()); +} + +TEST_F(LoggerTest, NoneBlocksAll) { + vrt::utils::Logger::setLogLevel(vrt::utils::LogLevel::NONE); + vrt::utils::Logger::log(vrt::utils::LogLevel::ERROR, "test", "blocked"); + vrt::utils::Logger::log(vrt::utils::LogLevel::WARN, "test", "blocked"); + EXPECT_TRUE(readLog().empty()); +} + +TEST_F(LoggerTest, WarnLevelPassesWarn) { + vrt::utils::Logger::setLogLevel(vrt::utils::LogLevel::WARN); + vrt::utils::Logger::log(vrt::utils::LogLevel::WARN, "test", "warn msg"); + EXPECT_NE(readLog().find("warn msg"), std::string::npos); +} + +TEST_F(LoggerTest, ErrorLevelBlockedByWarnThreshold) { + vrt::utils::Logger::setLogLevel(vrt::utils::LogLevel::WARN); + vrt::utils::Logger::log(vrt::utils::LogLevel::ERROR, "test", "error msg"); + EXPECT_EQ(readLog().find("error msg"), std::string::npos); +} + +TEST_F(LoggerTest, SetOutputToFile) { + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "file output"); + std::string log = readLog(); + EXPECT_FALSE(log.empty()); + EXPECT_NE(log.find("file output"), std::string::npos); +} + +TEST_F(LoggerTest, SetOutputInvalidPathFallsBack) { + EXPECT_NO_THROW(vrt::utils::Logger::setOutput("/nonexistent/path/log.txt")); +} + +TEST_F(LoggerTest, TimestampFormat) { + vrt::utils::Logger::log(vrt::utils::LogLevel::INFO, "test", "ts check"); + std::string log = readLog(); + std::regex tsPattern(R"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})"); + EXPECT_TRUE(std::regex_search(log, tsPattern)); +} diff --git a/vrt/tests/qdma_connection_test.cpp b/vrt/tests/qdma_connection_test.cpp new file mode 100644 index 00000000..b1ad2ca9 --- /dev/null +++ b/vrt/tests/qdma_connection_test.cpp @@ -0,0 +1,27 @@ +#include +#include + +TEST(QdmaConnectionTest, HostToDeviceDirection) { + vrt::QdmaConnection conn("myKernel", 0, "axis_port", "HostToDevice"); + EXPECT_EQ(conn.getDirection(), vrt::StreamDirection::HOST_TO_DEVICE); +} + +TEST(QdmaConnectionTest, DeviceToHostDirection) { + vrt::QdmaConnection conn("myKernel", 1, "axis_port", "DeviceToHost"); + EXPECT_EQ(conn.getDirection(), vrt::StreamDirection::DEVICE_TO_HOST); +} + +TEST(QdmaConnectionTest, GetKernel) { + vrt::QdmaConnection conn("testKernel", 3, "iface0", "HostToDevice"); + EXPECT_EQ(conn.getKernel(), "testKernel"); +} + +TEST(QdmaConnectionTest, GetQid) { + vrt::QdmaConnection conn("k", 42, "iface0", "HostToDevice"); + EXPECT_EQ(conn.getQid(), 42u); +} + +TEST(QdmaConnectionTest, GetInterface) { + vrt::QdmaConnection conn("k", 0, "my_interface", "DeviceToHost"); + EXPECT_EQ(conn.getInterface(), "my_interface"); +} diff --git a/vrt/tests/register_test.cpp b/vrt/tests/register_test.cpp new file mode 100644 index 00000000..d5b90069 --- /dev/null +++ b/vrt/tests/register_test.cpp @@ -0,0 +1,48 @@ +#include +#include + +TEST(RegisterTest, ParameterizedConstructor) { + vrt::Register reg("CTRL", 0x10, 32, "RW", "Control register"); + EXPECT_EQ(reg.getRegisterName(), "CTRL"); + EXPECT_EQ(reg.getOffset(), 0x10u); + EXPECT_EQ(reg.getWidth(), 32u); + EXPECT_EQ(reg.getRW(), "RW"); + EXPECT_EQ(reg.getDescription(), "Control register"); +} + +TEST(RegisterTest, DefaultConstructor) { + vrt::Register reg; + EXPECT_EQ(reg.getRegisterName(), ""); + EXPECT_EQ(reg.getRW(), ""); + EXPECT_EQ(reg.getDescription(), ""); +} + +TEST(RegisterTest, SetRegisterName) { + vrt::Register reg; + reg.setRegisterName("STATUS"); + EXPECT_EQ(reg.getRegisterName(), "STATUS"); +} + +TEST(RegisterTest, SetOffset) { + vrt::Register reg; + reg.setOffset(0x20); + EXPECT_EQ(reg.getOffset(), 0x20u); +} + +TEST(RegisterTest, SetWidth) { + vrt::Register reg; + reg.setWidth(64); + EXPECT_EQ(reg.getWidth(), 64u); +} + +TEST(RegisterTest, SetRW) { + vrt::Register reg; + reg.setRW("RO"); + EXPECT_EQ(reg.getRW(), "RO"); +} + +TEST(RegisterTest, SetDescription) { + vrt::Register reg; + reg.setDescription("Status register for monitoring"); + EXPECT_EQ(reg.getDescription(), "Status register for monitoring"); +} diff --git a/vrt/tests/test_helpers.hpp b/vrt/tests/test_helpers.hpp new file mode 100644 index 00000000..6a5e7769 --- /dev/null +++ b/vrt/tests/test_helpers.hpp @@ -0,0 +1,63 @@ +#ifndef VRT_TEST_HELPERS_HPP +#define VRT_TEST_HELPERS_HPP + +#include +#include +#include +#include +#include + +class ScopedEnv { + public: + explicit ScopedEnv(const char* name, std::optional value = std::nullopt) + : name_(name) { + const char* prev = std::getenv(name); + if (prev) { + oldValue_ = prev; + } + if (value) { + setenv(name, value->c_str(), 1); + } else { + unsetenv(name); + } + } + + ~ScopedEnv() { + if (oldValue_) { + setenv(name_.c_str(), oldValue_->c_str(), 1); + } else { + unsetenv(name_.c_str()); + } + } + + ScopedEnv(const ScopedEnv&) = delete; + ScopedEnv& operator=(const ScopedEnv&) = delete; + + private: + std::string name_; + std::optional oldValue_; +}; + +inline std::filesystem::path makeTempDir(const std::string& prefix) { + std::string tmpl = (std::filesystem::temp_directory_path() / (prefix + "-XXXXXX")).string(); + char* result = mkdtemp(tmpl.data()); + if (!result) { + throw std::runtime_error("Failed to create temp directory"); + } + return result; +} + +inline std::string writeTempFile(const std::filesystem::path& dir, const std::string& name, + const std::string& content) { + auto path = dir / name; + std::filesystem::create_directories(path.parent_path()); + std::ofstream ofs(path); + if (!ofs) { + throw std::runtime_error("Failed to create temp file: " + path.string()); + } + ofs << content; + ofs.close(); + return path.string(); +} + +#endif // VRT_TEST_HELPERS_HPP diff --git a/vrt/tests/utilization_data_test.cpp b/vrt/tests/utilization_data_test.cpp new file mode 100644 index 00000000..7d6e1f61 --- /dev/null +++ b/vrt/tests/utilization_data_test.cpp @@ -0,0 +1,57 @@ +#include +#include + +TEST(UtilizationDataTest, ResourceMetricsDefaults) { + vrt::ResourceMetrics m{}; + EXPECT_EQ(m.totalPplocs, 0u); + EXPECT_EQ(m.totalLuts, 0u); + EXPECT_EQ(m.lutram, 0u); + EXPECT_EQ(m.srl, 0u); + EXPECT_EQ(m.ff, 0u); + EXPECT_EQ(m.ramb36, 0u); + EXPECT_EQ(m.ramb18, 0u); + EXPECT_EQ(m.ramb, 0u); + EXPECT_EQ(m.uram, 0u); + EXPECT_EQ(m.dsp, 0u); +} + +TEST(UtilizationDataTest, OptionalFieldsDefaultToNullopt) { + vrt::ResourceMetrics m{}; + EXPECT_FALSE(m.totalLutsPct.has_value()); + EXPECT_FALSE(m.lutramPct.has_value()); + EXPECT_FALSE(m.srlPct.has_value()); + EXPECT_FALSE(m.ffPct.has_value()); + EXPECT_FALSE(m.ramb36Pct.has_value()); + EXPECT_FALSE(m.ramb18Pct.has_value()); + EXPECT_FALSE(m.uramPct.has_value()); + EXPECT_FALSE(m.dspPct.has_value()); +} + +TEST(UtilizationDataTest, ResourceMetricsAssignment) { + vrt::ResourceMetrics m{}; + m.totalLuts = 1000; + m.totalLutsPct = 5.2f; + EXPECT_EQ(m.totalLuts, 1000u); + ASSERT_TRUE(m.totalLutsPct.has_value()); + EXPECT_FLOAT_EQ(m.totalLutsPct.value(), 5.2f); +} + +TEST(UtilizationDataTest, UtilizationCellConstruction) { + vrt::UtilizationCell cell; + cell.instance = "k0"; + cell.module = "myKernel"; + cell.pr = "pblock_0"; + cell.metrics.totalLuts = 400; + EXPECT_EQ(cell.instance, "k0"); + EXPECT_EQ(cell.module, "myKernel"); + EXPECT_EQ(cell.metrics.totalLuts, 400u); +} + +TEST(UtilizationDataTest, UtilizationReportSlashPresent) { + vrt::UtilizationReport report; + report.slash.name = "slash"; + report.slash.totals.totalLuts = 500; + EXPECT_EQ(report.slash.name, "slash"); + EXPECT_EQ(report.slash.totals.totalLuts, 500u); + EXPECT_FALSE(report.serviceLayer.has_value()); +} diff --git a/vrt/tests/utilization_parser_test.cpp b/vrt/tests/utilization_parser_test.cpp new file mode 100644 index 00000000..5184dfb7 --- /dev/null +++ b/vrt/tests/utilization_parser_test.cpp @@ -0,0 +1,143 @@ +#include +#include + +#include + +#include "test_helpers.hpp" + +class UtilizationParserTest : public ::testing::Test { + protected: + std::filesystem::path tmpDir; + + void SetUp() override { tmpDir = makeTempDir("util-parser-test"); } + void TearDown() override { std::filesystem::remove_all(tmpDir); } + + std::string writeXml(const std::string& content) { + return writeTempFile(tmpDir, "utilization.xml", content); + } +}; + +TEST_F(UtilizationParserTest, ParseSlashBlock) { + auto path = writeXml(R"( + + + + +)"); + vrt::UtilizationParser parser(path); + parser.parse(); + auto& report = parser.getReport(); + EXPECT_EQ(report.slash.name, "slash"); + EXPECT_EQ(report.slash.totals.totalLuts, 1000u); + EXPECT_EQ(report.slash.totals.ff, 500u); + EXPECT_EQ(report.slash.totals.dsp, 10u); + EXPECT_EQ(report.slash.totals.ramb36, 5u); + EXPECT_EQ(report.slash.totals.ramb18, 3u); + EXPECT_EQ(report.slash.totals.uram, 2u); +} + +TEST_F(UtilizationParserTest, ParseSlashBlockWithKernels) { + auto path = writeXml(R"( + + + + + + + + + + +)"); + vrt::UtilizationParser parser(path); + parser.parse(); + auto& report = parser.getReport(); + ASSERT_TRUE(report.slash.subhierarchy.has_value()); + ASSERT_EQ(report.slash.subhierarchy->cells.size(), 1u); + EXPECT_EQ(report.slash.subhierarchy->cells[0].instance, "k0"); + EXPECT_EQ(report.slash.subhierarchy->cells[0].module, "myKernel"); + EXPECT_EQ(report.slash.subhierarchy->cells[0].metrics.totalLuts, 400u); + EXPECT_EQ(report.slash.subhierarchy->subhierarchySum.totalLuts, 400u); +} + +TEST_F(UtilizationParserTest, ParseSlashBlockWithSlashLogic) { + auto path = writeXml(R"( + + + + + + + + + + +)"); + vrt::UtilizationParser parser(path); + parser.parse(); + auto& sub = parser.getReport().slash.subhierarchy; + ASSERT_TRUE(sub.has_value()); + ASSERT_EQ(sub->slashLogic.size(), 1u); + EXPECT_EQ(sub->slashLogic[0].instance, "sl0"); + EXPECT_EQ(sub->slashLogicSum.totalLuts, 100u); +} + +TEST_F(UtilizationParserTest, ParseServiceLayer) { + auto path = writeXml(R"( + + + + + + + +)"); + vrt::UtilizationParser parser(path); + parser.parse(); + auto& report = parser.getReport(); + ASSERT_TRUE(report.serviceLayer.has_value()); + EXPECT_EQ(report.serviceLayer->name, "service_layer"); + EXPECT_EQ(report.serviceLayer->totals.totalLuts, 200u); + EXPECT_EQ(report.serviceLayer->totals.ff, 150u); +} + +TEST_F(UtilizationParserTest, ParseResourceMetricsPercentages) { + auto path = writeXml(R"( + + + + +)"); + vrt::UtilizationParser parser(path); + parser.parse(); + auto& m = parser.getReport().slash.totals; + ASSERT_TRUE(m.totalLutsPct.has_value()); + EXPECT_FLOAT_EQ(m.totalLutsPct.value(), 5.2f); + ASSERT_TRUE(m.ffPct.has_value()); + EXPECT_FLOAT_EQ(m.ffPct.value(), 3.1f); + EXPECT_FALSE(m.dspPct.has_value()); +} + +TEST_F(UtilizationParserTest, MissingSlashBlockThrows) { + auto path = writeXml(R"( +)"); + vrt::UtilizationParser parser(path); + EXPECT_THROW(parser.parse(), std::runtime_error); +} + +TEST_F(UtilizationParserTest, InvalidXmlThrows) { + auto path = writeTempFile(tmpDir, "bad.xml", "not valid xml <<<<"); + EXPECT_THROW(vrt::UtilizationParser parser(path), std::runtime_error); +} + +TEST_F(UtilizationParserTest, SlashBlockWithoutServiceLayer) { + auto path = writeXml(R"( + + + + +)"); + vrt::UtilizationParser parser(path); + parser.parse(); + EXPECT_FALSE(parser.getReport().serviceLayer.has_value()); +} diff --git a/vrt/tests/vrtbin_test.cpp b/vrt/tests/vrtbin_test.cpp new file mode 100644 index 00000000..e0ef1c42 --- /dev/null +++ b/vrt/tests/vrtbin_test.cpp @@ -0,0 +1,56 @@ +#include +#include + +#include +#include + +#include "test_helpers.hpp" + +class VrtbinHelperTest : public ::testing::Test { + protected: + std::filesystem::path tmpDir; + ScopedEnv* envSlashCache = nullptr; + + void SetUp() override { + tmpDir = makeTempDir("vrtbin-test"); + envSlashCache = new ScopedEnv("SLASH_CACHE_PATH", tmpDir.string()); + } + + void TearDown() override { + delete envSlashCache; + std::filesystem::remove_all(tmpDir); + } +}; + +TEST_F(VrtbinHelperTest, GetSystemMapPathFromBdf) { + auto path = vrt::Vrtbin::getSystemMapPathFromBdf("0000:01:00.0"); + EXPECT_NE(path.find("metadata_0000_01_00_0"), std::string::npos); + EXPECT_NE(path.find("system_map.xml"), std::string::npos); +} + +TEST_F(VrtbinHelperTest, GetUtilizationReportPathFromBdf) { + auto path = vrt::Vrtbin::getUtilizationReportPathFromBdf("0000:01:00.0"); + EXPECT_NE(path.find("metadata_0000_01_00_0"), std::string::npos); + EXPECT_NE(path.find("report_utilization.xml"), std::string::npos); +} + +TEST_F(VrtbinHelperTest, SanitizeAlnum) { + auto path = vrt::Vrtbin::getSystemMapPathFromBdf("abc123"); + EXPECT_NE(path.find("metadata_abc123"), std::string::npos); +} + +TEST_F(VrtbinHelperTest, SanitizeSpecialChars) { + auto path = vrt::Vrtbin::getSystemMapPathFromBdf("0000:01:00.0"); + EXPECT_NE(path.find("metadata_0000_01_00_0"), std::string::npos); + EXPECT_EQ(path.find(":"), std::string::npos); +} + +TEST_F(VrtbinHelperTest, SanitizeEmpty) { + auto path = vrt::Vrtbin::getSystemMapPathFromBdf(""); + EXPECT_NE(path.find("metadata_default"), std::string::npos); +} + +TEST_F(VrtbinHelperTest, PathStartsWithCacheDir) { + auto path = vrt::Vrtbin::getSystemMapPathFromBdf("test"); + EXPECT_EQ(path.rfind(tmpDir.string(), 0), 0u); +} diff --git a/vrt/tests/xml_parser_test.cpp b/vrt/tests/xml_parser_test.cpp new file mode 100644 index 00000000..1f889b29 --- /dev/null +++ b/vrt/tests/xml_parser_test.cpp @@ -0,0 +1,227 @@ +#include +#include +#include + +#include + +#include "test_helpers.hpp" + +class XMLParserTest : public ::testing::Test { + protected: + std::filesystem::path tmpDir; + + void SetUp() override { tmpDir = makeTempDir("xml-parser-test"); } + void TearDown() override { std::filesystem::remove_all(tmpDir); } + + std::string writeXml(const std::string& content) { + return writeTempFile(tmpDir, "system_map.xml", content); + } +}; + +TEST_F(XMLParserTest, ParseSingleKernel) { + auto path = writeXml(R"( + + + vadd + 0x1000 + 0x100 + + 300000000 + Hardware +)"); + vrt::XMLParser parser(path); + parser.parseXML(); + auto kernels = parser.getKernels(); + ASSERT_EQ(kernels.count("vadd"), 1u); + EXPECT_EQ(kernels["vadd"].getName(), "vadd"); + EXPECT_EQ(kernels["vadd"].getPhysAddr(), 0x1000u); +} + +TEST_F(XMLParserTest, ParseKernelRegisters) { + auto path = writeXml(R"( + + + k + 0x0 + 0x100 + + + Hardware +)"); + vrt::XMLParser parser(path); + parser.parseXML(); + auto kernels = parser.getKernels(); + ASSERT_EQ(kernels.count("k"), 1u); +} + +TEST_F(XMLParserTest, ParseKernelFunctionalArgs) { + auto path = writeXml(R"( + + + k + 0x0 + 0x100 + + + + + Hardware +)"); + vrt::XMLParser parser(path); + parser.parseXML(); + auto kernels = parser.getKernels(); + ASSERT_EQ(kernels.count("k"), 1u); + auto& args = kernels["k"].getFunctionalArgs(); + ASSERT_EQ(args.size(), 1u); + EXPECT_EQ(args[0].idx, 0u); + EXPECT_EQ(args[0].name, "input"); + EXPECT_EQ(args[0].type, "buffer"); + EXPECT_EQ(args[0].offset, 0x10u); + EXPECT_EQ(args[0].range, 64u); + EXPECT_FALSE(args[0].readable); + EXPECT_TRUE(args[0].writable); + EXPECT_EQ(args[0].port, "m_axi_gmem0"); +} + +TEST_F(XMLParserTest, FunctionalArgsSortedByIdx) { + auto path = writeXml(R"( + + + k + 0x0 + 0x100 + + + + + + + Hardware +)"); + vrt::XMLParser parser(path); + parser.parseXML(); + auto kernels = parser.getKernels(); + auto& args = kernels["k"].getFunctionalArgs(); + ASSERT_EQ(args.size(), 3u); + EXPECT_EQ(args[0].idx, 0u); + EXPECT_EQ(args[1].idx, 1u); + EXPECT_EQ(args[2].idx, 2u); +} + +TEST_F(XMLParserTest, ParseClockFrequency) { + auto path = writeXml(R"( + + 250000000 + Hardware +)"); + vrt::XMLParser parser(path); + parser.parseXML(); + EXPECT_EQ(parser.getClockFrequency(), 250000000u); +} + +TEST_F(XMLParserTest, ParsePlatformHardware) { + auto path = writeXml(R"( +Hardware)"); + vrt::XMLParser parser(path); + parser.parseXML(); + EXPECT_EQ(parser.getPlatform(), vrt::Platform::HARDWARE); +} + +TEST_F(XMLParserTest, ParsePlatformEmulation) { + auto path = writeXml(R"( +Emulation)"); + vrt::XMLParser parser(path); + parser.parseXML(); + EXPECT_EQ(parser.getPlatform(), vrt::Platform::EMULATION); +} + +TEST_F(XMLParserTest, ParsePlatformSimulation) { + auto path = writeXml(R"( +Simulation)"); + vrt::XMLParser parser(path); + parser.parseXML(); + EXPECT_EQ(parser.getPlatform(), vrt::Platform::SIMULATION); +} + +TEST_F(XMLParserTest, ParsePlatformUnknownThrows) { + auto path = writeXml(R"( +SomethingWeird)"); + vrt::XMLParser parser(path); + EXPECT_THROW(parser.parseXML(), std::runtime_error); +} + +TEST_F(XMLParserTest, ParseQdmaConnections) { + auto path = writeXml(R"( + + Hardware + + myKernel + axis_port + HostToDevice + 3 + +)"); + vrt::XMLParser parser(path); + parser.parseXML(); + auto conns = parser.getQdmaConnections(); + ASSERT_EQ(conns.size(), 1u); + EXPECT_EQ(conns[0].getKernel(), "myKernel"); + EXPECT_EQ(conns[0].getInterface(), "axis_port"); + EXPECT_EQ(conns[0].getDirection(), vrt::StreamDirection::HOST_TO_DEVICE); + EXPECT_EQ(conns[0].getQid(), 3u); +} + +TEST_F(XMLParserTest, ParseMultipleKernels) { + auto path = writeXml(R"( + + + k1 + 0x1000 + 0x100 + + + k2 + 0x2000 + 0x200 + + Hardware +)"); + vrt::XMLParser parser(path); + parser.parseXML(); + auto kernels = parser.getKernels(); + EXPECT_EQ(kernels.size(), 2u); + EXPECT_EQ(kernels.count("k1"), 1u); + EXPECT_EQ(kernels.count("k2"), 1u); +} + +TEST_F(XMLParserTest, InvalidXmlFileThrows) { + auto path = writeTempFile(tmpDir, "bad.xml", "not valid xml <<<<"); + EXPECT_THROW(vrt::XMLParser parser(path), std::runtime_error); +} + +TEST_F(XMLParserTest, EmptySystemMap) { + auto path = writeXml(R"( +)"); + vrt::XMLParser parser(path); + parser.parseXML(); + EXPECT_TRUE(parser.getKernels().empty()); + EXPECT_TRUE(parser.getQdmaConnections().empty()); +} + +TEST_F(XMLParserTest, ParseKernelConnections) { + auto path = writeXml(R"( + + + k + 0x0 + 0x100 + + + + Hardware +)"); + vrt::XMLParser parser(path); + parser.parseXML(); + auto kernels = parser.getKernels(); + ASSERT_EQ(kernels.count("k"), 1u); +} From 8c1c6cb0f80df2cc197adaa0514eeb055ef594f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Mon, 20 Apr 2026 13:24:24 +0100 Subject: [PATCH 07/15] Adding code coverage to the VRT unit tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- .github/workflows/vrt-unit-test.yml | 22 ++++++++++++++++++++-- vrt/CMakeLists.txt | 9 +++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/.github/workflows/vrt-unit-test.yml b/.github/workflows/vrt-unit-test.yml index 1700217b..cb0318bc 100644 --- a/.github/workflows/vrt-unit-test.yml +++ b/.github/workflows/vrt-unit-test.yml @@ -45,13 +45,31 @@ jobs: sudo apt upgrade -y sudo apt install -y cmake pkg-config ninja-build \ libxml2-dev libzmq3-dev libjsoncpp-dev zlib1g-dev \ - libsystemd-dev libinih-dev libcli11-dev + libsystemd-dev libinih-dev libcli11-dev lcov - name: Build and run VRT unit tests run: | mkdir vrt/build cd vrt/build - cmake -DVRT_INCLUDE_VRTD=1 -DVRTD_INCLUDE_LIBSLASH=1 .. + cmake -DVRT_INCLUDE_VRTD=1 -DVRTD_INCLUDE_LIBSLASH=1 -DENABLE_COVERAGE=1 .. make unit_tests -j$(nproc) cd tests && ctest --output-on-failure + + - name: Generate coverage report + run: | + cd vrt/build + lcov --capture --directory . --output-file coverage.info \ + --ignore-errors mismatch + lcov --remove coverage.info \ + '/usr/*' '*/build/_deps/*' '*/vrtd/*' '*/tests/*' \ + --output-file coverage.filtered.info \ + --ignore-errors unused + genhtml coverage.filtered.info --output-directory coverage-report + lcov --list coverage.filtered.info + + - name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: vrt-coverage-report + path: vrt/build/coverage-report/ \ No newline at end of file diff --git a/vrt/CMakeLists.txt b/vrt/CMakeLists.txt index 63b30fa4..3ca98321 100644 --- a/vrt/CMakeLists.txt +++ b/vrt/CMakeLists.txt @@ -55,6 +55,15 @@ if(ENABLE_SANITIZERS) add_link_options(-fsanitize=address,undefined) endif() +option(ENABLE_COVERAGE "Build with gcov coverage instrumentation" OFF) +if(ENABLE_COVERAGE) + if(ENABLE_SANITIZERS) + message(FATAL_ERROR "ENABLE_COVERAGE and ENABLE_SANITIZERS cannot be used together") + endif() + add_compile_options(--coverage -fno-inline) + add_link_options(--coverage) +endif() + # Generate the header configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/vrt_version.hpp.in" From a4936a65e984629982e5d81deecc9ddda73e5aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Mon, 20 Apr 2026 15:43:42 +0100 Subject: [PATCH 08/15] Adding unit tests for emulation and simulation using a stub VBIN MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- .github/workflows/vrt-unit-test.yml | 2 +- .gitignore | 1 + requirements.txt | 1 + vrt/include/vrt/streaming_buffer.hpp | 6 +- vrt/tests/CMakeLists.txt | 64 +++++++ vrt/tests/device_emu_test.cpp | 158 ++++++++++++++++++ vrt/tests/device_sim_test.cpp | 86 ++++++++++ .../fixtures/stub_vbin/emu_manifest.json | 30 ++++ .../fixtures/stub_vbin/system_map_emu.xml | 45 +++++ .../fixtures/stub_vbin/system_map_sim.xml | 45 +++++ .../fixtures/stub_vbin/vrt_stub_server.py | 86 ++++++++++ vrt/tests/vrtbin_integration_test.cpp | 93 +++++++++++ 12 files changed, 613 insertions(+), 4 deletions(-) create mode 100644 requirements.txt create mode 100644 vrt/tests/device_emu_test.cpp create mode 100644 vrt/tests/device_sim_test.cpp create mode 100644 vrt/tests/fixtures/stub_vbin/emu_manifest.json create mode 100644 vrt/tests/fixtures/stub_vbin/system_map_emu.xml create mode 100644 vrt/tests/fixtures/stub_vbin/system_map_sim.xml create mode 100644 vrt/tests/fixtures/stub_vbin/vrt_stub_server.py create mode 100644 vrt/tests/vrtbin_integration_test.cpp diff --git a/.github/workflows/vrt-unit-test.yml b/.github/workflows/vrt-unit-test.yml index cb0318bc..4a3e0a17 100644 --- a/.github/workflows/vrt-unit-test.yml +++ b/.github/workflows/vrt-unit-test.yml @@ -45,7 +45,7 @@ jobs: sudo apt upgrade -y sudo apt install -y cmake pkg-config ninja-build \ libxml2-dev libzmq3-dev libjsoncpp-dev zlib1g-dev \ - libsystemd-dev libinih-dev libcli11-dev lcov + libsystemd-dev libinih-dev libcli11-dev lcov python3-zmq - name: Build and run VRT unit tests run: | diff --git a/.gitignore b/.gitignore index 8e773bdb..fdd5a77d 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ Thumbs.db __pycache__/ *.pyc +.venv/ # Build files for package /pbuild/ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..02ec117e --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pyzmq diff --git a/vrt/include/vrt/streaming_buffer.hpp b/vrt/include/vrt/streaming_buffer.hpp index d0255b5a..a37525de 100644 --- a/vrt/include/vrt/streaming_buffer.hpp +++ b/vrt/include/vrt/streaming_buffer.hpp @@ -23,7 +23,7 @@ #include -#include "api/device.hpp" +#include "device.hpp" #include "qdma/qdma_connection.hpp" #include "qdma/qdma_intf.hpp" #include "utils/platform.hpp" @@ -110,7 +110,7 @@ template StreamingBuffer::StreamingBuffer(Device device, Kernel kernel, const std::string& portName, size_t size) : device(device), size(size), kernel(kernel), portName(portName) { - std::vector qdmaConnections = device.getQdmaConnections(); + std::vector qdmaConnections = device.getHandle()->getQdmaConnections(); bool gotQdma = false; for (const auto& con : qdmaConnections) { if (con.getKernel() == kernel.getName() && portName == con.getInterface()) { @@ -162,7 +162,7 @@ template void StreamingBuffer::sync() { Platform platform = device.getPlatform(); if (platform == Platform::EMULATION) { - ZmqServer* server = device.getHandle()->getZmqServer(); + auto server = device.getHandle()->getZmqServer(); if (syncType == StreamDirection::HOST_TO_DEVICE) { std::vector sendData; std::size_t dataSize = size * sizeof(T); diff --git a/vrt/tests/CMakeLists.txt b/vrt/tests/CMakeLists.txt index ee211c48..710fc03b 100644 --- a/vrt/tests/CMakeLists.txt +++ b/vrt/tests/CMakeLists.txt @@ -28,3 +28,67 @@ add_vrt_test(xml_parser_test xml_parser_test.cpp) add_vrt_test(utilization_parser_test utilization_parser_test.cpp) add_vrt_test(kernel_test kernel_test.cpp) add_vrt_test(vrtbin_test vrtbin_test.cpp) + +# --- Stub VBIN generation --- +set(STUB_VBIN_DIR ${CMAKE_CURRENT_SOURCE_DIR}/fixtures/stub_vbin) + +set(STUB_EMU_VBIN ${CMAKE_CURRENT_BINARY_DIR}/stub_emu.vbin) +add_custom_command( + OUTPUT ${STUB_EMU_VBIN} + COMMAND ${CMAKE_COMMAND} -E rm -rf ${CMAKE_CURRENT_BINARY_DIR}/stub_emu_staging + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/stub_emu_staging + COMMAND ${CMAKE_COMMAND} -E copy + ${STUB_VBIN_DIR}/system_map_emu.xml + ${CMAKE_CURRENT_BINARY_DIR}/stub_emu_staging/system_map.xml + COMMAND ${CMAKE_COMMAND} -E copy + ${STUB_VBIN_DIR}/emu_manifest.json + ${CMAKE_CURRENT_BINARY_DIR}/stub_emu_staging/emu_manifest.json + COMMAND ${CMAKE_COMMAND} -E copy + ${STUB_VBIN_DIR}/vrt_stub_server.py + ${CMAKE_CURRENT_BINARY_DIR}/stub_emu_staging/vpp_emu + COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/stub_emu_staging/vpp_emu + COMMAND tar cf ${STUB_EMU_VBIN} + -C ${CMAKE_CURRENT_BINARY_DIR}/stub_emu_staging . + DEPENDS + ${STUB_VBIN_DIR}/system_map_emu.xml + ${STUB_VBIN_DIR}/emu_manifest.json + ${STUB_VBIN_DIR}/vrt_stub_server.py + COMMENT "Creating emulation stub VBIN" +) + +set(STUB_SIM_VBIN ${CMAKE_CURRENT_BINARY_DIR}/stub_sim.vbin) +add_custom_command( + OUTPUT ${STUB_SIM_VBIN} + COMMAND ${CMAKE_COMMAND} -E rm -rf ${CMAKE_CURRENT_BINARY_DIR}/stub_sim_staging + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/stub_sim_staging + COMMAND ${CMAKE_COMMAND} -E copy + ${STUB_VBIN_DIR}/system_map_sim.xml + ${CMAKE_CURRENT_BINARY_DIR}/stub_sim_staging/system_map.xml + COMMAND ${CMAKE_COMMAND} -E copy + ${STUB_VBIN_DIR}/vrt_stub_server.py + ${CMAKE_CURRENT_BINARY_DIR}/stub_sim_staging/vpp_sim + COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/stub_sim_staging/vpp_sim + COMMAND tar cf ${STUB_SIM_VBIN} + -C ${CMAKE_CURRENT_BINARY_DIR}/stub_sim_staging . + DEPENDS + ${STUB_VBIN_DIR}/system_map_sim.xml + ${STUB_VBIN_DIR}/vrt_stub_server.py + COMMENT "Creating simulation stub VBIN" +) + +add_custom_target(stub_vbins DEPENDS ${STUB_EMU_VBIN} ${STUB_SIM_VBIN}) + +macro(add_vrt_vbin_test test_name test_source) + add_executable(${test_name} ${test_source}) + target_link_libraries(${test_name} PRIVATE GTest::gtest_main vrt::vrt) + target_compile_definitions(${test_name} PRIVATE + STUB_EMU_VBIN_PATH="${STUB_EMU_VBIN}" + STUB_SIM_VBIN_PATH="${STUB_SIM_VBIN}") + add_dependencies(${test_name} stub_vbins) + gtest_discover_tests(${test_name}) + add_dependencies(unit_tests ${test_name}) +endmacro() + +add_vrt_vbin_test(vrtbin_integration_test vrtbin_integration_test.cpp) +add_vrt_vbin_test(device_emu_test device_emu_test.cpp) +add_vrt_vbin_test(device_sim_test device_sim_test.cpp) diff --git a/vrt/tests/device_emu_test.cpp b/vrt/tests/device_emu_test.cpp new file mode 100644 index 00000000..4fddc04e --- /dev/null +++ b/vrt/tests/device_emu_test.cpp @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "test_helpers.hpp" + +class DeviceEmuTest : public ::testing::Test { + protected: + std::filesystem::path tmpDir; + ScopedEnv* envCache = nullptr; + vrt::Device device; + + void SetUp() override { + tmpDir = makeTempDir("device-emu-test"); + envCache = new ScopedEnv("SLASH_CACHE_PATH", tmpDir.string()); + device = vrt::Device("0000:00:00", STUB_EMU_VBIN_PATH, false); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + + void TearDown() override { + device.cleanup(); + delete envCache; + std::filesystem::remove_all(tmpDir); + } +}; + +TEST_F(DeviceEmuTest, Construction) { + SUCCEED(); +} + +TEST_F(DeviceEmuTest, GetPlatform) { + EXPECT_EQ(device.getPlatform(), vrt::Platform::EMULATION); +} + +TEST_F(DeviceEmuTest, GetFrequency) { + EXPECT_EQ(device.getFrequency(), 0u); +} + +TEST_F(DeviceEmuTest, GetKernelVadd) { + auto kernel = device.getKernel("vadd"); + EXPECT_EQ(kernel.getName(), "vadd"); + EXPECT_EQ(kernel.getPhysAddr(), 0x10000u); +} + +TEST_F(DeviceEmuTest, GetKernelPassthrough) { + auto kernel = device.getKernel("passthrough"); + EXPECT_EQ(kernel.getName(), "passthrough"); + EXPECT_EQ(kernel.getPhysAddr(), 0x20000u); +} + +TEST_F(DeviceEmuTest, GetKernelUnknownThrows) { + EXPECT_THROW(device.getKernel("nonexistent"), std::runtime_error); +} + +TEST_F(DeviceEmuTest, GetQdmaConnections) { + auto conns = device.getHandle()->getQdmaConnections(); + ASSERT_EQ(conns.size(), 2u); + EXPECT_EQ(conns[0].getKernel(), "vadd"); + EXPECT_EQ(conns[0].getInterface(), "axis_in"); + EXPECT_EQ(conns[0].getDirection(), vrt::StreamDirection::HOST_TO_DEVICE); + EXPECT_EQ(conns[0].getQid(), 0u); + EXPECT_EQ(conns[1].getInterface(), "axis_out"); + EXPECT_EQ(conns[1].getDirection(), vrt::StreamDirection::DEVICE_TO_HOST); + EXPECT_EQ(conns[1].getQid(), 1u); +} + +TEST_F(DeviceEmuTest, KernelSetArgAndCall) { + auto kernel = device.getKernel("vadd"); + kernel.setArg(0, static_cast(0x1000)); + kernel.setArg(1, static_cast(0x2000)); + kernel.setArg(2, static_cast(0x3000)); + kernel.setArg(3, static_cast(64)); + EXPECT_NO_THROW(kernel.call()); +} + +TEST_F(DeviceEmuTest, KernelCallByName) { + auto kernel = device.getKernel("vadd"); + kernel.setArg("in1", static_cast(0x1000)); + kernel.setArg("in2", static_cast(0x2000)); + kernel.setArg("out", static_cast(0x3000)); + kernel.setArg("size", static_cast(64)); + EXPECT_NO_THROW(kernel.call()); +} + +TEST_F(DeviceEmuTest, DISABLED_KernelStartAndWait) { + auto kernel = device.getKernel("passthrough"); + kernel.setArg(0, static_cast(42)); + EXPECT_NO_THROW(kernel.start()); + EXPECT_NO_THROW(kernel.wait()); +} + +TEST_F(DeviceEmuTest, KernelRead) { + auto kernel = device.getKernel("vadd"); + uint32_t val = kernel.read(0x00); + EXPECT_EQ(val, 0u); +} + +TEST_F(DeviceEmuTest, BufferDDRConstruction) { + EXPECT_NO_THROW({ + vrt::Buffer buf(device, 64, vrt::MemoryRangeType::DDR); + }); +} + +TEST_F(DeviceEmuTest, BufferHBMWithPort) { + EXPECT_NO_THROW({ + vrt::Buffer buf(device, 64, vrt::MemoryRangeType::HBM, 0); + }); +} + +TEST_F(DeviceEmuTest, BufferHBMVnoc) { + EXPECT_NO_THROW({ + vrt::Buffer buf(device, 64, vrt::MemoryRangeType::HBM_VNOC); + }); +} + +TEST_F(DeviceEmuTest, BufferSyncRoundTrip) { + vrt::Buffer buf(device, 4, vrt::MemoryRangeType::DDR); + buf[0] = 10; + buf[1] = 20; + buf[2] = 30; + buf[3] = 40; + buf.sync(vrt::SyncType::HOST_TO_DEVICE); + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + buf.sync(vrt::SyncType::DEVICE_TO_HOST); + EXPECT_EQ(buf[0], 10); + EXPECT_EQ(buf[1], 20); + EXPECT_EQ(buf[2], 30); + EXPECT_EQ(buf[3], 40); +} + +TEST_F(DeviceEmuTest, StreamingBufferH2D) { + auto kernel = device.getKernel("vadd"); + vrt::StreamingBuffer sbuf(device, kernel, "axis_in", 16); + sbuf[0] = 42; + EXPECT_NO_THROW(sbuf.sync()); +} + +TEST_F(DeviceEmuTest, StreamingBufferD2H) { + auto kernel = device.getKernel("vadd"); + vrt::StreamingBuffer sbuf(device, kernel, "axis_out", 16); + EXPECT_NO_THROW(sbuf.sync()); +} + +TEST_F(DeviceEmuTest, StreamingBufferWrongPortThrows) { + auto kernel = device.getKernel("vadd"); + EXPECT_THROW( + vrt::StreamingBuffer(device, kernel, "nonexistent_port", 16), + std::runtime_error); +} diff --git a/vrt/tests/device_sim_test.cpp b/vrt/tests/device_sim_test.cpp new file mode 100644 index 00000000..ec6c3bff --- /dev/null +++ b/vrt/tests/device_sim_test.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "test_helpers.hpp" + +class DeviceSimTest : public ::testing::Test { + protected: + std::filesystem::path tmpDir; + ScopedEnv* envCache = nullptr; + vrt::Device device; + + void SetUp() override { + tmpDir = makeTempDir("device-sim-test"); + envCache = new ScopedEnv("SLASH_CACHE_PATH", tmpDir.string()); + device = vrt::Device("0000:00:00", STUB_SIM_VBIN_PATH, false); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + + void TearDown() override { + device.cleanup(); + delete envCache; + std::filesystem::remove_all(tmpDir); + } +}; + +TEST_F(DeviceSimTest, Construction) { + SUCCEED(); +} + +TEST_F(DeviceSimTest, GetPlatform) { + EXPECT_EQ(device.getPlatform(), vrt::Platform::SIMULATION); +} + +TEST_F(DeviceSimTest, GetKernelVadd) { + auto kernel = device.getKernel("vadd"); + EXPECT_EQ(kernel.getName(), "vadd"); + EXPECT_EQ(kernel.getPhysAddr(), 0x10000u); +} + +TEST_F(DeviceSimTest, KernelWrite) { + auto kernel = device.getKernel("vadd"); + EXPECT_NO_THROW(kernel.write(0x10, 0xDEAD)); +} + +TEST_F(DeviceSimTest, KernelRead) { + auto kernel = device.getKernel("vadd"); + uint32_t val = kernel.read(0x10); + EXPECT_EQ(val, 0u); +} + +TEST_F(DeviceSimTest, BufferConstruction) { + EXPECT_NO_THROW({ + vrt::Buffer buf(device, 64, vrt::MemoryRangeType::DDR); + }); +} + +TEST_F(DeviceSimTest, BufferSyncRoundTrip) { + vrt::Buffer buf(device, 4, vrt::MemoryRangeType::DDR); + buf[0] = 100; + buf[1] = 200; + buf[2] = 300; + buf[3] = 400; + buf.sync(vrt::SyncType::HOST_TO_DEVICE); + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + buf.sync(vrt::SyncType::DEVICE_TO_HOST); + EXPECT_EQ(buf[0], 100); + EXPECT_EQ(buf[1], 200); + EXPECT_EQ(buf[2], 300); + EXPECT_EQ(buf[3], 400); +} + +TEST_F(DeviceSimTest, StreamingBufferThrowsNotImplemented) { + auto kernel = device.getKernel("vadd"); + vrt::StreamingBuffer sbuf(device, kernel, "axis_in", 16); + EXPECT_THROW(sbuf.sync(), std::runtime_error); +} diff --git a/vrt/tests/fixtures/stub_vbin/emu_manifest.json b/vrt/tests/fixtures/stub_vbin/emu_manifest.json new file mode 100644 index 00000000..2fd5b5c4 --- /dev/null +++ b/vrt/tests/fixtures/stub_vbin/emu_manifest.json @@ -0,0 +1,30 @@ +{ + "kernels": [ + { + "instance": "vadd", + "call_args": [ + {"arg": "arg0", "kind": "buffer"}, + {"arg": "arg1", "kind": "buffer"}, + {"arg": "arg2", "kind": "buffer"}, + {"arg": "arg3", "kind": "scalar"} + ] + }, + { + "instance": "passthrough", + "call_args": [ + {"arg": "arg0", "kind": "scalar"} + ] + } + ], + "fetch": { + "scalar": [ + { + "function": "vadd", + "arg": "size", + "source": { + "register_offset": 40 + } + } + ] + } +} diff --git a/vrt/tests/fixtures/stub_vbin/system_map_emu.xml b/vrt/tests/fixtures/stub_vbin/system_map_emu.xml new file mode 100644 index 00000000..95128896 --- /dev/null +++ b/vrt/tests/fixtures/stub_vbin/system_map_emu.xml @@ -0,0 +1,45 @@ + + + Emulation + 100000000 + + + vadd + 0x10000 + 0x1000 + + + + + + + + + + + + + + passthrough + 0x20000 + 0x1000 + + + + + + + + vadd + axis_in + HostToDevice + 0 + + + + vadd + axis_out + DeviceToHost + 1 + + diff --git a/vrt/tests/fixtures/stub_vbin/system_map_sim.xml b/vrt/tests/fixtures/stub_vbin/system_map_sim.xml new file mode 100644 index 00000000..8489c1a3 --- /dev/null +++ b/vrt/tests/fixtures/stub_vbin/system_map_sim.xml @@ -0,0 +1,45 @@ + + + Simulation + 100000000 + + + vadd + 0x10000 + 0x1000 + + + + + + + + + + + + + + passthrough + 0x20000 + 0x1000 + + + + + + + + vadd + axis_in + HostToDevice + 0 + + + + vadd + axis_out + DeviceToHost + 1 + + diff --git a/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py b/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py new file mode 100644 index 00000000..d499c27a --- /dev/null +++ b/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +import json +import zmq + +def main(): + ctx = zmq.Context() + sock = ctx.socket(zmq.REP) + sock.bind("tcp://*:5555") + + buffers = {} + streams = {} + registers = {} + + while True: + frames = [sock.recv()] + while sock.getsockopt(zmq.RCVMORE): + frames.append(sock.recv()) + + try: + msg = json.loads(frames[0]) + except (json.JSONDecodeError, UnicodeDecodeError): + sock.send(b"OK") + continue + + cmd = msg.get("command", "") + + if cmd == "exit": + sock.send(b"OK") + break + + elif cmd == "populate": + key = msg.get("name", str(msg.get("addr", ""))) + if len(frames) > 1: + buffers[key] = frames[1] + sock.send(b"OK") + + elif cmd == "stream_in": + key = msg.get("name", "") + if len(frames) > 1: + streams[key] = frames[1] + sock.send(b"OK") + + elif cmd == "stream_out": + key = msg.get("name", "") + size = msg.get("size", 0) + data = streams.get(key, b"\x00" * size) + sock.send(data) + + elif cmd == "fetch": + typ = msg.get("type", "") + if typ == "buffer": + key = msg.get("name", str(msg.get("addr", ""))) + if key in buffers: + data = list(buffers[key]) + else: + size = msg.get("size", 0) + data = [0] * size + sock.send_string(json.dumps(data)) + else: + addr = str(msg.get("addr", msg.get("name", ""))) + val = registers.get(addr, 0) + sock.send_string(json.dumps(val)) + + elif cmd == "read_register": + sock.send_string("0") + + elif cmd == "reg": + addr = str(msg.get("addr", "")) + val = int(msg.get("val", 0)) + if val & 0x1: + registers[addr] = 0x6 + else: + registers[addr] = val + sock.send(b"OK") + + elif cmd == "wait": + sock.send(b"OK") + + else: + sock.send(b"OK") + + sock.close() + ctx.term() + +if __name__ == "__main__": + main() diff --git a/vrt/tests/vrtbin_integration_test.cpp b/vrt/tests/vrtbin_integration_test.cpp new file mode 100644 index 00000000..a7680ffc --- /dev/null +++ b/vrt/tests/vrtbin_integration_test.cpp @@ -0,0 +1,93 @@ +#include +#include +#include + +#include + +#include "test_helpers.hpp" + +class VrtbinEmuTest : public ::testing::Test { + protected: + std::filesystem::path tmpDir; + ScopedEnv* envCache = nullptr; + + void SetUp() override { + tmpDir = makeTempDir("vrtbin-emu-test"); + envCache = new ScopedEnv("SLASH_CACHE_PATH", tmpDir.string()); + } + + void TearDown() override { + delete envCache; + std::filesystem::remove_all(tmpDir); + } +}; + +TEST_F(VrtbinEmuTest, ExtractAndFindSystemMap) { + vrt::Vrtbin vrtbin(STUB_EMU_VBIN_PATH, "0000:00:00"); + EXPECT_FALSE(vrtbin.getSystemMapPath().empty()); + EXPECT_TRUE(std::filesystem::exists(vrtbin.getSystemMapPath())); +} + +TEST_F(VrtbinEmuTest, DetectsPlatformEmulation) { + vrt::Vrtbin vrtbin(STUB_EMU_VBIN_PATH, "0000:00:00"); + EXPECT_EQ(vrtbin.getPlatform(), vrt::Platform::EMULATION); +} + +TEST_F(VrtbinEmuTest, FindsEmulationExec) { + vrt::Vrtbin vrtbin(STUB_EMU_VBIN_PATH, "0000:00:00"); + EXPECT_FALSE(vrtbin.getEmulationExec().empty()); + EXPECT_TRUE(std::filesystem::exists(vrtbin.getEmulationExec())); +} + +TEST_F(VrtbinEmuTest, FindsEmulationManifest) { + vrt::Vrtbin vrtbin(STUB_EMU_VBIN_PATH, "0000:00:00"); + EXPECT_FALSE(vrtbin.getEmulationManifest().empty()); + EXPECT_TRUE(std::filesystem::exists(vrtbin.getEmulationManifest())); +} + +TEST_F(VrtbinEmuTest, NoPdiFilesForEmulation) { + vrt::Vrtbin vrtbin(STUB_EMU_VBIN_PATH, "0000:00:00"); + EXPECT_TRUE(vrtbin.getPdiPaths().empty()); +} + +class VrtbinSimTest : public ::testing::Test { + protected: + std::filesystem::path tmpDir; + ScopedEnv* envCache = nullptr; + + void SetUp() override { + tmpDir = makeTempDir("vrtbin-sim-test"); + envCache = new ScopedEnv("SLASH_CACHE_PATH", tmpDir.string()); + } + + void TearDown() override { + delete envCache; + std::filesystem::remove_all(tmpDir); + } +}; + +TEST_F(VrtbinSimTest, ExtractAndFindSystemMap) { + vrt::Vrtbin vrtbin(STUB_SIM_VBIN_PATH, "0000:00:00"); + EXPECT_FALSE(vrtbin.getSystemMapPath().empty()); + EXPECT_TRUE(std::filesystem::exists(vrtbin.getSystemMapPath())); +} + +TEST_F(VrtbinSimTest, DetectsPlatformSimulation) { + vrt::Vrtbin vrtbin(STUB_SIM_VBIN_PATH, "0000:00:00"); + EXPECT_EQ(vrtbin.getPlatform(), vrt::Platform::SIMULATION); +} + +TEST_F(VrtbinSimTest, FindsSimulationExec) { + vrt::Vrtbin vrtbin(STUB_SIM_VBIN_PATH, "0000:00:00"); + EXPECT_FALSE(vrtbin.getSimulationExec().empty()); + EXPECT_TRUE(std::filesystem::exists(vrtbin.getSimulationExec())); +} + +TEST_F(VrtbinSimTest, NoPdiFilesForSimulation) { + vrt::Vrtbin vrtbin(STUB_SIM_VBIN_PATH, "0000:00:00"); + EXPECT_TRUE(vrtbin.getPdiPaths().empty()); +} + +TEST(VrtbinErrorTest, NonexistentVbinThrows) { + EXPECT_THROW(vrt::Vrtbin("/nonexistent/path.vbin", "0000:00:00"), std::runtime_error); +} From f4a84592f34269d1832e3f3c09b17a67ad3d84fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Mon, 20 Apr 2026 16:32:28 +0100 Subject: [PATCH 09/15] Also calling kernels in the sim tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- .github/workflows/vrt-unit-test.yml | 2 +- vrt/tests/device_sim_test.cpp | 35 +++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/.github/workflows/vrt-unit-test.yml b/.github/workflows/vrt-unit-test.yml index 4a3e0a17..27cff073 100644 --- a/.github/workflows/vrt-unit-test.yml +++ b/.github/workflows/vrt-unit-test.yml @@ -59,7 +59,7 @@ jobs: run: | cd vrt/build lcov --capture --directory . --output-file coverage.info \ - --ignore-errors mismatch + --ignore-errors mismatch --ignore-errors negative lcov --remove coverage.info \ '/usr/*' '*/build/_deps/*' '*/vrtd/*' '*/tests/*' \ --output-file coverage.filtered.info \ diff --git a/vrt/tests/device_sim_test.cpp b/vrt/tests/device_sim_test.cpp index ec6c3bff..a54c7ca1 100644 --- a/vrt/tests/device_sim_test.cpp +++ b/vrt/tests/device_sim_test.cpp @@ -44,6 +44,16 @@ TEST_F(DeviceSimTest, GetKernelVadd) { EXPECT_EQ(kernel.getPhysAddr(), 0x10000u); } +TEST_F(DeviceSimTest, GetKernelPassthrough) { + auto kernel = device.getKernel("passthrough"); + EXPECT_EQ(kernel.getName(), "passthrough"); + EXPECT_EQ(kernel.getPhysAddr(), 0x20000u); +} + +TEST_F(DeviceSimTest, GetKernelUnknownThrows) { + EXPECT_THROW(device.getKernel("nonexistent"), std::runtime_error); +} + TEST_F(DeviceSimTest, KernelWrite) { auto kernel = device.getKernel("vadd"); EXPECT_NO_THROW(kernel.write(0x10, 0xDEAD)); @@ -55,6 +65,31 @@ TEST_F(DeviceSimTest, KernelRead) { EXPECT_EQ(val, 0u); } +TEST_F(DeviceSimTest, KernelSetArgAndCall) { + auto kernel = device.getKernel("vadd"); + kernel.setArg(0, static_cast(0x1000)); + kernel.setArg(1, static_cast(0x2000)); + kernel.setArg(2, static_cast(0x3000)); + kernel.setArg(3, static_cast(64)); + EXPECT_NO_THROW(kernel.call()); +} + +TEST_F(DeviceSimTest, KernelCallByName) { + auto kernel = device.getKernel("vadd"); + kernel.setArg("in1", static_cast(0x1000)); + kernel.setArg("in2", static_cast(0x2000)); + kernel.setArg("out", static_cast(0x3000)); + kernel.setArg("size", static_cast(64)); + EXPECT_NO_THROW(kernel.call()); +} + +TEST_F(DeviceSimTest, DISABLED_KernelStartAndWait) { + auto kernel = device.getKernel("passthrough"); + kernel.setArg(0, static_cast(42)); + EXPECT_NO_THROW(kernel.start()); + EXPECT_NO_THROW(kernel.wait()); +} + TEST_F(DeviceSimTest, BufferConstruction) { EXPECT_NO_THROW({ vrt::Buffer buf(device, 64, vrt::MemoryRangeType::DDR); From 723b6ecafd69a2ec04f445d79c44045ed4eb5d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Mon, 20 Apr 2026 17:00:02 +0100 Subject: [PATCH 10/15] Removing the original VRT unit testing branch as a possible trigger for GitHub Action workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- .github/workflows/vrt-unit-test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/vrt-unit-test.yml b/.github/workflows/vrt-unit-test.yml index 27cff073..570732e1 100644 --- a/.github/workflows/vrt-unit-test.yml +++ b/.github/workflows/vrt-unit-test.yml @@ -25,7 +25,6 @@ on: branches: - main - dev - - feature/vrt-unit-testing pull_request: jobs: From 67171161d630285f1b4360aa72c729efe1ee729e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Tue, 21 Apr 2026 08:19:57 +0100 Subject: [PATCH 11/15] Adding copyright headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- vrt/tests/CMakeLists.txt | 1 - vrt/tests/device_emu_test.cpp | 19 +++++++++++++++++++ vrt/tests/device_sim_test.cpp | 19 +++++++++++++++++++ vrt/tests/filesystem_cache_test.cpp | 19 +++++++++++++++++++ .../fixtures/stub_vbin/vrt_stub_server.py | 19 +++++++++++++++++++ vrt/tests/hello_test.cpp | 6 ------ vrt/tests/kernel_test.cpp | 19 +++++++++++++++++++ vrt/tests/logger_test.cpp | 19 +++++++++++++++++++ vrt/tests/qdma_connection_test.cpp | 19 +++++++++++++++++++ vrt/tests/register_test.cpp | 19 +++++++++++++++++++ vrt/tests/test_helpers.hpp | 19 +++++++++++++++++++ vrt/tests/utilization_data_test.cpp | 19 +++++++++++++++++++ vrt/tests/utilization_parser_test.cpp | 19 +++++++++++++++++++ vrt/tests/vrtbin_integration_test.cpp | 19 +++++++++++++++++++ vrt/tests/vrtbin_test.cpp | 19 +++++++++++++++++++ vrt/tests/xml_parser_test.cpp | 19 +++++++++++++++++++ 16 files changed, 266 insertions(+), 7 deletions(-) delete mode 100644 vrt/tests/hello_test.cpp diff --git a/vrt/tests/CMakeLists.txt b/vrt/tests/CMakeLists.txt index 710fc03b..449e7769 100644 --- a/vrt/tests/CMakeLists.txt +++ b/vrt/tests/CMakeLists.txt @@ -18,7 +18,6 @@ macro(add_vrt_test test_name test_source) add_dependencies(unit_tests ${test_name}) endmacro() -add_vrt_test(hello_test hello_test.cpp) add_vrt_test(register_test register_test.cpp) add_vrt_test(qdma_connection_test qdma_connection_test.cpp) add_vrt_test(logger_test logger_test.cpp) diff --git a/vrt/tests/device_emu_test.cpp b/vrt/tests/device_emu_test.cpp index 4fddc04e..4a5e0b14 100644 --- a/vrt/tests/device_emu_test.cpp +++ b/vrt/tests/device_emu_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include #include diff --git a/vrt/tests/device_sim_test.cpp b/vrt/tests/device_sim_test.cpp index a54c7ca1..942a255d 100644 --- a/vrt/tests/device_sim_test.cpp +++ b/vrt/tests/device_sim_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include #include diff --git a/vrt/tests/filesystem_cache_test.cpp b/vrt/tests/filesystem_cache_test.cpp index 4381e5b4..dd86926a 100644 --- a/vrt/tests/filesystem_cache_test.cpp +++ b/vrt/tests/filesystem_cache_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include diff --git a/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py b/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py index d499c27a..36edb380 100644 --- a/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py +++ b/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py @@ -1,4 +1,23 @@ #!/usr/bin/env python3 +# ################################################################################################## +# The MIT License (MIT) +# Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software +# and associated documentation files (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, publish, distribute, +# sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or +# substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +# NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# ################################################################################################## import json import zmq diff --git a/vrt/tests/hello_test.cpp b/vrt/tests/hello_test.cpp deleted file mode 100644 index d3508aa9..00000000 --- a/vrt/tests/hello_test.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include - -TEST(HelloTest, BasicAssertions) { - EXPECT_STRNE("hello", "world"); - EXPECT_EQ(7 * 6, 42); -} \ No newline at end of file diff --git a/vrt/tests/kernel_test.cpp b/vrt/tests/kernel_test.cpp index c4dd32b2..823429fa 100644 --- a/vrt/tests/kernel_test.cpp +++ b/vrt/tests/kernel_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include #include diff --git a/vrt/tests/logger_test.cpp b/vrt/tests/logger_test.cpp index ecde2a82..3280c22e 100644 --- a/vrt/tests/logger_test.cpp +++ b/vrt/tests/logger_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include diff --git a/vrt/tests/qdma_connection_test.cpp b/vrt/tests/qdma_connection_test.cpp index b1ad2ca9..dfac64b6 100644 --- a/vrt/tests/qdma_connection_test.cpp +++ b/vrt/tests/qdma_connection_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include diff --git a/vrt/tests/register_test.cpp b/vrt/tests/register_test.cpp index d5b90069..60915b69 100644 --- a/vrt/tests/register_test.cpp +++ b/vrt/tests/register_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include diff --git a/vrt/tests/test_helpers.hpp b/vrt/tests/test_helpers.hpp index 6a5e7769..f8568182 100644 --- a/vrt/tests/test_helpers.hpp +++ b/vrt/tests/test_helpers.hpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #ifndef VRT_TEST_HELPERS_HPP #define VRT_TEST_HELPERS_HPP diff --git a/vrt/tests/utilization_data_test.cpp b/vrt/tests/utilization_data_test.cpp index 7d6e1f61..84aad4fc 100644 --- a/vrt/tests/utilization_data_test.cpp +++ b/vrt/tests/utilization_data_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include diff --git a/vrt/tests/utilization_parser_test.cpp b/vrt/tests/utilization_parser_test.cpp index 5184dfb7..dc2bdfa2 100644 --- a/vrt/tests/utilization_parser_test.cpp +++ b/vrt/tests/utilization_parser_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include diff --git a/vrt/tests/vrtbin_integration_test.cpp b/vrt/tests/vrtbin_integration_test.cpp index a7680ffc..d3c86879 100644 --- a/vrt/tests/vrtbin_integration_test.cpp +++ b/vrt/tests/vrtbin_integration_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include #include diff --git a/vrt/tests/vrtbin_test.cpp b/vrt/tests/vrtbin_test.cpp index e0ef1c42..1734e71d 100644 --- a/vrt/tests/vrtbin_test.cpp +++ b/vrt/tests/vrtbin_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include diff --git a/vrt/tests/xml_parser_test.cpp b/vrt/tests/xml_parser_test.cpp index 1f889b29..94b002b4 100644 --- a/vrt/tests/xml_parser_test.cpp +++ b/vrt/tests/xml_parser_test.cpp @@ -1,3 +1,22 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ #include #include #include From 72e61f1a8f063ce98a8783a28dae1815b33d747c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Tue, 21 Apr 2026 09:17:16 +0100 Subject: [PATCH 12/15] Merging the sim tests and emu tests into one test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- vrt/tests/CMakeLists.txt | 7 +- vrt/tests/device_sim_test.cpp | 140 ------------------ .../{device_emu_test.cpp => device_test.cpp} | 94 ++++++++---- 3 files changed, 70 insertions(+), 171 deletions(-) delete mode 100644 vrt/tests/device_sim_test.cpp rename vrt/tests/{device_emu_test.cpp => device_test.cpp} (67%) diff --git a/vrt/tests/CMakeLists.txt b/vrt/tests/CMakeLists.txt index 449e7769..267425e6 100644 --- a/vrt/tests/CMakeLists.txt +++ b/vrt/tests/CMakeLists.txt @@ -13,7 +13,7 @@ add_custom_target(unit_tests) macro(add_vrt_test test_name test_source) add_executable(${test_name} ${test_source}) - target_link_libraries(${test_name} PRIVATE GTest::gtest_main vrt::vrt) + target_link_libraries(${test_name} PRIVATE GTest::gtest_main GTest::gmock vrt::vrt) gtest_discover_tests(${test_name}) add_dependencies(unit_tests ${test_name}) endmacro() @@ -79,7 +79,7 @@ add_custom_target(stub_vbins DEPENDS ${STUB_EMU_VBIN} ${STUB_SIM_VBIN}) macro(add_vrt_vbin_test test_name test_source) add_executable(${test_name} ${test_source}) - target_link_libraries(${test_name} PRIVATE GTest::gtest_main vrt::vrt) + target_link_libraries(${test_name} PRIVATE GTest::gtest_main GTest::gmock vrt::vrt) target_compile_definitions(${test_name} PRIVATE STUB_EMU_VBIN_PATH="${STUB_EMU_VBIN}" STUB_SIM_VBIN_PATH="${STUB_SIM_VBIN}") @@ -89,5 +89,4 @@ macro(add_vrt_vbin_test test_name test_source) endmacro() add_vrt_vbin_test(vrtbin_integration_test vrtbin_integration_test.cpp) -add_vrt_vbin_test(device_emu_test device_emu_test.cpp) -add_vrt_vbin_test(device_sim_test device_sim_test.cpp) +add_vrt_vbin_test(device_test device_test.cpp) diff --git a/vrt/tests/device_sim_test.cpp b/vrt/tests/device_sim_test.cpp deleted file mode 100644 index 942a255d..00000000 --- a/vrt/tests/device_sim_test.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/** - * The MIT License (MIT) - * Copyright (c) 2025-2026 Advanced Micro Devices, Inc. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software - * and associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "test_helpers.hpp" - -class DeviceSimTest : public ::testing::Test { - protected: - std::filesystem::path tmpDir; - ScopedEnv* envCache = nullptr; - vrt::Device device; - - void SetUp() override { - tmpDir = makeTempDir("device-sim-test"); - envCache = new ScopedEnv("SLASH_CACHE_PATH", tmpDir.string()); - device = vrt::Device("0000:00:00", STUB_SIM_VBIN_PATH, false); - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - - void TearDown() override { - device.cleanup(); - delete envCache; - std::filesystem::remove_all(tmpDir); - } -}; - -TEST_F(DeviceSimTest, Construction) { - SUCCEED(); -} - -TEST_F(DeviceSimTest, GetPlatform) { - EXPECT_EQ(device.getPlatform(), vrt::Platform::SIMULATION); -} - -TEST_F(DeviceSimTest, GetKernelVadd) { - auto kernel = device.getKernel("vadd"); - EXPECT_EQ(kernel.getName(), "vadd"); - EXPECT_EQ(kernel.getPhysAddr(), 0x10000u); -} - -TEST_F(DeviceSimTest, GetKernelPassthrough) { - auto kernel = device.getKernel("passthrough"); - EXPECT_EQ(kernel.getName(), "passthrough"); - EXPECT_EQ(kernel.getPhysAddr(), 0x20000u); -} - -TEST_F(DeviceSimTest, GetKernelUnknownThrows) { - EXPECT_THROW(device.getKernel("nonexistent"), std::runtime_error); -} - -TEST_F(DeviceSimTest, KernelWrite) { - auto kernel = device.getKernel("vadd"); - EXPECT_NO_THROW(kernel.write(0x10, 0xDEAD)); -} - -TEST_F(DeviceSimTest, KernelRead) { - auto kernel = device.getKernel("vadd"); - uint32_t val = kernel.read(0x10); - EXPECT_EQ(val, 0u); -} - -TEST_F(DeviceSimTest, KernelSetArgAndCall) { - auto kernel = device.getKernel("vadd"); - kernel.setArg(0, static_cast(0x1000)); - kernel.setArg(1, static_cast(0x2000)); - kernel.setArg(2, static_cast(0x3000)); - kernel.setArg(3, static_cast(64)); - EXPECT_NO_THROW(kernel.call()); -} - -TEST_F(DeviceSimTest, KernelCallByName) { - auto kernel = device.getKernel("vadd"); - kernel.setArg("in1", static_cast(0x1000)); - kernel.setArg("in2", static_cast(0x2000)); - kernel.setArg("out", static_cast(0x3000)); - kernel.setArg("size", static_cast(64)); - EXPECT_NO_THROW(kernel.call()); -} - -TEST_F(DeviceSimTest, DISABLED_KernelStartAndWait) { - auto kernel = device.getKernel("passthrough"); - kernel.setArg(0, static_cast(42)); - EXPECT_NO_THROW(kernel.start()); - EXPECT_NO_THROW(kernel.wait()); -} - -TEST_F(DeviceSimTest, BufferConstruction) { - EXPECT_NO_THROW({ - vrt::Buffer buf(device, 64, vrt::MemoryRangeType::DDR); - }); -} - -TEST_F(DeviceSimTest, BufferSyncRoundTrip) { - vrt::Buffer buf(device, 4, vrt::MemoryRangeType::DDR); - buf[0] = 100; - buf[1] = 200; - buf[2] = 300; - buf[3] = 400; - buf.sync(vrt::SyncType::HOST_TO_DEVICE); - buf[0] = 0; - buf[1] = 0; - buf[2] = 0; - buf[3] = 0; - buf.sync(vrt::SyncType::DEVICE_TO_HOST); - EXPECT_EQ(buf[0], 100); - EXPECT_EQ(buf[1], 200); - EXPECT_EQ(buf[2], 300); - EXPECT_EQ(buf[3], 400); -} - -TEST_F(DeviceSimTest, StreamingBufferThrowsNotImplemented) { - auto kernel = device.getKernel("vadd"); - vrt::StreamingBuffer sbuf(device, kernel, "axis_in", 16); - EXPECT_THROW(sbuf.sync(), std::runtime_error); -} diff --git a/vrt/tests/device_emu_test.cpp b/vrt/tests/device_test.cpp similarity index 67% rename from vrt/tests/device_emu_test.cpp rename to vrt/tests/device_test.cpp index 4a5e0b14..b7f20364 100644 --- a/vrt/tests/device_emu_test.cpp +++ b/vrt/tests/device_test.cpp @@ -18,6 +18,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include +#include #include #include #include @@ -29,16 +30,30 @@ #include "test_helpers.hpp" -class DeviceEmuTest : public ::testing::Test { +using ::testing::Contains; + +class DeviceTest : public ::testing::Test, public ::testing::WithParamInterface { protected: std::filesystem::path tmpDir; ScopedEnv* envCache = nullptr; + vrt::Platform platform; vrt::Device device; void SetUp() override { - tmpDir = makeTempDir("device-emu-test"); + tmpDir = makeTempDir("device-test"); envCache = new ScopedEnv("SLASH_CACHE_PATH", tmpDir.string()); - device = vrt::Device("0000:00:00", STUB_EMU_VBIN_PATH, false); + + platform = GetParam(); + std::array supported_platforms{vrt::Platform::EMULATION, vrt::Platform::SIMULATION}; + EXPECT_THAT(supported_platforms, Contains(platform)); + + std::string vbin_path; + if (platform == vrt::Platform::EMULATION) { + vbin_path = STUB_EMU_VBIN_PATH; + } else { + vbin_path = STUB_SIM_VBIN_PATH; + } + device = vrt::Device("0000:00:00", vbin_path, false); std::this_thread::sleep_for(std::chrono::milliseconds(500)); } @@ -49,35 +64,35 @@ class DeviceEmuTest : public ::testing::Test { } }; -TEST_F(DeviceEmuTest, Construction) { +TEST_P(DeviceTest, Construction) { SUCCEED(); } -TEST_F(DeviceEmuTest, GetPlatform) { - EXPECT_EQ(device.getPlatform(), vrt::Platform::EMULATION); +TEST_P(DeviceTest, GetPlatform) { + EXPECT_EQ(device.getPlatform(), platform); } -TEST_F(DeviceEmuTest, GetFrequency) { +TEST_P(DeviceTest, GetFrequency) { EXPECT_EQ(device.getFrequency(), 0u); } -TEST_F(DeviceEmuTest, GetKernelVadd) { +TEST_P(DeviceTest, GetKernelVadd) { auto kernel = device.getKernel("vadd"); EXPECT_EQ(kernel.getName(), "vadd"); EXPECT_EQ(kernel.getPhysAddr(), 0x10000u); } -TEST_F(DeviceEmuTest, GetKernelPassthrough) { +TEST_P(DeviceTest, GetKernelPassthrough) { auto kernel = device.getKernel("passthrough"); EXPECT_EQ(kernel.getName(), "passthrough"); EXPECT_EQ(kernel.getPhysAddr(), 0x20000u); } -TEST_F(DeviceEmuTest, GetKernelUnknownThrows) { +TEST_P(DeviceTest, GetKernelUnknownThrows) { EXPECT_THROW(device.getKernel("nonexistent"), std::runtime_error); } -TEST_F(DeviceEmuTest, GetQdmaConnections) { +TEST_P(DeviceTest, GetQdmaConnections) { auto conns = device.getHandle()->getQdmaConnections(); ASSERT_EQ(conns.size(), 2u); EXPECT_EQ(conns[0].getKernel(), "vadd"); @@ -89,7 +104,18 @@ TEST_F(DeviceEmuTest, GetQdmaConnections) { EXPECT_EQ(conns[1].getQid(), 1u); } -TEST_F(DeviceEmuTest, KernelSetArgAndCall) { +TEST_P(DeviceTest, KernelWrite) { + auto kernel = device.getKernel("vadd"); + EXPECT_NO_THROW(kernel.write(0x10, 0xDEAD)); +} + +TEST_P(DeviceTest, KernelRead) { + auto kernel = device.getKernel("vadd"); + uint32_t val = kernel.read(0x10); + EXPECT_EQ(val, 0u); +} + +TEST_P(DeviceTest, KernelSetArgAndCall) { auto kernel = device.getKernel("vadd"); kernel.setArg(0, static_cast(0x1000)); kernel.setArg(1, static_cast(0x2000)); @@ -98,7 +124,7 @@ TEST_F(DeviceEmuTest, KernelSetArgAndCall) { EXPECT_NO_THROW(kernel.call()); } -TEST_F(DeviceEmuTest, KernelCallByName) { +TEST_P(DeviceTest, KernelCallByName) { auto kernel = device.getKernel("vadd"); kernel.setArg("in1", static_cast(0x1000)); kernel.setArg("in2", static_cast(0x2000)); @@ -107,38 +133,32 @@ TEST_F(DeviceEmuTest, KernelCallByName) { EXPECT_NO_THROW(kernel.call()); } -TEST_F(DeviceEmuTest, DISABLED_KernelStartAndWait) { +TEST_P(DeviceTest, DISABLED_KernelStartAndWait) { auto kernel = device.getKernel("passthrough"); kernel.setArg(0, static_cast(42)); EXPECT_NO_THROW(kernel.start()); EXPECT_NO_THROW(kernel.wait()); } -TEST_F(DeviceEmuTest, KernelRead) { - auto kernel = device.getKernel("vadd"); - uint32_t val = kernel.read(0x00); - EXPECT_EQ(val, 0u); -} - -TEST_F(DeviceEmuTest, BufferDDRConstruction) { +TEST_P(DeviceTest, BufferDDRConstruction) { EXPECT_NO_THROW({ vrt::Buffer buf(device, 64, vrt::MemoryRangeType::DDR); }); } -TEST_F(DeviceEmuTest, BufferHBMWithPort) { +TEST_P(DeviceTest, BufferHBMWithPort) { EXPECT_NO_THROW({ vrt::Buffer buf(device, 64, vrt::MemoryRangeType::HBM, 0); }); } -TEST_F(DeviceEmuTest, BufferHBMVnoc) { +TEST_P(DeviceTest, BufferHBMVnoc) { EXPECT_NO_THROW({ vrt::Buffer buf(device, 64, vrt::MemoryRangeType::HBM_VNOC); }); } -TEST_F(DeviceEmuTest, BufferSyncRoundTrip) { +TEST_P(DeviceTest, BufferSyncRoundTrip) { vrt::Buffer buf(device, 4, vrt::MemoryRangeType::DDR); buf[0] = 10; buf[1] = 20; @@ -156,22 +176,42 @@ TEST_F(DeviceEmuTest, BufferSyncRoundTrip) { EXPECT_EQ(buf[3], 40); } -TEST_F(DeviceEmuTest, StreamingBufferH2D) { +TEST_P(DeviceTest, StreamingBufferH2D) { + if (platform == vrt::Platform::SIMULATION) { + GTEST_SKIP(); + } auto kernel = device.getKernel("vadd"); vrt::StreamingBuffer sbuf(device, kernel, "axis_in", 16); sbuf[0] = 42; EXPECT_NO_THROW(sbuf.sync()); } -TEST_F(DeviceEmuTest, StreamingBufferD2H) { +TEST_P(DeviceTest, StreamingBufferD2H) { + if (platform == vrt::Platform::SIMULATION) { + GTEST_SKIP(); + } auto kernel = device.getKernel("vadd"); vrt::StreamingBuffer sbuf(device, kernel, "axis_out", 16); EXPECT_NO_THROW(sbuf.sync()); } -TEST_F(DeviceEmuTest, StreamingBufferWrongPortThrows) { +TEST_P(DeviceTest, StreamingBufferWrongPortThrows) { + if (platform == vrt::Platform::SIMULATION) { + GTEST_SKIP(); + } auto kernel = device.getKernel("vadd"); EXPECT_THROW( vrt::StreamingBuffer(device, kernel, "nonexistent_port", 16), std::runtime_error); } + +TEST_P(DeviceTest, StreamingBufferThrowsNotImplemented) { + if (platform != vrt::Platform::SIMULATION) { + GTEST_SKIP(); + } + auto kernel = device.getKernel("vadd"); + vrt::StreamingBuffer sbuf(device, kernel, "axis_in", 16); + EXPECT_THROW(sbuf.sync(), std::runtime_error); +} + +INSTANTIATE_TEST_SUITE_P(DeviceTestSuite, DeviceTest, ::testing::Values(vrt::Platform::EMULATION, vrt::Platform::SIMULATION)); From c8be9dca2d7e22559cc72682e5454e36db47a9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Tue, 21 Apr 2026 10:05:02 +0100 Subject: [PATCH 13/15] Adding round-trip testing functionality to the device tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- vrt/tests/device_test.cpp | 51 +++---- .../fixtures/stub_vbin/vrt_stub_server.py | 141 ++++++++++++------ 2 files changed, 121 insertions(+), 71 deletions(-) diff --git a/vrt/tests/device_test.cpp b/vrt/tests/device_test.cpp index b7f20364..d2a360de 100644 --- a/vrt/tests/device_test.cpp +++ b/vrt/tests/device_test.cpp @@ -115,31 +115,6 @@ TEST_P(DeviceTest, KernelRead) { EXPECT_EQ(val, 0u); } -TEST_P(DeviceTest, KernelSetArgAndCall) { - auto kernel = device.getKernel("vadd"); - kernel.setArg(0, static_cast(0x1000)); - kernel.setArg(1, static_cast(0x2000)); - kernel.setArg(2, static_cast(0x3000)); - kernel.setArg(3, static_cast(64)); - EXPECT_NO_THROW(kernel.call()); -} - -TEST_P(DeviceTest, KernelCallByName) { - auto kernel = device.getKernel("vadd"); - kernel.setArg("in1", static_cast(0x1000)); - kernel.setArg("in2", static_cast(0x2000)); - kernel.setArg("out", static_cast(0x3000)); - kernel.setArg("size", static_cast(64)); - EXPECT_NO_THROW(kernel.call()); -} - -TEST_P(DeviceTest, DISABLED_KernelStartAndWait) { - auto kernel = device.getKernel("passthrough"); - kernel.setArg(0, static_cast(42)); - EXPECT_NO_THROW(kernel.start()); - EXPECT_NO_THROW(kernel.wait()); -} - TEST_P(DeviceTest, BufferDDRConstruction) { EXPECT_NO_THROW({ vrt::Buffer buf(device, 64, vrt::MemoryRangeType::DDR); @@ -214,4 +189,30 @@ TEST_P(DeviceTest, StreamingBufferThrowsNotImplemented) { EXPECT_THROW(sbuf.sync(), std::runtime_error); } +TEST_P(DeviceTest, KernelVaddRoundTrip) { + constexpr int N = 4; + vrt::Kernel kernel(device, "vadd"); + vrt::Buffer in1(device, N, vrt::MemoryRangeType::HBM, 0); + vrt::Buffer in2(device, N, vrt::MemoryRangeType::DDR); + vrt::Buffer out(device, N, vrt::MemoryRangeType::HBM_VNOC); + + for (int i = 0; i < N; ++i) { + in1[i] = i + 1; + in2[i] = (i + 1) * 10; + } + in1.sync(vrt::SyncType::HOST_TO_DEVICE); + in2.sync(vrt::SyncType::HOST_TO_DEVICE); + + kernel.setArg(0, static_cast(in1.getPhysAddr())); + kernel.setArg(1, static_cast(in2.getPhysAddr())); + kernel.setArg(2, static_cast(out.getPhysAddr())); + kernel.setArg(3, static_cast(N)); + ASSERT_NO_THROW(kernel.call()); + + out.sync(vrt::SyncType::DEVICE_TO_HOST); + for (int i = 0; i < N; ++i) { + EXPECT_EQ(out[i], in1[i] + in2[i]); + } +} + INSTANTIATE_TEST_SUITE_P(DeviceTestSuite, DeviceTest, ::testing::Values(vrt::Platform::EMULATION, vrt::Platform::SIMULATION)); diff --git a/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py b/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py index 36edb380..e712e07b 100644 --- a/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py +++ b/vrt/tests/fixtures/stub_vbin/vrt_stub_server.py @@ -19,87 +19,136 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # ################################################################################################## import json +import struct import zmq +# Kernel base addresses (must match system_map.xml) +VADD_BASE = 0x10000 + +# Functional arg register offsets relative to kernel base (must match system_map.xml) +VADD_IN1_OFFSET = 0x10 +VADD_IN2_OFFSET = 0x18 +VADD_OUT_OFFSET = 0x20 +VADD_SIZE_OFFSET = 0x28 + + +def run_vadd(buffers, in1_key, in2_key, out_key, size): + """Add two int32 buffers and store the result.""" + in1_bytes = buffers.get(in1_key, b"\x00" * (size * 4)) + in2_bytes = buffers.get(in2_key, b"\x00" * (size * 4)) + + n = min(size, len(in1_bytes) // 4, len(in2_bytes) // 4) + in1 = struct.unpack_from(f"<{n}i", in1_bytes) + in2 = struct.unpack_from(f"<{n}i", in2_bytes) + out = [a + b for a, b in zip(in1, in2)] + buffers[out_key] = struct.pack(f"<{n}i", *out) + + +def reconstruct_64bit(registers, base, offset): + """Reconstruct a 64-bit address from two consecutive 32-bit register writes.""" + lo = registers.get(base + offset, 0) + hi = registers.get(base + offset + 4, 0) + return (hi << 32) | lo + + def main(): - ctx = zmq.Context() - sock = ctx.socket(zmq.REP) - sock.bind("tcp://*:5555") + context = zmq.Context() + socket = context.socket(zmq.REP) + socket.bind("tcp://*:5555") buffers = {} streams = {} registers = {} while True: - frames = [sock.recv()] - while sock.getsockopt(zmq.RCVMORE): - frames.append(sock.recv()) + frames = [socket.recv()] + while socket.getsockopt(zmq.RCVMORE): + frames.append(socket.recv()) try: - msg = json.loads(frames[0]) + message = json.loads(frames[0]) except (json.JSONDecodeError, UnicodeDecodeError): - sock.send(b"OK") + socket.send(b"OK") continue - cmd = msg.get("command", "") + command = message.get("command", "") - if cmd == "exit": - sock.send(b"OK") + if command == "exit": + socket.send(b"OK") break - elif cmd == "populate": - key = msg.get("name", str(msg.get("addr", ""))) + elif command == "populate": + key = message.get("name", str(message.get("addr", ""))) if len(frames) > 1: buffers[key] = frames[1] - sock.send(b"OK") + socket.send(b"OK") - elif cmd == "stream_in": - key = msg.get("name", "") + elif command == "stream_in": + key = message.get("name", "") if len(frames) > 1: streams[key] = frames[1] - sock.send(b"OK") + socket.send(b"OK") - elif cmd == "stream_out": - key = msg.get("name", "") - size = msg.get("size", 0) + elif command == "stream_out": + key = message.get("name", "") + size = message.get("size", 0) data = streams.get(key, b"\x00" * size) - sock.send(data) + socket.send(data) - elif cmd == "fetch": - typ = msg.get("type", "") + elif command == "fetch": + typ = message.get("type", "") if typ == "buffer": - key = msg.get("name", str(msg.get("addr", ""))) + key = message.get("name", str(message.get("addr", ""))) if key in buffers: data = list(buffers[key]) else: - size = msg.get("size", 0) + size = message.get("size", 0) data = [0] * size - sock.send_string(json.dumps(data)) + socket.send_string(json.dumps(data)) else: - addr = str(msg.get("addr", msg.get("name", ""))) - val = registers.get(addr, 0) - sock.send_string(json.dumps(val)) - - elif cmd == "read_register": - sock.send_string("0") - - elif cmd == "reg": - addr = str(msg.get("addr", "")) - val = int(msg.get("val", 0)) - if val & 0x1: - registers[addr] = 0x6 + address = int(message.get("addr", message.get("name", ""))) + val = registers.get(address, 0) + socket.send_string(json.dumps(val)) + + elif command == "read_register": + socket.send_string("0") + + elif command == "reg": + address = int(message.get("addr", 0)) + val = int(message.get("val", 0)) + + if val & 0x1 and address == VADD_BASE: + # ap_start written to vadd CTRL — reconstruct args and run + in1_addr = reconstruct_64bit(registers, VADD_BASE, VADD_IN1_OFFSET) + in2_addr = reconstruct_64bit(registers, VADD_BASE, VADD_IN2_OFFSET) + out_addr = reconstruct_64bit(registers, VADD_BASE, VADD_OUT_OFFSET) + size_val = registers.get(VADD_BASE + VADD_SIZE_OFFSET, 0) + run_vadd(buffers, str(in1_addr), str(in2_addr), str(out_addr), size_val) + registers[address] = 0x6 # ap_done | ap_idle else: - registers[addr] = val - sock.send(b"OK") - - elif cmd == "wait": - sock.send(b"OK") + registers[address] = val + socket.send(b"OK") + + elif command == "call": + function = message.get("function", "") + arguments = message.get("args", {}) + if function == "vadd": + in1_name = arguments.get("arg0", {}).get("name", "") + in2_name = arguments.get("arg1", {}).get("name", "") + out_name = arguments.get("arg2", {}).get("name", "") + size_val = arguments.get("arg3", {}).get("value", 0) + run_vadd(buffers, in1_name, in2_name, out_name, size_val) + socket.send(b"OK") + + elif command == "wait": + socket.send(b"OK") else: - sock.send(b"OK") + socket.send(b"OK") + + socket.close() + context.term() - sock.close() - ctx.term() if __name__ == "__main__": main() From 34d74760a9ea7b77c2be0066164676791bc81b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Tue, 21 Apr 2026 11:39:14 +0100 Subject: [PATCH 14/15] Fixing the Device::getKernel function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- vrt/include/vrt/kernel.hpp | 6 ++++++ vrt/src/device.cpp | 14 +++++++++----- vrt/src/kernel.cpp | 2 ++ vrt/tests/device_test.cpp | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/vrt/include/vrt/kernel.hpp b/vrt/include/vrt/kernel.hpp index 43910a3a..48b75971 100644 --- a/vrt/include/vrt/kernel.hpp +++ b/vrt/include/vrt/kernel.hpp @@ -229,6 +229,12 @@ class Kernel { */ void setVrtdBar(const std::optional& bar); + /** + * @brief Sets the ZeroMQ server for emulation and simulation. + * @param server The ZeroMQ server handle. + */ + void setServer(std::shared_ptr server); + /** * @brief Writes a value to a register. * @param offset The offset of the register. diff --git a/vrt/src/device.cpp b/vrt/src/device.cpp index cf8865a6..22eab66c 100644 --- a/vrt/src/device.cpp +++ b/vrt/src/device.cpp @@ -306,12 +306,16 @@ void Device::parseSystemMap() { clockFreq = parser.getClockFrequency(); this->platform = parser.getPlatform(); kernels = parser.getKernels(); + + std::optional barHandle = std::nullopt; if (platform == Platform::HARDWARE && vrtdDevice.has_value()) { - std::optional barHandle = vrtdDevice->getBar(bar); - for (auto& kernel : kernels) { - kernel.second.setVrtdBar(barHandle); - kernel.second.setPlatform(platform); - } + barHandle = vrtdDevice->getBar(bar); + } + + for (auto&kernel : kernels) { + kernel.second.setPlatform(platform); + kernel.second.setVrtdBar(barHandle); + kernel.second.setServer(zmqServer); } this->qdmaConnections = parser.getQdmaConnections(); } diff --git a/vrt/src/kernel.cpp b/vrt/src/kernel.cpp index d02e2598..637cd29e 100644 --- a/vrt/src/kernel.cpp +++ b/vrt/src/kernel.cpp @@ -139,6 +139,8 @@ uint32_t Kernel::read(uint32_t offset) { void Kernel::setVrtdBar(const std::optional& bar) { this->vrtdBar = bar; } +void Kernel::setServer(std::shared_ptr server) { this->server = server; } + void Kernel::setFunctionalArgs(const std::vector& args) { functionalArgs = args; std::sort(functionalArgs.begin(), functionalArgs.end(), diff --git a/vrt/tests/device_test.cpp b/vrt/tests/device_test.cpp index d2a360de..593a8970 100644 --- a/vrt/tests/device_test.cpp +++ b/vrt/tests/device_test.cpp @@ -191,7 +191,7 @@ TEST_P(DeviceTest, StreamingBufferThrowsNotImplemented) { TEST_P(DeviceTest, KernelVaddRoundTrip) { constexpr int N = 4; - vrt::Kernel kernel(device, "vadd"); + vrt::Kernel kernel = device.getKernel("vadd"); vrt::Buffer in1(device, N, vrt::MemoryRangeType::HBM, 0); vrt::Buffer in2(device, N, vrt::MemoryRangeType::DDR); vrt::Buffer out(device, N, vrt::MemoryRangeType::HBM_VNOC); From 2d35a31939acccbf2893c360550f8494cff6fd26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Oliver=20Opdenh=C3=B6vel?= Date: Fri, 24 Apr 2026 15:42:03 +0100 Subject: [PATCH 15/15] Making VRT unit tests optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Oliver Opdenhövel --- submodules/AVED | 1 + vrt/CMakeLists.txt | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 160000 submodules/AVED diff --git a/submodules/AVED b/submodules/AVED new file mode 160000 index 00000000..839b4ad6 --- /dev/null +++ b/submodules/AVED @@ -0,0 +1 @@ +Subproject commit 839b4ad6a75433ab6a43f9f95790a61c2b85bb16 diff --git a/vrt/CMakeLists.txt b/vrt/CMakeLists.txt index 3ca98321..bdd8c2b8 100644 --- a/vrt/CMakeLists.txt +++ b/vrt/CMakeLists.txt @@ -43,6 +43,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) option(VRT_INCLUDE_VRTD "Include vrtd subdirectory instead of building from system" OFF) +option(VRT_BUILD_TESTS "Build unit tests" OFF) include(GNUInstallDirs) include(CMakePackageConfigHelpers) @@ -141,7 +142,9 @@ target_link_libraries(vrt ) # Testing -add_subdirectory(tests) +if(VRT_BUILD_TESTS) + add_subdirectory(tests) +endif() # Installation