mirror of
https://github.com/orange-cpp/omath.git
synced 2026-02-12 22:53:27 +00:00
* Implement coverage for Windows/Linux/MacOS (#126) * reverted --------- Co-authored-by: Saikari <lin@sz.cn.eu.org>
This commit is contained in:
104
.github/workflows/cmake-multi-platform.yml
vendored
104
.github/workflows/cmake-multi-platform.yml
vendored
@@ -24,13 +24,15 @@ jobs:
|
||||
triplet: x64-linux
|
||||
runner: ubuntu-latest
|
||||
preset: linux-release-vcpkg
|
||||
coverage: true
|
||||
install_cmd: |
|
||||
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
|
||||
sudo add-apt-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y git build-essential cmake ninja-build \
|
||||
zip unzip curl pkg-config ca-certificates \
|
||||
clang-21 lld-21 libc++-21-dev libc++abi-21-dev
|
||||
clang-21 lld-21 libc++-21-dev libc++abi-21-dev \
|
||||
llvm-21
|
||||
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-21 100
|
||||
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-21 100
|
||||
sudo update-alternatives --install /usr/bin/lld lld /usr/bin/lld-21 100
|
||||
@@ -38,6 +40,7 @@ jobs:
|
||||
triplet: x86-linux
|
||||
runner: ubuntu-latest
|
||||
preset: linux-release-vcpkg-x86
|
||||
coverage: false
|
||||
install_cmd: |
|
||||
# Add LLVM 21 repository
|
||||
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
|
||||
@@ -65,6 +68,7 @@ jobs:
|
||||
triplet: arm64-linux
|
||||
runner: ubuntu-24.04-arm
|
||||
preset: linux-release-vcpkg-arm64
|
||||
coverage: false
|
||||
install_cmd: |
|
||||
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc
|
||||
sudo add-apt-repository -y "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-21 main"
|
||||
@@ -91,7 +95,7 @@ jobs:
|
||||
- name: Set up vcpkg
|
||||
shell: bash
|
||||
run: |
|
||||
git clone --branch 2025.12.12 --single-branch https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
cd "$VCPKG_ROOT"
|
||||
./bootstrap-vcpkg.sh
|
||||
|
||||
@@ -102,6 +106,7 @@ jobs:
|
||||
-DVCPKG_INSTALL_OPTIONS="--allow-unsupported" \
|
||||
-DOMATH_BUILD_TESTS=ON \
|
||||
-DOMATH_BUILD_BENCHMARK=OFF \
|
||||
-DOMATH_ENABLE_COVERAGE=${{ matrix.coverage == true && 'ON' || 'OFF' }} \
|
||||
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
|
||||
|
||||
- name: Build
|
||||
@@ -112,6 +117,25 @@ jobs:
|
||||
shell: bash
|
||||
run: ./out/Release/unit_tests
|
||||
|
||||
- name: Run Coverage
|
||||
if: ${{ matrix.coverage == true }}
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get install lcov
|
||||
chmod +x scripts/coverage-llvm.sh
|
||||
./scripts/coverage-llvm.sh \
|
||||
"${{ github.workspace }}" \
|
||||
"cmake-build/build/${{ matrix.preset }}" \
|
||||
"./out/Release/unit_tests" \
|
||||
"cmake-build/build/${{ matrix.preset }}/coverage"
|
||||
|
||||
- name: Upload Coverage Report
|
||||
if: ${{ matrix.coverage == true }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-report-linux
|
||||
path: cmake-build/build/${{ matrix.preset }}/coverage/
|
||||
|
||||
- name: Upload logs on failure
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -164,7 +188,7 @@ jobs:
|
||||
|
||||
- name: Configure (cmake --preset)
|
||||
shell: bash
|
||||
run: cmake --preset ${{ matrix.preset }} -DOMATH_BUILD_TESTS=ON -DOMATH_BUILD_BENCHMARK=OFF -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
|
||||
run: cmake --preset ${{ matrix.preset }} -DOMATH_BUILD_TESTS=ON -DOMATH_BUILD_BENCHMARK=OFF -DOMATH_ENABLE_COVERAGE=OFF -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
@@ -174,6 +198,36 @@ jobs:
|
||||
shell: bash
|
||||
run: ./out/Release/unit_tests.exe
|
||||
|
||||
- name: Install OpenCppCoverage with Chocolatey
|
||||
if: ${{ matrix.triplet == 'x64-windows' }}
|
||||
run: choco install opencppcoverage -y
|
||||
|
||||
- name: Build Debug for Coverage
|
||||
if: ${{ matrix.triplet == 'x64-windows' }}
|
||||
shell: bash
|
||||
run: |
|
||||
cmake --preset ${{ matrix.preset }} \
|
||||
-DOMATH_BUILD_TESTS=ON \
|
||||
-DOMATH_BUILD_BENCHMARK=OFF \
|
||||
-DOMATH_ENABLE_COVERAGE=ON \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
|
||||
cmake --build cmake-build/build/${{ matrix.preset }} --config Debug --target unit_tests omath
|
||||
|
||||
- name: Run Coverage
|
||||
if: ${{ matrix.triplet == 'x64-windows' }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
$env:Path = "C:\Program Files\OpenCppCoverage;$env:Path"
|
||||
cmake --build cmake-build/build/${{ matrix.preset }} --target coverage --config Debug
|
||||
|
||||
- name: Upload Coverage
|
||||
if: ${{ matrix.triplet == 'x64-windows' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-report-windows
|
||||
path: cmake-build/build/${{ matrix.preset }}/coverage/
|
||||
|
||||
- name: Upload logs on failure
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -193,13 +247,15 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- name: macOS (AppleClang) (arm64-osx)
|
||||
runner: macOS-latest
|
||||
runner: macos-latest
|
||||
preset: darwin-release-vcpkg
|
||||
triplet: arm64-osx
|
||||
coverage: true
|
||||
- name: macOS (AppleClang) (x64-osx)
|
||||
runner: macos-15-intel
|
||||
preset: darwin-release-vcpkg-x64
|
||||
triplet: x64-osx
|
||||
coverage: false
|
||||
fail-fast: false
|
||||
env:
|
||||
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
|
||||
@@ -217,20 +273,44 @@ jobs:
|
||||
- name: Set up vcpkg
|
||||
shell: bash
|
||||
run: |
|
||||
git clone --branch 2025.12.12 --single-branch https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
|
||||
- name: Configure (cmake --preset)
|
||||
shell: bash
|
||||
run: cmake --preset ${{matrix.preset}} -DOMATH_BUILD_TESTS=ON -DOMATH_BUILD_BENCHMARK=OFF -DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
|
||||
run: |
|
||||
cmake --preset ${{ matrix.preset }} \
|
||||
-DOMATH_BUILD_TESTS=ON \
|
||||
-DOMATH_BUILD_BENCHMARK=OFF \
|
||||
-DOMATH_ENABLE_COVERAGE=${{ matrix.coverage == true && 'ON' || 'OFF' }} \
|
||||
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests"
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: cmake --build cmake-build/build/${{matrix.preset}} --target unit_tests omath
|
||||
run: cmake --build cmake-build/build/${{ matrix.preset }} --target unit_tests omath
|
||||
|
||||
- name: Run unit_tests
|
||||
shell: bash
|
||||
run: ./out/Release/unit_tests
|
||||
|
||||
- name: Run Coverage
|
||||
if: ${{ matrix.coverage == true }}
|
||||
shell: bash
|
||||
run: |
|
||||
brew install lcov
|
||||
chmod +x scripts/coverage-llvm.sh
|
||||
./scripts/coverage-llvm.sh \
|
||||
"${{ github.workspace }}" \
|
||||
"cmake-build/build/${{ matrix.preset }}" \
|
||||
"./out/Release/unit_tests" \
|
||||
"cmake-build/build/${{ matrix.preset }}/coverage"
|
||||
|
||||
- name: Upload Coverage Report
|
||||
if: ${{ matrix.coverage == true }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: coverage-report-macos
|
||||
path: cmake-build/build/${{ matrix.preset }}/coverage/
|
||||
|
||||
- name: Upload logs on failure
|
||||
if: ${{ failure() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -268,7 +348,7 @@ jobs:
|
||||
- name: Set up vcpkg
|
||||
shell: bash
|
||||
run: |
|
||||
git clone --branch 2025.12.12 --single-branch https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
cd "$VCPKG_ROOT"
|
||||
./bootstrap-vcpkg.sh
|
||||
|
||||
@@ -330,7 +410,7 @@ jobs:
|
||||
# Build vcpkg in /tmp to avoid sshfs timestamp sync issues
|
||||
export VCPKG_ROOT=/tmp/vcpkg
|
||||
rm -rf "$VCPKG_ROOT"
|
||||
git clone --branch 2025.12.12 --single-branch https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
cd "$VCPKG_ROOT"
|
||||
./bootstrap-vcpkg.sh
|
||||
cd -
|
||||
@@ -399,7 +479,7 @@ jobs:
|
||||
- name: Set up vcpkg
|
||||
shell: bash
|
||||
run: |
|
||||
git clone --branch 2025.12.12 --single-branch https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
cd "$VCPKG_ROOT"
|
||||
./bootstrap-vcpkg.sh
|
||||
|
||||
@@ -468,7 +548,7 @@ jobs:
|
||||
- name: Set up vcpkg
|
||||
shell: bash
|
||||
run: |
|
||||
git clone --branch 2025.12.12 --single-branch https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
cd "$VCPKG_ROOT"
|
||||
./bootstrap-vcpkg.sh
|
||||
|
||||
@@ -554,7 +634,7 @@ jobs:
|
||||
|
||||
- name: Set up vcpkg
|
||||
run: |
|
||||
git clone --branch 2025.12.12 --single-branch https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
git clone https://github.com/microsoft/vcpkg "$VCPKG_ROOT"
|
||||
cd "$VCPKG_ROOT"
|
||||
./bootstrap-vcpkg.sh
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,4 +4,5 @@
|
||||
/extlibs/vcpkg
|
||||
.idea/workspace.xml
|
||||
/build/
|
||||
/clang-coverage/
|
||||
*.gcov
|
||||
@@ -5,6 +5,7 @@ project(omath VERSION ${OMATH_VERSION} LANGUAGES CXX)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(cmake/Coverage.cmake)
|
||||
|
||||
if (MSVC)
|
||||
check_cxx_compiler_flag("/arch:AVX2" COMPILER_SUPPORTS_AVX2)
|
||||
@@ -23,7 +24,7 @@ option(OMATH_STATIC_MSVC_RUNTIME_LIBRARY "Force Omath to link static runtime" OF
|
||||
option(OMATH_SUPRESS_SAFETY_CHECKS "Supress some safety checks in release build to improve general performance" ON)
|
||||
option(OMATH_USE_UNITY_BUILD "Will enable unity build to speed up compilation" OFF)
|
||||
option(OMATH_ENABLE_LEGACY "Will enable legacy classes that MUST be used ONLY for backward compatibility" ON)
|
||||
|
||||
option(OMATH_ENABLE_COVERAGE "Enable coverage" OFF)
|
||||
|
||||
if (VCPKG_MANIFEST_FEATURES)
|
||||
foreach (omath_feature IN LISTS VCPKG_MANIFEST_FEATURES)
|
||||
@@ -145,6 +146,9 @@ target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)
|
||||
if (OMATH_BUILD_TESTS)
|
||||
add_subdirectory(tests)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_BUILD_TESTS)
|
||||
if(OMATH_ENABLE_COVERAGE)
|
||||
omath_setup_coverage(${PROJECT_NAME})
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (OMATH_BUILD_BENCHMARK)
|
||||
|
||||
122
cmake/Coverage.cmake
Normal file
122
cmake/Coverage.cmake
Normal file
@@ -0,0 +1,122 @@
|
||||
# cmake/Coverage.cmake
|
||||
include_guard(GLOBAL)
|
||||
|
||||
function(omath_setup_coverage TARGET_NAME)
|
||||
if(ANDROID OR IOS OR EMSCRIPTEN)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang")
|
||||
# Apply to ALL configs when coverage is enabled (not just Debug)
|
||||
target_compile_options(${TARGET_NAME} PRIVATE
|
||||
-fprofile-instr-generate
|
||||
-fcoverage-mapping
|
||||
-g
|
||||
-O0
|
||||
)
|
||||
target_link_options(${TARGET_NAME} PRIVATE
|
||||
-fprofile-instr-generate
|
||||
-fcoverage-mapping
|
||||
)
|
||||
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
target_compile_options(${TARGET_NAME} PRIVATE
|
||||
--coverage
|
||||
-g
|
||||
-O0
|
||||
)
|
||||
target_link_options(${TARGET_NAME} PRIVATE
|
||||
--coverage
|
||||
)
|
||||
|
||||
elseif(MSVC)
|
||||
target_compile_options(${TARGET_NAME} PRIVATE
|
||||
/Zi
|
||||
/Od
|
||||
/Ob0
|
||||
)
|
||||
target_link_options(${TARGET_NAME} PRIVATE
|
||||
/DEBUG:FULL
|
||||
/INCREMENTAL:NO
|
||||
)
|
||||
endif()
|
||||
|
||||
# Create coverage target only once
|
||||
if(TARGET coverage)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(MSVC OR MINGW)
|
||||
# Windows: OpenCppCoverage
|
||||
find_program(OPENCPPCOVERAGE_EXECUTABLE
|
||||
NAMES OpenCppCoverage OpenCppCoverage.exe
|
||||
PATHS
|
||||
"$ENV{ProgramFiles}/OpenCppCoverage"
|
||||
"$ENV{ProgramW6432}/OpenCppCoverage"
|
||||
"C:/Program Files/OpenCppCoverage"
|
||||
DOC "Path to OpenCppCoverage executable"
|
||||
)
|
||||
|
||||
if(NOT OPENCPPCOVERAGE_EXECUTABLE)
|
||||
message(WARNING "OpenCppCoverage not found. Install with: choco install opencppcoverage")
|
||||
set(OPENCPPCOVERAGE_EXECUTABLE "C:/Program Files/OpenCppCoverage/OpenCppCoverage.exe")
|
||||
else()
|
||||
message(STATUS "Found OpenCppCoverage: ${OPENCPPCOVERAGE_EXECUTABLE}")
|
||||
endif()
|
||||
|
||||
file(TO_NATIVE_PATH "${CMAKE_SOURCE_DIR}" COVERAGE_ROOT_PATH)
|
||||
file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/coverage" COVERAGE_OUTPUT_PATH)
|
||||
file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/coverage.xml" COVERAGE_XML_PATH)
|
||||
file(TO_NATIVE_PATH "${OPENCPPCOVERAGE_EXECUTABLE}" OPENCPPCOVERAGE_NATIVE)
|
||||
|
||||
add_custom_target(coverage
|
||||
DEPENDS unit_tests
|
||||
COMMAND "${OPENCPPCOVERAGE_NATIVE}"
|
||||
--verbose
|
||||
--sources "${COVERAGE_ROOT_PATH}"
|
||||
--modules "${COVERAGE_ROOT_PATH}"
|
||||
--excluded_sources "*\\tests\\*"
|
||||
--excluded_sources "*\\gtest\\*"
|
||||
--excluded_sources "*\\googletest\\*"
|
||||
--excluded_sources "*\\_deps\\*"
|
||||
--excluded_sources "*\\vcpkg_installed\\*"
|
||||
--export_type "html:${COVERAGE_OUTPUT_PATH}"
|
||||
--export_type "cobertura:${COVERAGE_XML_PATH}"
|
||||
--cover_children
|
||||
-- "$<TARGET_FILE:unit_tests>"
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Running OpenCppCoverage"
|
||||
)
|
||||
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang")
|
||||
# Linux/macOS: LLVM coverage via script
|
||||
add_custom_target(coverage
|
||||
DEPENDS unit_tests
|
||||
COMMAND bash "${CMAKE_SOURCE_DIR}/scripts/coverage-llvm.sh"
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
"${CMAKE_BINARY_DIR}"
|
||||
"$<TARGET_FILE:unit_tests>"
|
||||
"${CMAKE_BINARY_DIR}/coverage"
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Running LLVM coverage"
|
||||
)
|
||||
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# GCC: lcov/gcov
|
||||
add_custom_target(coverage
|
||||
DEPENDS unit_tests
|
||||
COMMAND $<TARGET_FILE:unit_tests> || true
|
||||
COMMAND lcov --capture --directory "${CMAKE_BINARY_DIR}"
|
||||
--output-file "${CMAKE_BINARY_DIR}/coverage.info"
|
||||
--ignore-errors mismatch,gcov
|
||||
COMMAND lcov --remove "${CMAKE_BINARY_DIR}/coverage.info"
|
||||
"*/tests/*" "*/gtest/*" "*/googletest/*" "*/_deps/*" "/usr/*"
|
||||
--output-file "${CMAKE_BINARY_DIR}/coverage.info"
|
||||
--ignore-errors unused
|
||||
COMMAND genhtml "${CMAKE_BINARY_DIR}/coverage.info"
|
||||
--output-directory "${CMAKE_BINARY_DIR}/coverage"
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||
COMMENT "Running lcov/genhtml"
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -46,27 +46,26 @@ namespace omath
|
||||
|
||||
switch (i % 6)
|
||||
{
|
||||
case 0:
|
||||
r = value, g = t, b = p;
|
||||
break;
|
||||
case 1:
|
||||
r = q, g = value, b = p;
|
||||
break;
|
||||
case 2:
|
||||
r = p, g = value, b = t;
|
||||
break;
|
||||
case 3:
|
||||
r = p, g = q, b = value;
|
||||
break;
|
||||
case 4:
|
||||
r = t, g = p, b = value;
|
||||
break;
|
||||
case 5:
|
||||
r = value, g = p, b = q;
|
||||
break;
|
||||
|
||||
default:
|
||||
return {0.f, 0.f, 0.f, 0.f};
|
||||
case 0:
|
||||
r = value, g = t, b = p;
|
||||
break;
|
||||
case 1:
|
||||
r = q, g = value, b = p;
|
||||
break;
|
||||
case 2:
|
||||
r = p, g = value, b = t;
|
||||
break;
|
||||
case 3:
|
||||
r = p, g = q, b = value;
|
||||
break;
|
||||
case 4:
|
||||
r = t, g = p, b = value;
|
||||
break;
|
||||
case 5:
|
||||
r = value, g = p, b = q;
|
||||
break;
|
||||
default:
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
return {r, g, b, 1.f};
|
||||
|
||||
169
scripts/coverage-llvm.sh
Executable file
169
scripts/coverage-llvm.sh
Executable file
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/coverage-llvm.sh
|
||||
# LLVM coverage script that generates LCOV-style reports
|
||||
|
||||
set -e
|
||||
|
||||
SOURCE_DIR="${1:-.}"
|
||||
BINARY_DIR="${2:-cmake-build/build}"
|
||||
TEST_BINARY="${3:-}"
|
||||
OUTPUT_DIR="${4:-${BINARY_DIR}/coverage}"
|
||||
|
||||
echo "[*] Source dir: ${SOURCE_DIR}"
|
||||
echo "[*] Binary dir: ${BINARY_DIR}"
|
||||
echo "[*] Output dir: ${OUTPUT_DIR}"
|
||||
|
||||
# Find llvm tools - handle versioned names (Linux) and xcrun (macOS)
|
||||
find_llvm_tool() {
|
||||
local tool_name="$1"
|
||||
|
||||
# macOS: use xcrun
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
if xcrun --find "${tool_name}" &>/dev/null; then
|
||||
echo "xcrun ${tool_name}"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# Try versioned names (Linux with LLVM 21, 20, 19, etc.)
|
||||
for version in 21 20 19 18 17 ""; do
|
||||
local versioned_name="${tool_name}${version:+-$version}"
|
||||
if command -v "${versioned_name}" &>/dev/null; then
|
||||
echo "${versioned_name}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
return 1
|
||||
}
|
||||
|
||||
LLVM_PROFDATA=$(find_llvm_tool "llvm-profdata")
|
||||
LLVM_COV=$(find_llvm_tool "llvm-cov")
|
||||
|
||||
if [[ -z "${LLVM_PROFDATA}" ]] || [[ -z "${LLVM_COV}" ]]; then
|
||||
echo "Error: llvm-profdata or llvm-cov not found" >&2
|
||||
echo "On Linux, install llvm or clang package" >&2
|
||||
echo "On macOS, Xcode command line tools should provide these" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Using: ${LLVM_PROFDATA}"
|
||||
echo "[*] Using: ${LLVM_COV}"
|
||||
|
||||
# Find test binary
|
||||
if [[ -z "${TEST_BINARY}" ]]; then
|
||||
for path in \
|
||||
"${SOURCE_DIR}/out/Debug/unit_tests" \
|
||||
"${SOURCE_DIR}/out/Release/unit_tests" \
|
||||
"${BINARY_DIR}/unit_tests" \
|
||||
"${BINARY_DIR}/tests/unit_tests"; do
|
||||
if [[ -x "${path}" ]]; then
|
||||
TEST_BINARY="${path}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ -z "${TEST_BINARY}" ]] || [[ ! -x "${TEST_BINARY}" ]]; then
|
||||
echo "Error: unit_tests binary not found" >&2
|
||||
echo "Searched in: out/Debug, out/Release, ${BINARY_DIR}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Test binary: ${TEST_BINARY}"
|
||||
|
||||
# Clean previous coverage data
|
||||
rm -rf "${OUTPUT_DIR}"
|
||||
rm -f "${BINARY_DIR}"/*.profraw "${BINARY_DIR}"/*.profdata
|
||||
mkdir -p "${OUTPUT_DIR}"
|
||||
|
||||
# Run tests with profiling enabled
|
||||
PROFILE_FILE="${BINARY_DIR}/default_%p.profraw"
|
||||
echo "[*] Running tests with LLVM_PROFILE_FILE=${PROFILE_FILE}"
|
||||
|
||||
export LLVM_PROFILE_FILE="${PROFILE_FILE}"
|
||||
"${TEST_BINARY}" || echo "[!] Some tests failed, continuing with coverage..."
|
||||
|
||||
# Find all generated .profraw files
|
||||
PROFRAW_FILES=$(find "${BINARY_DIR}" -name "*.profraw" -type f 2>/dev/null)
|
||||
if [[ -z "${PROFRAW_FILES}" ]]; then
|
||||
# Also check current directory
|
||||
PROFRAW_FILES=$(find . -maxdepth 3 -name "*.profraw" -type f 2>/dev/null)
|
||||
fi
|
||||
|
||||
if [[ -z "${PROFRAW_FILES}" ]]; then
|
||||
echo "Error: No .profraw files generated" >&2
|
||||
echo "Make sure the binary was built with -fprofile-instr-generate -fcoverage-mapping" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Found profraw files:"
|
||||
echo "${PROFRAW_FILES}"
|
||||
|
||||
# Merge profiles
|
||||
PROFDATA_FILE="${BINARY_DIR}/coverage.profdata"
|
||||
echo "[*] Merging profiles into ${PROFDATA_FILE}"
|
||||
${LLVM_PROFDATA} merge -sparse ${PROFRAW_FILES} -o "${PROFDATA_FILE}"
|
||||
|
||||
# Generate text summary
|
||||
echo "[*] Coverage Summary:"
|
||||
${LLVM_COV} report "${TEST_BINARY}" \
|
||||
-instr-profile="${PROFDATA_FILE}" \
|
||||
-ignore-filename-regex="tests/.*" \
|
||||
-ignore-filename-regex="googletest/.*" \
|
||||
-ignore-filename-regex="gtest/.*" \
|
||||
-ignore-filename-regex="_deps/.*" \
|
||||
-ignore-filename-regex="vcpkg_installed/.*"
|
||||
|
||||
# Export lcov format (for tools like codecov)
|
||||
LCOV_FILE="${OUTPUT_DIR}/coverage.lcov"
|
||||
echo "[*] Exporting LCOV format to ${LCOV_FILE}"
|
||||
${LLVM_COV} export "${TEST_BINARY}" \
|
||||
-instr-profile="${PROFDATA_FILE}" \
|
||||
-format=lcov \
|
||||
-ignore-filename-regex="tests/.*" \
|
||||
-ignore-filename-regex="googletest/.*" \
|
||||
-ignore-filename-regex="gtest/.*" \
|
||||
-ignore-filename-regex="_deps/.*" \
|
||||
-ignore-filename-regex="vcpkg_installed/.*" \
|
||||
> "${LCOV_FILE}" || true
|
||||
|
||||
# Generate LCOV-style HTML report using genhtml
|
||||
if command -v genhtml >/dev/null 2>&1; then
|
||||
echo "[*] Generating LCOV-style HTML report using genhtml"
|
||||
genhtml "${LCOV_FILE}" \
|
||||
--ignore-errors inconsistent,corrupt \
|
||||
--output-directory "${OUTPUT_DIR}" \
|
||||
--title "Omath Coverage Report" \
|
||||
--show-details \
|
||||
--legend \
|
||||
--demangle-cpp \
|
||||
--num-spaces 4 \
|
||||
--sort \
|
||||
--function-coverage \
|
||||
--branch-coverage
|
||||
|
||||
echo "[*] LCOV-style HTML report generated at: ${OUTPUT_DIR}/index.html"
|
||||
else
|
||||
echo "[!] genhtml not found. Installing lcov package..."
|
||||
echo "[!] On Ubuntu/Debian: sudo apt-get install lcov"
|
||||
echo "[!] On macOS: brew install lcov"
|
||||
echo "[!] Falling back to LLVM HTML report..."
|
||||
|
||||
# Fall back to LLVM HTML report
|
||||
${LLVM_COV} show "${TEST_BINARY}" \
|
||||
-instr-profile="${PROFDATA_FILE}" \
|
||||
-format=html \
|
||||
-output-dir="${OUTPUT_DIR}" \
|
||||
-show-line-counts-or-regions \
|
||||
-show-instantiations=false \
|
||||
-ignore-filename-regex="tests/.*" \
|
||||
-ignore-filename-regex="googletest/.*" \
|
||||
-ignore-filename-regex="gtest/.*" \
|
||||
-ignore-filename-regex="_deps/.*" \
|
||||
-ignore-filename-regex="vcpkg_installed/.*"
|
||||
fi
|
||||
|
||||
echo "[*] Coverage report generated at: ${OUTPUT_DIR}/index.html"
|
||||
echo "[*] LCOV file at: ${LCOV_FILE}"
|
||||
8
scripts/coverage.bat.in
Normal file
8
scripts/coverage.bat.in
Normal file
@@ -0,0 +1,8 @@
|
||||
@echo off
|
||||
REM scripts/coverage.bat.in
|
||||
REM Simple wrapper to run coverage.ps1
|
||||
|
||||
set SOURCE_DIR=@CMAKE_SOURCE_DIR@
|
||||
set BINARY_DIR=@CMAKE_BINARY_DIR@
|
||||
|
||||
powershell -ExecutionPolicy Bypass -File "%BINARY_DIR%\scripts\coverage.ps1" -SourceDir "%SOURCE_DIR%" -BinaryDir "%BINARY_DIR%" %*
|
||||
132
scripts/coverage.ps1.in
Normal file
132
scripts/coverage.ps1.in
Normal file
@@ -0,0 +1,132 @@
|
||||
# scripts/coverage.ps1.in
|
||||
# Windows coverage script using OpenCppCoverage
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$SourceDir,
|
||||
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$BinaryDir,
|
||||
|
||||
[string]$TestBinary = "",
|
||||
[switch]$Cobertura,
|
||||
[switch]$Html
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# CMake-injected variables
|
||||
$LCOV_IGNORE_ERRORS = '@LCOV_IGNORE_ERRORS@'
|
||||
|
||||
# Resolve paths
|
||||
$SourceDir = Resolve-Path $SourceDir
|
||||
$BinaryDir = Resolve-Path $BinaryDir
|
||||
|
||||
Write-Host "[*] Source directory: $SourceDir" -ForegroundColor Cyan
|
||||
Write-Host "[*] Binary directory: $BinaryDir" -ForegroundColor Cyan
|
||||
|
||||
# Find test binary
|
||||
if (-not $TestBinary) {
|
||||
$searchPaths = @(
|
||||
"$BinaryDir\Debug\unit_tests.exe",
|
||||
"$BinaryDir\Release\unit_tests.exe",
|
||||
"$BinaryDir\unit_tests.exe",
|
||||
"$SourceDir\out\Debug\unit_tests.exe",
|
||||
"$SourceDir\out\Release\unit_tests.exe"
|
||||
)
|
||||
|
||||
foreach ($path in $searchPaths) {
|
||||
if (Test-Path $path) {
|
||||
$TestBinary = $path
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $TestBinary -or -not (Test-Path $TestBinary)) {
|
||||
Write-Error "unit_tests.exe not found. Searched: $($searchPaths -join ', ')"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$TestBinary = Resolve-Path $TestBinary
|
||||
Write-Host "[*] Test binary: $TestBinary" -ForegroundColor Cyan
|
||||
|
||||
# Check for OpenCppCoverage
|
||||
$opencppcov = Get-Command "OpenCppCoverage" -ErrorAction SilentlyContinue
|
||||
if (-not $opencppcov) {
|
||||
# Try common installation paths
|
||||
$possiblePaths = @(
|
||||
"$env:ProgramFiles\OpenCppCoverage\OpenCppCoverage.exe",
|
||||
"${env:ProgramFiles(x86)}\OpenCppCoverage\OpenCppCoverage.exe",
|
||||
"$env:LOCALAPPDATA\Programs\OpenCppCoverage\OpenCppCoverage.exe"
|
||||
)
|
||||
|
||||
foreach ($path in $possiblePaths) {
|
||||
if (Test-Path $path) {
|
||||
$opencppcov = Get-Item $path
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $opencppcov) {
|
||||
Write-Host @"
|
||||
OpenCppCoverage not found!
|
||||
|
||||
Install it from: https://github.com/OpenCppCoverage/OpenCppCoverage/releases
|
||||
|
||||
Or via Chocolatey:
|
||||
choco install opencppcoverage
|
||||
|
||||
Or via winget:
|
||||
winget install OpenCppCoverage.OpenCppCoverage
|
||||
"@ -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$OpenCppCoveragePath = if ($opencppcov.Source) { $opencppcov.Source } else { $opencppcov.FullName }
|
||||
Write-Host "[*] Using OpenCppCoverage: $OpenCppCoveragePath" -ForegroundColor Cyan
|
||||
|
||||
# Create output directory
|
||||
$CoverageDir = Join-Path $BinaryDir "coverage"
|
||||
if (-not (Test-Path $CoverageDir)) {
|
||||
New-Item -ItemType Directory -Path $CoverageDir | Out-Null
|
||||
}
|
||||
|
||||
# Build OpenCppCoverage arguments
|
||||
$coverageArgs = @(
|
||||
"--sources", "$SourceDir\include",
|
||||
"--sources", "$SourceDir\source",
|
||||
"--excluded_sources", "*\tests\*",
|
||||
"--excluded_sources", "*\googletest\*",
|
||||
"--excluded_sources", "*\gtest\*",
|
||||
"--excluded_sources", "*\_deps\*",
|
||||
"--excluded_sources", "*\vcpkg_installed\*",
|
||||
"--export_type", "html:$CoverageDir",
|
||||
"--export_type", "cobertura:$CoverageDir\coverage.xml",
|
||||
"--cover_children",
|
||||
"--"
|
||||
)
|
||||
|
||||
Write-Host "[*] Running OpenCppCoverage..." -ForegroundColor Cyan
|
||||
Write-Host " Command: $OpenCppCoveragePath $($coverageArgs -join ' ') $TestBinary"
|
||||
|
||||
& $OpenCppCoveragePath @coverageArgs $TestBinary
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Warning "OpenCppCoverage exited with code $LASTEXITCODE (tests may have failed)"
|
||||
}
|
||||
|
||||
# Check outputs
|
||||
$htmlIndex = Join-Path $CoverageDir "index.html"
|
||||
$coberturaXml = Join-Path $CoverageDir "coverage.xml"
|
||||
|
||||
if (Test-Path $htmlIndex) {
|
||||
Write-Host "[*] HTML coverage report: $htmlIndex" -ForegroundColor Green
|
||||
}
|
||||
|
||||
if (Test-Path $coberturaXml) {
|
||||
Write-Host "[*] Cobertura XML report: $coberturaXml" -ForegroundColor Green
|
||||
}
|
||||
|
||||
Write-Host "[*] Coverage collection complete!" -ForegroundColor Green
|
||||
@@ -21,6 +21,11 @@ else() # GTest is being linked as vcpkg package
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE GTest::gtest GTest::gtest_main omath::omath)
|
||||
endif()
|
||||
|
||||
if(OMATH_ENABLE_COVERAGE)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/Coverage.cmake)
|
||||
omath_setup_coverage(${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
# Skip test discovery for Android/iOS builds or when cross-compiling - binaries cannot run on host
|
||||
if (NOT (ANDROID OR IOS OR EMSCRIPTEN))
|
||||
gtest_discover_tests(${PROJECT_NAME})
|
||||
|
||||
297
tests/engines/unit_test_traits_engines.cpp
Normal file
297
tests/engines/unit_test_traits_engines.cpp
Normal file
@@ -0,0 +1,297 @@
|
||||
// Tests for engine trait headers to improve header coverage
|
||||
#include <gtest/gtest.h>
|
||||
#include <omath/engines/frostbite_engine/traits/pred_engine_trait.hpp>
|
||||
#include <omath/engines/frostbite_engine/traits/mesh_trait.hpp>
|
||||
#include <omath/engines/frostbite_engine/traits/camera_trait.hpp>
|
||||
|
||||
#include <omath/engines/iw_engine/traits/pred_engine_trait.hpp>
|
||||
#include <omath/engines/iw_engine/traits/mesh_trait.hpp>
|
||||
#include <omath/engines/iw_engine/traits/camera_trait.hpp>
|
||||
|
||||
#include <omath/engines/opengl_engine/traits/pred_engine_trait.hpp>
|
||||
#include <omath/engines/opengl_engine/traits/mesh_trait.hpp>
|
||||
#include <omath/engines/opengl_engine/traits/camera_trait.hpp>
|
||||
|
||||
#include <omath/engines/unity_engine/traits/pred_engine_trait.hpp>
|
||||
#include <omath/engines/unity_engine/traits/mesh_trait.hpp>
|
||||
#include <omath/engines/unity_engine/traits/camera_trait.hpp>
|
||||
|
||||
#include <omath/engines/unreal_engine/traits/pred_engine_trait.hpp>
|
||||
#include <omath/engines/unreal_engine/traits/mesh_trait.hpp>
|
||||
#include <omath/engines/unreal_engine/traits/camera_trait.hpp>
|
||||
|
||||
#include <omath/projectile_prediction/projectile.hpp>
|
||||
#include <omath/projectile_prediction/target.hpp>
|
||||
#include <optional>
|
||||
|
||||
using namespace omath;
|
||||
|
||||
// Small helper to compare matrices roughly (templated to avoid concrete typedef)
|
||||
template<typename MatT>
|
||||
static void expect_matrix_near(const MatT& a, const MatT& b, float eps = 1e-5f)
|
||||
{
|
||||
for (std::size_t r = 0; r < 4; ++r)
|
||||
for (std::size_t c = 0; c < 4; ++c)
|
||||
EXPECT_NEAR(a.at(r, c), b.at(r, c), eps);
|
||||
}
|
||||
|
||||
// Generic tests for PredEngineTrait behaviour across engines
|
||||
TEST(TraitTests, Frostbite_Pred_And_Mesh_And_Camera)
|
||||
{
|
||||
namespace E = omath::frostbite_engine;
|
||||
|
||||
projectile_prediction::Projectile p;
|
||||
p.m_origin = {0.f, 0.f, 0.f};
|
||||
p.m_launch_speed = 10.f;
|
||||
p.m_gravity_scale = 1.f;
|
||||
|
||||
const auto pos = E::PredEngineTrait::predict_projectile_position(p, 0.f, 0.f, 1.f, 9.81f);
|
||||
EXPECT_NEAR(pos.x, 0.f, 1e-4f);
|
||||
EXPECT_NEAR(pos.z, 10.f, 1e-4f);
|
||||
EXPECT_NEAR(pos.y, -9.81f * 0.5f, 1e-4f);
|
||||
|
||||
projectile_prediction::Target t;
|
||||
t.m_origin = {0.f, 5.f, 0.f};
|
||||
t.m_velocity = {2.f, 0.f, 0.f};
|
||||
t.m_is_airborne = true;
|
||||
const auto pred = E::PredEngineTrait::predict_target_position(t, 2.f, 9.81f);
|
||||
EXPECT_NEAR(pred.x, 4.f, 1e-6f);
|
||||
EXPECT_NEAR(pred.y, 5.f - 9.81f * (2.f * 2.f) * 0.5f, 1e-6f);
|
||||
|
||||
// Also test non-airborne path (no gravity applied)
|
||||
t.m_is_airborne = false;
|
||||
const auto pred_ground = E::PredEngineTrait::predict_target_position(t, 2.f, 9.81f);
|
||||
EXPECT_NEAR(pred_ground.x, 4.f, 1e-6f);
|
||||
EXPECT_NEAR(pred_ground.y, 5.f, 1e-6f);
|
||||
|
||||
EXPECT_NEAR(E::PredEngineTrait::calc_vector_2d_distance({3.f, 0.f, 4.f}), 5.f, 1e-6f);
|
||||
EXPECT_NEAR(E::PredEngineTrait::get_vector_height_coordinate({1.f, 2.5f, 3.f}), 2.5f, 1e-6f);
|
||||
|
||||
std::optional<float> pitch = 45.f;
|
||||
auto vp = E::PredEngineTrait::calc_viewpoint_from_angles(p, {10.f, 0.f, 0.f}, pitch);
|
||||
EXPECT_NEAR(vp.y, 0.f + 10.f * std::tan(angles::degrees_to_radians(45.f)), 1e-6f);
|
||||
|
||||
// Direct angles
|
||||
Vector3<float> origin{0.f, 0.f, 0.f};
|
||||
Vector3<float> view_to{0.f, 1.f, 1.f};
|
||||
const auto pitch_calc = E::PredEngineTrait::calc_direct_pitch_angle(origin, view_to);
|
||||
const auto dir = (view_to - origin).normalized();
|
||||
EXPECT_NEAR(pitch_calc, angles::radians_to_degrees(std::asin(dir.y)), 1e-3f);
|
||||
|
||||
const auto yaw_calc = E::PredEngineTrait::calc_direct_yaw_angle(origin, view_to);
|
||||
EXPECT_NEAR(yaw_calc, angles::radians_to_degrees(std::atan2(dir.x, dir.z)), 1e-3f);
|
||||
|
||||
// MeshTrait simply forwards to rotation_matrix; ensure it compiles and returns something
|
||||
E::ViewAngles va;
|
||||
const auto m1 = E::MeshTrait::rotation_matrix(va);
|
||||
const auto m2 = E::rotation_matrix(va);
|
||||
expect_matrix_near(m1, m2);
|
||||
|
||||
// CameraTrait look at should be callable
|
||||
const auto angles = E::CameraTrait::calc_look_at_angle({0, 0, 0}, {0, 1, 1});
|
||||
(void)angles;
|
||||
const auto proj = E::CameraTrait::calc_projection_matrix(projection::FieldOfView::from_degrees(60.f), {1280.f, 720.f}, 0.1f, 1000.f);
|
||||
const auto expected = E::calc_perspective_projection_matrix(60.f, 1280.f / 720.f, 0.1f, 1000.f);
|
||||
expect_matrix_near(proj, expected);
|
||||
}
|
||||
|
||||
TEST(TraitTests, IW_Pred_And_Mesh_And_Camera)
|
||||
{
|
||||
namespace E = omath::iw_engine;
|
||||
|
||||
projectile_prediction::Projectile p;
|
||||
p.m_origin = {0.f, 0.f, 0.f};
|
||||
p.m_launch_speed = 10.f;
|
||||
p.m_gravity_scale = 1.f;
|
||||
|
||||
const auto pos = E::PredEngineTrait::predict_projectile_position(p, 0.f, 0.f, 1.f, 9.81f);
|
||||
EXPECT_NEAR(pos.x, 10.f, 1e-4f);
|
||||
EXPECT_NEAR(pos.z, -9.81f * 0.5f, 1e-4f);
|
||||
|
||||
projectile_prediction::Target t;
|
||||
t.m_origin = {0.f, 0.f, 5.f};
|
||||
t.m_velocity = {0.f, 0.f, 2.f};
|
||||
t.m_is_airborne = true;
|
||||
const auto pred = E::PredEngineTrait::predict_target_position(t, 2.f, 9.81f);
|
||||
// predicted = origin + velocity * t -> z = 5 + 2*2 = 9; then gravity applied
|
||||
EXPECT_NEAR(pred.z, 9.f - 9.81f * (2.f * 2.f) * 0.5f, 1e-6f);
|
||||
|
||||
EXPECT_NEAR(E::PredEngineTrait::calc_vector_2d_distance({3.f, 4.f, 0.f}), 5.f, 1e-6f);
|
||||
EXPECT_NEAR(E::PredEngineTrait::get_vector_height_coordinate({1.f, 2.5f, 3.f}), 3.f, 1e-6f);
|
||||
|
||||
std::optional<float> pitch = 45.f;
|
||||
auto vp = E::PredEngineTrait::calc_viewpoint_from_angles(p, {10.f, 0.f, 0.f}, pitch);
|
||||
EXPECT_NEAR(vp.z, 0.f + 10.f * std::tan(angles::degrees_to_radians(45.f)), 1e-6f);
|
||||
|
||||
Vector3<float> origin{0.f, 0.f, 0.f};
|
||||
Vector3<float> view_to{1.f, 1.f, 1.f};
|
||||
const auto pitch_calc = E::PredEngineTrait::calc_direct_pitch_angle(origin, view_to);
|
||||
const auto dist = origin.distance_to(view_to);
|
||||
EXPECT_NEAR(pitch_calc, angles::radians_to_degrees(std::asin((view_to.z - origin.z) / dist)), 1e-3f);
|
||||
|
||||
const auto yaw_calc = E::PredEngineTrait::calc_direct_yaw_angle(origin, view_to);
|
||||
const auto delta = view_to - origin;
|
||||
EXPECT_NEAR(yaw_calc, angles::radians_to_degrees(std::atan2(delta.y, delta.x)), 1e-3f);
|
||||
|
||||
E::ViewAngles va;
|
||||
expect_matrix_near(E::MeshTrait::rotation_matrix(va), E::rotation_matrix(va));
|
||||
|
||||
const auto proj = E::CameraTrait::calc_projection_matrix(projection::FieldOfView::from_degrees(45.f), {1920.f, 1080.f}, 0.1f, 1000.f);
|
||||
const auto expected = E::calc_perspective_projection_matrix(45.f, 1920.f / 1080.f, 0.1f, 1000.f);
|
||||
expect_matrix_near(proj, expected);
|
||||
|
||||
// non-airborne
|
||||
t.m_is_airborne = false;
|
||||
const auto pred_ground_iw = E::PredEngineTrait::predict_target_position(t, 2.f, 9.81f);
|
||||
EXPECT_NEAR(pred_ground_iw.z, 9.f, 1e-6f);
|
||||
}
|
||||
|
||||
TEST(TraitTests, OpenGL_Pred_And_Mesh_And_Camera)
|
||||
{
|
||||
namespace E = omath::opengl_engine;
|
||||
|
||||
projectile_prediction::Projectile p;
|
||||
p.m_origin = {0.f, 0.f, 0.f};
|
||||
p.m_launch_speed = 10.f;
|
||||
p.m_gravity_scale = 1.f;
|
||||
|
||||
const auto pos = E::PredEngineTrait::predict_projectile_position(p, 0.f, 0.f, 1.f, 9.81f);
|
||||
EXPECT_NEAR(pos.z, -10.f, 1e-4f);
|
||||
EXPECT_NEAR(pos.y, -9.81f * 0.5f, 1e-4f);
|
||||
|
||||
projectile_prediction::Target t;
|
||||
t.m_origin = {0.f, 5.f, 0.f};
|
||||
t.m_velocity = {2.f, 0.f, 0.f};
|
||||
t.m_is_airborne = true;
|
||||
const auto pred = E::PredEngineTrait::predict_target_position(t, 2.f, 9.81f);
|
||||
EXPECT_NEAR(pred.x, 4.f, 1e-6f);
|
||||
EXPECT_NEAR(pred.y, 5.f - 9.81f * (2.f * 2.f) * 0.5f, 1e-6f);
|
||||
|
||||
EXPECT_NEAR(E::PredEngineTrait::calc_vector_2d_distance({3.f, 0.f, 4.f}), 5.f, 1e-6f);
|
||||
EXPECT_NEAR(E::PredEngineTrait::get_vector_height_coordinate({1.f, 2.5f, 3.f}), 2.5f, 1e-6f);
|
||||
|
||||
std::optional<float> pitch = 45.f;
|
||||
auto vp = E::PredEngineTrait::calc_viewpoint_from_angles(p, {10.f, 0.f, 0.f}, pitch);
|
||||
EXPECT_NEAR(vp.y, 0.f + 10.f * std::tan(angles::degrees_to_radians(45.f)), 1e-6f);
|
||||
|
||||
Vector3<float> origin{0.f, 0.f, 0.f};
|
||||
Vector3<float> view_to{0.f, 1.f, 1.f};
|
||||
const auto pitch_calc = E::PredEngineTrait::calc_direct_pitch_angle(origin, view_to);
|
||||
const auto dir = (view_to - origin).normalized();
|
||||
EXPECT_NEAR(pitch_calc, angles::radians_to_degrees(std::asin(dir.y)), 1e-3f);
|
||||
|
||||
const auto yaw_calc = E::PredEngineTrait::calc_direct_yaw_angle(origin, view_to);
|
||||
EXPECT_NEAR(yaw_calc, angles::radians_to_degrees(-std::atan2(dir.x, -dir.z)), 1e-3f);
|
||||
|
||||
E::ViewAngles va;
|
||||
expect_matrix_near(E::MeshTrait::rotation_matrix(va), E::rotation_matrix(va));
|
||||
|
||||
const auto proj = E::CameraTrait::calc_projection_matrix(projection::FieldOfView::from_degrees(60.f), {1280.f, 720.f}, 0.1f, 1000.f);
|
||||
const auto expected = E::calc_perspective_projection_matrix(60.f, 1280.f / 720.f, 0.1f, 1000.f);
|
||||
expect_matrix_near(proj, expected);
|
||||
|
||||
// non-airborne
|
||||
t.m_is_airborne = false;
|
||||
const auto pred_ground_gl = E::PredEngineTrait::predict_target_position(t, 2.f, 9.81f);
|
||||
EXPECT_NEAR(pred_ground_gl.x, 4.f, 1e-6f);
|
||||
}
|
||||
|
||||
TEST(TraitTests, Unity_Pred_And_Mesh_And_Camera)
|
||||
{
|
||||
namespace E = omath::unity_engine;
|
||||
|
||||
projectile_prediction::Projectile p;
|
||||
p.m_origin = {0.f, 0.f, 0.f};
|
||||
p.m_launch_speed = 10.f;
|
||||
p.m_gravity_scale = 1.f;
|
||||
|
||||
const auto pos = E::PredEngineTrait::predict_projectile_position(p, 0.f, 0.f, 1.f, 9.81f);
|
||||
EXPECT_NEAR(pos.z, 10.f, 1e-4f);
|
||||
EXPECT_NEAR(pos.y, -9.81f * 0.5f, 1e-4f);
|
||||
|
||||
projectile_prediction::Target t;
|
||||
t.m_origin = {0.f, 5.f, 0.f};
|
||||
t.m_velocity = {2.f, 0.f, 0.f};
|
||||
t.m_is_airborne = true;
|
||||
const auto pred = E::PredEngineTrait::predict_target_position(t, 2.f, 9.81f);
|
||||
EXPECT_NEAR(pred.x, 4.f, 1e-6f);
|
||||
EXPECT_NEAR(pred.y, 5.f - 9.81f * (2.f * 2.f) * 0.5f, 1e-6f);
|
||||
|
||||
EXPECT_NEAR(E::PredEngineTrait::calc_vector_2d_distance({3.f, 0.f, 4.f}), 5.f, 1e-6f);
|
||||
EXPECT_NEAR(E::PredEngineTrait::get_vector_height_coordinate({1.f, 2.5f, 3.f}), 2.5f, 1e-6f);
|
||||
|
||||
std::optional<float> pitch = 45.f;
|
||||
auto vp = E::PredEngineTrait::calc_viewpoint_from_angles(p, {10.f, 0.f, 0.f}, pitch);
|
||||
EXPECT_NEAR(vp.y, 0.f + 10.f * std::tan(angles::degrees_to_radians(45.f)), 1e-6f);
|
||||
|
||||
Vector3<float> origin{0.f, 0.f, 0.f};
|
||||
Vector3<float> view_to{0.f, 1.f, 1.f};
|
||||
const auto pitch_calc = E::PredEngineTrait::calc_direct_pitch_angle(origin, view_to);
|
||||
const auto dir = (view_to - origin).normalized();
|
||||
EXPECT_NEAR(pitch_calc, angles::radians_to_degrees(std::asin(dir.y)), 1e-3f);
|
||||
|
||||
const auto yaw_calc = E::PredEngineTrait::calc_direct_yaw_angle(origin, view_to);
|
||||
EXPECT_NEAR(yaw_calc, angles::radians_to_degrees(std::atan2(dir.x, dir.z)), 1e-3f);
|
||||
|
||||
E::ViewAngles va;
|
||||
expect_matrix_near(E::MeshTrait::rotation_matrix(va), E::rotation_matrix(va));
|
||||
|
||||
const auto proj = E::CameraTrait::calc_projection_matrix(projection::FieldOfView::from_degrees(60.f), {1280.f, 720.f}, 0.1f, 1000.f);
|
||||
const auto expected = E::calc_perspective_projection_matrix(60.f, 1280.f / 720.f, 0.1f, 1000.f);
|
||||
expect_matrix_near(proj, expected);
|
||||
|
||||
// non-airborne
|
||||
t.m_is_airborne = false;
|
||||
const auto pred_ground_unity = E::PredEngineTrait::predict_target_position(t, 2.f, 9.81f);
|
||||
EXPECT_NEAR(pred_ground_unity.x, 4.f, 1e-6f);
|
||||
}
|
||||
|
||||
TEST(TraitTests, Unreal_Pred_And_Mesh_And_Camera)
|
||||
{
|
||||
namespace E = omath::unreal_engine;
|
||||
|
||||
projectile_prediction::Projectile p;
|
||||
p.m_origin = {0.f, 0.f, 0.f};
|
||||
p.m_launch_speed = 10.f;
|
||||
p.m_gravity_scale = 1.f;
|
||||
|
||||
const auto pos = E::PredEngineTrait::predict_projectile_position(p, 0.f, 0.f, 1.f, 9.81f);
|
||||
EXPECT_NEAR(pos.x, 10.f, 1e-4f);
|
||||
EXPECT_NEAR(pos.y, -9.81f * 0.5f, 1e-4f);
|
||||
|
||||
projectile_prediction::Target t;
|
||||
t.m_origin = {0.f, 5.f, 0.f};
|
||||
t.m_velocity = {2.f, 0.f, 0.f};
|
||||
t.m_is_airborne = true;
|
||||
const auto pred = E::PredEngineTrait::predict_target_position(t, 2.f, 9.81f);
|
||||
EXPECT_NEAR(pred.x, 4.f, 1e-6f);
|
||||
EXPECT_NEAR(pred.y, 5.f - 9.81f * (2.f * 2.f) * 0.5f, 1e-6f);
|
||||
|
||||
EXPECT_NEAR(E::PredEngineTrait::calc_vector_2d_distance({3.f, 0.f, 4.f}), 5.f, 1e-6f);
|
||||
EXPECT_NEAR(E::PredEngineTrait::get_vector_height_coordinate({1.f, 2.5f, 3.f}), 2.5f, 1e-6f);
|
||||
|
||||
std::optional<float> pitch = 45.f;
|
||||
auto vp = E::PredEngineTrait::calc_viewpoint_from_angles(p, {10.f, 0.f, 0.f}, pitch);
|
||||
EXPECT_NEAR(vp.z, 0.f + 10.f * std::tan(angles::degrees_to_radians(45.f)), 1e-6f);
|
||||
|
||||
Vector3<float> origin{0.f, 0.f, 0.f};
|
||||
Vector3<float> view_to{1.f, 1.f, 1.f};
|
||||
const auto pitch_calc = E::PredEngineTrait::calc_direct_pitch_angle(origin, view_to);
|
||||
const auto dir = (view_to - origin).normalized();
|
||||
EXPECT_NEAR(pitch_calc, angles::radians_to_degrees(std::asin(dir.z)), 1e-3f);
|
||||
|
||||
const auto yaw_calc = E::PredEngineTrait::calc_direct_yaw_angle(origin, view_to);
|
||||
EXPECT_NEAR(yaw_calc, angles::radians_to_degrees(std::atan2(dir.y, dir.x)), 1e-3f);
|
||||
|
||||
E::ViewAngles va;
|
||||
expect_matrix_near(E::MeshTrait::rotation_matrix(va), E::rotation_matrix(va));
|
||||
|
||||
const auto proj = E::CameraTrait::calc_projection_matrix(projection::FieldOfView::from_degrees(60.f), {1280.f, 720.f}, 0.1f, 1000.f);
|
||||
const auto expected = E::calc_perspective_projection_matrix(60.f, 1280.f / 720.f, 0.1f, 1000.f);
|
||||
expect_matrix_near(proj, expected);
|
||||
|
||||
// non-airborne
|
||||
t.m_is_airborne = false;
|
||||
const auto pred_ground_unreal = E::PredEngineTrait::predict_target_position(t, 2.f, 9.81f);
|
||||
EXPECT_NEAR(pred_ground_unreal.x, 4.f, 1e-6f);
|
||||
}
|
||||
@@ -8,6 +8,29 @@
|
||||
using namespace omath;
|
||||
using namespace omath::collision;
|
||||
|
||||
TEST(SimplexTest, HandleEmptySimplex)
|
||||
{
|
||||
Simplex<Vector3<float>> simplex;
|
||||
Vector3<float> direction{1, 0, 0};
|
||||
|
||||
EXPECT_EQ(simplex.size(), 0);
|
||||
EXPECT_FALSE(simplex.handle(direction));
|
||||
}
|
||||
|
||||
TEST(SimplexTest, HandleLineCollinearWithXAxis)
|
||||
{
|
||||
using Vec3 = Vector3<float>;
|
||||
Simplex<Vec3> simplex;
|
||||
|
||||
simplex.push_front(Vec3{1, 0, 0});
|
||||
simplex.push_front(Vec3{-1, 0, 0});
|
||||
|
||||
Vec3 direction{};
|
||||
simplex.handle(direction);
|
||||
|
||||
EXPECT_NEAR(direction.x, 0.f, 1e-6f);
|
||||
}
|
||||
|
||||
TEST(CollisionExtra, SimplexLineHandle)
|
||||
{
|
||||
Simplex<Vector3<float>> s;
|
||||
|
||||
Reference in New Issue
Block a user