-
Notifications
You must be signed in to change notification settings - Fork 703
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CI: add test framework for C/C++ code
- Loading branch information
1 parent
358c627
commit ad97f00
Showing
5 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# Run CMake / CTest C++ unit tests | ||
|
||
name: ctest | ||
|
||
on: | ||
push: | ||
paths: | ||
- '**.cc?' | ||
- '**.cpp' | ||
- '**.cxx' | ||
- '**.hh?' | ||
- '**.hpp' | ||
- '**.hxx' | ||
- '**.CMakeLists' | ||
- '.github/workflows/ctest.yml' | ||
pull_request: | ||
paths: | ||
- '**.cc?' | ||
- '**.cpp' | ||
- '**.cxx' | ||
- '**.hh?' | ||
- '**.hpp' | ||
- '**.hxx' | ||
- '**.CMakeLists' | ||
- '.github/workflows/ctest.yml' | ||
|
||
jobs: | ||
ctest: | ||
runs-on: ${{ matrix.os }} | ||
name: Test C++ ${{ matrix.os }} | ||
|
||
strategy: | ||
fail-fast: false | ||
matrix: | ||
os: [ubuntu-latest, windows-latest] | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: ilammy/msvc-dev-cmd@v1 | ||
if: startsWith(matrix.os,'windows') | ||
- uses: Bacondish2023/setup-googletest@v1 | ||
with: | ||
build-type: 'Release' | ||
- name: Build tests | ||
run: | | ||
cd test/cpp | ||
mkdir build | ||
cmake -S . -B build/ -DCMAKE_BUILD_TYPE=Release | ||
cmake --build build/ --config Release | ||
ls | ||
- name: Run tests | ||
run: | | ||
cd test | ||
ctest --test-dir build/ -C Release --output-on-failure |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
cmake_minimum_required(VERSION 3.5) | ||
project(ap-cpp-tests) | ||
|
||
enable_testing() | ||
|
||
find_package(GTest REQUIRED) | ||
|
||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") | ||
add_definitions("/source-charset:utf-8") | ||
set(CMAKE_CXX_FLAGS_DEBUG "/MTd") | ||
set(CMAKE_CXX_FLAGS_RELEASE "/MT") | ||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") | ||
# enable static analysis for gcc | ||
add_compile_options(-fanalyzer -Werror) | ||
# disable stuff that gets triggered by googletest | ||
add_compile_options(-Wno-analyzer-malloc-leak) | ||
# enable asan for gcc | ||
add_compile_options(-fsanitize=address) | ||
add_link_options(-fsanitize=address) | ||
endif () | ||
|
||
add_executable(test_default) | ||
|
||
target_include_directories(test_default | ||
PRIVATE | ||
${GTEST_INCLUDE_DIRS} | ||
) | ||
|
||
target_link_libraries(test_default | ||
${GTEST_BOTH_LIBRARIES} | ||
) | ||
|
||
add_test( | ||
NAME test_default | ||
COMMAND test_default | ||
) | ||
|
||
set_property( | ||
TEST test_default | ||
PROPERTY ENVIRONMENT "ASAN_OPTIONS=allocator_may_return_null=1" | ||
) | ||
|
||
file(GLOB ITEMS *) | ||
foreach(item ${ITEMS}) | ||
if(IS_DIRECTORY ${item} AND EXISTS ${item}/CMakeLists.txt) | ||
message(${item}) | ||
add_subdirectory(${item}) | ||
endif() | ||
endforeach() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# C++ tests | ||
|
||
Test framework for C and C++ code in AP. | ||
|
||
## Adding a Test | ||
|
||
### GoogleTest | ||
|
||
Adding GoogleTests is as simple as creating a directory with | ||
* one or more `test_*.cpp` files that defines tests using | ||
[GoogleTest API](https://google.github.io/googletest/) | ||
* a `CMakeLists.txt` that adds the .cpp files to `test_default` target using | ||
[target_sources](https://cmake.org/cmake/help/latest/command/target_sources.html) | ||
|
||
### CTest | ||
|
||
If either GoogleTest is not suitable for the test or the build flags / sources / libraries are incompatible, | ||
you can add another CTest to project using add_target and add_test, similar to how it's done for `test_default`. | ||
|
||
## Running Tests | ||
|
||
* Install [CMake](https://cmake.org/). | ||
* Build and/or install GoogleTest and make sure | ||
[CMake can find it](https://cmake.org/cmake/help/latest/module/FindGTest.html), or | ||
[create a parent `CMakeLists.txt` that fetches GoogleTest](https://google.github.io/googletest/quickstart-cmake.html). | ||
* Enter the directory with the top-most `CMakeLists.txt` and run | ||
```sh | ||
mkdir build | ||
cmake -S . -B build/ -DCMAKE_BUILD_TYPE=Release | ||
cmake --build build/ --config Release && \ | ||
ctest --test-dir build/ -C Release --output-on-failure | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
target_sources(test_default | ||
PRIVATE | ||
${CMAKE_CURRENT_SOURCE_DIR}/test_intset.cpp | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
#include <limits> | ||
#include <cstdint> | ||
#include <gtest/gtest.h> | ||
|
||
// uint32Set | ||
#define INTSET_NAME uint32Set | ||
#define INTSET_TYPE uint32_t | ||
#include "../../../intset.h" | ||
#undef INTSET_NAME | ||
#undef INTSET_TYPE | ||
|
||
// int64Set | ||
#define INTSET_NAME int64Set | ||
#define INTSET_TYPE int64_t | ||
#include "../../../intset.h" | ||
|
||
|
||
TEST(IntsetTest, ZeroBuckets) | ||
{ | ||
// trying to allocate with zero buckets has to either fail or be functioning | ||
uint32Set *set = uint32Set_new(0); | ||
if (!set) | ||
return; // failed -> OK | ||
|
||
EXPECT_FALSE(uint32Set_contains(set, 1)); | ||
EXPECT_TRUE(uint32Set_add(set, 1)); | ||
EXPECT_TRUE(uint32Set_contains(set, 1)); | ||
uint32Set_free(set); | ||
} | ||
|
||
TEST(IntsetTest, Duplicate) | ||
{ | ||
// adding the same number again can't fail | ||
uint32Set *set = uint32Set_new(2); | ||
ASSERT_TRUE(set); | ||
EXPECT_TRUE(uint32Set_add(set, 0)); | ||
EXPECT_TRUE(uint32Set_add(set, 0)); | ||
EXPECT_TRUE(uint32Set_contains(set, 0)); | ||
uint32Set_free(set); | ||
} | ||
|
||
__attribute__((no_sanitize("address"))) | ||
static int64Set* int64Set_new_unchecked(size_t buckets) | ||
{ | ||
return int64Set_new(buckets); | ||
} | ||
|
||
TEST(IntsetTest, SetAllocFailure) | ||
{ | ||
// try to allocate 100TB of RAM, should fail and return NULL | ||
if (sizeof(size_t) < 8) | ||
GTEST_SKIP() << "Alloc error not testable on 32bit"; | ||
int64Set *set = int64Set_new_unchecked(6250000000000ULL); | ||
EXPECT_FALSE(set); | ||
int64Set_free(set); | ||
} | ||
|
||
TEST(IntsetTest, SetAllocOverflow) | ||
{ | ||
// try to overflow argument passed to malloc | ||
int64Set *set = int64Set_new(std::numeric_limits<size_t>::max()); | ||
EXPECT_FALSE(set); | ||
int64Set_free(set); | ||
} | ||
|
||
TEST(IntsetTest, NullFree) | ||
{ | ||
// free(NULL) should not try to free buckets | ||
uint32Set_free(NULL); | ||
int64Set_free(NULL); | ||
} | ||
|
||
TEST(IntsetTest, BucketRealloc) | ||
{ | ||
// add a couple of values to the same bucket to test growing the bucket | ||
uint32Set* set = uint32Set_new(1); | ||
ASSERT_TRUE(set); | ||
EXPECT_FALSE(uint32Set_contains(set, 0)); | ||
EXPECT_TRUE(uint32Set_add(set, 0)); | ||
EXPECT_TRUE(uint32Set_contains(set, 0)); | ||
for (uint32_t i = 1; i < 32; ++i) { | ||
EXPECT_TRUE(uint32Set_add(set, i)); | ||
EXPECT_TRUE(uint32Set_contains(set, i - 1)); | ||
EXPECT_TRUE(uint32Set_contains(set, i)); | ||
EXPECT_FALSE(uint32Set_contains(set, i + 1)); | ||
} | ||
uint32Set_free(set); | ||
} | ||
|
||
TEST(IntSet, Max) | ||
{ | ||
constexpr auto n = std::numeric_limits<uint32_t>::max(); | ||
uint32Set *set = uint32Set_new(1); | ||
ASSERT_TRUE(set); | ||
EXPECT_FALSE(uint32Set_contains(set, n)); | ||
EXPECT_TRUE(uint32Set_add(set, n)); | ||
EXPECT_TRUE(uint32Set_contains(set, n)); | ||
uint32Set_free(set); | ||
} | ||
|
||
TEST(InsetTest, Negative) | ||
{ | ||
constexpr auto n = std::numeric_limits<int64_t>::min(); | ||
static_assert(n < 0, "n not negative"); | ||
int64Set *set = int64Set_new(1); | ||
ASSERT_TRUE(set); | ||
EXPECT_FALSE(int64Set_contains(set, n)); | ||
EXPECT_TRUE(int64Set_add(set, n)); | ||
EXPECT_TRUE(int64Set_contains(set, n)); | ||
int64Set_free(set); | ||
} |