diff --git a/.github/workflows/build-android.yml b/.github/workflows/build-android.yml
index 06bce4267..2d50517d8 100644
--- a/.github/workflows/build-android.yml
+++ b/.github/workflows/build-android.yml
@@ -17,13 +17,18 @@ on:
- main
- dev
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
jobs:
build:
runs-on: windows-latest
name: Build for Android
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
submodules: false
- name: Update submodules
@@ -32,7 +37,7 @@ jobs:
git config --global submodule.lib/modules.update none
git -c protocol.version=2 submodule update --init --force --depth=1
- name: Setup Java
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v5
with:
distribution: 'adopt'
java-version: '17'
@@ -40,14 +45,13 @@ jobs:
# Workaround for: 'Unable to decrypt local Maven settings credentials'
run: rm $Env:USERPROFILE\.m2\settings.xml
- name: Setup Android SDK
- uses: android-actions/setup-android@v2
+ uses: android-actions/setup-android@v3
- name: Install NDK
run: |
java -version
gci env:* | sort-object name
- new-item "C:\Users\runneradmin\.android\repositories.cfg" -ItemType "file"
- echo yes | .\sdkmanager.bat "ndk-bundle" "cmake;3.10.2.4988404" "ndk;27.0.12077973" --sdk_root=$Env:ANDROID_SDK_ROOT
- working-directory: ${{ env.ANDROID_SDK_ROOT }}\cmdline-tools\7.0\bin
+ new-item "$Env:USERPROFILE\.android\repositories.cfg" -ItemType "file"
+ echo yes | sdkmanager "ndk-bundle" "cmake;3.10.2.4988404" "ndk;27.0.12077973" --sdk_root=$Env:ANDROID_SDK_ROOT
- name: Chocolatey
run: |
choco install --no-progress -y ninja
diff --git a/.github/workflows/build-ios-mac.yml b/.github/workflows/build-ios-mac.yml
index 80c5f981d..be9ec53f9 100644
--- a/.github/workflows/build-ios-mac.yml
+++ b/.github/workflows/build-ios-mac.yml
@@ -19,36 +19,42 @@ on:
schedule:
- cron: 0 2 * * 1-5
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
jobs:
build:
strategy:
matrix:
- os: [macos-13, macos-15]
+ os: [macos-14, macos-15]
config: [release, debug]
simulator: ["'iPhone 15'", "'iPad Pro (11-inch) (4th generation)'", "'iPhone 16'", "'iPad Air 11-inch (M2)'"]
exclude:
- - os: macos-13
+ - os: macos-14
simulator: "'iPhone 16'"
- - os: macos-13
+ - os: macos-14
simulator: "'iPad Air 11-inch (M2)'"
- os: macos-15
simulator: "'iPhone 15'"
- os: macos-15
simulator: "'iPad Pro (11-inch) (4th generation)'"
runs-on: ${{ matrix.os }}
+ timeout-minutes: 30
env:
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
steps:
- name: Grant write permissions to /usr/local
run: |
sudo chown -R $USER:staff /usr/local
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
with:
submodules: 'true'
continue-on-error: true
- name: build
run: |
- if [[ "${{ matrix.os }}" == "macos-13" ]]; then
+ if [[ "${{ matrix.os }}" == "macos-14" ]]; then
export IOS_DEPLOYMENT_TARGET=13.0;
elif [[ "${{ matrix.os }}" == "macos-15" ]]; then
export IOS_DEPLOYMENT_TARGET=15.0;
diff --git a/.github/workflows/build-posix-latest.yml b/.github/workflows/build-posix-latest.yml
index 2271a459b..657caa6e0 100644
--- a/.github/workflows/build-posix-latest.yml
+++ b/.github/workflows/build-posix-latest.yml
@@ -19,6 +19,11 @@ on:
schedule:
- cron: 0 2 * * 1-5
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
jobs:
build:
@@ -32,7 +37,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v1
+ uses: actions/checkout@v4
continue-on-error: true
- name: Test ${{ matrix.os }} ${{ matrix.config }}
run: ./build-tests.sh ${{ matrix.config }}
diff --git a/.github/workflows/build-ubuntu-2204.yml b/.github/workflows/build-ubuntu-2204.yml
index e8f456bbc..e0ea03ac5 100644
--- a/.github/workflows/build-ubuntu-2204.yml
+++ b/.github/workflows/build-ubuntu-2204.yml
@@ -19,6 +19,11 @@ on:
schedule:
- cron: 0 2 * * 1-5
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
jobs:
build:
@@ -32,7 +37,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v1
+ uses: actions/checkout@v4
continue-on-error: true
- name: Test ${{ matrix.os }} ${{ matrix.config }}
run: ./build-tests.sh ${{ matrix.config }}
\ No newline at end of file
diff --git a/.github/workflows/build-windows-clang.yaml.off b/.github/workflows/build-windows-clang.yaml.off
index 9f2c790ea..2621613c1 100644
--- a/.github/workflows/build-windows-clang.yaml.off
+++ b/.github/workflows/build-windows-clang.yaml.off
@@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
continue-on-error: true
- name: Setup Tools
diff --git a/.github/workflows/build-windows-vs2017.yaml.off b/.github/workflows/build-windows-vs2017.yaml.off
index 1db4607db..a2d22827b 100644
--- a/.github/workflows/build-windows-vs2017.yaml.off
+++ b/.github/workflows/build-windows-vs2017.yaml.off
@@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
continue-on-error: true
- name: Setup Tools
diff --git a/.github/workflows/build-windows-vs2022.yaml b/.github/workflows/build-windows-vs2022.yaml
index a8a18394e..20605b442 100644
--- a/.github/workflows/build-windows-vs2022.yaml
+++ b/.github/workflows/build-windows-vs2022.yaml
@@ -22,7 +22,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
continue-on-error: true
- name: Build
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 503cae673..3a55f2bc5 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -14,6 +14,11 @@ on:
schedule:
- cron: '0 8 * * 1'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
jobs:
analyze:
name: Analyze
@@ -34,7 +39,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
continue-on-error: true
# Initializes the CodeQL tools for scanning.
@@ -87,7 +92,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
continue-on-error: true
- name: Update submodules
@@ -102,21 +107,20 @@ jobs:
languages: java
- name: Setup Java
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v5
with:
distribution: 'adopt'
java-version: '17'
- name: Remove default github maven configuration
run: rm $Env:USERPROFILE\.m2\settings.xml
- name: Setup Android SDK
- uses: android-actions/setup-android@v2
+ uses: android-actions/setup-android@v3
- name: Install NDK
run: |
java -version
gci env:* | sort-object name
- new-item "C:\Users\runneradmin\.android\repositories.cfg" -ItemType "file"
- echo yes | .\sdkmanager.bat "ndk-bundle" "cmake;3.10.2.4988404" "ndk;27.0.12077973" --sdk_root=$Env:ANDROID_SDK_ROOT
- working-directory: ${{ env.ANDROID_SDK_ROOT }}\cmdline-tools\7.0\bin
+ new-item "$Env:USERPROFILE\.android\repositories.cfg" -ItemType "file"
+ echo yes | sdkmanager "ndk-bundle" "cmake;3.10.2.4988404" "ndk;27.0.12077973" --sdk_root=$Env:ANDROID_SDK_ROOT
- name: Chocolatey
run: |
choco install --no-progress -y ninja
diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml
index 69b136fa1..912594c61 100644
--- a/.github/workflows/spellcheck.yml
+++ b/.github/workflows/spellcheck.yml
@@ -6,6 +6,11 @@ on:
pull_request:
branches: [ master, main ]
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
jobs:
build:
runs-on: ubuntu-latest
@@ -13,7 +18,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
continue-on-error: true
- name: install misspell
diff --git a/.github/workflows/test-android-mac.yml.off b/.github/workflows/test-android-mac.yml.off
index 56b87579a..96e17c4a5 100644
--- a/.github/workflows/test-android-mac.yml.off
+++ b/.github/workflows/test-android-mac.yml.off
@@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
with:
submodules: true
depth: 1
@@ -42,7 +42,7 @@ jobs:
script: ./testandlog
- name: Upload
if: ${{ always() }}
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: logcat
path: ./lib/android_build/logcat.txt
\ No newline at end of file
diff --git a/.github/workflows/test-win-latest.yml b/.github/workflows/test-win-latest.yml
index 760c88fb0..19b20ef53 100644
--- a/.github/workflows/test-win-latest.yml
+++ b/.github/workflows/test-win-latest.yml
@@ -19,6 +19,11 @@ on:
schedule:
- cron: 0 2 * * 1-5
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' }}
+
jobs:
test:
name: Test on Windows ${{ matrix.arch }}-${{ matrix.build }}
@@ -32,13 +37,13 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v1
+ uses: actions/checkout@v4
continue-on-error: true
- name: setup-msbuild
- uses: microsoft/setup-msbuild@v1.1
+ uses: microsoft/setup-msbuild@v2
with:
- vs-version: '[16,)'
+ vs-version: '[17,)'
- name: Test ${{ matrix.arch }} ${{ matrix.build }}
shell: cmd
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index 281bf4b1f..dec4558cd 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -1,11 +1,19 @@
version: 2
+build:
+ os: ubuntu-24.04
+ tools:
+ python: "3.12"
+ apt_packages:
+ - doxygen
+ - graphviz
+
submodules:
exclude: all
python:
- install:
- - requirements: docs/public/requirements.txt
+ install:
+ - requirements: docs/public/requirements.txt
sphinx:
- configuration: docs/public/conf.py
+ configuration: docs/public/conf.py
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 959134a61..3117d4b29 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.1.0)
-project(MSTelemetry)
+project(MSTelemetry LANGUAGES C CXX)
# Set installation prefix for macOS and Linux
if(UNIX AND NOT DEFINED CMAKE_INSTALL_PREFIX)
@@ -25,7 +25,7 @@ endif()
# Enable ARC for obj-c on Apple
if(APPLE)
- message("-- BUILD_IOS: ${BUILD_IOS}")
+ message(STATUS "BUILD_IOS: ${BUILD_IOS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fobjc-arc")
# iOS build options
@@ -77,9 +77,9 @@ if(APPLE)
OUTPUT_VARIABLE CMAKE_OSX_SYSROOT
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
- message("-- CMAKE_OSX_SYSROOT ${CMAKE_OSX_SYSROOT}")
- message("-- ARCHITECTURE: ${CMAKE_SYSTEM_PROCESSOR}")
- message("-- PLATFORM: ${IOS_PLATFORM}")
+ message(STATUS "CMAKE_OSX_SYSROOT ${CMAKE_OSX_SYSROOT}")
+ message(STATUS "ARCHITECTURE: ${CMAKE_SYSTEM_PROCESSOR}")
+ message(STATUS "PLATFORM: ${IOS_PLATFORM}")
else()
if(${MAC_ARCH} STREQUAL "x86_64")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch x86_64")
@@ -99,26 +99,29 @@ if(APPLE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch x86_64 -arch arm64")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch x86_64 -arch arm64")
endif()
- message("-- MAC_ARCH: ${MAC_ARCH}")
+ message(STATUS "MAC_ARCH: ${MAC_ARCH}")
endif()
endif()
-message("-- CMAKE_SYSTEM_INFO_FILE: ${CMAKE_SYSTEM_INFO_FILE}")
-message("-- CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
-message("-- CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
-message("-- CMAKE_SYSTEM: ${CMAKE_SYSTEM}")
-message("-- CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}")
-message("-- CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
-message("-- TARGET_ARCH: ${TARGET_ARCH}")
-message("-- CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}")
+message(STATUS "CMAKE_SYSTEM_INFO_FILE: ${CMAKE_SYSTEM_INFO_FILE}")
+message(STATUS "CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}")
+message(STATUS "CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}")
+message(STATUS "CMAKE_SYSTEM: ${CMAKE_SYSTEM}")
+message(STATUS "CMAKE_SYSTEM_VERSION: ${CMAKE_SYSTEM_VERSION}")
+message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
+message(STATUS "TARGET_ARCH: ${TARGET_ARCH}")
+message(STATUS "CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}")
include(tools/ParseOsRelease.cmake)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
-set(WARN_FLAGS "/W4 /WX")
+ set(WARN_FLAGS "/W4 /WX")
+elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+ # -Wno-unknown-warning-option is Clang-only, omitted here
+ set(WARN_FLAGS "-Wall -Werror -Wextra -Wno-unused-parameter -Wno-unused-but-set-variable")
else()
-# No -pedantic -Wno-extra-semi -Wno-gnu-zero-variadic-macro-arguments
-set(WARN_FLAGS "-Wall -Werror -Wextra -Wno-unused-parameter -Wno-unknown-warning-option -Wno-unused-but-set-variable")
+ # No -pedantic -Wno-extra-semi -Wno-gnu-zero-variadic-macro-arguments
+ set(WARN_FLAGS "-Wall -Werror -Wextra -Wno-unused-parameter -Wno-unknown-warning-option -Wno-unused-but-set-variable -Wno-nan-infinity-disabled")
endif()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
@@ -144,12 +147,12 @@ set(DBG_FLAGS "-ggdb -gdwarf-2 -O0 ${WARN_FLAGS} -fno-builtin-malloc -fno-built
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
#TODO: -fno-rtti
- message("Building Release ...")
+ message(STATUS "Building Release ...")
set(CMAKE_C_FLAGS "$ENV{CFLAGS} ${CMAKE_C_FLAGS} -std=c11 ${REL_FLAGS}")
set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS} -std=c++11 ${REL_FLAGS}")
else()
set(USE_TCMALLOC 1)
- message("Building Debug ...")
+ message(STATUS "Building Debug ...")
include(tools/FindTcmalloc.cmake)
set(CMAKE_C_FLAGS "$ENV{CFLAGS} ${CMAKE_C_FLAGS} -std=c11 ${DBG_FLAGS}")
set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS} -std=c++11 ${DBG_FLAGS}")
@@ -190,7 +193,7 @@ endif()
set(PAL_IMPLEMENTATION ${DEFAULT_PAL_IMPLEMENTATION})
-message(STATUS "-- PAL implementation: ${PAL_IMPLEMENTATION}")
+message(STATUS "PAL implementation: ${PAL_IMPLEMENTATION}")
string(TOUPPER ${PAL_IMPLEMENTATION} PAL_IMPLEMENTATION_UPPER)
add_definitions(-DMATSDK_PAL_${PAL_IMPLEMENTATION_UPPER}=1)
@@ -222,7 +225,7 @@ add_definitions(-DNOMINMAX)
set(SDK_VERSION_PREFIX "EVT")
add_definitions("-DMATSDK_VERSION_PREFIX=\"${SDK_VERSION_PREFIX}\"")
-set(MATSDK_API_VERSION "3.4")
+set(MATSDK_API_VERSION "3.10")
string(TIMESTAMP DAYNUMBER "%j")
string(REGEX REPLACE "^00" "" DAYNUMBER ${DAYNUMBER})
string(REGEX REPLACE "^0" "" DAYNUMBER ${DAYNUMBER})
@@ -238,7 +241,7 @@ else()
set(MATSDK_BUILD_VERSION ${MATSDK_API_VERSION}.${DAYNUMBER}.0)
endif()
-message(STATUS "-- SDK version: ${SDK_VERSION_PREFIX}-${MATSDK_BUILD_VERSION}")
+message(STATUS "SDK version: ${SDK_VERSION_PREFIX}-${MATSDK_BUILD_VERSION}")
################################################################################################
# HTTP stack section
@@ -286,9 +289,9 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
endif()
if(BUILD_UNIT_TESTS OR BUILD_FUNC_TESTS)
- message("Adding gtest")
+ message(STATUS "Adding gtest")
add_library(gtest STATIC IMPORTED GLOBAL)
- message("Adding gmock")
+ message(STATUS "Adding gmock")
add_library(gmock STATIC IMPORTED GLOBAL)
endif()
@@ -320,7 +323,7 @@ if(BUILD_LIBRARY)
endif()
if(BUILD_UNIT_TESTS OR BUILD_FUNC_TESTS)
- message("Building tests")
+ message(STATUS "Building tests")
enable_testing()
add_subdirectory(tests)
endif()
diff --git a/README.md b/README.md
index c4da1e1c6..3ddcbb580 100644
--- a/README.md
+++ b/README.md
@@ -94,6 +94,9 @@ Other resources to learn how to setup the build system:
* **Supported** - these platforms are known to work well with the SDK in
production.
* **Covered by CI** - these platforms are tested as part of CI.
+* For iOS simulator, CI covers representative supported simulator
+ configurations on the current macOS runner images rather than every
+ supported iOS 12+ runtime.
## Test
diff --git a/Solutions/before.targets b/Solutions/before.targets
index 63a526c90..052e5d77f 100644
--- a/Solutions/before.targets
+++ b/Solutions/before.targets
@@ -3,12 +3,12 @@
$(SolutionDir)\..\third_party\krabsetw\krabs;$(CustomIncludePath)
- v141
- v142
- v143
-
+
+ v145
+ v143
+ v142
+ v141
v141
- $(PlatformToolset)
$([Microsoft.Build.Utilities.ToolLocationHelper]::GetLatestSDKTargetPlatformVersion('Windows', '10.0'))
$(LatestTargetPlatformVersion)
diff --git a/Solutions/build.MIP.props b/Solutions/build.MIP.props
index 9bd6e6d91..2419c4c36 100644
--- a/Solutions/build.MIP.props
+++ b/Solutions/build.MIP.props
@@ -1,20 +1,24 @@
-
-
-
-
- %(PreprocessorDefinitions);CONFIG_CUSTOM_H="config-MIP.h";MATSDK_SHARED_LIB=1;
-
-
- ucrtbased.dll; %(DelayLoadDLLs)
-
-
-
- true
- v141
- 14.1
-
-
- mip_ClientTelemetry
-
-
-
+
+
+
+
+ %(PreprocessorDefinitions);CONFIG_CUSTOM_H="config-MIP.h";MATSDK_SHARED_LIB=1;
+
+
+ ucrtbased.dll; %(DelayLoadDLLs)
+
+
+
+ true
+ $(DefaultPlatformToolset)
+ v145
+ v143
+ v142
+ v141
+ v141
+
+
+ mip_ClientTelemetry
+
+
+
diff --git a/build-tests-ios.sh b/build-tests-ios.sh
index bf29a50b3..3e4a40f46 100755
--- a/build-tests-ios.sh
+++ b/build-tests-ios.sh
@@ -7,14 +7,67 @@ set -e
./build-ios.sh ${SKU}
-# dyld_info /Users/runner/work/cpp_client_telemetry/cpp_client_telemetry/out/lib/libmat.a
-
cd tests/unittests
xcrun simctl list devices available
echo 'End of xcrun simctl list devices available'
-xcodebuild test -scheme iOSUnitTests -destination "platform=iOS Simulator,name=$SIMULATOR"
+# Resolve simulator UUID from simctl JSON using an exact name match.
+# If the same device name exists across multiple iOS runtimes, pick the
+# newest runtime and fail if that runtime still contains duplicate matches.
+SIM_MATCH=$(
+ xcrun simctl list devices available --json | python3 -c '
+import json
+import re
+import sys
+
+simulator_name = sys.argv[1]
+devices_by_runtime = json.load(sys.stdin).get("devices", {})
+matches = []
+
+for runtime, devices in devices_by_runtime.items():
+ if not runtime.startswith("com.apple.CoreSimulator.SimRuntime.iOS-"):
+ continue
+
+ match = re.search(r"iOS-(\d+)(?:-(\d+))?$", runtime)
+ if match is None:
+ continue
+
+ version = tuple(int(part) for part in match.groups(default="0"))
+ runtime_label = "iOS " + ".".join(str(part) for part in version)
+
+ for device in devices:
+ if device.get("name") == simulator_name and device.get("isAvailable", True):
+ matches.append((version, runtime_label, device["udid"]))
+
+if not matches:
+ print(f"ERROR: No available simulator found for {simulator_name!r}", file=sys.stderr)
+ raise SystemExit(1)
+
+newest_version = max(version for version, _, _ in matches)
+newest_matches = [(runtime_label, udid) for version, runtime_label, udid in matches if version == newest_version]
+
+if len(newest_matches) != 1:
+ print(f"ERROR: Multiple available simulators found for {simulator_name!r} in newest runtime:", file=sys.stderr)
+ for runtime_label, udid in newest_matches:
+ print(f" - {runtime_label}: {udid}", file=sys.stderr)
+ raise SystemExit(1)
+
+runtime_label, udid = newest_matches[0]
+print(f"{udid}|{runtime_label}")
+' "$SIMULATOR"
+)
+SIM_ID=${SIM_MATCH%%|*}
+SIM_RUNTIME=${SIM_MATCH#*|}
+
+if [ -z "$SIM_ID" ]; then
+ echo "ERROR: No available simulator found for '$SIMULATOR'"
+ exit 1
+fi
+
+echo "Using simulator: $SIMULATOR ($SIM_RUNTIME, id=$SIM_ID)"
+
+xcodebuild test -scheme iOSUnitTests -destination "id=$SIM_ID"
cd ../functests
-xcodebuild test -scheme iOSFuncTests -destination "platform=iOS Simulator,name=$SIMULATOR"
+xcodebuild test -scheme iOSFuncTests -destination "id=$SIM_ID"
diff --git a/docs/cpp-start-ios.md b/docs/cpp-start-ios.md
index 35313e7fa..ad80866e0 100644
--- a/docs/cpp-start-ios.md
+++ b/docs/cpp-start-ios.md
@@ -16,12 +16,38 @@ Run `build-ios.sh [clean] [release|debug]` script in the root folder of the sour
Run `build-ios.sh [clean] [release|debug] [arm64|arm64e]`.
+### Run iOS tests
+
+Run `./build-tests-ios.sh [release|debug] ""`.
+
+The script requires `python3` on `PATH` to parse `simctl --json` when resolving the simulator name.
+
+Example:
+
+```sh
+./build-tests-ios.sh release "iPhone 17"
+```
+
+Use a simulator name returned by `xcrun simctl list devices available`.
+The script resolves an exact device name from `simctl --json`, prefers the
+newest installed iOS runtime for that device name, and fails if that newest
+runtime still contains multiple matches.
+
+If Xcode reports that the requested simulator runtime is missing, install it
+from Xcode > Settings > Components or run
+`xcodebuild -downloadPlatform iOS -architectureVariant arm64`.
+
## 3. Integrate the SDK into your C++ project
-SDK package contains headers and library installed at the following locations:
+SDK package contains headers and library installed at the following locations
+by default:
* Headers: /usr/local/include/mat
-* Library: /usr/local/lib/${arch}/libmat.a
+* Library: /usr/local/lib/libmat.a
+
+If you set a custom install prefix via
+`CMAKE_OPTS="-DCMAKE_INSTALL_PREFIX=/path/to/install"`, the SDK is installed
+under `/include/mat` and `/lib/libmat.a`.
1DS SDK is built using cmake, but you can explore building it with any other build system of your choice.
diff --git a/docs/public/conf.py b/docs/public/conf.py
index d38fa3f7b..1d29ebe99 100644
--- a/docs/public/conf.py
+++ b/docs/public/conf.py
@@ -16,7 +16,7 @@
# -- Project information -----------------------------------------------------
-project = 'Microsoft C++ Client Telemetry SDK"'
+project = 'Microsoft C++ Client Telemetry SDK'
copyright = 'Microsoft Corporation'
author = 'Microsoft Corporation'
@@ -28,8 +28,6 @@
# This is necessary so the readthedocs build works. It doesn't invoke the
# Makefile, but just runs sphinx on this conf.py.
import os
-import shutil
-import subprocess
if not os.path.exists('doxyoutput'):
os.makedirs('doxyoutput')
@@ -61,7 +59,7 @@
primary_domain = "cpp"
-higlight_language = "cpp"
+highlight_language = "cpp"
# Add any paths that contain templates here, relative to this directory.
@@ -78,10 +76,9 @@
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
-#html_theme = "furo"
-html_theme = "sphinx_rtd_theme"
+html_theme = "furo"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
\ No newline at end of file
+html_static_path = []
diff --git a/docs/public/requirements.txt b/docs/public/requirements.txt
index 7419647ac..92d9b3915 100644
--- a/docs/public/requirements.txt
+++ b/docs/public/requirements.txt
@@ -1,3 +1,4 @@
+sphinx<10
breathe
exhale
-furo
\ No newline at end of file
+furo
diff --git a/examples/cpp/SampleCpp/SampleCpp.vcxproj b/examples/cpp/SampleCpp/SampleCpp.vcxproj
index 500d966f6..ffa49633a 100644
--- a/examples/cpp/SampleCpp/SampleCpp.vcxproj
+++ b/examples/cpp/SampleCpp/SampleCpp.vcxproj
@@ -1080,7 +1080,6 @@
-
diff --git a/examples/cpp/SampleCpp/SampleCpp.vcxproj.filters b/examples/cpp/SampleCpp/SampleCpp.vcxproj.filters
index 9a605a027..ae87cc1f8 100644
--- a/examples/cpp/SampleCpp/SampleCpp.vcxproj.filters
+++ b/examples/cpp/SampleCpp/SampleCpp.vcxproj.filters
@@ -15,9 +15,6 @@
-
- Header Files
-
Source Files
diff --git a/examples/cpp/SampleCppMini/SampleCppMini.vcxproj b/examples/cpp/SampleCppMini/SampleCppMini.vcxproj
index 88bca8d6d..424394f7e 100644
--- a/examples/cpp/SampleCppMini/SampleCppMini.vcxproj
+++ b/examples/cpp/SampleCppMini/SampleCppMini.vcxproj
@@ -1542,7 +1542,6 @@
-
diff --git a/examples/cpp/SampleCppMini/SampleCppMini.vcxproj.filters b/examples/cpp/SampleCppMini/SampleCppMini.vcxproj.filters
index ad3de7523..2df19ab39 100644
--- a/examples/cpp/SampleCppMini/SampleCppMini.vcxproj.filters
+++ b/examples/cpp/SampleCppMini/SampleCppMini.vcxproj.filters
@@ -14,11 +14,6 @@
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
-
-
- Header Files
-
-
Source Files
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index a39e65b98..8824427b4 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,7 +1,7 @@
# Honor visibility properties for all target types
cmake_policy(SET CMP0063 NEW)
-include_directories( . ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include/public ${CMAKE_CURRENT_SOURCE_DIR}/include/public ${CMAKE_CURRENT_SOURCE_DIR}/include/mat ${CMAKE_CURRENT_SOURCE_DIR}/pal ${CMAKE_CURRENT_SOURCE_DIR}/utils ${CMAKE_CURRENT_SOURCE_DIR}/modules/exp ${CMAKE_CURRENT_SOURCE_DIR}/modules/dataviewer ${CMAKE_CURRENT_SOURCE_DIR}/modules/privacyguard ${CMAKE_CURRENT_SOURCE_DIR}/modules/liveeventinspector ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/Reachability ${CMAKE_CURRENT_SOURCE_DIR}/modules/cds ${CMAKE_CURRENT_SOURCE_DIR}/modules/signals ${CMAKE_CURRENT_SOURCE_DIR}/modules/sanitizer /usr/local/include )
+include_directories( . ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include/public ${CMAKE_CURRENT_SOURCE_DIR}/include/mat ${CMAKE_CURRENT_SOURCE_DIR}/pal ${CMAKE_CURRENT_SOURCE_DIR}/utils ${CMAKE_CURRENT_SOURCE_DIR}/modules/exp ${CMAKE_CURRENT_SOURCE_DIR}/modules/dataviewer ${CMAKE_CURRENT_SOURCE_DIR}/modules/privacyguard ${CMAKE_CURRENT_SOURCE_DIR}/modules/liveeventinspector ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/Reachability ${CMAKE_CURRENT_SOURCE_DIR}/modules/cds ${CMAKE_CURRENT_SOURCE_DIR}/modules/signals ${CMAKE_CURRENT_SOURCE_DIR}/modules/sanitizer /usr/local/include )
set(SRCS decorators/BaseDecorator.cpp
packager/BondSplicer.cpp
@@ -60,7 +60,7 @@ if(BUILD_AZMON)
include(modules/azmon/CMakeLists.txt OPTIONAL)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/exp/)
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/exp/")
list(APPEND SRCS
modules/exp/afd/afdclient/AFDClientUtils.cpp
modules/exp/afd/afdclient/AFDClient.cpp
@@ -74,14 +74,14 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/exp/)
)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/dataviewer/)
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/dataviewer/")
list(APPEND SRCS
modules/dataviewer/DefaultDataViewer.cpp
modules/dataviewer/OnDisableNotificationCollection.cpp
)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/privacyguard/ AND BUILD_PRIVACYGUARD)
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/privacyguard/" AND BUILD_PRIVACYGUARD)
list(APPEND SRCS
modules/privacyguard/PrivacyGuard.cpp
modules/privacyguard/RegisteredFileTypes.cpp
@@ -89,14 +89,14 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/privacyguard/ AND BUILD_PRIVACYGUA
)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/liveeventinspector/ AND BUILD_LIVEEVENTINSPECTOR)
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/liveeventinspector/" AND BUILD_LIVEEVENTINSPECTOR)
list(APPEND SRCS
modules/liveeventinspector/LiveEventInspector.cpp
modules/liveeventinspector/LiveEventInspector.hpp
)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/cds/ AND BUILD_CDS)
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/cds/" AND BUILD_CDS)
add_definitions(-DHAVE_MAT_CDS)
list(APPEND SRCS
modules/cds/CdsFactory.hpp
@@ -104,14 +104,14 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/cds/ AND BUILD_CDS)
)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/signals/ AND BUILD_SIGNALS)
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/signals/" AND BUILD_SIGNALS)
list(APPEND SRCS
modules/signals/Signals.cpp
modules/signals/SignalsEncoder.cpp
)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/sanitizer/ AND BUILD_SANITIZER)
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/sanitizer/" AND BUILD_SANITIZER)
list(APPEND SRCS
modules/sanitizer/detectors/EmailAddressDetector.cpp
modules/sanitizer/detectors/JwtDetector.cpp
@@ -124,6 +124,15 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/sanitizer/ AND BUILD_SANITIZER)
modules/sanitizer/SanitizerTrie.cpp
modules/sanitizer/SanitizerTrieNode.cpp
)
+ # Suppress -Wreorder for Sanitizer.cpp only: the submodule declares
+ # m_sanitizerprovider before m_notificationEventName in Sanitizer.hpp
+ # but the constructor init list reverses that order. The proper fix
+ # belongs in the lib/modules submodule; this scopes the suppression
+ # until that is done.
+ if(NOT MSVC)
+ set_source_files_properties(modules/sanitizer/Sanitizer.cpp
+ PROPERTIES COMPILE_FLAGS "-Wno-reorder")
+ endif()
endif()
if(PAL_IMPLEMENTATION STREQUAL "CPP11")
@@ -172,7 +181,7 @@ if(PAL_IMPLEMENTATION STREQUAL "CPP11")
)
endif()
if(APPLE AND BUILD_OBJC_WRAPPER)
- message("Include ObjC Wrappers")
+ message(STATUS "Include ObjC Wrappers")
list(APPEND SRCS
../wrappers/obj-c/ODWLogger.mm
../wrappers/obj-c/ODWLogManager.mm
@@ -180,19 +189,19 @@ if(PAL_IMPLEMENTATION STREQUAL "CPP11")
../wrappers/obj-c/ODWLogConfiguration.mm
../wrappers/obj-c/ODWSemanticContext.mm
)
- if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/dataviewer/)
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/dataviewer/")
list(APPEND SRCS
../wrappers/obj-c/ODWDiagnosticDataViewer.mm
)
endif()
- if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/privacyguard/ AND BUILD_PRIVACYGUARD)
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/privacyguard/" AND BUILD_PRIVACYGUARD)
list(APPEND SRCS
../wrappers/obj-c/ODWCommonDataContext.mm
../wrappers/obj-c/ODWPrivacyGuard.mm
../wrappers/obj-c/ODWPrivacyGuardInitConfig.mm
)
endif()
- if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/sanitizer/ AND BUILD_SANITIZER)
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/sanitizer/" AND BUILD_SANITIZER)
list(APPEND SRCS
../wrappers/obj-c/ODWSanitizerInitConfig.mm
../wrappers/obj-c/ODWSanitizer.mm
@@ -201,7 +210,7 @@ if(PAL_IMPLEMENTATION STREQUAL "CPP11")
endif()
if(APPLE AND BUILD_SWIFT_WRAPPER)
- message("Building Swift Wrappers")
+ message(STATUS "Building Swift Wrappers")
# Run swift build for the Swift Wrappers Package
string(TOLOWER ${CMAKE_BUILD_TYPE} LOWER_BUILD_TYPE)
execute_process(
@@ -213,9 +222,9 @@ if(PAL_IMPLEMENTATION STREQUAL "CPP11")
)
if(SWIFT_BUILD_RESULT EQUAL 0)
- message("Swift Wrappers build succeeded!")
+ message(STATUS "Swift Wrappers build succeeded!")
else()
- message(FATAL_ERROR, "Swift build failed with error code: ${SWIFT_BUILD_RESULT}")
+ message(FATAL_ERROR "Swift build failed with error code: ${SWIFT_BUILD_RESULT}")
endif()
endif()
@@ -238,7 +247,7 @@ remove_definitions(-D_MBCS)
)
# UTC module
- if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/utc)
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/utc")
list(APPEND SRCS
modules/utc/desktop/UtcHelpers.cpp
modules/utc/UtcTelemetrySystem.cpp
@@ -250,7 +259,7 @@ else()
endif()
# Filtering module
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/filter)
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/filter")
list(APPEND SRCS
modules/filter/CompliantByDefaultEventFilterModule.cpp
modules/filter/CompliantByDefaultFilterApi.cpp
@@ -269,7 +278,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
endif()
if(BUILD_SHARED_LIBS STREQUAL "ON")
- message("-- Building shared SDK library")
+ message(STATUS "Building shared SDK library")
# include(FindCURL)
# find_package(CURL REQUIRED)
@@ -304,13 +313,19 @@ if(BUILD_SHARED_LIBS STREQUAL "ON")
# target_link_libraries(mat PUBLIC libsqlite3 libcurl.a libz.a libssl.a libcrypto.a "${SQLITE_LIBRARY}" "${CMAKE_THREAD_LIBS_INIT}" "${CMAKE_DL_LIBS}" )
install(TARGETS mat EXPORT mat LIBRARY DESTINATION ${INSTALL_LIB_DIR})
else()
- message("-- Building static SDK library")
+ message(STATUS "Building static SDK library")
add_library(mat STATIC ${SRCS})
if(LINK_STATIC_DEPENDS)
if(PAL_IMPLEMENTATION STREQUAL "WIN32")
target_link_libraries(mat ${LIBS} "${CMAKE_THREAD_LIBS_INIT}" "${CMAKE_DL_LIBS}" )
else()
add_library(sqlite3 STATIC IMPORTED GLOBAL)
+ find_library(SQLITE3_STATIC_LIB NAMES libsqlite3.a
+ PATHS /usr/local/lib /usr/local/opt/sqlite/lib /opt/homebrew/opt/sqlite/lib
+ NO_DEFAULT_PATH)
+ if(SQLITE3_STATIC_LIB)
+ set_target_properties(sqlite3 PROPERTIES IMPORTED_LOCATION ${SQLITE3_STATIC_LIB})
+ endif()
add_library(z STATIC IMPORTED GLOBAL)
#
# TODO: allow adding "${Tcmalloc_LIBRARIES}" to target_link_libraries for memory leak debugging
@@ -321,10 +336,6 @@ else()
install(TARGETS mat EXPORT mat ARCHIVE DESTINATION ${INSTALL_LIB_DIR})
endif()
-message("-- Library will be installed to ${INSTALL_LIB_DIR}")
+message(STATUS "Library will be installed to ${INSTALL_LIB_DIR}")
-#if(PAL_IMPLEMENTATION STREQUAL "CPP11")
-# #target_link_libraries(mat PUBLIC libcurl.a libz.a libssl.a libcrypto.a "${SQLITE_LIBRARY}" "${CMAKE_THREAD_LIBS_INIT}" "${CMAKE_DL_LIBS}" )
-# #target_link_libraries(mat PUBLIC libsqlite3.a libz.a ${LIBS} "${CMAKE_THREAD_LIBS_INIT}" "${CMAKE_DL_LIBS}" )
-#endif()
diff --git a/lib/android_build/maesdk/src/main/cpp/CMakeLists.txt b/lib/android_build/maesdk/src/main/cpp/CMakeLists.txt
index dbea0dc98..f53d5403a 100644
--- a/lib/android_build/maesdk/src/main/cpp/CMakeLists.txt
+++ b/lib/android_build/maesdk/src/main/cpp/CMakeLists.txt
@@ -45,6 +45,7 @@ set(SRCS
${SDK_ROOT}/lib/bond/BondSerializer.cpp
${SDK_ROOT}/lib/callbacks/DebugSource.cpp
${SDK_ROOT}/lib/compression/HttpDeflateCompression.cpp
+ ${SDK_ROOT}/lib/decoder/PayloadDecoder.cpp
${SDK_ROOT}/lib/decorators/BaseDecorator.cpp
${SDK_ROOT}/lib/filter/EventFilterCollection.cpp
${SDK_ROOT}/lib/http/HttpClientFactory.cpp
diff --git a/lib/api/LogManagerImpl.cpp b/lib/api/LogManagerImpl.cpp
index fac8fdbd5..2f0e8933d 100644
--- a/lib/api/LogManagerImpl.cpp
+++ b/lib/api/LogManagerImpl.cpp
@@ -289,13 +289,7 @@ namespace MAT_NS_BEGIN
if (m_httpClient == nullptr)
{
m_httpClient = HttpClientFactory::Create();
-#ifdef HAVE_MAT_WININET_HTTP_CLIENT
- HttpClient_WinInet* client = static_cast(m_httpClient.get());
- if (client != nullptr)
- {
- client->SetMsRootCheck(m_logConfiguration[CFG_MAP_HTTP][CFG_BOOL_HTTP_MS_ROOT_CHECK]);
- }
-#endif
+ m_httpClient->ApplySettings(m_logConfiguration);
}
else
{
@@ -366,14 +360,10 @@ namespace MAT_NS_BEGIN
///
void LogManagerImpl::Configure()
{
- // TODO: [maxgolov] - add other config params.
-#ifdef HAVE_MAT_WININET_HTTP_CLIENT
- HttpClient_WinInet* client = static_cast(m_httpClient.get());
- if (client != nullptr)
+ if (m_httpClient != nullptr)
{
- client->SetMsRootCheck(m_logConfiguration[CFG_MAP_HTTP][CFG_BOOL_HTTP_MS_ROOT_CHECK]);
+ m_httpClient->ApplySettings(m_logConfiguration);
}
-#endif
}
LogManagerImpl::~LogManagerImpl() noexcept
diff --git a/lib/config/RuntimeConfig_Default.hpp b/lib/config/RuntimeConfig_Default.hpp
index 27a8f6758..504aeefe3 100644
--- a/lib/config/RuntimeConfig_Default.hpp
+++ b/lib/config/RuntimeConfig_Default.hpp
@@ -33,6 +33,9 @@ namespace MAT_NS_BEGIN
{/* Parameter that allows to split stats events by tenant */
{"split", false},
{"interval", 1800},
+ /* Stats are disabled by default for the built-in shared token
+ to reduce OneCollector load (see #1420). Set to true to opt in. */
+ {"enabled", false},
{"tokenProd", STATS_TOKEN_PROD},
{"tokenInt", STATS_TOKEN_INT}}},
{"utc",
@@ -57,7 +60,11 @@ namespace MAT_NS_BEGIN
,
{"contentEncoding", "deflate"},
/* Optional parameter to require Microsoft Root CA */
- {CFG_BOOL_HTTP_MS_ROOT_CHECK, false}}},
+ {CFG_BOOL_HTTP_MS_ROOT_CHECK, false},
+ /* Optional parameter for SSL certificate verification (curl) */
+ {CFG_BOOL_HTTP_SSL_VERIFY, true},
+ /* Optional CA bundle path for OpenSSL-backed curl */
+ {CFG_STR_HTTP_SSL_CAINFO, ""}}},
{CFG_MAP_TPM,
{
{CFG_INT_TPM_MAX_BLOB_BYTES, 2097152},
diff --git a/lib/http/HttpClient_Curl.cpp b/lib/http/HttpClient_Curl.cpp
index 18ddabce9..b910cdf28 100644
--- a/lib/http/HttpClient_Curl.cpp
+++ b/lib/http/HttpClient_Curl.cpp
@@ -14,6 +14,7 @@
#include "utils/Utils.hpp"
#include "HttpClient_Curl.hpp"
+#include "ILogConfiguration.hpp"
namespace MAT_NS_BEGIN {
@@ -74,7 +75,13 @@ namespace MAT_NS_BEGIN {
requestHeaders[header.first] = header.second;
}
- auto curlOperation = std::make_shared(curlRequest->m_method, curlRequest->m_url, callback, requestHeaders, curlRequest->m_body);
+ std::string sslCaInfo;
+ {
+ std::lock_guard lock(m_requestsMtx);
+ sslCaInfo = m_sslCaInfo;
+ }
+
+ auto curlOperation = std::make_shared(curlRequest->m_method, curlRequest->m_url, callback, requestHeaders, curlRequest->m_body, false, HTTP_CONN_TIMEOUT, m_sslVerify, sslCaInfo);
curlRequest->SetOperation(curlOperation);
// The lifetime of curlOperation is guarnteed by the call to result.wait() in the d'tor.
@@ -125,6 +132,20 @@ namespace MAT_NS_BEGIN {
}
}
+ void HttpClient_Curl::ApplySettings(ILogConfiguration& config)
+ {
+ SetSslVerification(
+ config[CFG_MAP_HTTP][CFG_BOOL_HTTP_SSL_VERIFY],
+ (const char *)config[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO]);
+ }
+
+ void HttpClient_Curl::SetSslVerification(bool sslVerify, const std::string& caInfo)
+ {
+ m_sslVerify = sslVerify;
+ std::lock_guard lock(m_requestsMtx);
+ m_sslCaInfo = caInfo;
+ }
+
void HttpClient_Curl::EraseRequest(std::string const& id)
{
std::lock_guard lock(m_requestsMtx);
diff --git a/lib/http/HttpClient_Curl.hpp b/lib/http/HttpClient_Curl.hpp
index cb89ec7e6..c7a5bdecb 100644
--- a/lib/http/HttpClient_Curl.hpp
+++ b/lib/http/HttpClient_Curl.hpp
@@ -23,6 +23,7 @@
#include
#include
+#include
#include
#include
@@ -55,12 +56,17 @@ class HttpClient_Curl : public IHttpClient {
virtual void SendRequestAsync(IHttpRequest* request, IHttpResponseCallback* callback) override;
virtual void CancelRequestAsync(std::string const& id) override;
+ virtual void ApplySettings(ILogConfiguration& config) override;
+ void SetSslVerification(bool sslVerify, const std::string& caInfo = "");
+
private:
void EraseRequest(std::string const& id);
void AddRequest(IHttpRequest* request);
std::mutex m_requestsMtx;
std::map m_requests;
+ std::atomic m_sslVerify { true };
+ std::string m_sslCaInfo;
};
class CurlHttpOperation {
@@ -91,7 +97,10 @@ class CurlHttpOperation {
const std::vector& requestBody = std::vector(),
// Default connectivity and response size options
bool rawResponse = false,
- size_t httpConnTimeout = HTTP_CONN_TIMEOUT) :
+ size_t httpConnTimeout = HTTP_CONN_TIMEOUT,
+ // SSL certificate verification options
+ bool sslVerify = true,
+ const std::string& sslCaInfo = "") :
// Optional connection params
rawResponse(rawResponse),
@@ -100,6 +109,7 @@ class CurlHttpOperation {
m_callback(callback),
m_method(method),
m_url(url),
+ m_sslCaInfo(sslCaInfo),
// Local vars
requestHeaders(requestHeaders),
@@ -129,9 +139,11 @@ class CurlHttpOperation {
// Specify target URL
curl_easy_setopt(curl, CURLOPT_URL, m_url.c_str());
- // TODO: expose SSL cert verification opts via ILogConfiguration
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); // 1L
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); // 2L
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, sslVerify ? 1L : 0L);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, sslVerify ? 2L : 0L);
+ if (!m_sslCaInfo.empty()) {
+ curl_easy_setopt(curl, CURLOPT_CAINFO, m_sslCaInfo.c_str());
+ }
// HTTP/2 please, fallback to HTTP/1.1 if not supported
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
@@ -423,6 +435,7 @@ class CurlHttpOperation {
// Request values
std::string m_method;
std::string m_url;
+ std::string m_sslCaInfo;
const std::map& requestHeaders;
const std::vector& requestBody;
struct curl_slist *m_headersChunk = nullptr;
@@ -452,28 +465,12 @@ class CurlHttpOperation {
*/
static int WaitOnSocket(curl_socket_t sockfd, int for_recv, long timeout_ms)
{
- struct timeval tv;
- fd_set infd, outfd, errfd;
- int res;
-
- tv.tv_sec = timeout_ms / 1000;
- tv.tv_usec = (timeout_ms % 1000) * 1000;
-
- FD_ZERO(&infd);
- FD_ZERO(&outfd);
- FD_ZERO(&errfd);
-
- FD_SET(sockfd, &errfd); /* always check for error */
-
- if(for_recv) {
- FD_SET(sockfd, &infd);
- } else {
- FD_SET(sockfd, &outfd);
- }
-
- /* select() returns the number of signalled sockets or -1 */
- res = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv);
- return res;
+ struct pollfd pfd;
+ pfd.fd = sockfd;
+ pfd.events = for_recv ? POLLIN : POLLOUT;
+ // Cap timeout to max int value to avoid overflow in poll()
+ auto timeout = std::min(timeout_ms, static_cast(std::numeric_limits::max()));
+ return poll(&pfd, 1, static_cast(timeout));
}
// Raw response buffer
@@ -539,4 +536,3 @@ class CurlHttpOperation {
#endif // HAVE_MAT_DEFAULT_HTTP_CLIENT
#endif // HTTPCLIENTCURL_HPP
-
diff --git a/lib/http/HttpClient_WinInet.cpp b/lib/http/HttpClient_WinInet.cpp
index 637b10778..eaefb2318 100644
--- a/lib/http/HttpClient_WinInet.cpp
+++ b/lib/http/HttpClient_WinInet.cpp
@@ -546,6 +546,11 @@ void HttpClient_WinInet::CancelAllRequests()
/// Enforces MS-root server certificate check.
///
/// if set to true [enforce verification that server cert is MS-Rooted].
+void HttpClient_WinInet::ApplySettings(ILogConfiguration& config)
+{
+ SetMsRootCheck(config[CFG_MAP_HTTP][CFG_BOOL_HTTP_MS_ROOT_CHECK]);
+}
+
void HttpClient_WinInet::SetMsRootCheck(bool enforceMsRoot)
{
m_msRootCheck = enforceMsRoot;
diff --git a/lib/http/HttpClient_WinInet.hpp b/lib/http/HttpClient_WinInet.hpp
index e5936fcc3..7e9379ded 100644
--- a/lib/http/HttpClient_WinInet.hpp
+++ b/lib/http/HttpClient_WinInet.hpp
@@ -30,6 +30,8 @@ class HttpClient_WinInet : public IHttpClient {
virtual void CancelRequestAsync(std::string const& id) final;
virtual void CancelAllRequests() final;
+ virtual void ApplySettings(ILogConfiguration& config) override;
+
// Methods unique to WinInet implementation.
void SetMsRootCheck(bool enforceMsRoot);
bool IsMsRootCheckRequired();
diff --git a/lib/include/public/IHttpClient.hpp b/lib/include/public/IHttpClient.hpp
index e9a71a210..89e5e6cf0 100644
--- a/lib/include/public/IHttpClient.hpp
+++ b/lib/include/public/IHttpClient.hpp
@@ -18,6 +18,7 @@
///@cond INTERNAL_DOCS
namespace MAT_NS_BEGIN
{
+ class ILogConfiguration;
///
/// The HttpHeaders class contains a set of HTTP headers.
///
@@ -543,6 +544,14 @@ namespace MAT_NS_BEGIN
virtual void CancelRequestAsync(std::string const& id) = 0;
virtual void CancelAllRequests() {}
+
+ ///
+ /// Apply HTTP settings from the log configuration.
+ /// Subclasses override to handle platform-specific options.
+ /// Default implementation is a no-op.
+ ///
+ /// The log configuration to read settings from.
+ virtual void ApplySettings(ILogConfiguration& /*config*/) {}
};
/// @endcond
diff --git a/lib/include/public/ILogConfiguration.hpp b/lib/include/public/ILogConfiguration.hpp
index 952bc2651..1cb8103b8 100644
--- a/lib/include/public/ILogConfiguration.hpp
+++ b/lib/include/public/ILogConfiguration.hpp
@@ -361,6 +361,16 @@ namespace MAT_NS_BEGIN
///
static constexpr const char* const CFG_BOOL_HTTP_COMPRESSION = "compress";
+ ///
+ /// HTTP configuration: SSL certificate verification (peer + host)
+ ///
+ static constexpr const char* const CFG_BOOL_HTTP_SSL_VERIFY = "sslVerify";
+
+ ///
+ /// HTTP configuration: SSL CA bundle file path (for libcurl/OpenSSL)
+ ///
+ static constexpr const char* const CFG_STR_HTTP_SSL_CAINFO = "sslCaInfo";
+
///
/// TPM configuration map
///
diff --git a/lib/include/public/Version.hpp b/lib/include/public/Version.hpp
index ce9e8fd79..850519486 100644
--- a/lib/include/public/Version.hpp
+++ b/lib/include/public/Version.hpp
@@ -6,8 +6,8 @@
#define MAT_VERSION_HPP
// WARNING: DO NOT MODIFY THIS FILE!
// This file has been automatically generated, manual changes will be lost.
-#define BUILD_VERSION_STR "3.10.40.1"
-#define BUILD_VERSION 3,10,40,1
+#define BUILD_VERSION_STR "3.10.100.1"
+#define BUILD_VERSION 3,10,100,1
#ifndef RESOURCE_COMPILER_INVOKED
#include "ctmacros.hpp"
@@ -18,7 +18,7 @@ namespace MAT_NS_BEGIN {
uint64_t const Version =
((uint64_t)3 << 48) |
((uint64_t)10 << 32) |
- ((uint64_t)40 << 16) |
+ ((uint64_t)100 << 16) |
((uint64_t)1);
} MAT_NS_END
diff --git a/lib/offline/OfflineStorage_Room.cpp b/lib/offline/OfflineStorage_Room.cpp
index ab7d43264..423aecde3 100644
--- a/lib/offline/OfflineStorage_Room.cpp
+++ b/lib/offline/OfflineStorage_Room.cpp
@@ -11,6 +11,19 @@
namespace
{
static constexpr bool s_throwExceptions = true;
+
+ // RAII guard that deletes a JNI global class reference on all exit paths,
+ // including std::logic_error (ThrowLogic) and std::runtime_error (ThrowRuntime).
+ struct GlobalRefGuard {
+ JNIEnv* jni;
+ jclass* ref_ptr;
+ ~GlobalRefGuard() noexcept {
+ if (ref_ptr && *ref_ptr) {
+ jni->DeleteGlobalRef(*ref_ptr);
+ *ref_ptr = nullptr;
+ }
+ }
+ };
}
namespace MAT_NS_BEGIN
@@ -387,17 +400,23 @@ namespace MAT_NS_BEGIN
{
break; // out of r > c loop; no more records
}
- // we don't collect these here because GetObjectClass is
- // less fragile than FindClass
- jclass record_class = nullptr;
- jfieldID id_id;
- jfieldID tenantToken_id;
- jfieldID latency_id;
- jfieldID persistence_id;
- jfieldID timestamp_id;
- jfieldID retryCount_id;
- jfieldID reservedUntil_id;
- jfieldID blob_id;
+ // Field IDs are looked up once from the first record's class and reused.
+ // record_class is stored as a global reference so it remains valid across
+ // pushLocalFrame/popLocalFrame boundaries (local refs are freed on popLocalFrame,
+ // causing a JNI abort on ART if reused in subsequent iterations).
+ jclass record_class = nullptr;
+ jfieldID id_id = nullptr;
+ jfieldID tenantToken_id = nullptr;
+ jfieldID latency_id = nullptr;
+ jfieldID persistence_id = nullptr;
+ jfieldID timestamp_id = nullptr;
+ jfieldID retryCount_id = nullptr;
+ jfieldID reservedUntil_id = nullptr;
+ jfieldID blob_id = nullptr;
+ // RAII guard: deletes record_class global ref on all exit paths,
+ // including std::logic_error (ThrowLogic) and std::runtime_error
+ // (ThrowRuntime) which the catch block below would not otherwise clean up.
+ GlobalRefGuard record_class_guard{env.getInner(), &record_class};
// Set limits for conversion from int to enum
int latency_lb = static_cast(EventLatency_Off);
@@ -412,7 +431,14 @@ namespace MAT_NS_BEGIN
ThrowLogic(env, "getAndReserve element");
if (!record_class)
{
- record_class = env->GetObjectClass(record);
+ // Promote to a global ref so it survives popLocalFrame on
+ // subsequent iterations. Freed by record_class_guard on exit.
+ jclass local_class = env->GetObjectClass(record);
+ record_class = static_cast(env->NewGlobalRef(local_class));
+ if (!record_class)
+ {
+ MATSDK_THROW(std::runtime_error("NewGlobalRef failed"));
+ }
id_id = env->GetFieldID(record_class, "id", "J");
ThrowLogic(env, "gar id");
tenantToken_id = env->GetFieldID(record_class, "tenantToken",
@@ -663,9 +689,12 @@ namespace MAT_NS_BEGIN
if (tokens > 0)
{
DroppedMap dropped;
- jclass bt_class = nullptr;
- jfieldID token_id;
- jfieldID count_id;
+ // bt_class stored as a global ref to survive popLocalFrame across iterations.
+ jclass bt_class = nullptr;
+ jfieldID token_id = nullptr;
+ jfieldID count_id = nullptr;
+ // RAII guard: frees bt_class on all exit paths including exceptions.
+ GlobalRefGuard bt_class_guard{env.getInner(), &bt_class};
for (size_t index = 0; index < tokens; ++index)
{
env.pushLocalFrame(8);
@@ -673,7 +702,14 @@ namespace MAT_NS_BEGIN
ThrowRuntime(env, "Exception fetching element from results");
if (!bt_class)
{
- bt_class = env->GetObjectClass(byTenant);
+ // Promote to a global ref so it survives popLocalFrame.
+ // Freed by bt_class_guard on exit.
+ jclass local_class = env->GetObjectClass(byTenant);
+ bt_class = static_cast(env->NewGlobalRef(local_class));
+ if (!bt_class)
+ {
+ MATSDK_THROW(std::runtime_error("NewGlobalRef failed"));
+ }
token_id = env->GetFieldID(bt_class, "tenantToken",
"Ljava/lang/String;");
ThrowLogic(env, "Error fetching tenantToken field id");
@@ -1160,15 +1196,18 @@ namespace MAT_NS_BEGIN
"(ZIJ)[Lcom/microsoft/applications/events/StorageRecord;");
ThrowLogic(env, "getRecords method");
- jclass record_class = nullptr;
- jfieldID id_id = nullptr;
- jfieldID tenantToken_id;
- jfieldID latency_id;
- jfieldID persistence_id;
- jfieldID timestamp_id;
- jfieldID retryCount_id;
- jfieldID reservedUntil_id;
- jfieldID blob_id;
+ // record_class stored as a global ref to survive popLocalFrame across iterations.
+ jclass record_class = nullptr;
+ jfieldID id_id = nullptr;
+ jfieldID tenantToken_id = nullptr;
+ jfieldID latency_id = nullptr;
+ jfieldID persistence_id = nullptr;
+ jfieldID timestamp_id = nullptr;
+ jfieldID retryCount_id = nullptr;
+ jfieldID reservedUntil_id = nullptr;
+ jfieldID blob_id = nullptr;
+ // RAII guard: frees record_class on all exit paths including exceptions.
+ GlobalRefGuard record_class_guard{env.getInner(), &record_class};
auto java_records = static_cast(env->CallObjectMethod(m_room,
method,
@@ -1185,7 +1224,14 @@ namespace MAT_NS_BEGIN
ThrowLogic(env, "access result element");
if (!record_class)
{
- record_class = env->GetObjectClass(record);
+ // Promote to a global ref so it survives popLocalFrame.
+ // Freed by record_class_guard on exit.
+ jclass local_class = env->GetObjectClass(record);
+ record_class = static_cast(env->NewGlobalRef(local_class));
+ if (!record_class)
+ {
+ MATSDK_THROW(std::runtime_error("NewGlobalRef failed"));
+ }
id_id = env->GetFieldID(record_class, "id", "J");
ThrowLogic(env, "id field");
tenantToken_id = env->GetFieldID(record_class, "tenantToken",
diff --git a/lib/shared/Shared.vcxitems b/lib/shared/Shared.vcxitems
index bf3d5df64..4b7c095ff 100644
--- a/lib/shared/Shared.vcxitems
+++ b/lib/shared/Shared.vcxitems
@@ -24,7 +24,6 @@
-
diff --git a/lib/shared/Shared.vcxitems.filters b/lib/shared/Shared.vcxitems.filters
index c488b869b..1868316bf 100644
--- a/lib/shared/Shared.vcxitems.filters
+++ b/lib/shared/Shared.vcxitems.filters
@@ -1,7 +1,6 @@
-
diff --git a/lib/stats/Statistics.cpp b/lib/stats/Statistics.cpp
index 2f421714c..4abf09563 100644
--- a/lib/stats/Statistics.cpp
+++ b/lib/stats/Statistics.cpp
@@ -7,6 +7,7 @@
#include "Statistics.hpp"
#include "ILogManager.hpp"
+#include "mat/config.h"
#include "utils/Utils.hpp"
#include
@@ -60,12 +61,21 @@ namespace MAT_NS_BEGIN {
return;
}
+ std::string tenantToken = m_config.GetMetaStatsTenantToken();
+ // Stats are disabled by default for the built-in shared token to
+ // reduce OneCollector load (see #1420). Custom tokens always send.
+ // Set config["metaStats"]["enabled"] = true to opt in.
+ bool isDefaultToken = (tenantToken == STATS_TOKEN_PROD || tenantToken == STATS_TOKEN_INT);
+ if (isDefaultToken && !static_cast(m_config[CFG_MAP_METASTATS_CONFIG]["enabled"]))
+ {
+ return;
+ }
+
std::vector< ::CsProtocol::Record> records;
{
LOCKGUARD(m_metaStats_mtx);
records = m_metaStats.generateStatsEvent(rollupKind);
}
- std::string tenantToken = m_config.GetMetaStatsTenantToken();
for (auto& record : records)
{
diff --git a/tests/functests/AISendTests.cpp b/tests/functests/AISendTests.cpp
index 180e9b074..dfd0bf185 100644
--- a/tests/functests/AISendTests.cpp
+++ b/tests/functests/AISendTests.cpp
@@ -118,7 +118,7 @@ class AISendTests : public ::testing::Test,
}
int port = server.addListeningPort(HTTP_PORT);
std::ostringstream os;
- os << "localhost:" << port;
+ os << "127.0.0.1:" << port;
serverAddress = "http://" + os.str() + "/v2/track";
server.setServerName(os.str());
server.addHandler("/v2/track", *this);
@@ -142,6 +142,9 @@ class AISendTests : public ::testing::Test,
fileName += PATH_SEPARATOR_CHAR;
fileName += TEST_STORAGE_FILENAME;
std::remove(fileName.c_str());
+ std::remove((fileName + "-wal").c_str());
+ std::remove((fileName + "-shm").c_str());
+ std::remove((fileName + "-journal").c_str());
}
virtual void Initialize(DebugEventListener& debugListener, std::string const& path, bool compression)
diff --git a/tests/functests/APITest.cpp b/tests/functests/APITest.cpp
index 11524cd5a..0347807f6 100644
--- a/tests/functests/APITest.cpp
+++ b/tests/functests/APITest.cpp
@@ -302,7 +302,11 @@ static std::string GetStoragePath()
static void CleanStorage()
{
- std::remove(GetStoragePath().c_str());
+ std::string path = GetStoragePath();
+ std::remove(path.c_str());
+ std::remove((path + "-wal").c_str());
+ std::remove((path + "-shm").c_str());
+ std::remove((path + "-journal").c_str());
}
#if 0
@@ -391,6 +395,10 @@ TEST(APITest, LogManager_Initialize_DebugEventListener)
LogManager::GetLogger()->LogEvent(eventToLog);
}
LogManager::Flush();
+ // Storage-full callback fires asynchronously; give it time to arrive
+ for (int i = 0; i < 50 && debugListener.storageFullPct.load() < 100; i++) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ }
EXPECT_GE(debugListener.storageFullPct.load(), (unsigned)100);
LogManager::FlushAndTeardown();
@@ -404,8 +412,20 @@ TEST(APITest, LogManager_Initialize_DebugEventListener)
debugListener.numSent = 0;
debugListener.numLogged = 0;
- CleanStorage();
+ // Use a unique DB path for Phase 2/3 on every invocation. The SDK
+ // closes SQLite with sqlite3_close_v2(), which defers file-descriptor
+ // cleanup when prepared statements linger. If we reuse a fixed path
+ // and std::remove() the old files while deferred fds are still open,
+ // iOS emits "vnode unlinked while in use" and may invalidate the new
+ // DB's descriptors. A fresh, never-before-seen path sidesteps the
+ // problem entirely — no stale files, no collisions, no sleep needed.
+ static std::atomic s_phase2Counter{0};
+ std::string phase2Path = GetStoragePath() + ".phase2." +
+ std::to_string(s_phase2Counter.fetch_add(1));
+ configuration[CFG_STR_CACHE_FILE_PATH] = phase2Path;
+ configuration[CFG_INT_CACHE_FILE_SIZE] = 0; // No size limit for phase 2
ILogger *result = LogManager::Initialize(TEST_TOKEN, configuration);
+ LogManager::PauseTransmission(); // Pause before logging to avoid production uploads
// Log some foo
size_t numIterations = MAX_ITERATIONS;
@@ -416,10 +436,6 @@ TEST(APITest, LogManager_Initialize_DebugEventListener)
EXPECT_EQ(0u, debugListener.numDropped);
EXPECT_EQ(0u, debugListener.numReject);
- LogManager::UploadNow(); // Try to upload whatever we got
- PAL::sleep(10000); // Give enough time to upload at least one event
- EXPECT_NE(0u, debugListener.numSent); // Some posts must succeed within 500ms
- LogManager::PauseTransmission(); // There could still be some pending at this point
LogManager::Flush(); // Save all pending to disk
numIterations = MAX_ITERATIONS;
@@ -434,15 +450,27 @@ TEST(APITest, LogManager_Initialize_DebugEventListener)
LogManager::Flush();
EXPECT_EQ(MAX_ITERATIONS, debugListener.numCached);
+ // Phase 3: resume transmission and upload the cached events
LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::ResumeTransmission();
+ LogManager::UploadNow();
+ PAL::sleep(10000); // Give enough time to upload
LogManager::FlushAndTeardown();
// Check that we sent all of logged + whatever left overs
// prior to PauseTransmission
EXPECT_GE(debugListener.numSent, debugListener.numLogged);
+
debugListener.printStats();
removeAllListeners(debugListener);
+
+ // Best-effort cleanup. Assertions have already passed, so if
+ // sqlite3_close_v2 deferred cleanup triggers a vnode warning here
+ // it is harmless.
+ std::remove(phase2Path.c_str());
+ std::remove((phase2Path + "-wal").c_str());
+ std::remove((phase2Path + "-shm").c_str());
+ std::remove((phase2Path + "-journal").c_str());
}
#ifdef _WIN32
@@ -1180,10 +1208,12 @@ TEST(APITest, LogManager_BadNetwork_Test)
// Clean temp file first
const char *cacheFilePath = "bad-network.db";
std::string fileName = MAT::GetTempDirectory();
- fileName += "\\";
fileName += cacheFilePath;
printf("remove %s\n", fileName.c_str());
std::remove(fileName.c_str());
+ std::remove((fileName + "-wal").c_str());
+ std::remove((fileName + "-shm").c_str());
+ std::remove((fileName + "-journal").c_str());
for (auto url : {
#if 0 /* [MG}: Temporary change to avoid GitHub Actions crash #92 */
diff --git a/tests/functests/BasicFuncTests.cpp b/tests/functests/BasicFuncTests.cpp
index 51848d054..438411425 100644
--- a/tests/functests/BasicFuncTests.cpp
+++ b/tests/functests/BasicFuncTests.cpp
@@ -154,7 +154,7 @@ class BasicFuncTests : public ::testing::Test,
}
int port = server.addListeningPort(HTTP_PORT);
std::ostringstream os;
- os << "localhost:" << port;
+ os << "127.0.0.1:" << port;
serverAddress = "http://" + os.str() + "/simple/";
server.setServerName(os.str());
server.addHandler("/simple/", *this);
@@ -179,6 +179,11 @@ class BasicFuncTests : public ::testing::Test,
fileName += PATH_SEPARATOR_CHAR;
fileName += TEST_STORAGE_FILENAME;
std::remove(fileName.c_str());
+ // SQLite WAL mode creates companion journal files that must also
+ // be removed to avoid "vnode unlinked while in use" on iOS.
+ std::remove((fileName + "-wal").c_str());
+ std::remove((fileName + "-shm").c_str());
+ std::remove((fileName + "-journal").c_str());
}
virtual void Initialize()
@@ -196,16 +201,22 @@ class BasicFuncTests : public ::testing::Test,
configuration[CFG_INT_RAM_QUEUE_SIZE] = 4096 * 20;
configuration[CFG_STR_CACHE_FILE_PATH] = TEST_STORAGE_FILENAME;
+ configuration[CFG_INT_CACHE_FILE_SIZE] = 4096 * 1024; // 4MB default
configuration[CFG_INT_MAX_TEARDOWN_TIME] = 2; // 2 seconds wait on shutdown
+ configuration[CFG_INT_STORAGE_FULL_PCT] = 75; // default
+ configuration[CFG_INT_STORAGE_FULL_CHECK_TIME] = 5000; // default 5s
configuration[CFG_STR_COLLECTOR_URL] = serverAddress.c_str();
configuration[CFG_MAP_HTTP][CFG_BOOL_HTTP_COMPRESSION] = false; // disable compression for now
+ configuration[CFG_MAP_TPM][CFG_STR_TPM_BACKOFF] = "E,500,5000,2,1"; // faster retry for localhost tests
configuration[CFG_MAP_METASTATS_CONFIG][CFG_INT_METASTATS_INTERVAL] = 30 * 60; // 30 mins
+ configuration[CFG_MAP_METASTATS_CONFIG]["enabled"] = true; // opt in to stats (disabled by default since #1420)
configuration["name"] = __FILE__;
configuration["version"] = "1.0.0";
configuration["config"] = { { "host", __FILE__ } }; // Host instance
LogManager::Initialize(TEST_TOKEN, configuration);
+ LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::SetLevelFilter(DIAG_LEVEL_DEFAULT, { DIAG_LEVEL_DEFAULT_MIN, DIAG_LEVEL_DEFAULT_MAX });
LogManager::ResumeTransmission();
@@ -257,15 +268,16 @@ class BasicFuncTests : public ::testing::Test,
size_t lastIdx = 0;
while ( ((PAL::getUtcSystemTimeMs()-start)<(1000* timeOutSec)) && (receivedEvents!=expected_count) )
{
- /* Give time for our friendly HTTP server thread to process incoming request */
- std::this_thread::yield();
+ /* Give time for HTTP server thread to process incoming request.
+ * sleep(10) instead of yield() reduces CPU contention on single-core
+ * iOS simulator runners and gives the network stack time to deliver. */
+ PAL::sleep(10);
{
LOCKGUARD(mtx_requests);
if (receivedRequests.size())
{
size_t size = receivedRequests.size();
- //requests can come within 100 milisec sleep
for (size_t index = lastIdx; index < size; index++)
{
auto request = receivedRequests.at(index);
@@ -573,6 +585,7 @@ TEST_F(BasicFuncTests, sendNoPriorityEvents)
event2.SetProperty("property2", "another value");
logger->LogEvent(event2);
+ LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::UploadNow();
waitForEvents(1, 3);
EXPECT_GE(receivedRequests.size(), (size_t)1);
@@ -671,6 +684,7 @@ TEST_F(BasicFuncTests, sendDifferentPriorityEvents)
logger->LogEvent(event2);
+ LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::UploadNow();
// 2 x customer events + 1 x evt_stats on start
waitForEvents(1, 3);
@@ -718,6 +732,7 @@ TEST_F(BasicFuncTests, sendMultipleTenantsTogether)
logger2->LogEvent(event2);
+ LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::UploadNow();
// 2 x customer events + 1 x evt_stats on start
@@ -748,6 +763,7 @@ TEST_F(BasicFuncTests, configDecorations)
EventProperties event4("4th_event");
logger->LogEvent(event4);
+ LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::UploadNow();
waitForEvents(2, 5);
@@ -785,10 +801,11 @@ TEST_F(BasicFuncTests, restartRecoversEventsFromStorage)
fooEvent.SetLatency(EventLatency_RealTime);
fooEvent.SetPersistence(EventPersistence_Critical);
LogManager::GetLogger()->LogEvent(fooEvent);
+ LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::UploadNow();
// 1st request for realtime event
- waitForEvents(3, 5); // start, first_event, second_event, ongoing, stop, start, fooEvent
+ waitForEvents(10, 5); // start, first_event, second_event, ongoing, stop, start, fooEvent
// we drop two of the events during pause, though.
EXPECT_GE(receivedRequests.size(), (size_t)1);
if (receivedRequests.size() != 0)
@@ -852,7 +869,7 @@ TEST_F(BasicFuncTests, storageFileSizeDoesntExceedConfiguredSize)
{
Initialize();
- waitForEvents(2, 8);
+ waitForEvents(5, 8);
if (receivedRequests.size())
{
auto payload = decodeRequest(receivedRequests[0], false);
@@ -897,8 +914,9 @@ TEST_F(BasicFuncTests, sendMetaStatsOnStart)
// Check
Initialize();
LogManager::ResumeTransmission(); // ?
+ LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::UploadNow();
- PAL::sleep(2000);
+ waitForEvents(5, 4); // (start + stop) + (2 events + start)
auto r2 = records();
ASSERT_GE(r2.size(), (size_t)4); // (start + stop) + (2 events + start)
@@ -927,8 +945,9 @@ TEST_F(BasicFuncTests, DiagLevelRequiredOnly_OneEventWithoutLevelOneWithButNotAl
eventWithAllowedLevel.SetLevel(DIAG_LEVEL_REQUIRED);
logger->LogEvent(eventWithAllowedLevel);
+ LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::UploadNow();
- waitForEvents(1 /*timeout*/, 2 /*expected count*/); // Start and EventWithAllowedLevel
+ waitForEvents(5 /*timeout*/, 2 /*expected count*/); // Start and EventWithAllowedLevel
ASSERT_EQ(records().size(), static_cast(2)); // Start and EventWithAllowedLevel
@@ -970,8 +989,9 @@ TEST_F(BasicFuncTests, DiagLevelRequiredOnly_SendTwoEventsUpdateAllowedLevelsSen
LogManager::SetLevelFilter(DIAG_LEVEL_OPTIONAL, { DIAG_LEVEL_OPTIONAL, DIAG_LEVEL_REQUIRED });
SendEventWithOptionalThenRequired(logger);
+ LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::UploadNow();
- waitForEvents(2 /*timeout*/, 4 /*expected count*/); // Start and EventWithAllowedLevel
+ waitForEvents(5 /*timeout*/, 4 /*expected count*/); // Start and EventWithAllowedLevel
auto sentRecords = records();
ASSERT_EQ(sentRecords.size(), static_cast(4)); // Start and EventWithAllowedLevel
@@ -1140,6 +1160,7 @@ TEST_F(BasicFuncTests, killSwitchWorks)
configuration[CFG_STR_COLLECTOR_URL] = serverAddress.c_str();
configuration[CFG_MAP_HTTP][CFG_BOOL_HTTP_COMPRESSION] = false; // disable compression for now
configuration[CFG_MAP_METASTATS_CONFIG]["interval"] = 30 * 60; // 30 mins
+ configuration[CFG_MAP_METASTATS_CONFIG]["enabled"] = true; // opt in to stats (disabled by default since #1420)
configuration["name"] = __FILE__;
configuration["version"] = "1.0.0";
@@ -1173,7 +1194,8 @@ TEST_F(BasicFuncTests, killSwitchWorks)
myLogger->LogEvent(event2);
}
}
- // Try to upload and wait for 2 seconds to complete
+ // Try to upload and wait for completion
+ LogManager::SetTransmitProfile(TransmitProfile_RealTime);
LogManager::UploadNow();
PAL::sleep(2000);
@@ -1222,6 +1244,7 @@ TEST_F(BasicFuncTests, killIsTemporary)
configuration[CFG_STR_COLLECTOR_URL] = serverAddress.c_str();
configuration[CFG_MAP_HTTP][CFG_BOOL_HTTP_COMPRESSION] = false; // disable compression for now
configuration[CFG_MAP_METASTATS_CONFIG]["interval"] = 30 * 60; // 30 mins
+ configuration[CFG_MAP_METASTATS_CONFIG]["enabled"] = true; // opt in to stats (disabled by default since #1420)
configuration["name"] = __FILE__;
configuration["version"] = "1.0.0";
diff --git a/tests/functests/CMakeLists.txt b/tests/functests/CMakeLists.txt
index 656f8f866..cb295e5c8 100644
--- a/tests/functests/CMakeLists.txt
+++ b/tests/functests/CMakeLists.txt
@@ -1,4 +1,4 @@
-message("--- functests")
+message(STATUS "Building functests")
set(SRCS
APITest.cpp
@@ -8,47 +8,40 @@ set(SRCS
MultipleLogManagersTests.cpp
)
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/privacyguard/ AND BUILD_PRIVACYGUARD)
+if(EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/privacyguard/" AND BUILD_PRIVACYGUARD)
add_definitions(-DHAVE_MAT_PRIVACYGUARD)
list(APPEND SRCS
- PrivacyGuardFuncTests.cpp
+ ${CMAKE_SOURCE_DIR}/lib/modules/privacyguard/tests/functests/PrivacyGuardFuncTests.cpp
)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/sanitizer/ AND BUILD_SANITIZER)
+if(EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/sanitizer/" AND BUILD_SANITIZER)
list(APPEND SRCS
- SanitizerFuncTests.cpp
+ ${CMAKE_SOURCE_DIR}/lib/modules/sanitizer/tests/functests/SanitizerFuncTests.cpp
)
endif()
-if(EXISTS ${CMAKE_SOURCE_DIR}/lib/modules/dataviewer/)
+if(EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/dataviewer/")
list(APPEND SRCS
${CMAKE_SOURCE_DIR}/lib/modules/dataviewer/tests/functests/DefaultDataViewerFuncTests.cpp
)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/liveeventinspector/ AND BUILD_LIVEEVENTINSPECTOR)
+if(EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/liveeventinspector/" AND BUILD_LIVEEVENTINSPECTOR)
add_definitions(-DHAVE_MAT_LIVEEVENTINSPECTOR)
list(APPEND SRCS
- LiveEventInspectorFuncTests.cpp
+ ${CMAKE_SOURCE_DIR}/lib/modules/liveeventinspector/tests/functests/LiveEventInspectorFuncTests.cpp
)
endif()
-if (EXISTS ${CMAKE_SOURCE_DIR}/lib/modules/exp/tests)
+if (EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/exp/tests")
list(APPEND SRCS
${CMAKE_SOURCE_DIR}/lib/modules/exp/tests/functests/ECSClientFuncTests.cpp
${CMAKE_SOURCE_DIR}/lib/modules/exp/tests/functests/ECSClientRealworldFuncTests.cpp
${CMAKE_SOURCE_DIR}/lib/modules/exp/tests/functests/ECSConfigCacheFuncTests.cpp
)
- if (EXISTS ${CMAKE_SOURCE_DIR}/lib/modules/exp/tests/functests/test.json)
- if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.21")
- # Use file(COPY_FILE ...) for CMake 3.21 and later
- file(COPY_FILE ${CMAKE_SOURCE_DIR}/lib/modules/exp/tests/functests/test.json ${CMAKE_BINARY_DIR}/test.json)
- else()
- # Use file(COPY ...) as an alternative for older versions
- file(COPY ${CMAKE_SOURCE_DIR}/lib/modules/exp/tests/functests/test.json
- DESTINATION ${CMAKE_BINARY_DIR})
- endif()
+ if (EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/exp/tests/functests/test.json")
+ configure_file(${CMAKE_SOURCE_DIR}/lib/modules/exp/tests/functests/test.json ${CMAKE_BINARY_DIR}/test.json COPYONLY)
endif()
endif()
@@ -63,11 +56,11 @@ endif()
if(PAL_IMPLEMENTATION STREQUAL "WIN32")
# Link against prebuilt libraries on Windows
- message("--- WIN32: Linking against prebuilt libraries")
- message("--- WIN32: ... ${CMAKE_BINARY_DIR}/gtest")
- message("--- WIN32: ... ${CMAKE_BINARY_DIR}/gmock")
- message("--- WIN32: ... ${CMAKE_BINARY_DIR}/zlib")
- message("--- WIN32: ... ${CMAKE_BINARY_DIR}/sqlite")
+ message(STATUS "WIN32: Linking against prebuilt libraries")
+ message(STATUS "WIN32: ... ${CMAKE_BINARY_DIR}/gtest")
+ message(STATUS "WIN32: ... ${CMAKE_BINARY_DIR}/gmock")
+ message(STATUS "WIN32: ... ${CMAKE_BINARY_DIR}/zlib")
+ message(STATUS "WIN32: ... ${CMAKE_BINARY_DIR}/sqlite")
# link_directories(${CMAKE_BINARY_DIR}/gtest/ ${CMAKE_BINARY_DIR}/gmock/ ${CMAKE_BINARY_DIR}/zlib/ ${CMAKE_BINARY_DIR}/sqlite/)
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../../zlib )
target_link_libraries(FuncTests
@@ -109,10 +102,9 @@ else()
set (PLATFORM_LIBS "atomic")
endif()
- # Find libraries
- message("--- Linking libraries! ")
- message("Current Dir: ${CMAKE_CURRENT_SOURCE_DIR}")
- message("Binary Dir: ${CMAKE_BINARY_DIR}")
+ message(STATUS "Linking libraries")
+ message(STATUS "Current Dir: ${CMAKE_CURRENT_SOURCE_DIR}")
+ message(STATUS "Binary Dir: ${CMAKE_BINARY_DIR}")
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER)
diff --git a/tests/functests/FuncTests.vcxproj b/tests/functests/FuncTests.vcxproj
index 50f4e2427..87b0b5b53 100644
--- a/tests/functests/FuncTests.vcxproj
+++ b/tests/functests/FuncTests.vcxproj
@@ -436,7 +436,6 @@
-
diff --git a/tests/functests/FuncTests.vcxproj.filters b/tests/functests/FuncTests.vcxproj.filters
index 22f2ac5d9..e19381a72 100644
--- a/tests/functests/FuncTests.vcxproj.filters
+++ b/tests/functests/FuncTests.vcxproj.filters
@@ -53,9 +53,6 @@
mocks
-
- mocks
-
mocks
diff --git a/tests/functests/MultipleLogManagersTests.cpp b/tests/functests/MultipleLogManagersTests.cpp
index 25f99f175..eac2bfd00 100644
--- a/tests/functests/MultipleLogManagersTests.cpp
+++ b/tests/functests/MultipleLogManagersTests.cpp
@@ -54,7 +54,7 @@ class RequestHandler : public HttpServer::Callback
}
private:
- size_t m_count {};
+ std::atomic m_count {};
int m_id ;
};
@@ -63,9 +63,9 @@ class MultipleLogManagersTests : public ::testing::Test
protected:
std::string serverAddress;
ILogConfiguration config1, config2, config3;
- RequestHandler callback1 = RequestHandler(1);
- RequestHandler callback2 = RequestHandler(2);
- RequestHandler callback3 = RequestHandler(3);
+ RequestHandler callback1{1};
+ RequestHandler callback2{2};
+ RequestHandler callback3{3};
HttpServer server;
@@ -74,7 +74,7 @@ class MultipleLogManagersTests : public ::testing::Test
{
int port = server.addListeningPort(0);
std::ostringstream os;
- os << "localhost:" << port;
+ os << "127.0.0.1:" << port;
server.setServerName(os.str());
serverAddress = "http://" + os.str();
@@ -196,7 +196,7 @@ TEST_F(MultipleLogManagersTests, ThreeInstancesCoexist)
lm2->GetLogController()->UploadNow();
lm3->GetLogController()->UploadNow();
- waitForRequestsMultipleLogManager(10000, 1, 1, 1);
+ waitForRequestsMultipleLogManager(20000, 1, 1, 1);
lm1.reset();
lm2.reset();
@@ -224,7 +224,7 @@ TEST_F(MultipleLogManagersTests, MultiProcessesLogManager)
CAPTURE_PERF_STATS("Events Sent");
lm->GetLogController()->UploadNow();
CAPTURE_PERF_STATS("Events Uploaded");
- waitForRequestsSingleLogManager(10000, 2);
+ waitForRequestsSingleLogManager(20000, 2);
lm.reset();
CAPTURE_PERF_STATS("Log Manager deleted");
}
diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt
index 891150d34..1b29db3bf 100644
--- a/tests/unittests/CMakeLists.txt
+++ b/tests/unittests/CMakeLists.txt
@@ -1,4 +1,4 @@
-message("--- unittests")
+message(STATUS "Building unittests")
set(SRCS
AIJsonSerializerTests.cpp
@@ -19,6 +19,7 @@ set(SRCS
EventPropertiesTests.cpp
GuidTests.cpp
HttpClientCAPITests.cpp
+ HttpClientCurlTests.cpp
HttpClientManagerTests.cpp
HttpClientTests.cpp
HttpDeflateCompressionTests.cpp
@@ -53,7 +54,7 @@ set_source_files_properties(${SRCS} PROPERTIES COMPILE_FLAGS -Wno-deprecated-dec
# Enable Azure Monitor unit tests when the module is present.
# The AIJsonSerializer test sources are guarded by HAVE_MAT_AI.
-if (EXISTS ${CMAKE_SOURCE_DIR}/lib/modules/azmon/AIJsonSerializer.hpp)
+if (EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/azmon/AIJsonSerializer.hpp")
add_definitions(-DHAVE_MAT_AI)
endif()
@@ -65,7 +66,7 @@ if (APPLE)
endif()
endif()
-if (EXISTS ${CMAKE_SOURCE_DIR}/lib/modules/exp/tests)
+if (EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/exp/tests")
list(APPEND SRCS
${CMAKE_SOURCE_DIR}/lib/modules/exp/tests/unittests/ECSConfigCacheTests.cpp
${CMAKE_SOURCE_DIR}/lib/modules/exp/tests/unittests/ECSClientUtilsTests.cpp
@@ -73,7 +74,7 @@ if (EXISTS ${CMAKE_SOURCE_DIR}/lib/modules/exp/tests)
)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/privacyguard/ AND BUILD_PRIVACYGUARD)
+if(EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/privacyguard/" AND BUILD_PRIVACYGUARD)
add_definitions(-DHAVE_MAT_PRIVACYGUARD)
list(APPEND SRCS
${CMAKE_SOURCE_DIR}/lib/modules/privacyguard/tests/unittests/InitializationConfigurationTests.cpp
@@ -84,7 +85,7 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/privacyguard/ AND BUILD_PRIVACYGUA
)
endif()
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/sanitizer/ AND BUILD_SANITIZER)
+if(EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/sanitizer/" AND BUILD_SANITIZER)
list(APPEND SRCS
${CMAKE_SOURCE_DIR}/lib/modules/sanitizer/tests/unittests/SanitizerJwtTests.cpp
${CMAKE_SOURCE_DIR}/lib/modules/sanitizer/tests/unittests/SanitizerProviderTests.cpp
@@ -96,7 +97,7 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/modules/sanitizer/ AND BUILD_SANITIZER)
)
endif()
-if(EXISTS ${CMAKE_SOURCE_DIR}/lib/modules/dataviewer/)
+if(EXISTS "${CMAKE_SOURCE_DIR}/lib/modules/dataviewer/")
list(APPEND SRCS
${CMAKE_SOURCE_DIR}/lib/modules/dataviewer/tests/unittests/DefaultDataViewerTests.cpp
DataViewerCollectionTests.cpp
@@ -114,11 +115,11 @@ endif()
if(PAL_IMPLEMENTATION STREQUAL "WIN32")
# Link against prebuilt libraries on Windows
- message("--- WIN32: Linking against prebuilt libraries")
- message("--- WIN32: ... ${CMAKE_BINARY_DIR}/gtest")
- message("--- WIN32: ... ${CMAKE_BINARY_DIR}/gmock")
- message("--- WIN32: ... ${CMAKE_BINARY_DIR}/zlib")
- message("--- WIN32: ... ${CMAKE_BINARY_DIR}/sqlite")
+ message(STATUS "WIN32: Linking against prebuilt libraries")
+ message(STATUS "WIN32: ... ${CMAKE_BINARY_DIR}/gtest")
+ message(STATUS "WIN32: ... ${CMAKE_BINARY_DIR}/gmock")
+ message(STATUS "WIN32: ... ${CMAKE_BINARY_DIR}/zlib")
+ message(STATUS "WIN32: ... ${CMAKE_BINARY_DIR}/sqlite")
# link_directories(${CMAKE_BINARY_DIR}/gtest/ ${CMAKE_BINARY_DIR}/gmock/ ${CMAKE_BINARY_DIR}/zlib/ ${CMAKE_BINARY_DIR}/sqlite/)
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../../zlib )
target_link_libraries(UnitTests
@@ -136,6 +137,9 @@ else()
set (SQLITE3_LIB "/usr/local/lib/libsqlite3.a")
elseif(EXISTS "/usr/local/opt/sqlite/lib/libsqlite3.a")
set (SQLITE3_LIB "/usr/local/opt/sqlite/lib/libsqlite3.a")
+ elseif(EXISTS "/opt/homebrew/opt/sqlite/lib/libsqlite3.a")
+ # Apple Silicon homebrew installs to /opt/homebrew instead of /usr/local
+ set (SQLITE3_LIB "/opt/homebrew/opt/sqlite/lib/libsqlite3.a")
else()
set (SQLITE3_LIB "sqlite3")
endif()
@@ -158,10 +162,9 @@ else()
set (PLATFORM_LIBS "atomic")
endif()
- # Find libraries
- message("--- Linking libraries! ")
- message("Current Dir: ${CMAKE_CURRENT_SOURCE_DIR}")
- message("Binary Dir: ${CMAKE_BINARY_DIR}")
+ message(STATUS "Linking libraries")
+ message(STATUS "Current Dir: ${CMAKE_CURRENT_SOURCE_DIR}")
+ message(STATUS "Binary Dir: ${CMAKE_BINARY_DIR}")
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/ )
@@ -179,8 +182,8 @@ else()
${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/googletest/build/lib/
)
- message("GTEST: ${LIBGTEST}")
- message("GMOCK: ${LIBGMOCK}")
+ message(STATUS "GTEST: ${LIBGTEST}")
+ message(STATUS "GMOCK: ${LIBGMOCK}")
target_link_libraries(UnitTests
${LIBGTEST}
diff --git a/tests/unittests/HttpClientCurlTests.cpp b/tests/unittests/HttpClientCurlTests.cpp
new file mode 100644
index 000000000..889d2ffe7
--- /dev/null
+++ b/tests/unittests/HttpClientCurlTests.cpp
@@ -0,0 +1,126 @@
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+//
+#include "mat/config.h"
+
+// These tests only apply to the curl HTTP client path (Linux, non-Apple, non-Android)
+#if defined(MATSDK_PAL_CPP11) && !defined(_MSC_VER) && defined(HAVE_MAT_DEFAULT_HTTP_CLIENT) \
+ && !defined(__APPLE__) && !defined(ANDROID)
+
+#include "common/Common.hpp"
+#include "http/HttpClient_Curl.hpp"
+#include "config/RuntimeConfig_Default.hpp"
+
+using namespace testing;
+using namespace MAT;
+
+class HttpClientCurlTests : public ::testing::Test
+{
+protected:
+ HttpClient_Curl m_client;
+};
+
+// --- SetSslVerification wiring ---
+
+TEST_F(HttpClientCurlTests, SslVerification_DefaultsToTrue)
+{
+ CurlHttpOperation op("GET", "https://example.com", nullptr);
+ ASSERT_NE(op.GetHandle(), nullptr);
+}
+
+TEST_F(HttpClientCurlTests, CurlHttpOperation_ConstructsWithVerifyTrue)
+{
+ CurlHttpOperation op("GET", "https://example.com", nullptr,
+ std::map(), std::vector(),
+ false, 5, true, "");
+ ASSERT_NE(op.GetHandle(), nullptr);
+}
+
+TEST_F(HttpClientCurlTests, CurlHttpOperation_ConstructsWithVerifyFalse)
+{
+ CurlHttpOperation op("GET", "https://example.com", nullptr,
+ std::map(), std::vector(),
+ false, 5, false, "");
+ ASSERT_NE(op.GetHandle(), nullptr);
+}
+
+TEST_F(HttpClientCurlTests, CurlHttpOperation_ConstructsWithCaInfo)
+{
+ CurlHttpOperation op("GET", "https://example.com", nullptr,
+ std::map(), std::vector(),
+ false, 5, true, "/etc/ssl/certs/ca-certificates.crt");
+ ASSERT_NE(op.GetHandle(), nullptr);
+}
+
+// --- ILogConfiguration integration ---
+
+TEST(HttpClientCurlConfigTests, LogConfiguration_SslVerify_DefaultIsTrue)
+{
+ // defaultRuntimeConfig from RuntimeConfig_Default.hpp has the defaults
+ bool sslVerify = defaultRuntimeConfig[CFG_MAP_HTTP][CFG_BOOL_HTTP_SSL_VERIFY];
+ EXPECT_TRUE(sslVerify);
+}
+
+TEST(HttpClientCurlConfigTests, LogConfiguration_SslCaInfo_DefaultIsEmpty)
+{
+ const char* caInfo = defaultRuntimeConfig[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO];
+ EXPECT_STREQ(caInfo, "");
+}
+
+TEST(HttpClientCurlConfigTests, LogConfiguration_SslVerify_CanBeDisabled)
+{
+ ILogConfiguration config;
+ config[CFG_MAP_HTTP][CFG_BOOL_HTTP_SSL_VERIFY] = false;
+ bool sslVerify = config[CFG_MAP_HTTP][CFG_BOOL_HTTP_SSL_VERIFY];
+ EXPECT_FALSE(sslVerify);
+}
+
+TEST(HttpClientCurlConfigTests, LogConfiguration_SslCaInfo_CanBeSet)
+{
+ ILogConfiguration config;
+ config[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO] = "/custom/ca-bundle.crt";
+ const char* caInfo = config[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO];
+ EXPECT_STREQ(caInfo, "/custom/ca-bundle.crt");
+}
+
+// --- ApplySettings integration ---
+
+TEST_F(HttpClientCurlTests, ApplySettings_ReadsSslConfigFromLogConfiguration)
+{
+ ILogConfiguration config;
+ config[CFG_MAP_HTTP][CFG_BOOL_HTTP_SSL_VERIFY] = false;
+ config[CFG_MAP_HTTP][CFG_STR_HTTP_SSL_CAINFO] = "/custom/ca.pem";
+ m_client.ApplySettings(config);
+ // Verify indirectly -- constructing an operation should not fail
+ SUCCEED();
+}
+
+TEST_F(HttpClientCurlTests, ApplySettings_DefaultConfigEnablesVerification)
+{
+ ILogConfiguration config;
+ m_client.ApplySettings(config);
+ SUCCEED();
+}
+
+// --- Thread safety: SetSslVerification concurrent with reads ---
+
+TEST_F(HttpClientCurlTests, SetSslVerification_ConcurrentCallsNoRace)
+{
+ // Exercise the atomic + mutex path under contention.
+ // No assertions on output -- this is a sanitizer/TSAN target.
+ std::vector> futures;
+ for (int i = 0; i < 10; ++i)
+ {
+ futures.push_back(std::async(std::launch::async, [this, i]() {
+ m_client.SetSslVerification(i % 2 == 0, (i % 2 == 0) ? "/some/path" : "");
+ }));
+ }
+ for (auto& f : futures)
+ {
+ f.get();
+ }
+ SUCCEED();
+}
+
+#endif // MATSDK_PAL_CPP11 && !_MSC_VER && HAVE_MAT_DEFAULT_HTTP_CLIENT
diff --git a/tests/unittests/HttpClientTests.cpp b/tests/unittests/HttpClientTests.cpp
index 99d73248b..4b17bcce5 100644
--- a/tests/unittests/HttpClientTests.cpp
+++ b/tests/unittests/HttpClientTests.cpp
@@ -53,7 +53,7 @@ class HttpClientTests : public ::testing::Test,
{
_port = _server.addListeningPort(0);
std::ostringstream os;
- os << "localhost:" << _port;
+ os << "127.0.0.1:" << _port;
_hostname = os.str();
_server.setServerName(_hostname);
_server.addHandler("/simple/", *this);
diff --git a/tests/unittests/OfflineStorageTests_Room.cpp b/tests/unittests/OfflineStorageTests_Room.cpp
index 5e26154fe..5f5b56af6 100644
--- a/tests/unittests/OfflineStorageTests_Room.cpp
+++ b/tests/unittests/OfflineStorageTests_Room.cpp
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#ifdef ANDROID
#include
#endif
@@ -527,6 +528,107 @@ TEST_P(OfflineStorageTestsRoom, ReleaseActuallyReleases) {
);
}
+// Regression test for JNI stale local reference bug in GetAndReserveRecords,
+// GetRecords, and ReleaseRecords. Each per-record iteration uses
+// pushLocalFrame/popLocalFrame; the jclass obtained on iteration 0 must be a
+// global reference to remain valid on iteration 1+. Without the fix, ART's JNI
+// checker fires JniAbort (SIGABRT) on the second call to GetObjectClass /
+// GetFieldID with the stale local ref.
+TEST_P(OfflineStorageTestsRoom, MultiRecordIterationFieldIdValidity)
+{
+ auto now = PAL::getUtcSystemTimeMs();
+ StorageRecordVector input;
+ // Store 3 records: enough to exercise iterations 0, 1, and 2 of the per-record
+ // loop, covering both the "first time" (class lookup) and "subsequent" paths.
+ for (size_t i = 0; i < 3; ++i) {
+ auto id = "reg-" + std::to_string(i);
+ input.emplace_back(
+ id,
+ id,
+ EventLatency_Normal,
+ EventPersistence_Normal,
+ now,
+ StorageBlob {static_cast(i + 1), 2, 3});
+ }
+ offlineStorage->StoreRecords(input);
+ ASSERT_EQ(size_t { 3 }, offlineStorage->GetRecordCount(EventLatency_Normal));
+
+ // GetAndReserveRecords: all 3 records must be returned with correct field values.
+ StorageRecordVector found;
+ EXPECT_TRUE(offlineStorage->GetAndReserveRecords(
+ [&found](StorageRecord && record) -> bool {
+ found.push_back(std::move(record));
+ return true;
+ }, 5000));
+ ASSERT_EQ(size_t { 3 }, found.size());
+ {
+ // Set-based check: return order is implementation-defined
+ // (SQLite/Room: insertion order; Memory: LIFO).
+ std::set blob0_values;
+ for (auto const& r : found) {
+ EXPECT_EQ(EventLatency_Normal, r.latency);
+ EXPECT_EQ(EventPersistence_Normal, r.persistence);
+ ASSERT_EQ(size_t { 3 }, r.blob.size());
+ blob0_values.insert(r.blob[0]);
+ }
+ EXPECT_EQ((std::set{1, 2, 3}), blob0_values);
+ }
+
+ // GetRecords: same 3 records readable via GetRecords (shutdown path).
+ // Memory's GetRecords delegates to GetAndReserveRecords, so it returns
+ // nothing when records are already reserved — skip that check for Memory.
+ if (implementation != StorageImplementation::Memory) {
+ auto shutdown_found = offlineStorage->GetRecords(true, EventLatency_Unspecified, 0);
+ ASSERT_EQ(size_t { 3 }, shutdown_found.size());
+ std::set blob0_values;
+ for (auto const& r : shutdown_found) {
+ EXPECT_EQ(EventLatency_Normal, r.latency);
+ ASSERT_EQ(size_t { 3 }, r.blob.size());
+ blob0_values.insert(r.blob[0]);
+ }
+ EXPECT_EQ((std::set{1, 2, 3}), blob0_values);
+ }
+
+ // Un-reserve without using a retry slot so the retry loop below can start fresh.
+ {
+ std::vector initial_ids;
+ initial_ids.reserve(found.size());
+ for (auto const& r : found) {
+ initial_ids.push_back(r.id);
+ }
+ bool fromMemory = false;
+ offlineStorage->ReleaseRecords(initial_ids, false, HttpHeaders(), fromMemory);
+ }
+
+ // ReleaseRecords bt_class path: cycle GetAndReserveRecords + ReleaseRecords(true)
+ // GetMaximumRetryCount()+1 times. On the final cycle the Room impl drops the 3
+ // records and returns a non-empty byTenant array, exercising the bt_class loop.
+ // Without the global-ref fix, iteration 1+ of that loop produces a JNI abort.
+ auto retries = configMock.GetMaximumRetryCount() + 1;
+ if (implementation != StorageImplementation::Memory) {
+ EXPECT_CALL(observerMock, OnStorageRecordsDropped(SizeIs(3))).WillOnce(Return());
+ }
+ for (size_t retry = 0; retry < retries; ++retry) {
+ found.clear();
+ offlineStorage->GetAndReserveRecords(
+ [&found](StorageRecord && record) -> bool {
+ found.push_back(std::move(record));
+ return true;
+ }, 5000);
+ EXPECT_EQ(size_t { 3 }, found.size()) << "retry=" << retry;
+ std::vector ids;
+ ids.reserve(found.size());
+ for (auto const& r : found) {
+ ids.push_back(r.id);
+ }
+ bool fromMemory = false;
+ offlineStorage->ReleaseRecords(ids, true, HttpHeaders(), fromMemory);
+ }
+ if (implementation != StorageImplementation::Memory) {
+ EXPECT_EQ(size_t { 0 }, offlineStorage->GetRecordCount(EventLatency_Normal));
+ }
+}
+
TEST_P(OfflineStorageTestsRoom, DeleteByToken)
{
StorageRecordVector records;
diff --git a/tests/unittests/PalTests.cpp b/tests/unittests/PalTests.cpp
index 1ec078916..15bb98e5e 100644
--- a/tests/unittests/PalTests.cpp
+++ b/tests/unittests/PalTests.cpp
@@ -85,7 +85,7 @@ TEST_F(PalTests, SystemTime)
int64_t t1 = PAL::getUtcSystemTimeMs();
EXPECT_THAT(t1, Gt(t0 + 360));
- EXPECT_THAT(t1, Lt(t0 + 550));
+ EXPECT_THAT(t1, Lt(t0 + 1000));
}
TEST_F(PalTests, FormatUtcTimestampMsAsISO8601)
@@ -103,7 +103,7 @@ TEST_F(PalTests, MonotonicTime)
int64_t t1 = PAL::getMonotonicTimeMs();
EXPECT_THAT(t1 - t0, Gt(780));
- EXPECT_THAT(t1 - t0, Lt(950));
+ EXPECT_THAT(t1 - t0, Lt(1500));
}
TEST_F(PalTests, SemanticContextPopulation)
diff --git a/tests/unittests/UnitTests.vcxproj b/tests/unittests/UnitTests.vcxproj
index 579c52a83..12f44eae7 100644
--- a/tests/unittests/UnitTests.vcxproj
+++ b/tests/unittests/UnitTests.vcxproj
@@ -495,7 +495,6 @@
-
diff --git a/tests/unittests/obj-c/ODWReachabilityTests.mm b/tests/unittests/obj-c/ODWReachabilityTests.mm
index 501a3be7a..cc8a7c24e 100644
--- a/tests/unittests/obj-c/ODWReachabilityTests.mm
+++ b/tests/unittests/obj-c/ODWReachabilityTests.mm
@@ -13,11 +13,7 @@
#import
#import
-#import
#import
-#import
-#import
-#import
@interface ODWReachabilityTests : XCTestCase
@end
@@ -110,4 +106,3 @@ - (void)testReachabilityForLocalWiFi
}
@end
-
diff --git a/third_party/Reachability/ODWReachability.m b/third_party/Reachability/ODWReachability.m
index 0e13591d5..7947f7df0 100644
--- a/third_party/Reachability/ODWReachability.m
+++ b/third_party/Reachability/ODWReachability.m
@@ -29,11 +29,7 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#import
#import
-#import
#import
-#import
-#import
-#import
NSString *const kNetworkReachabilityChangedNotification = @"NetworkReachabilityChangedNotification";
diff --git a/tools/MakeDeb.cmake b/tools/MakeDeb.cmake
index efec5ad32..4f839ab5b 100644
--- a/tools/MakeDeb.cmake
+++ b/tools/MakeDeb.cmake
@@ -24,7 +24,7 @@ set(CPACK_PACKAGE_VERSION_PATCH "${PATCH_VERSION}")
# FIXME: add architecture name in file name
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}-${CPACK_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
-message("-- Package name: ${CPACK_PACKAGE_FILE_NAME}.deb")
+message(STATUS "Package name: ${CPACK_PACKAGE_FILE_NAME}.deb")
#install(TARGETS ${MAT_SDK_LIB_DIR}/libMAT.a ARCHIVE DESTINATION lib/MAT COMPONENT headers)
#install(FILES ${MAT_SDK_INC_DIR}/*.* DESTINATION include/MAT COMPONENT libraries)
diff --git a/tools/MakeRpm.cmake b/tools/MakeRpm.cmake
index c4db45de0..8d9cf0b67 100644
--- a/tools/MakeRpm.cmake
+++ b/tools/MakeRpm.cmake
@@ -18,7 +18,7 @@ set(CPACK_PACKAGE_NAME "mat_sdk")
set(CPACK_PACKAGE_RELEASE "0")
set(CPACK_PACKAGE_VENDOR "Microsoft")
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}")
-message("-- Package name: ${CPACK_RPM_PACKAGE_FILE_NAME}.rpm")
+message(STATUS "Package name: ${CPACK_PACKAGE_FILE_NAME}.rpm")
#configure_file("${CMAKE_CURRENT_SOURCE_DIR}/mat-sdk.spec.in" "${CMAKE_CURRENT_BINARY_DIR}/arka-sdk.spec" @ONLY IMMEDIATE)
#set(CPACK_RPM_USER_BINARY_SPECFILE "${CMAKE_CURRENT_BINARY_DIR}/mat-sdk.spec")
diff --git a/tools/MakeTgz.cmake b/tools/MakeTgz.cmake
index bf159ab8d..44c308e32 100644
--- a/tools/MakeTgz.cmake
+++ b/tools/MakeTgz.cmake
@@ -24,8 +24,7 @@ file(GLOB ALL_TARGET_LIBS "${CMAKE_CURRENT_BINARY_DIR}/lib/libmat.*")
install(FILES ${ALL_TARGET_LIBS} DESTINATION lib COMPONENT libraries)
#install(FILES ${MAT_SDK_INC_DIR}/*.* DESTINATION include/mat COMPONENT libraries)
-# FIXME: add architecture name in file name
set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}-${CPACK_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
-message("-- Package name: ${CPACK_PACKAGE_FILE_NAME}.tgz")
+message(STATUS "Package name: ${CPACK_PACKAGE_FILE_NAME}.tgz")
include(CPack)
diff --git a/tools/ParseOsRelease.cmake b/tools/ParseOsRelease.cmake
index 48bd0398a..fc3abe9af 100644
--- a/tools/ParseOsRelease.cmake
+++ b/tools/ParseOsRelease.cmake
@@ -1,6 +1,6 @@
# Parse /etc/os-release to determine Linux distro
-if(EXISTS /etc/os-release)
+if(EXISTS "/etc/os-release")
file(STRINGS /etc/os-release OS_RELEASE)
foreach(NameAndValue ${OS_RELEASE})
@@ -13,7 +13,7 @@ foreach(NameAndValue ${OS_RELEASE})
# Strip quotes from value
string(REPLACE "\"" "" Value ${Value})
# Set the variable
- message("-- /etc/os_release : ${Name}=${Value}")
+ message(STATUS "/etc/os_release : ${Name}=${Value}")
set("OS_RELEASE_${Name}" "${Value}")
endforeach()
diff --git a/tools/setup-buildtools.cmd b/tools/setup-buildtools.cmd
index 5c4705a15..890467256 100644
--- a/tools/setup-buildtools.cmd
+++ b/tools/setup-buildtools.cmd
@@ -16,8 +16,9 @@ if ERRORLEVEL 0 (
vswhere -property installationPath
)
-REM Install tools needed to build SDK with either Visual Studio or CMake
-choco install -y cmake svn git llvm zip
+REM Install baseline tools needed to build SDK with either Visual Studio or CMake
+REM LLVM is optional and handled below when INSTALL_LLVM is defined.
+choco install -y cmake svn git zip
REM Try to autodetect Visual Studio
call "%~dp0\vcvars.cmd"