From 4cbcdf8f3614a7be0b7ea66b3dee631c8764fb15 Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 11:26:25 +0300 Subject: [PATCH 01/17] add macos to build workflow --- .github/workflows/cmake.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 57b377b0..17d2c713 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -19,7 +19,7 @@ jobs: # # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest, windows-latest, macOS-latest] build_type: [Release] c_compiler: [gcc, clang, cl] include: @@ -32,6 +32,9 @@ jobs: - os: ubuntu-latest c_compiler: clang cpp_compiler: clang++ + - os: macOS-latest + c_compiler: clang + cpp_compiler: clang++ exclude: - os: windows-latest c_compiler: gcc @@ -39,6 +42,10 @@ jobs: c_compiler: clang - os: ubuntu-latest c_compiler: cl + - os: macOS-latest + c_compiler: cl + - os: macOS-latest + c_compiler: gcc steps: - uses: actions/checkout@v4 From 66defac585fe2ce9ba21ee11f1156cf925970f10 Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 11:26:43 +0300 Subject: [PATCH 02/17] make C17 required --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bf452a6..15e3f288 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,9 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG -D_DEBUG") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG") set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS NO) + include_directories( # gameanalytics includes From 27e9311bb63f13d7a5d82d97a28307db65a880fb Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 12:29:48 +0300 Subject: [PATCH 03/17] for osx build both archs --- CMakeLists.txt | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 15e3f288..c18c77c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,7 +107,32 @@ else() message(STATUS "Using user-specified PLATFORM: ${PLATFORM}") endif() -# --------------------------- END --------------------------- # +# --------------------------- Detect Architecture Automatically --------------------------- # + +# Print the system architecture +message(STATUS "System architecture: ${CMAKE_SYSTEM_PROCESSOR}") + +if(${PLATFORM} STREQUAL "osx") + # Set archs to be build for osx to both x86_64 and arm64 + set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") + + if(DEFINED CMAKE_OSX_ARCHITECTURES) + message(STATUS "Target architectures (CMAKE_OSX_ARCHITECTURES): ${CMAKE_OSX_ARCHITECTURES}") + else() + message(STATUS "CMAKE_OSX_ARCHITECTURES is not defined.") + endif() +else() + # Detect if it's 32-bit or 64-bit for other systems based on the pointer size + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + message(STATUS "Target is 64-bit") + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + message(STATUS "Target is 32-bit") + else() + message(WARNING "Unknown architecture") + endif() +endif() + +# --------------------------- Settings --------------------------- # if(${GA_USE_PACKAGE}) From 6f2fd854092f919e34f7f858cece0732e77019c4 Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 13:04:35 +0300 Subject: [PATCH 04/17] Add temp artifacts to builds --- .github/workflows/cmake.yml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 17d2c713..ca401e4a 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -77,4 +77,26 @@ jobs: working-directory: ${{ steps.strings.outputs.build-output-dir }} # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest --build-config ${{ matrix.build_type }} --verbose + run: | + # Run the tests and output Google Test results in XML format + ctest \ + --build-config ${{ matrix.build_type }} \ + --verbose \ + --output-on-failure \ + --gtest_output=xml:./test-results.xml + + - name: Package Build Artifacts + if: ${{ success() }} + run: | + mkdir -p ${{ steps.strings.outputs.build-output-dir }}/package + # Adjust the path to your built library files + cp ${{ steps.strings.outputs.build-output-dir }}/${{ matrix.build_type }}/*.a ${{ steps.strings.outputs.build-output-dir }}/package/ + cp -r ${{ github.workspace }}/include ${{ steps.strings.outputs.build-output-dir }}/package/ + + - name: Upload Build Artifact + if: ${{ success() }} + uses: actions/upload-artifact@v4 + with: + name: ga-cpp-sdk-${{ matrix.os }}-${{ matrix.c_compiler }}-${{ matrix.build_type }} + path: ${{ steps.strings.outputs.build-output-dir }}/package/ + From e609ae384295726f97b6e5d7349d3b1eb2641d6f Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 13:07:08 +0300 Subject: [PATCH 05/17] fix cmake.yml --- .github/workflows/cmake.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index ca401e4a..0b152e67 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -77,22 +77,25 @@ jobs: working-directory: ${{ steps.strings.outputs.build-output-dir }} # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: | - # Run the tests and output Google Test results in XML format - ctest \ - --build-config ${{ matrix.build_type }} \ - --verbose \ - --output-on-failure \ - --gtest_output=xml:./test-results.xml + run: ctest --build-config ${{ matrix.build_type }} --verbose --output-on-failure - name: Package Build Artifacts if: ${{ success() }} run: | mkdir -p ${{ steps.strings.outputs.build-output-dir }}/package # Adjust the path to your built library files - cp ${{ steps.strings.outputs.build-output-dir }}/${{ matrix.build_type }}/*.a ${{ steps.strings.outputs.build-output-dir }}/package/ + cp ${{ steps.strings.outputs.build-output-dir }}/${{ matrix.build_type }}/*GameAnalytics.* ${{ steps.strings.outputs.build-output-dir }}/package/ cp -r ${{ github.workspace }}/include ${{ steps.strings.outputs.build-output-dir }}/package/ - + + - name: Print Package Contents on Windows + if: matrix.os == 'windows-latest' + run: Get-ChildItem -Recurse "${{ steps.strings.outputs.build-output-dir }}\package" + shell: pwsh + + - name: Print Package Contents on non-Windows (Linux/macOS) + if: matrix.os != 'windows-latest' + run: ls -la ${{ steps.strings.outputs.build-output-dir }}/package + - name: Upload Build Artifact if: ${{ success() }} uses: actions/upload-artifact@v4 From 1e97c5e7968290d1bf7836e5dfbf0b0285297e70 Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 15:30:24 +0300 Subject: [PATCH 06/17] Create setup.py --- setup.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 setup.py diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..a6ec2627 --- /dev/null +++ b/setup.py @@ -0,0 +1,77 @@ +import argparse +import os +import subprocess +import shutil +import glob + +def run_command(command, shell=True, cwd=None): + result = subprocess.run(command, shell=shell, check=True, text=True, cwd=cwd) + return result + +def main(): + parser = argparse.ArgumentParser(description="CMake Build and Test Script") + parser.add_argument('--os', required=True, choices=['linux', 'windows', 'macos'], help='Operating System') + parser.add_argument('--build_type', default='Debug', choices=['Release', 'Debug'], help='Build Type') + parser.add_argument('--platform', choices=['linux_x64', 'linux_x86', 'osx', 'win32', 'win64', 'uwp'], help='Platform string for CMake') + parser.add_argument('--build', action='store_true', help='Execute the build step') + parser.add_argument('--test', action='store_true', help='Execute the test step') + args = parser.parse_args() + + build_output_dir = os.path.join(os.getcwd(), 'build') + os.makedirs(build_output_dir, exist_ok=True) + + if args.os == 'windows': + c_compiler = 'cl' + cpp_compiler = 'cl' + elif args.os == 'linux': + c_compiler = 'gcc' + cpp_compiler = 'g++' + elif args.os == 'macos': + c_compiler = 'clang' + cpp_compiler = 'clang++' + + # Configure CMake + cmake_command = f'cmake -B {build_output_dir} -DCMAKE_CXX_COMPILER={cpp_compiler} -DCMAKE_C_COMPILER={c_compiler} -DCMAKE_BUILD_TYPE={args.build_type} -S {os.getcwd()}' + if args.os == 'macos': + cmake_command += ' -G "Xcode"' + if args.platform: + cmake_command += f' -DPLATFORM:STRING={args.platform}' + run_command(cmake_command) + + # Build + if args.build: + run_command(f'cmake --build {build_output_dir} --config {args.build_type}') + else: + exit(0) + + # Test + if args.test: + run_command(f'ctest --build-config {args.build_type} --verbose --output-on-failure', cwd=build_output_dir) + + + # Package Build Artifacts + package_dir = os.path.join(build_output_dir, 'package') + os.makedirs(package_dir, exist_ok=True) + files_to_copy = glob.glob(f'{build_output_dir}/{args.build_type}/*GameAnalytics.*') + for file in files_to_copy: + shutil.copy(file, package_dir) + shutil.copytree(os.path.join(os.getcwd(), 'include'), os.path.join(package_dir, 'include'), dirs_exist_ok=True) + + # Print Package Contents + if args.os == 'windows': + run_command(f'dir {package_dir}', shell=True) + else: + run_command(f'ls -la {package_dir}', shell=True) + + # Print architecture information + #use lipo on macos and linux and dumpbin on windows + if args.os == 'macos': + run_command(f'lipo -info {package_dir}/*GameAnalytics.*') + elif args.os == 'linux': + run_command(f'file {package_dir}/*GameAnalytics.*') + elif args.os == 'windows': + run_command(f'dumpbin /headers {package_dir}/*GameAnalytics.*') + + +if __name__ == "__main__": + main() \ No newline at end of file From 5fdeb5db6d444033efb11c606486a10b7282ba06 Mon Sep 17 00:00:00 2001 From: andreidabijax Date: Thu, 10 Oct 2024 15:51:28 +0300 Subject: [PATCH 07/17] Update setup.py --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a6ec2627..a732248d 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,8 @@ import glob def run_command(command, shell=True, cwd=None): + if os.name == 'nt': # Check if the OS is Windows + command = f'powershell.exe -Command "{command}"' result = subprocess.run(command, shell=shell, check=True, text=True, cwd=cwd) return result @@ -70,7 +72,7 @@ def main(): elif args.os == 'linux': run_command(f'file {package_dir}/*GameAnalytics.*') elif args.os == 'windows': - run_command(f'dumpbin /headers {package_dir}/*GameAnalytics.*') + run_command(f'dumpbin /headers {package_dir}/GameAnalytics.lib | findstr machine') if __name__ == "__main__": From d048381c73a0fe70a13bfc882670af12bfcee16d Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 17:01:35 +0300 Subject: [PATCH 08/17] Create coverage.yml --- .github/workflows/coverage.yml | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/coverage.yml diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 00000000..abe2772e --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,47 @@ +name: Test Coverage + +on: [push, workflow_dispatch] + +jobs: + build: + name: Report Test Coverage + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Create Build Environment + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Install Coverage Tools + run: | + sudo apt-get update + sudo apt-get install -y lcov + + - name: Configure CMake + shell: bash + working-directory: ${{github.workspace}}/build + run: cmake .. + + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + run: cmake --build . + + - name: Prepare coverage data + working-directory: ${{github.workspace}}/build + shell: bash + run: cmake --build . --target cov_data + + - name: Report code coverage + uses: zgosalvez/github-actions-report-lcov@v1 + with: + coverage-files: build/cov.info.cleaned + minimum-coverage: 70 + artifact-name: code-coverage-report + github-token: ${{ secrets.GITHUB_TOKEN }} + working-directory: ${{github.workspace}} + + From 9bfef991b4ec7fd6997a21686a90c834d3ce6414 Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 17:17:14 +0300 Subject: [PATCH 09/17] Update CMakeLists.txt --- CMakeLists.txt | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index c18c77c8..2a4a69f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -245,3 +245,82 @@ target_link_libraries(${UT_PROJECT_NAME} ${PROJECT_NAME}) add_test(NAME ${UT_PROJECT_NAME} COMMAND GameAnalyticsUnitTests) +# --------------------------- Google Test Setup --------------------------- # + +find_program(GCOV_PATH gcov) +if (NOT GCOV_PATH) + message(WARNING "program gcov not found") +endif() + +find_program(LCOV_PATH lcov) +if (NOT LCOV_PATH) + message(WARNING "program lcov not found") +endif() + +find_program(GENHTML_PATH genhtml) +if (NOT GENHTML_PATH) + message(WARNING "program genhtml not found") +endif() + +if (LCOV_PATH AND GCOV_PATH) + + target_compile_options( + GameAnalytics + PRIVATE + -g -O0 -fprofile-arcs -ftest-coverage + ) + + target_link_libraries( + GameAnalytics + PRIVATE + --coverage + ) + + set(covname cov) + + add_custom_target(cov_data + # Cleanup lcov + ${LCOV_PATH} --directory . --zerocounters + + # Run tests + COMMAND GameAnalyticsUnitTests + + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info + COMMAND ${LCOV_PATH} --remove ${covname}.info + '${CMAKE_SOURCE_DIR}/lib/include/*' + '${CMAKE_SOURCE_DIR}/test/*' + '${CMAKE_SOURCE_DIR}/catch2/*' + '${CMAKE_SOURCE_DIR}/fakeit/*' + '/usr/*' + --output-file ${covname}.info.cleaned + ) + + if (GENHTML_PATH) + add_custom_target(cov + # Cleanup lcov + ${LCOV_PATH} --directory . --zerocounters + + # Run tests + COMMAND TestSampleLib + + # Capturing lcov counters and generating report + COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info + COMMAND ${LCOV_PATH} --remove ${covname}.info + '${CMAKE_SOURCE_DIR}/include/*' + '${CMAKE_SOURCE_DIR}/source/*' + '${CMAKE_SOURCE_DIR}/test/*' + --output-file ${covname}.info.cleaned + COMMAND ${GENHTML_PATH} -o ${covname} ${covname}.info.cleaned + COMMAND ${CMAKE_COMMAND} -E remove ${covname}.info ${covname}.info.cleaned + + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + else() + message(WARNING "unable to generate coverage report: missing genhtml") + endif() + +else() + message(WARNING "unable to add coverage targets: missing coverage tools") +endif() + From 7044011f989c6d54910668de4d5641fcb64a4b24 Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 17:50:00 +0300 Subject: [PATCH 10/17] improve coverage --- .github/workflows/coverage.yml | 14 ++++++-------- CMakeLists.txt | 11 ++++++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index abe2772e..0937b8b1 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,6 +1,6 @@ name: Test Coverage -on: [push, workflow_dispatch] +on: [pull_request, workflow_dispatch] jobs: build: @@ -15,10 +15,8 @@ jobs: - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build - - name: Install Coverage Tools - run: | - sudo apt-get update - sudo apt-get install -y lcov + - name: Setup LCOV + uses: hrishikesh-kadam/setup-lcov@v1.1.0 - name: Configure CMake shell: bash @@ -36,12 +34,12 @@ jobs: run: cmake --build . --target cov_data - name: Report code coverage - uses: zgosalvez/github-actions-report-lcov@v1 + uses: zgosalvez/github-actions-report-lcov@v3 with: coverage-files: build/cov.info.cleaned - minimum-coverage: 70 + minimum-coverage: 30 artifact-name: code-coverage-report github-token: ${{ secrets.GITHUB_TOKEN }} working-directory: ${{github.workspace}} - + update-comment: true diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a4a69f4..334afbfe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,19 +285,20 @@ if (LCOV_PATH AND GCOV_PATH) # Run tests COMMAND GameAnalyticsUnitTests + message(STATUS "Capturing lcov counters and generating report") # Capturing lcov counters and generating report COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info COMMAND ${LCOV_PATH} --remove ${covname}.info - '${CMAKE_SOURCE_DIR}/lib/include/*' + '${CMAKE_SOURCE_DIR}/include/*' + '${CMAKE_SOURCE_DIR}/source/gameanalytics/*' '${CMAKE_SOURCE_DIR}/test/*' - '${CMAKE_SOURCE_DIR}/catch2/*' - '${CMAKE_SOURCE_DIR}/fakeit/*' - '/usr/*' --output-file ${covname}.info.cleaned ) if (GENHTML_PATH) add_custom_target(cov + + message(STATUS, "Generating HTML report...") # Cleanup lcov ${LCOV_PATH} --directory . --zerocounters @@ -308,7 +309,7 @@ if (LCOV_PATH AND GCOV_PATH) COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info COMMAND ${LCOV_PATH} --remove ${covname}.info '${CMAKE_SOURCE_DIR}/include/*' - '${CMAKE_SOURCE_DIR}/source/*' + '${CMAKE_SOURCE_DIR}/source/gameanalytics/*' '${CMAKE_SOURCE_DIR}/test/*' --output-file ${covname}.info.cleaned COMMAND ${GENHTML_PATH} -o ${covname} ${covname}.info.cleaned From 06716500050b9b80f17e5101d73ccd73d8fe4bfb Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 18:09:33 +0300 Subject: [PATCH 11/17] update coverage --- .github/workflows/coverage.yml | 2 +- CMakeLists.txt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 0937b8b1..2d0eb104 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -16,7 +16,7 @@ jobs: run: cmake -E make_directory ${{github.workspace}}/build - name: Setup LCOV - uses: hrishikesh-kadam/setup-lcov@v1.1.0 + uses: hrishikesh-kadam/setup-lcov@v1 - name: Configure CMake shell: bash diff --git a/CMakeLists.txt b/CMakeLists.txt index 334afbfe..bc969b2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,9 +289,9 @@ if (LCOV_PATH AND GCOV_PATH) # Capturing lcov counters and generating report COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info COMMAND ${LCOV_PATH} --remove ${covname}.info - '${CMAKE_SOURCE_DIR}/include/*' - '${CMAKE_SOURCE_DIR}/source/gameanalytics/*' + '${CMAKE_SOURCE_DIR}/source/dependencies/*' '${CMAKE_SOURCE_DIR}/test/*' + '/usr/*' --output-file ${covname}.info.cleaned ) @@ -308,9 +308,9 @@ if (LCOV_PATH AND GCOV_PATH) # Capturing lcov counters and generating report COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info COMMAND ${LCOV_PATH} --remove ${covname}.info - '${CMAKE_SOURCE_DIR}/include/*' - '${CMAKE_SOURCE_DIR}/source/gameanalytics/*' + '${CMAKE_SOURCE_DIR}/source/dependencies/*' '${CMAKE_SOURCE_DIR}/test/*' + '/usr/*' --output-file ${covname}.info.cleaned COMMAND ${GENHTML_PATH} -o ${covname} ${covname}.info.cleaned COMMAND ${CMAKE_COMMAND} -E remove ${covname}.info ${covname}.info.cleaned From 146782a3338c862f483d0041a2f6588a035e683d Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 18:14:27 +0300 Subject: [PATCH 12/17] Update CMakeLists.txt --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc969b2e..a5ebf3e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,7 +285,6 @@ if (LCOV_PATH AND GCOV_PATH) # Run tests COMMAND GameAnalyticsUnitTests - message(STATUS "Capturing lcov counters and generating report") # Capturing lcov counters and generating report COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info COMMAND ${LCOV_PATH} --remove ${covname}.info @@ -298,7 +297,6 @@ if (LCOV_PATH AND GCOV_PATH) if (GENHTML_PATH) add_custom_target(cov - message(STATUS, "Generating HTML report...") # Cleanup lcov ${LCOV_PATH} --directory . --zerocounters From 07c19674cb735a85cd930132206277a16a5c0235 Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 18:21:14 +0300 Subject: [PATCH 13/17] Update CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5ebf3e8..be767e89 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -301,7 +301,7 @@ if (LCOV_PATH AND GCOV_PATH) ${LCOV_PATH} --directory . --zerocounters # Run tests - COMMAND TestSampleLib + COMMAND GameAnalyticsUnitTests # Capturing lcov counters and generating report COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info From 6dd4e424f3c333a4402e7cfdba15013566c9ff49 Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 18:35:49 +0300 Subject: [PATCH 14/17] Update CMakeLists.txt --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index be767e89..3cbc56b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,12 +286,16 @@ if (LCOV_PATH AND GCOV_PATH) COMMAND GameAnalyticsUnitTests # Capturing lcov counters and generating report + COMMENT "Capturing lcov counters and generating report..." COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info + COMMENT "Removing unwanted files from coverage report..." COMMAND ${LCOV_PATH} --remove ${covname}.info '${CMAKE_SOURCE_DIR}/source/dependencies/*' '${CMAKE_SOURCE_DIR}/test/*' '/usr/*' + '/Applications/Xcode.app/*' --output-file ${covname}.info.cleaned + COMMENT "Report Done!" ) if (GENHTML_PATH) @@ -309,6 +313,7 @@ if (LCOV_PATH AND GCOV_PATH) '${CMAKE_SOURCE_DIR}/source/dependencies/*' '${CMAKE_SOURCE_DIR}/test/*' '/usr/*' + '/Applications/Xcode.app/*' --output-file ${covname}.info.cleaned COMMAND ${GENHTML_PATH} -o ${covname} ${covname}.info.cleaned COMMAND ${CMAKE_COMMAND} -E remove ${covname}.info ${covname}.info.cleaned From 6bf3b88476c3d59e51b93e6c913895d86b06e265 Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 19:02:36 +0300 Subject: [PATCH 15/17] Update CMakeLists.txt --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cbc56b6..566855d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -280,22 +280,20 @@ if (LCOV_PATH AND GCOV_PATH) add_custom_target(cov_data # Cleanup lcov + COMMENT "Resetting code coverage counters to zero." ${LCOV_PATH} --directory . --zerocounters # Run tests COMMAND GameAnalyticsUnitTests # Capturing lcov counters and generating report - COMMENT "Capturing lcov counters and generating report..." COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info - COMMENT "Removing unwanted files from coverage report..." COMMAND ${LCOV_PATH} --remove ${covname}.info '${CMAKE_SOURCE_DIR}/source/dependencies/*' '${CMAKE_SOURCE_DIR}/test/*' '/usr/*' '/Applications/Xcode.app/*' --output-file ${covname}.info.cleaned - COMMENT "Report Done!" ) if (GENHTML_PATH) From 8a939ddb89c74e99b7238123362db20dc4cfb48a Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 19:37:22 +0300 Subject: [PATCH 16/17] Update CMakeLists.txt --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 566855d0..e8fc3716 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -306,14 +306,14 @@ if (LCOV_PATH AND GCOV_PATH) COMMAND GameAnalyticsUnitTests # Capturing lcov counters and generating report - COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info + COMMAND ${LCOV_PATH} --directory . --capture --output-file ${covname}.info --rc lcov_branch_coverage=1 --rc derive_function_end_line=0 COMMAND ${LCOV_PATH} --remove ${covname}.info '${CMAKE_SOURCE_DIR}/source/dependencies/*' - '${CMAKE_SOURCE_DIR}/test/*' '/usr/*' - '/Applications/Xcode.app/*' --output-file ${covname}.info.cleaned - COMMAND ${GENHTML_PATH} -o ${covname} ${covname}.info.cleaned + --rc lcov_branch_coverage=1 + --rc derive_function_end_line=0 + COMMAND ${GENHTML_PATH} -o ${covname} ${covname}.info.cleaned --rc lcov_branch_coverage=1 --rc derive_function_end_line=0 COMMAND ${CMAKE_COMMAND} -E remove ${covname}.info ${covname}.info.cleaned COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." From 23dac119d96943025655fca4a1485504572fd353 Mon Sep 17 00:00:00 2001 From: Andrei Dabija Date: Thu, 10 Oct 2024 21:26:59 +0300 Subject: [PATCH 17/17] add GAHealth tests --- setup.py | 2 +- source/gameanalytics/GAHealth.cpp | 7 +- test/GAHealth_test.cpp | 209 ++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 test/GAHealth_test.cpp diff --git a/setup.py b/setup.py index a732248d..58438a26 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def run_command(command, shell=True, cwd=None): if os.name == 'nt': # Check if the OS is Windows command = f'powershell.exe -Command "{command}"' - result = subprocess.run(command, shell=shell, check=True, text=True, cwd=cwd) + result = subprocess.run(command, shell=shell, check=True, text=True) return result def main(): diff --git a/source/gameanalytics/GAHealth.cpp b/source/gameanalytics/GAHealth.cpp index 46a8e624..b3cdbb8e 100644 --- a/source/gameanalytics/GAHealth.cpp +++ b/source/gameanalytics/GAHealth.cpp @@ -35,10 +35,11 @@ namespace gameanalytics if((memory > 0) && (_totalMemory > 0)) { int memoryPercent = std::round(static_cast(memory) / static_cast(_totalMemory) * 100.0); - return memoryPercent; + return std::min(memoryPercent, 100); } - return -1; + return 0; + } void GAHealth::doAppMemoryReading(int64_t memory) @@ -172,4 +173,4 @@ namespace gameanalytics } } -} \ No newline at end of file +} diff --git a/test/GAHealth_test.cpp b/test/GAHealth_test.cpp new file mode 100644 index 00000000..43807dd5 --- /dev/null +++ b/test/GAHealth_test.cpp @@ -0,0 +1,209 @@ +#include +#include +#include +#include + +using namespace gameanalytics; +using ::testing::Return; + +namespace gameanalytics +{ + class MockGAPlatform : public GAPlatform + { + public: + MOCK_METHOD(std::string, getOSVersion, (), (override)); + MOCK_METHOD(std::string, getDeviceManufacturer, (), (override)); + MOCK_METHOD(std::string, getBuildPlatform, (), (override)); + MOCK_METHOD(std::string, getPersistentPath, (), (override)); + MOCK_METHOD(std::string, getDeviceModel, (), (override)); + MOCK_METHOD(std::string, getConnectionType, (), (override)); + + // Mocking non-pure virtual methods + MOCK_METHOD(std::string, getAdvertisingId, (), (override)); + MOCK_METHOD(std::string, getDeviceId, (), (override)); + MOCK_METHOD(void, setupUncaughtExceptionHandler, (), (override)); + MOCK_METHOD(void, onInit, (), (override)); + + // Mocking const methods + MOCK_METHOD(std::string, getCpuModel, (), (const, override)); + MOCK_METHOD(std::string, getGpuModel, (), (const, override)); + MOCK_METHOD(int, getNumCpuCores, (), (const, override)); + MOCK_METHOD(int64_t, getTotalDeviceMemory, (), (const, override)); + MOCK_METHOD(int64_t, getAppMemoryUsage, (), (const, override)); + MOCK_METHOD(int64_t, getSysMemoryUsage, (), (const, override)); + MOCK_METHOD(int64_t, getBootTime, (), (const, override)); + + MockGAPlatform() + { + ON_CALL(*this, getOSVersion).WillByDefault(Return("10.0")); + ON_CALL(*this, getDeviceManufacturer).WillByDefault(Return("GenericManufacturer")); + ON_CALL(*this, getBuildPlatform).WillByDefault(Return("Windows")); + ON_CALL(*this, getPersistentPath).WillByDefault(Return("/persistent/path")); + ON_CALL(*this, getDeviceModel).WillByDefault(Return("DeviceModelX")); + ON_CALL(*this, getConnectionType).WillByDefault(Return("WiFi")); + + ON_CALL(*this, getAdvertisingId).WillByDefault(Return("ad-id-123")); + ON_CALL(*this, getDeviceId).WillByDefault(Return("device-id-456")); + ON_CALL(*this, setupUncaughtExceptionHandler).WillByDefault(Return()); + ON_CALL(*this, onInit).WillByDefault(Return()); + + ON_CALL(*this, getCpuModel).WillByDefault(Return("Intel Core i7")); + ON_CALL(*this, getGpuModel).WillByDefault(Return("Nvidia GTX 1080")); + ON_CALL(*this, getNumCpuCores).WillByDefault(Return(8)); + ON_CALL(*this, getTotalDeviceMemory).WillByDefault(Return(16384)); // 16GB + ON_CALL(*this, getAppMemoryUsage).WillByDefault(Return(1024)); // 1GB + ON_CALL(*this, getSysMemoryUsage).WillByDefault(Return(2048)); // 2GB + ON_CALL(*this, getBootTime).WillByDefault(Return(30000)); // 30 seconds + } + }; +} + + +// Test subclass to access protected members +class GAHealthTestable : public gameanalytics::GAHealth +{ +public: + using gameanalytics::GAHealth::GAHealth; // Inherit constructor + using gameanalytics::GAHealth::_fpsReadings; // Expose protected member for testing + using gameanalytics::GAHealth::_appMemoryUsage; // Expose protected memory usage for testing + using gameanalytics::GAHealth::_sysMemoryUsage; // Expose system memory usage for testing + using gameanalytics::GAHealth::getMemoryPercent; + using gameanalytics::GAHealth::_totalMemory; +}; + + +class GAHealthTest : public ::testing::Test +{ +protected: + MockGAPlatform* mockPlatform; + GAHealthTestable* gaHealth; + + virtual void SetUp() override + { + mockPlatform = new MockGAPlatform(); + gaHealth = new GAHealthTestable(mockPlatform); + } + + virtual void TearDown() override + { + delete gaHealth; + delete mockPlatform; + } +}; + +TEST_F(GAHealthTest, ConstructorInitializesPlatform) +{ + EXPECT_CALL(*mockPlatform, getCpuModel()).WillOnce(Return("Intel")); + EXPECT_CALL(*mockPlatform, getNumCpuCores()).WillOnce(Return(4)); + EXPECT_CALL(*mockPlatform, getDeviceModel()).WillOnce(Return("Device123")); + EXPECT_CALL(*mockPlatform, getGpuModel()).WillOnce(Return("Nvidia")); + EXPECT_CALL(*mockPlatform, getTotalDeviceMemory()).WillOnce(Return(8192)); + + gameanalytics::GAHealth health(mockPlatform); + health.enableHardwareTracking = true; + + json out; + health.addHealthAnnotations(out); + + std::cout << std::setw(4) << out << '\n'; + + EXPECT_EQ(out["cpu_model"], "Intel"); + EXPECT_EQ(out["cpu_num_cores"], 4); + EXPECT_EQ(out["hardware"], "Device123"); +} + +TEST_F(GAHealthTest, AddHealthAnnotationsIncludesHardwareTracking) +{ + EXPECT_CALL(*mockPlatform, getCpuModel()).WillOnce(Return("Intel")); + EXPECT_CALL(*mockPlatform, getNumCpuCores()).WillOnce(Return(4)); + EXPECT_CALL(*mockPlatform, getDeviceModel()).WillOnce(Return("Device123")); + + GAHealthTestable* _localHealthTracker = new GAHealthTestable(mockPlatform); + + _localHealthTracker->enableHardwareTracking = true; + + json healthEvent; + _localHealthTracker->addHealthAnnotations(healthEvent); + + std::cout << std::setw(4) << healthEvent["cpu_model"] << '\n'; + + EXPECT_EQ(healthEvent["cpu_model"], "Intel"); + EXPECT_EQ(healthEvent["cpu_num_cores"], 4); + EXPECT_EQ(healthEvent["hardware"], "Device123"); +} + +TEST_F(GAHealthTest, DoFpsReadingIncrementsBucketCorrectly) +{ + float testFps = 60.0f; + + gaHealth->doFpsReading(testFps); + + EXPECT_EQ(gaHealth->_fpsReadings[60], 1); +} + +TEST_F(GAHealthTest, GetMemoryPercentReturnsCorrectValue) +{ + int percent = gaHealth->getMemoryPercent(4096); + EXPECT_EQ(percent, 25); // 25% memory usage +} + +// Test getMemoryPercent with various inputs +TEST_F(GAHealthTest, GetMemoryPercentReturnsCorrectValues) +{ + // Case 1: 50% memory usage + int64_t totalMemory = 1000; + gaHealth->_totalMemory = totalMemory; + int memory = 500; + int expectedPercent = 50; + EXPECT_EQ(gaHealth->getMemoryPercent(memory), expectedPercent); + + // Case 2: 100% memory usage + memory = 1000; + expectedPercent = 100; + EXPECT_EQ(gaHealth->getMemoryPercent(memory), expectedPercent); + + // Case 3: 0% memory usage + memory = 1; + expectedPercent = 0; + EXPECT_EQ(gaHealth->getMemoryPercent(memory), expectedPercent); + + // Case 4: More than 100% memory usage (should not happen, but edge case) + memory = 2000; + expectedPercent = 100; // Assuming 100% cap + EXPECT_EQ(gaHealth->getMemoryPercent(memory), expectedPercent); + + // Case 5: Negative memory value (should return 0 or handle gracefully) + memory = -100; + expectedPercent = 0; // Assuming a negative value will result in 0% + EXPECT_EQ(gaHealth->getMemoryPercent(memory), expectedPercent); +} + +TEST_F(GAHealthTest, AddPerformanceDataIncludesFPSTracking) +{ + gaHealth->enableFPSTracking = true; + + // Fill FPS readings with some values + gaHealth->_fpsReadings[60] = 5; + gaHealth->_fpsReadings[30] = 2; + + json performanceData; + gaHealth->addPerformanceData(performanceData); + + + json expectedFpsData; + expectedFpsData["fps_data_table"] = gaHealth->_fpsReadings; + + EXPECT_EQ(performanceData["fps_data_table"], expectedFpsData["fps_data_table"]); +} + +TEST_F(GAHealthTest, AddSDKInitDataIncludesBootTime) +{ + gaHealth->enableAppBootTimeTracking = true; + + EXPECT_CALL(*mockPlatform, getBootTime()).WillOnce(Return(5000)); + + json sdkInitEvent; + gaHealth->addSDKInitData(sdkInitEvent); + + EXPECT_EQ(sdkInitEvent["app_boot_time"], 5000); +}