Skip to content

Commit c38c577

Browse files
committed
Add memory boundary test
- Added unit tests to CI workflow. - Added Dockerfile for testing environment setup. - Created `TESTING.md` for guidelines on running tests. - Implemented memory boundary test in `tests/memory_boundary_test.cpp` with CMake configuration.
1 parent ad6167d commit c38c577

File tree

8 files changed

+216
-0
lines changed

8 files changed

+216
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: CI Unit Tests
2+
3+
on:
4+
push:
5+
paths:
6+
- '**.cpp'
7+
- '**.h'
8+
- '**CMakeLists.txt'
9+
- '.github/workflows/**'
10+
- 'conanfile.py'
11+
pull_request:
12+
paths:
13+
- '**.cpp'
14+
- '**.h'
15+
- '**CMakeLists.txt'
16+
- '.github/workflows/**'
17+
- 'conanfile.py'
18+
19+
jobs:
20+
unit-tests:
21+
name: unit-tests (C++20)
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v4
25+
26+
- name: Cache Conan
27+
uses: actions/cache@v4
28+
with:
29+
path: ~/.conan
30+
key: conan-${{ runner.os }}-unit-cpp20-${{ hashFiles('conanfile.txt') }}
31+
32+
- name: Update submodules
33+
run: git submodule update --init --recursive
34+
35+
- name: Install dependencies
36+
run: |
37+
sudo apt-get update
38+
sudo apt-get install -y g++ python3-pip cmake ninja-build
39+
pip3 install conan --break-system-packages
40+
cmake --version
41+
42+
- name: Configure
43+
run: >
44+
cmake --preset Debug -B build
45+
-DBUILD_TESTING=ON
46+
-DCMAKE_CXX_STANDARD=20
47+
48+
- name: Build
49+
run: cmake --build build -j
50+
51+
- name: Run unit tests
52+
run: ctest --output-on-failure
53+
working-directory: build

CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ project(scc VERSION 2025.09 LANGUAGES CXX C)
66

77
option(USE_CWR_SYSTEMC "Use Synopsys Virtualizer SystemC" OFF)
88
option(USE_NCSC_SYSTEMC "Cadence Xcelium SystemC" OFF)
9+
option(BUILD_TESTING "Enable building tests" OFF)
910
option(BUILD_SCC_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" OFF)
1011
option(FULL_TRACE_TYPE_LIST "Test for extended set of templated datatypes" OFF)
1112
#Note: this needs to match the SystemC kernel build options
@@ -106,6 +107,10 @@ include(CheckSymbolExists)
106107
# Check for function getenv()
107108
check_symbol_exists(getenv "stdlib.h" HAVE_GETENV)
108109

110+
if(BUILD_TESTING)
111+
include(CTest)
112+
endif()
113+
109114
if(NOT TARGET lz4::lz4)
110115
message(STATUS "${PROJECT_NAME}: using built-in version of lz4")
111116
add_subdirectory(third_party/lz4-1.9.4)
@@ -126,6 +131,10 @@ if(SystemC_FOUND)
126131
endif()
127132
endif()
128133

134+
if(BUILD_TESTING)
135+
add_subdirectory(tests)
136+
endif()
137+
129138
# Define the scc library
130139
add_library(scc INTERFACE)
131140
add_library(scc::scc ALIAS scc)

Dockerfile.test

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM ubuntu:24.04
2+
3+
ENV DEBIAN_FRONTEND=noninteractive
4+
5+
RUN apt-get update && \
6+
apt-get install -y --no-install-recommends \
7+
build-essential \
8+
cmake \
9+
ninja-build \
10+
git \
11+
python3 \
12+
python3-pip \
13+
ca-certificates && \
14+
pip3 install "conan" && \
15+
rm -rf /var/lib/apt/lists/*
16+
17+
WORKDIR /work

TESTING.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Running Tests
2+
3+
This repository has a GoogleTest-based suite (see `tests/`). The tests target the CMake option `BUILD_TESTING=ON` and require the same dependencies as the main build plus GoogleTest (fetched automatically via `FetchContent`).
4+
5+
## Prerequisites
6+
7+
- CMake ≥ 3.20
8+
- A C++ compiler (GCC/Clang) with C++17 or later
9+
- Ninja build system
10+
- Python 3 with `conan` installed. A virtual environment is recommended:
11+
12+
```bash
13+
python3 -m venv .venv
14+
source .venv/bin/activate
15+
pip install conan
16+
```
17+
18+
If Conan cannot supply SystemC/CCI on your host, use the Docker flow below or point CMake at a local SystemC install via `SYSTEMC_HOME`.
19+
20+
## Configure and Build
21+
22+
```bash
23+
cmake --preset Debug -B build -DBUILD_TESTING=ON -DCMAKE_CXX_STANDARD=20
24+
cmake --build build -j
25+
```
26+
27+
## Run Tests
28+
29+
```bash
30+
cd build
31+
32+
# unit tests
33+
ctest --output-on-failure
34+
35+
# transaction recording example binary
36+
./examples/transaction_recording/transaction_recording
37+
```
38+
39+
## Run Tests in Docker (Linux toolchain)
40+
41+
If your host setup cannot fetch SystemC via Conan, use the provided Dockerfile:
42+
43+
```bash
44+
# from repo root
45+
docker build -f Dockerfile.test -t scc-tests .
46+
docker run --rm -v "$PWD":/work -w /work scc-tests /bin/bash -lc "\
47+
cmake --preset Debug -B build -DBUILD_TESTING=ON -DCMAKE_CXX_STANDARD=20 && \
48+
cmake --build build -j && \
49+
cd build && ctest --output-on-failure && \
50+
./examples/transaction_recording/transaction_recording"
51+
```
52+
53+
## CI
54+
55+
### Unit Tests
56+
Tests run in `.github/workflows/ci-unit-tests.yml`, which configures with `-DBUILD_TESTING=ON`, builds, and runs `ctest` with C++20.
57+
58+
### C++ Standards Compliance
59+
The repository is tested for compliance with multiple C++ standards in `.github/workflows/ci-compliance.yml`. This workflow:
60+
- Tests against C++11, C++14, C++17, and C++20
61+
- Builds the project with each standard
62+
- Runs the `transaction_recording` example to verify basic functionality
63+
64+
To test a specific C++ standard locally:
65+
66+
```bash
67+
cmake --preset Debug -B build -DCMAKE_CXX_STANDARD=<11|14|17|20>
68+
cmake --build build -j
69+
./build/examples/transaction_recording/transaction_recording
70+
```

conanfile.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,13 @@ def requirements(self):
3939
self.requires("systemc/2.3.4")
4040
else:
4141
self.requires("systemc/3.0.1")
42+
if cppstd == "11":
43+
self.requires("gtest/1.10.0")
44+
else:
45+
self.requires("gtest/1.14.0")
4246
else:
4347
self.requires("systemc/2.3.4")
48+
self.requires("gtest/1.14.0")
4449
self.requires("fmt/8.0.1")
4550
self.requires("spdlog/1.9.2")
4651
self.requires("boost/1.85.0")

tests/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
find_package(GTest REQUIRED)
2+
3+
add_executable(memory_boundary_test
4+
memory_boundary_test.cpp
5+
gtest_sc_main.cpp
6+
)
7+
8+
target_link_libraries(memory_boundary_test
9+
PRIVATE
10+
GTest::gtest
11+
scc::scc
12+
SystemC::systemc
13+
)
14+
target_compile_definitions(memory_boundary_test PRIVATE SC_DISABLE_API_VERSION_CHECK)
15+
16+
add_test(NAME memory_boundary_test COMMAND memory_boundary_test)

tests/gtest_sc_main.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include <gtest/gtest.h>
2+
#include <systemc>
3+
4+
int sc_main(int argc, char* argv[]) {
5+
::testing::InitGoogleTest(&argc, argv);
6+
return RUN_ALL_TESTS();
7+
}

tests/memory_boundary_test.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Validates that handle_operation does not overwrite past the requested length
2+
#include <array>
3+
#include <systemc>
4+
#include <tlm>
5+
#include <gtest/gtest.h>
6+
7+
#include "components/scc/memory.h"
8+
#include "scc/cci_broker.h"
9+
10+
TEST(MemoryBoundaryTest, ReadAcrossPageDoesNotOverwriteBuffer) {
11+
static scc::cci_broker global_broker("global_broker");
12+
static auto broker_handle = cci::cci_register_broker(&global_broker);
13+
constexpr uint64_t kPageSize = 1ull << 24; // matches util::sparse_array default page size
14+
scc::memory<(kPageSize + 2u)> mem{"mem"};
15+
16+
std::array<uint8_t, 2> write_data{{0xAAu, 0xBBu}};
17+
tlm::tlm_generic_payload write;
18+
sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
19+
write.set_command(tlm::TLM_WRITE_COMMAND);
20+
write.set_address(kPageSize - 1); // straddles page boundary
21+
write.set_data_length(write_data.size());
22+
write.set_streaming_width(write_data.size());
23+
write.set_data_ptr(write_data.data());
24+
mem.handle_operation(write, delay);
25+
26+
std::array<uint8_t, 4> read_buf{{0xEEu, 0x00u, 0x00u, 0xEEu}};
27+
tlm::tlm_generic_payload read;
28+
read.set_command(tlm::TLM_READ_COMMAND);
29+
read.set_address(kPageSize - 1);
30+
read.set_data_length(write_data.size());
31+
read.set_streaming_width(write_data.size());
32+
read.set_data_ptr(read_buf.data() + 1); // leave guard bytes at both ends
33+
mem.handle_operation(read, delay);
34+
35+
EXPECT_EQ(read_buf[0], 0xEEu); // leading guard untouched
36+
EXPECT_EQ(read_buf[1], 0xAAu); // first byte read correctly
37+
EXPECT_EQ(read_buf[2], 0xBBu); // second byte read correctly
38+
EXPECT_EQ(read_buf[3], 0xEEu); // trailing guard must remain
39+
}

0 commit comments

Comments
 (0)