diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..8507ac840 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,11 @@ +*Issue #, if available:* + +*What was changed?* + +*Why was it changed?* + +*How was it changed?* + +*What testing was done for the changes?* + +By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1cb6989e..ac6f9a300 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,9 +34,22 @@ jobs: - name: Build repository run: | mkdir build && cd build - sh -c 'cmake .. -DBUILD_TEST=TRUE;cmake .. -DBUILD_TEST=TRUE' + cmake .. -DBUILD_TEST=TRUE make - ./kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Run tests + run: | + cd build + ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Build With ALIGNED_MEMORY_MODEL and FIXUP_ANNEX_B_TRAILING_NALU_ZERO definitions TRUE + run: | + rm -rf build + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DALIGNED_MEMORY_MODEL=TRUE -DFIXUP_ANNEX_B_TRAILING_NALU_ZERO=TRUE + make + - name: Run tests + run: | + cd build + ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" mac-os-build-clang: runs-on: macos-latest env: @@ -47,9 +60,82 @@ jobs: - name: Build repository run: | mkdir build && cd build - sh -c 'cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE;cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE' + cmake .. -DBUILD_TEST=TRUE -DCOMPILER_WARNINGS=TRUE + make + - name: Run tests + run: | + cd build + ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Build With ALIGNED_MEMORY_MODEL and FIXUP_ANNEX_B_TRAILING_NALU_ZERO definitions TRUE + run: | + rm -rf build + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DALIGNED_MEMORY_MODEL=TRUE -DFIXUP_ANNEX_B_TRAILING_NALU_ZERO=TRUE + make + - name: Run tests + run: | + cd build + ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + mac-os-m1-build-clang: + runs-on: macos-13-xlarge + env: + AWS_KVS_LOG_LEVEL: 2 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Build repository + run: | + brew unlink openssl + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DCMAKE_C_COMPILER=$(brew --prefix llvm@15)/bin/clang -DCMAKE_CXX_COMPILER=$(brew --prefix llvm@15)/bin/clang++ + make + - name: Run tests + run: | + cd build + ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Build With ALIGNED_MEMORY_MODEL and FIXUP_ANNEX_B_TRAILING_NALU_ZERO definitions TRUE + run: | + rm -rf build + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DALIGNED_MEMORY_MODEL=TRUE -DFIXUP_ANNEX_B_TRAILING_NALU_ZERO=TRUE -DCMAKE_C_COMPILER=$(brew --prefix llvm@15)/bin/clang -DCMAKE_CXX_COMPILER=$(brew --prefix llvm@15)/bin/clang++ + make + - name: Run tests + run: | + cd build + ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + mac-os-m1-build-gcc: + runs-on: macos-13-xlarge + env: + CC: /opt/homebrew/bin/gcc-13 + CXX: /opt/homebrew/bin/g++-13 + AWS_KVS_LOG_LEVEL: 2 + steps: + - name: Setup XCode version 15.2 + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '15.2' + - name: Clone repository + uses: actions/checkout@v2 + - name: Build repository + run: | + brew unlink openssl + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE make - ./kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Run tests + run: | + cd build + ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Build With ALIGNED_MEMORY_MODEL and FIXUP_ANNEX_B_TRAILING_NALU_ZERO definitions TRUE + run: | + rm -rf build + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DALIGNED_MEMORY_MODEL=TRUE -DFIXUP_ANNEX_B_TRAILING_NALU_ZERO=TRUE + make + - name: Run tests + run: | + cd build + ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" linux-gcc-code-coverage: runs-on: ubuntu-latest env: @@ -62,8 +148,22 @@ jobs: mkdir build && cd build cmake .. -DCODE_COVERAGE=TRUE -DBUILD_TEST=TRUE make + - name: Run tests + run: | + cd build + ulimit -c unlimited -S + timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Build With ALIGNED_MEMORY_MODEL and FIXUP_ANNEX_B_TRAILING_NALU_ZERO definitions TRUE + run: | + rm -rf build + mkdir build && cd build + cmake .. -DCODE_COVERAGE=TRUE -DBUILD_TEST=TRUE -DALIGNED_MEMORY_MODEL=TRUE -DFIXUP_ANNEX_B_TRAILING_NALU_ZERO=TRUE + make + - name: Run tests + run: | + cd build ulimit -c unlimited -S - timeout --signal=SIGABRT 40m ./kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" - name: Code coverage run: | for test_file in $(find CMakeFiles/kvspic.dir CMakeFiles/kvspicClient.dir CMakeFiles/kvspicState.dir CMakeFiles/kvspicUtils.dir -name '*.gcno'); do gcov $test_file; done @@ -80,14 +180,27 @@ jobs: mkdir build && cd build cmake .. -DBUILD_TEST=TRUE -DADDRESS_SANITIZER=TRUE make + - name: Run tests + run: | + cd build ulimit -c unlimited -S - timeout --signal=SIGABRT 40m ./kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Build With ALIGNED_MEMORY_MODEL and FIXUP_ANNEX_B_TRAILING_NALU_ZERO definitions TRUE + run: | + rm -rf build + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DADDRESS_SANITIZER=TRUE -DALIGNED_MEMORY_MODEL=TRUE -DFIXUP_ANNEX_B_TRAILING_NALU_ZERO=TRUE + make + - name: Run tests + run: | + cd build + timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" undefined-behavior-sanitizer: runs-on: ubuntu-latest env: CC: clang CXX: clang++ - UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1:suppressions=../src/utils/tst/suppressions/UBSAN.supp + UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1:suppressions=../../tst/suppressions/UBSAN.supp AWS_KVS_LOG_LEVEL: 2 steps: - name: Clone repository @@ -101,8 +214,21 @@ jobs: mkdir build && cd build cmake .. -DBUILD_TEST=TRUE -DUNDEFINED_BEHAVIOR_SANITIZER=TRUE make + - name: Run tests + run: | + cd build ulimit -c unlimited -S - timeout --signal=SIGABRT 40m ./kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Build With ALIGNED_MEMORY_MODEL and FIXUP_ANNEX_B_TRAILING_NALU_ZERO definitions TRUE + run: | + rm -rf build + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DUNDEFINED_BEHAVIOR_SANITIZER=TRUE -DALIGNED_MEMORY_MODEL=TRUE -DFIXUP_ANNEX_B_TRAILING_NALU_ZERO=TRUE + make + - name: Run tests + run: | + cd build + timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" linux-gcc-4_4: runs-on: ubuntu-20.04 env: @@ -125,8 +251,21 @@ jobs: mkdir build && cd build cmake .. -DBUILD_TEST=TRUE make + - name: Run tests + run: | + cd build ulimit -c unlimited -S - timeout --signal=SIGABRT 40m ./kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Build With ALIGNED_MEMORY_MODEL and FIXUP_ANNEX_B_TRAILING_NALU_ZERO definitions TRUE + run: | + rm -rf build + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DALIGNED_MEMORY_MODEL=TRUE -DFIXUP_ANNEX_B_TRAILING_NALU_ZERO=TRUE + make + - name: Run tests + run: | + cd build + timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" # memory-sanitizer: # runs-on: ubuntu-18.04 # env: @@ -146,27 +285,41 @@ jobs: # cmake .. -DBUILD_TEST=TRUE -DMEMORY_SANITIZER=TRUE # make # ulimit -c unlimited -S - # timeout --signal=SIGABRT 40m ./kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" - # thread-sanitizer: - # runs-on: ubuntu-18.04 - # env: - # CC: clang-7 - # CXX: clang++-7 - # AWS_KVS_LOG_LEVEL: 2 - # TSAN_OPTIONS: halt_on_error=1:suppressions=../src/utils/tst/suppressions/TSAN.supp - # steps: - # - name: Clone repository - # uses: actions/checkout@v2 - # - name: Install dependencies - # run: | - # sudo apt-get update - # sudo apt-get -y install clang-7 - # - name: Build repository - # run: | - # mkdir build && cd build - # cmake .. -DBUILD_TEST=TRUE -DTHREAD_SANITIZER=TRUE - # make - # ./kvspic_test --gtest_break_on_failure --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*:ThreadFunctionalityTest.ThreadCreateAndCancel" + # timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + thread-sanitizer: + runs-on: ubuntu-latest + env: + CC: clang + CXX: clang++ + TSAN_OPTIONS: halt_on_error=1:suppressions=../../tst/suppressions/TSAN.supp + AWS_KVS_LOG_LEVEL: 2 + steps: + - name: Clone repository + uses: actions/checkout@v2 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install clang + - name: Build repository + run: | + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DTHREAD_SANITIZER=TRUE + make + - name: Run tests + run: | + cd build + ulimit -c unlimited -S + timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" + - name: Build With ALIGNED_MEMORY_MODEL and FIXUP_ANNEX_B_TRAILING_NALU_ZERO definitions TRUE + run: | + rm -rf build + mkdir build && cd build + cmake .. -DBUILD_TEST=TRUE -DTHREAD_SANITIZER=TRUE -DALIGNED_MEMORY_MODEL=TRUE -DFIXUP_ANNEX_B_TRAILING_NALU_ZERO=TRUE + make + - name: Run tests + run: | + cd build + timeout --signal=SIGABRT 40m ./tst/kvspic_test --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*" windows-msvc: runs-on: windows-2022 env: @@ -177,7 +330,9 @@ jobs: - name: Build and run run: | .github/build_windows.bat - D:\a\amazon-kinesis-video-streams-pic\amazon-kinesis-video-streams-pic\build\kvspic_test.exe --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*:PermutatedStreamInfo/StateTransitionFunctionalityTest.ControlPlaneServiceCallExhaustRetry*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.ValidateTimerInvokedBeforeTime*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.ValidateTimerInvokedAfterFirstPeriod*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.ValidateLastUpdateTimeOfStreamUpdated*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.MultiTrackVerifyNoInvocationsWithSingleTrackProducer*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.ValidateNoConsecutiveEOFR*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.ValidateErrorOnForceConsecutiveEOFR*:*StreamStateTransitionsTest*:*PermutatedStreamInfo/StateTransitionFunctionalityTest.StreamTerminatedAndGoToGetEndpointState*:*PermutatedStreamInfo/StateTransitionFunctionalityTest.StreamTerminatedAndGoToDescribeState*:*PermutatedStreamInfo/StateTransitionFunctionalityTest*" + - name: Run tests + run: | + D:\a\amazon-kinesis-video-streams-pic\amazon-kinesis-video-streams-pic\build\tst\kvspic_test.exe --gtest_filter="-TimerQueueFunctionalityTest.*:HeapPerfTest.*:PermutatedStreamInfo/StateTransitionFunctionalityTest.ControlPlaneServiceCallExhaustRetry*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.ValidateTimerInvokedBeforeTime*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.ValidateTimerInvokedAfterFirstPeriod*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.ValidateLastUpdateTimeOfStreamUpdated*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.MultiTrackVerifyNoInvocationsWithSingleTrackProducer*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.ValidateNoConsecutiveEOFR*:PermutatedStreamInfo/IntermittentProducerAutomaticStreamingTest.ValidateErrorOnForceConsecutiveEOFR*:*StreamStateTransitionsTest*:*PermutatedStreamInfo/StateTransitionFunctionalityTest.StreamTerminatedAndGoToGetEndpointState*:*PermutatedStreamInfo/StateTransitionFunctionalityTest.StreamTerminatedAndGoToDescribeState*:*PermutatedStreamInfo/StateTransitionFunctionalityTest*" arm64-cross-compilation: runs-on: ubuntu-latest env: diff --git a/.github/workflows/pr-desc-lint.yml b/.github/workflows/pr-desc-lint.yml new file mode 100644 index 000000000..e4fcc9216 --- /dev/null +++ b/.github/workflows/pr-desc-lint.yml @@ -0,0 +1,55 @@ +name: PR Description Check + +on: + pull_request: + branches: + - develop + - master + types: + - opened + - synchronize + - reopened + - edited + +jobs: + check-description: + runs-on: macos-latest + steps: + - name: Install GitHub CLI + run: | + brew install gh + + - name: Check PR Description + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + pr_description=$(gh pr view https://github.com/${GITHUB_REPOSITORY}/pull/${{ github.event.pull_request.number }} --json body -q ".body") + error_occurred=0 + # Define minimum character count for each section + MIN_CHARS=25 + + # Extract contents + what_changed=$(echo "$pr_description" | sed -n -e '/\*What was changed?\*/,/\*/p' | sed '$d' | sed '1d') + why_changed=$(echo "$pr_description" | sed -n -e '/\*Why was it changed?\*/,/\*/p' | sed '$d' | sed '1d') + how_changed=$(echo "$pr_description" | sed -n -e '/\*How was it changed?\*/,/\*/p' | sed '$d' | sed '1d') + testing_done=$(echo "$pr_description" | sed -n -e '/\*What testing was done for the changes?\*/,/\*/p' | sed '$d' | sed '1d') + + if [[ ${#what_changed} -lt $MIN_CHARS ]]; then + echo "PR description for what changed section is either missing or too short." + error_occurred=1 + fi + if [[ ${#why_changed} -lt $MIN_CHARS ]]; then + echo "PR description for why it changed section is either missing or too short." + error_occurred=1 + fi + if [[ ${#how_changed} -lt $MIN_CHARS ]]; then + echo "PR description for how was it changed section is either missing or too short." + error_occurred=1 + fi + if [[ ${#testing_done} -lt $MIN_CHARS ]]; then + echo "PR description for testing section are either missing or too short." + error_occurred=1 + fi + if [[ $error_occurred -eq 1 ]]; then + exit 1 + fi diff --git a/CMakeLists.txt b/CMakeLists.txt index d0e97c88c..71ca13cef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.6.3) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") include(Utilities) -project(pic_project) +project(pic_project LANGUAGES C) include(GNUInstallDirs) @@ -31,6 +31,16 @@ add_definitions(-DSDK_VERSION=\"${GIT_COMMIT_HASH}\") add_definitions(-DDETECTED_GIT_HASH) +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting CMAKE_BUILD_TYPE to Release by default") + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) +elseif() + string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPER) # To ensure consistency + if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG") + add_definitions(-DDEBUG_BUILD) + endif() +endif() + if(BUILD_SHARED_LIBS) set(LIBTYPE SHARED) elseif() @@ -111,8 +121,6 @@ file(GLOB PIC_UTILS_SOURCE_FILES "src/utils/src/*.c") file(GLOB PIC_VIEW_SOURCE_FILES "src/view/src/*.c") -file(GLOB PIC_TEST_SOURCE_FILES "src/*/tst/*.cpp") - file(GLOB PIC_HEADERS "${KINESIS_VIDEO_PIC_SRC}/src/*/include") include_directories(${PIC_HEADERS}) @@ -194,32 +202,5 @@ install( DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") if(BUILD_TEST) - set(CMAKE_CXX_STANDARD 11) - - if (OPEN_SRC_INSTALL_PREFIX) - find_package(GTest REQUIRED PATHS ${OPEN_SRC_INSTALL_PREFIX}) - else() - find_package(GTest REQUIRED) - endif() - - SET(GTEST_LIBNAME GTest::gtest) - if (TARGET GTest::GTest) - SET(GTEST_LIBNAME GTest::GTest) - endif() - - add_executable(kvspic_test ${PIC_TEST_SOURCE_FILES} - ${PIC_CLIENT_SOURCE_FILES} - ${PIC_DURATION_SOURCE_FILES} - ${PIC_HEAP_SOURCE_FILES} - ${PIC_MKVGEN_SOURCE_FILES} - ${PIC_STATE_SOURCE_FILES} - ${PIC_TRACE_SOURCE_FILES} - ${PIC_UTILS_SOURCE_FILES} - ${PIC_VIEW_SOURCE_FILES}) - target_compile_definitions(kvspic_test PRIVATE ALIGNED_MEMORY_MODEL=TRUE FIXUP_ANNEX_B_TRAILING_NALU_ZERO=TRUE) - target_link_libraries(kvspic_test ${GTEST_LIBNAME} ${CMAKE_DL_LIBS} Threads::Threads) - if(UNIX AND NOT APPLE) - # rt needed for clock_gettime - target_link_libraries(kvspic_test rt) - endif() + add_subdirectory(tst) endif() diff --git a/README.md b/README.md index a4d12a0c6..4ad19f600 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ If you wish to cross-compile `CC` and `CXX` are respected when building the libr You can pass the following options to `cmake ..` * `-DBUILD_DEPENDENCIES` -- Whether or not to build depending libraries from source -* `-DBUILD_TEST=TRUE` -- Build unit/integration tests, may be useful for confirm support for your device. `./kvspic_test` +* `-DBUILD_TEST=TRUE` -- Build unit/integration tests, may be useful for confirm support for your device. `./tst/kvspic_test` * `-DCODE_COVERAGE` -- Enable coverage reporting * `-DCOMPILER_WARNINGS` -- Enable all compiler warnings * `-DADDRESS_SANITIZER` -- Build with AddressSanitizer diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index fd1be3304..f4a1dd1c6 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(client) +project(client LANGUAGES C) kinesis_video_library_setup(${PROJECT_NAME}) target_link_libraries(client mkvgen heap view utils state) kinesis_video_library_install() diff --git a/src/client/include/com/amazonaws/kinesis/video/client/Include.h b/src/client/include/com/amazonaws/kinesis/video/client/Include.h index 3d153ad1f..f2284b7e8 100644 --- a/src/client/include/com/amazonaws/kinesis/video/client/Include.h +++ b/src/client/include/com/amazonaws/kinesis/video/client/Include.h @@ -966,9 +966,9 @@ typedef enum { /** * Macros checking for the stream event types */ -#define CHECK_STREAM_EVENT_TYPE_IMAGE_GENERATION(f) (((f) &STREAM_EVENT_TYPE_IMAGE_GENERATION) != STREAM_EVENT_TYPE_NONE) -#define CHECK_STREAM_EVENT_TYPE_NOTIFICATION(f) (((f) &STREAM_EVENT_TYPE_NOTIFICATION) != STREAM_EVENT_TYPE_NONE) -#define CHECK_STREAM_EVENT_TYPE_LAST(f) (((f) &STREAM_EVENT_TYPE_LAST) != STREAM_EVENT_TYPE_NONE) +#define CHECK_STREAM_EVENT_TYPE_IMAGE_GENERATION(f) (((f) & STREAM_EVENT_TYPE_IMAGE_GENERATION) != STREAM_EVENT_TYPE_NONE) +#define CHECK_STREAM_EVENT_TYPE_NOTIFICATION(f) (((f) & STREAM_EVENT_TYPE_NOTIFICATION) != STREAM_EVENT_TYPE_NONE) +#define CHECK_STREAM_EVENT_TYPE_LAST(f) (((f) & STREAM_EVENT_TYPE_LAST) != STREAM_EVENT_TYPE_NONE) typedef enum { diff --git a/src/client/src/Client.c b/src/client/src/Client.c index 3b3b6a0f4..7c07d011a 100644 --- a/src/client/src/Client.c +++ b/src/client/src/Client.c @@ -286,10 +286,9 @@ STATUS createKinesisVideoClient(PDeviceInfo pDeviceInfo, PClientCallbacks pClien pKinesisVideoClient->clientCallbacks.createMutexFn(pKinesisVideoClient->clientCallbacks.customData, TRUE); // Create the state machine and step it - CHK_STATUS(createStateMachine(CLIENT_STATE_MACHINE_STATES, CLIENT_STATE_MACHINE_STATE_COUNT, TO_CUSTOM_DATA(pKinesisVideoClient), - pKinesisVideoClient->clientCallbacks.getCurrentTimeFn, pKinesisVideoClient->clientCallbacks.customData, - &pStateMachine)); - + CHK_STATUS(createStateMachineWithName(CLIENT_STATE_MACHINE_STATES, CLIENT_STATE_MACHINE_STATE_COUNT, TO_CUSTOM_DATA(pKinesisVideoClient), + pKinesisVideoClient->clientCallbacks.getCurrentTimeFn, pKinesisVideoClient->clientCallbacks.customData, + CLIENT_STATE_MACHINE_NAME, &pStateMachine)); pKinesisVideoClient->base.pStateMachine = pStateMachine; if (pKinesisVideoClient->deviceInfo.clientInfo.automaticStreamingFlags == AUTOMATIC_STREAMING_INTERMITTENT_PRODUCER) { diff --git a/src/client/src/Include_i.h b/src/client/src/Include_i.h index 67a7c15be..aff2748b5 100644 --- a/src/client/src/Include_i.h +++ b/src/client/src/Include_i.h @@ -108,6 +108,8 @@ typedef STATUS (*KinesisVideoClientCallbackHookFunc)(UINT64); */ #define KINESIS_VIDEO_CLIENT_CURRENT_VERSION 0 +#define CLIENT_STATE_MACHINE_NAME (PCHAR) "CLIENT" +#define STREAM_STATE_MACHINE_NAME (PCHAR) "STREAM" /** * Kinesis Video client states definitions */ diff --git a/src/client/src/InputValidator.c b/src/client/src/InputValidator.c index 5395c1abd..6a1046e68 100644 --- a/src/client/src/InputValidator.c +++ b/src/client/src/InputValidator.c @@ -372,7 +372,7 @@ VOID fixupClientInfo(PClientInfo pClientInfo, PClientInfo pOrigClientInfo) pClientInfo->offlineBufferAvailabilityTimeout = MAX_BLOCKING_PUT_WAIT; } - if (pClientInfo->loggerLogLevel == 0 || pClientInfo->loggerLogLevel > LOG_LEVEL_SILENT) { + if (pClientInfo->loggerLogLevel == 0 || pClientInfo->loggerLogLevel > LOG_LEVEL_PROFILE) { pClientInfo->loggerLogLevel = LOG_LEVEL_WARN; } diff --git a/src/client/src/Stream.c b/src/client/src/Stream.c index c5add2ce3..e17c6d3d6 100644 --- a/src/client/src/Stream.c +++ b/src/client/src/Stream.c @@ -246,9 +246,9 @@ STATUS createStream(PKinesisVideoClient pKinesisVideoClient, PStreamInfo pStream CHK_STATUS(generateEosMetadata(pKinesisVideoStream)); // Create the state machine - CHK_STATUS(createStateMachine(STREAM_STATE_MACHINE_STATES, STREAM_STATE_MACHINE_STATE_COUNT, TO_CUSTOM_DATA(pKinesisVideoStream), - pKinesisVideoClient->clientCallbacks.getCurrentTimeFn, pKinesisVideoClient->clientCallbacks.customData, - &pStateMachine)); + CHK_STATUS(createStateMachineWithName(STREAM_STATE_MACHINE_STATES, STREAM_STATE_MACHINE_STATE_COUNT, TO_CUSTOM_DATA(pKinesisVideoStream), + pKinesisVideoClient->clientCallbacks.getCurrentTimeFn, pKinesisVideoClient->clientCallbacks.customData, + STREAM_STATE_MACHINE_NAME, &pStateMachine)); pKinesisVideoStream->base.pStateMachine = pStateMachine; // Create the stream upload handle queue @@ -297,7 +297,7 @@ STATUS createStream(PKinesisVideoClient pKinesisVideoClient, PStreamInfo pStream pKinesisVideoStream->diagnostics.createTime + pKinesisVideoClient->deviceInfo.clientInfo.metricLoggingPeriod; // Call to transition the state machine - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + CHK_STATUS(iterateStreamStateMachine(pKinesisVideoStream)); CleanUp: @@ -363,9 +363,16 @@ STATUS freeStream(PKinesisVideoStream pKinesisVideoStream) freeMetadataTracker(&pKinesisVideoStream->eosTracker); freeMetadataTracker(&pKinesisVideoStream->metadataTracker); + // unlock the stream freeFrameOrderCoordinator acquires the FrameOrderCoordinator mutex, we cannot acquire this + // mutex while holding the stream mutex or else we will deadlock with putFrame which first acquires + // FrameOrderCoordinator mutex and then the stream mutex + pKinesisVideoClient->clientCallbacks.unlockMutexFn(pKinesisVideoClient->clientCallbacks.customData, pKinesisVideoStream->base.lock); + // Free FrameOrderCoordinator freeFrameOrderCoordinator(pKinesisVideoStream, &pKinesisVideoStream->pFrameOrderCoordinator); + pKinesisVideoClient->clientCallbacks.lockMutexFn(pKinesisVideoClient->clientCallbacks.customData, pKinesisVideoStream->base.lock); + // Lock the client to update the streams pKinesisVideoClient->clientCallbacks.lockMutexFn(pKinesisVideoClient->clientCallbacks.customData, pKinesisVideoClient->base.lock); @@ -809,7 +816,7 @@ STATUS putFrame(PKinesisVideoStream pKinesisVideoStream, PFrame pFrame) // NOTE: If the connection has been reset we need to start from a new header if (pKinesisVideoStream->streamState == STREAM_STATE_NEW && pKinesisVideoStream->streamReady) { // Step the state machine once to get out of the Ready state - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + CHK_STATUS(iterateStreamStateMachine(pKinesisVideoStream)); } // if we need to reset the generator on the next key frame (during the rotation only) @@ -1267,12 +1274,12 @@ STATUS getStreamData(PKinesisVideoStream pKinesisVideoStream, UPLOAD_HANDLE uplo case UPLOAD_HANDLE_STATE_TERMINATED: // This path get invoked if a connection get terminated by calling reset connection - DLOGW("[%s] Indicating an end-of-stream for a terminated stream upload handle %", PRIu64, pKinesisVideoStream->streamInfo.name, + DLOGW("[%s] Indicating an end-of-stream for a terminated stream upload handle %" PRIu64, pKinesisVideoStream->streamInfo.name, uploadHandle); CHK(FALSE, STATUS_END_OF_STREAM); case UPLOAD_HANDLE_STATE_ERROR: - DLOGW("[%s] Indicating an abort for a errored stream upload handle %", PRIu64, pKinesisVideoStream->streamInfo.name, uploadHandle); + DLOGW("[%s] Indicating an abort for a errored stream upload handle %" PRIu64, pKinesisVideoStream->streamInfo.name, uploadHandle); CHK(FALSE, STATUS_UPLOAD_HANDLE_ABORTED); default: // no-op for other UPLOAD_HANDLE states @@ -3544,7 +3551,7 @@ STATUS resetStream(PKinesisVideoStream pKinesisVideoStream) } // step out of stopped state - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + CHK_STATUS(iterateStreamStateMachine(pKinesisVideoStream)); // Unlock the stream pKinesisVideoClient->clientCallbacks.unlockMutexFn(pKinesisVideoClient->clientCallbacks.customData, pKinesisVideoStream->base.lock); diff --git a/src/client/src/Stream.h b/src/client/src/Stream.h index 2be0240cc..4594b2919 100644 --- a/src/client/src/Stream.h +++ b/src/client/src/Stream.h @@ -270,7 +270,7 @@ typedef enum { } UPLOAD_HANDLE_STATE; -#define CHECK_UPLOAD_CONNECTION_STATE_IN_USE(f) (((f) &UPLOAD_CONNECTION_STATE_IN_USE) != UPLOAD_CONNECTION_STATE_NONE) +#define CHECK_UPLOAD_CONNECTION_STATE_IN_USE(f) (((f) & UPLOAD_CONNECTION_STATE_IN_USE) != UPLOAD_CONNECTION_STATE_NONE) /** * Upload connection state enum type definition */ @@ -377,6 +377,9 @@ struct __KinesisVideoStream { // Stream state for running/stopping UINT64 streamState; + // Whether to continue iterating the stream state machine + BOOL keepIteratingStateMachine; + // Fragment ACK parser for streaming ACKs FragmentAckParser fragmentAckParser; @@ -841,6 +844,11 @@ STATUS streamFragmentErrorAck(PKinesisVideoStream, UINT64, UINT64, SERVICE_CALL_ /////////////////////////////////////////////////////////////////////////// STATUS getStreamData(PKinesisVideoStream, UPLOAD_HANDLE, PBYTE, UINT32, PUINT32); +/////////////////////////////////////////////////////////////////////////// +// State machine iterator +/////////////////////////////////////////////////////////////////////////// +STATUS iterateStreamStateMachine(PKinesisVideoStream pKinesisVideoStream); + /////////////////////////////////////////////////////////////////////////// // State machine callback functionality /////////////////////////////////////////////////////////////////////////// diff --git a/src/client/src/StreamEvent.c b/src/client/src/StreamEvent.c index ab6e95758..ae4b54626 100644 --- a/src/client/src/StreamEvent.c +++ b/src/client/src/StreamEvent.c @@ -286,7 +286,7 @@ STATUS describeStreamResult(PKinesisVideoStream pKinesisVideoStream, SERVICE_CAL } // Step the machine - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + CHK_STATUS(iterateStreamStateMachine(pKinesisVideoStream)); CleanUp: @@ -347,7 +347,7 @@ STATUS createStreamResult(PKinesisVideoStream pKinesisVideoStream, SERVICE_CALL_ } // Step the machine - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + CHK_STATUS(iterateStreamStateMachine(pKinesisVideoStream)); CleanUp: @@ -432,7 +432,7 @@ STATUS getStreamingTokenResult(PKinesisVideoStream pKinesisVideoStream, SERVICE_ } // Step the machine - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + CHK_STATUS(iterateStreamStateMachine(pKinesisVideoStream)); CleanUp: @@ -493,7 +493,7 @@ STATUS getStreamingEndpointResult(PKinesisVideoStream pKinesisVideoStream, SERVI } // Step the machine - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + CHK_STATUS(iterateStreamStateMachine(pKinesisVideoStream)); CleanUp: @@ -568,7 +568,7 @@ STATUS putStreamResult(PKinesisVideoStream pKinesisVideoStream, SERVICE_CALL_RES CHK_STATUS(stackQueueEnqueue(pKinesisVideoStream->pUploadInfoQueue, (UINT64) pUploadHandleInfo)); // Step the machine - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + CHK_STATUS(iterateStreamStateMachine(pKinesisVideoStream)); CleanUp: @@ -625,7 +625,7 @@ STATUS tagStreamResult(PKinesisVideoStream pKinesisVideoStream, SERVICE_CALL_RES pKinesisVideoStream->base.result = callResult; // Step the machine - retStatus = stepStateMachine(pKinesisVideoStream->base.pStateMachine); + retStatus = iterateStreamStateMachine(pKinesisVideoStream); CHK(retStatus == STATUS_SUCCESS || retStatus == STATUS_TAG_STREAM_CALL_FAILED, retStatus); // Override tagStream failure because it is not critical to streaming. @@ -637,7 +637,7 @@ STATUS tagStreamResult(PKinesisVideoStream pKinesisVideoStream, SERVICE_CALL_RES INVALID_TIMESTAMP_VALUE, STATUS_TAG_STREAM_CALL_FAILED); pKinesisVideoStream->base.result = SERVICE_CALL_RESULT_OK; - retStatus = stepStateMachine(pKinesisVideoStream->base.pStateMachine); + retStatus = iterateStreamStateMachine(pKinesisVideoStream); } CleanUp: @@ -783,7 +783,7 @@ STATUS streamTerminatedEvent(PKinesisVideoStream pKinesisVideoStream, UPLOAD_HAN pKinesisVideoStream->base.result = callResult; // Step the machine - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + CHK_STATUS(iterateStreamStateMachine(pKinesisVideoStream)); } CleanUp: diff --git a/src/client/src/StreamState.c b/src/client/src/StreamState.c index 19cbae25e..e25fe7a8a 100644 --- a/src/client/src/StreamState.c +++ b/src/client/src/StreamState.c @@ -39,6 +39,31 @@ StateMachineState STREAM_STATE_MACHINE_STATES[] = { UINT32 STREAM_STATE_MACHINE_STATE_COUNT = SIZEOF(STREAM_STATE_MACHINE_STATES) / SIZEOF(StateMachineState); +/////////////////////////////////////////////////////////////////////////// +// State machine transition iterator +/////////////////////////////////////////////////////////////////////////// + +STATUS iterateStreamStateMachine(PKinesisVideoStream pKinesisVideoStream) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PStateMachine pStateMachine = NULL; + + CHK(pKinesisVideoStream != NULL, STATUS_NULL_ARG); + pStateMachine = pKinesisVideoStream->base.pStateMachine; + CHK(pStateMachine != NULL, STATUS_NULL_ARG); + + do { + pKinesisVideoStream->keepIteratingStateMachine = FALSE; + CHK_STATUS(stepStateMachine(pStateMachine)); + } while (pKinesisVideoStream->keepIteratingStateMachine); + +CleanUp: + + LEAVES(); + return retStatus; +} + /////////////////////////////////////////////////////////////////////////// // State machine callback functions /////////////////////////////////////////////////////////////////////////// @@ -126,7 +151,7 @@ STATUS executeNewStreamState(UINT64 customData, UINT64 time) CHK(pKinesisVideoStream != NULL, STATUS_NULL_ARG); // Step the state machine to automatically invoke the Describe API - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + pKinesisVideoStream->keepIteratingStateMachine = TRUE; CleanUp: @@ -720,7 +745,7 @@ STATUS executeReadyStreamState(UINT64 customData, UINT64 time) // Check if we need to also call put stream API if (pKinesisVideoStream->streamState == STREAM_STATE_READY || pKinesisVideoStream->streamState == STREAM_STATE_STOPPED || viewByteSize != 0) { // Step the state machine to automatically invoke the PutStream API - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + pKinesisVideoStream->keepIteratingStateMachine = TRUE; } CleanUp: @@ -825,7 +850,7 @@ STATUS executeStoppedStreamState(UINT64 customData, UINT64 time) } // Auto-prime the state machine - CHK_STATUS(stepStateMachine(pKinesisVideoStream->base.pStateMachine)); + pKinesisVideoStream->keepIteratingStateMachine = TRUE; CleanUp: diff --git a/src/client/tst/CMakeLists.txt b/src/client/tst/CMakeLists.txt deleted file mode 100644 index 704d331d6..000000000 --- a/src/client/tst/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -cmake_minimum_required (VERSION 2.6) -project(PlatformIndependentNativeRepository LANGUAGES CXX) -get_filename_component(ROOT "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) - -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") - -project (clientTest) -file (GLOB ClientTestSources *.cpp) - -add_executable(${PROJECT_NAME} ${ClientTestSources}) -target_link_libraries(${PROJECT_NAME} state) -target_link_libraries(${PROJECT_NAME} client) -target_link_libraries(${PROJECT_NAME} gtest gtest_main) - -add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 7265fdbb8..e5f4608dd 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(common) +project(common LANGUAGES C) set(LIBRARY_NAME ${PROJECT_NAME}) install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION . diff --git a/src/common/include/com/amazonaws/kinesis/video/common/CommonDefs.h b/src/common/include/com/amazonaws/kinesis/video/common/CommonDefs.h index 83065b37c..b12e3847a 100644 --- a/src/common/include/com/amazonaws/kinesis/video/common/CommonDefs.h +++ b/src/common/include/com/amazonaws/kinesis/video/common/CommonDefs.h @@ -74,9 +74,15 @@ extern "C" { // 64/32 bit check on GCC // #if defined __GNUC__ || defined __GNUG__ -#if defined __x86_64__ || defined __ppc64__ +#if defined __x86_64__ || defined __ppc64__ || __aarch64__ #define SIZE_64 -#define __LLP64__ // win64 uses LLP64 data model +#if defined __APPLE__ +#define __LLP64__ +#else +#ifndef __LP64__ +#define __LP64__ // Linux uses LP64 data model +#endif +#endif #else #define SIZE_32 #endif @@ -228,6 +234,9 @@ typedef UINT64 MUTEX; #if defined __WINDOWS_BUILD__ typedef PCONDITION_VARIABLE CVAR; #else +#if defined(__linux__) && !defined(_GNU_SOURCE) +#define _GNU_SOURCE +#endif #include #include typedef pthread_cond_t* CVAR; @@ -368,11 +377,11 @@ typedef INT_PTR SSIZE_T, *PSSIZE_T; // NOTE: Timer precision is in 100ns intervals. This is used in heuristics and in time functionality // #define DEFAULT_TIME_UNIT_IN_NANOS 100 -#define HUNDREDS_OF_NANOS_IN_A_MICROSECOND 10LL -#define HUNDREDS_OF_NANOS_IN_A_MILLISECOND (HUNDREDS_OF_NANOS_IN_A_MICROSECOND * 1000LL) -#define HUNDREDS_OF_NANOS_IN_A_SECOND (HUNDREDS_OF_NANOS_IN_A_MILLISECOND * 1000LL) -#define HUNDREDS_OF_NANOS_IN_A_MINUTE (HUNDREDS_OF_NANOS_IN_A_SECOND * 60LL) -#define HUNDREDS_OF_NANOS_IN_AN_HOUR (HUNDREDS_OF_NANOS_IN_A_MINUTE * 60LL) +#define HUNDREDS_OF_NANOS_IN_A_MICROSECOND ((INT64) 10) +#define HUNDREDS_OF_NANOS_IN_A_MILLISECOND (HUNDREDS_OF_NANOS_IN_A_MICROSECOND * ((INT64) 1000)) +#define HUNDREDS_OF_NANOS_IN_A_SECOND (HUNDREDS_OF_NANOS_IN_A_MILLISECOND * ((INT64) 1000)) +#define HUNDREDS_OF_NANOS_IN_A_MINUTE (HUNDREDS_OF_NANOS_IN_A_SECOND * ((INT64) 60)) +#define HUNDREDS_OF_NANOS_IN_AN_HOUR (HUNDREDS_OF_NANOS_IN_A_MINUTE * ((INT64) 60)) // // Infinite time @@ -527,11 +536,11 @@ typedef INT_PTR SSIZE_T, *PSSIZE_T; #endif #ifndef S_ISDIR -#define S_ISDIR(mode) (((mode) &S_IFMT) == S_IFDIR) +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif #ifndef S_ISREG -#define S_ISREG(mode) (((mode) &S_IFMT) == S_IFREG) +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif // Definition of the mkdir for Windows with 1 param @@ -677,6 +686,8 @@ extern getTName globalGetThreadName; // typedef UINT64 (*getTime)(); +typedef struct tm* (*getTmTime)(const time_t*); + // // Default time library functions // @@ -684,11 +695,21 @@ typedef UINT64 (*getTime)(); PUBLIC_API UINT64 defaultGetTime(); +// +// The C library function gmtime is not threadsafe, but we need a thread +// safe impl. This provides that by wrapping the gmtime call around +// a global mutex specific for gmtime calls. All instances of GMTIME +// can be safely replaced with the new GMTIME_THREAD_SAFE. +// On Windows gmtime is threadsafe so no impact there. +// +PUBLIC_API struct tm* defaultGetThreadSafeTmTime(const time_t*); + // // Thread related functionality // extern getTime globalGetTime; extern getTime globalGetRealTime; +extern getTmTime globalGetThreadSafeTmTime; // // Thread library function definitions @@ -861,6 +882,11 @@ extern PUBLIC_API atomicXor globalAtomicXor; // #define IS_EMPTY_STRING(str) ((str)[0] == '\0') +// +// Check if string is null or empty +// +#define IS_NULL_OR_EMPTY_STRING(str) (str == NULL || IS_EMPTY_STRING(str)) + // // Pseudo-random functionality // @@ -896,12 +922,23 @@ extern PUBLIC_API atomicXor globalAtomicXor; #ifndef FREAD #define FREAD fread #endif +#ifndef FEOF +#define FEOF feof +#endif #ifndef FSEEK +#if defined _WIN32 || defined _WIN64 +#define FSEEK _fseeki64 +#else #define FSEEK fseek #endif +#endif #ifndef FTELL +#if defined _WIN32 || defined _WIN64 +#define FTELL _ftelli64 +#else #define FTELL ftell #endif +#endif #ifndef FREMOVE #define FREMOVE remove #endif @@ -979,6 +1016,12 @@ extern PUBLIC_API atomicXor globalAtomicXor; #define STRFTIME strftime #define GMTIME gmtime +#if defined _WIN32 || defined _WIN64 || defined __CYGWIN__ +#define GMTIME_THREAD_SAFE GMTIME +#else +#define GMTIME_THREAD_SAFE globalGetThreadSafeTmTime +#endif + // // Mutex functionality // @@ -1069,9 +1112,9 @@ typedef SIZE_T ATOMIC_BOOL; #define LOW_INT32(x) ((INT32) (x)) #define HIGH_INT32(x) ((INT32) (((INT64) (x) >> 32) & 0xFFFFFFFF)) -#define MAKE_INT16(a, b) ((INT16) (((UINT8) ((UINT16) (a) &0xff)) | ((UINT16) ((UINT8) ((UINT16) (b) &0xff))) << 8)) -#define MAKE_INT32(a, b) ((INT32) (((UINT16) ((UINT32) (a) &0xffff)) | ((UINT32) ((UINT16) ((UINT32) (b) &0xffff))) << 16)) -#define MAKE_INT64(a, b) ((INT64) (((UINT32) ((UINT64) (a) &0xffffffff)) | ((UINT64) ((UINT32) ((UINT64) (b) &0xffffffff))) << 32)) +#define MAKE_INT16(a, b) ((INT16) (((UINT8) ((UINT16) (a) & 0xff)) | ((UINT16) ((UINT8) ((UINT16) (b) & 0xff))) << 8)) +#define MAKE_INT32(a, b) ((INT32) (((UINT16) ((UINT32) (a) & 0xffff)) | ((UINT32) ((UINT16) ((UINT32) (b) & 0xffff))) << 16)) +#define MAKE_INT64(a, b) ((INT64) (((UINT32) ((UINT64) (a) & 0xffffffff)) | ((UINT64) ((UINT32) ((UINT64) (b) & 0xffffffff))) << 32)) #define SWAP_INT16(x) MAKE_INT16(HIGH_BYTE(x), LOW_BYTE(x)) diff --git a/src/common/include/com/amazonaws/kinesis/video/common/PlatformUtils.h b/src/common/include/com/amazonaws/kinesis/video/common/PlatformUtils.h index 05254ead0..7db5364e1 100644 --- a/src/common/include/com/amazonaws/kinesis/video/common/PlatformUtils.h +++ b/src/common/include/com/amazonaws/kinesis/video/common/PlatformUtils.h @@ -22,12 +22,12 @@ extern "C" { #endif // Log print function definition -typedef VOID (*logPrintFunc)(UINT32, PCHAR, PCHAR, ...); +typedef VOID (*logPrintFunc)(UINT32, const PCHAR, const PCHAR, ...); // // Default logger function // -PUBLIC_API VOID defaultLogPrint(UINT32 level, PCHAR tag, PCHAR fmt, ...); +PUBLIC_API VOID defaultLogPrint(UINT32 level, const PCHAR tag, const PCHAR fmt, ...); extern logPrintFunc globalCustomLogPrintFn; @@ -35,14 +35,21 @@ extern logPrintFunc globalCustomLogPrintFn; // Compiling with NDK #include #define __LOG(p1, p2, p3, ...) __android_log_print(p1, p2, p3, ##__VA_ARGS__) -#define __ASSERT(p1, p2, p3, ...) __android_log_assert(p1, p2, p3, ##__VA_ARGS__) +#define __ASSERT(p1, p2, p3, ...) __android_log_assert(p1, p2, p3, ##__VA_ARGS__) // This is unaffected by release vs debug build unlike C assert(). #else // Compiling under non-NDK #include #include #include + +#ifdef DEBUG_BUILD #define __ASSERT(p1, p2, p3, ...) assert(p1) -#define __LOG globalCustomLogPrintFn +#else +PUBLIC_API VOID customAssert(INT64 condition, const CHAR* fileName, INT64 lineNumber, const CHAR* functionName); +#define __ASSERT(p1, p2, p3, ...) customAssert((p1), __FILE__, __LINE__, __FUNCTION__) +#endif + +#define __LOG globalCustomLogPrintFn #endif // ANDROID_BUILD #define LOG_LEVEL_VERBOSE 1 @@ -52,38 +59,40 @@ extern logPrintFunc globalCustomLogPrintFn; #define LOG_LEVEL_ERROR 5 #define LOG_LEVEL_FATAL 6 #define LOG_LEVEL_SILENT 7 + +// Adding this after LOG_LEVEL_SILENT to ensure we do not break backward compat #define LOG_LEVEL_PROFILE 8 -#define LOG_LEVEL_VERBOSE_STR (PCHAR) "VERBOSE" -#define LOG_LEVEL_DEBUG_STR (PCHAR) "DEBUG" -#define LOG_LEVEL_INFO_STR (PCHAR) "INFO" -#define LOG_LEVEL_WARN_STR (PCHAR) "WARN" -#define LOG_LEVEL_ERROR_STR (PCHAR) "ERROR" -#define LOG_LEVEL_FATAL_STR (PCHAR) "FATAL" -#define LOG_LEVEL_SILENT_STR (PCHAR) "SILENT" -#define LOG_LEVEL_PROFILE_STR (PCHAR) "PROFILE" +#define LOG_LEVEL_VERBOSE_STR (const PCHAR) "VERBOSE" +#define LOG_LEVEL_DEBUG_STR (const PCHAR) "DEBUG" +#define LOG_LEVEL_INFO_STR (const PCHAR) "INFO" +#define LOG_LEVEL_WARN_STR (const PCHAR) "WARN" +#define LOG_LEVEL_ERROR_STR (const PCHAR) "ERROR" +#define LOG_LEVEL_FATAL_STR (const PCHAR) "FATAL" +#define LOG_LEVEL_SILENT_STR (const PCHAR) "SILENT" +#define LOG_LEVEL_PROFILE_STR (const PCHAR) "PROFILE" // LOG_LEVEL_VERBOSE_STR string length #define MAX_LOG_LEVEL_STRLEN 7 // Extra logging macros #ifndef DLOGE -#define DLOGE(fmt, ...) __LOG(LOG_LEVEL_ERROR, (PCHAR) LOG_CLASS, (PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) +#define DLOGE(fmt, ...) __LOG(LOG_LEVEL_ERROR, (const PCHAR) LOG_CLASS, (const PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) #endif #ifndef DLOGW -#define DLOGW(fmt, ...) __LOG(LOG_LEVEL_WARN, (PCHAR) LOG_CLASS, (PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) +#define DLOGW(fmt, ...) __LOG(LOG_LEVEL_WARN, (const PCHAR) LOG_CLASS, (const PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) #endif #ifndef DLOGI -#define DLOGI(fmt, ...) __LOG(LOG_LEVEL_INFO, (PCHAR) LOG_CLASS, (PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) +#define DLOGI(fmt, ...) __LOG(LOG_LEVEL_INFO, (const PCHAR) LOG_CLASS, (const PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) #endif #ifndef DLOGD -#define DLOGD(fmt, ...) __LOG(LOG_LEVEL_DEBUG, (PCHAR) LOG_CLASS, (PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) +#define DLOGD(fmt, ...) __LOG(LOG_LEVEL_DEBUG, (const PCHAR) LOG_CLASS, (const PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) #endif #ifndef DLOGV -#define DLOGV(fmt, ...) __LOG(LOG_LEVEL_VERBOSE, (PCHAR) LOG_CLASS, (PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) +#define DLOGV(fmt, ...) __LOG(LOG_LEVEL_VERBOSE, (const PCHAR) LOG_CLASS, (const PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) #endif #ifndef DLOGP -#define DLOGP(fmt, ...) __LOG(LOG_LEVEL_PROFILE, (PCHAR) LOG_CLASS, (PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) +#define DLOGP(fmt, ...) __LOG(LOG_LEVEL_PROFILE, (const PCHAR) LOG_CLASS, (const PCHAR) "%s(): " fmt, __FUNCTION__, ##__VA_ARGS__) #endif #ifndef ENTER @@ -119,25 +128,26 @@ extern logPrintFunc globalCustomLogPrintFn; #endif #endif #ifndef LOG_ALWAYS_FATAL_IF -#define LOG_ALWAYS_FATAL_IF(cond, ...) ((CONDITION(cond)) ? ((void) __ASSERT(FALSE, (PCHAR) LOG_CLASS, ##__VA_ARGS__)) : (void) 0) +#define LOG_ALWAYS_FATAL_IF(cond, ...) ((CONDITION(cond)) ? ((void) __ASSERT(FALSE, (const PCHAR) LOG_CLASS, ##__VA_ARGS__)) : (void) 0) #endif #ifndef LOG_ALWAYS_FATAL -#define LOG_ALWAYS_FATAL(...) (((void) __ASSERT(FALSE, (PCHAR) LOG_CLASS, ##__VA_ARGS__))) +#define LOG_ALWAYS_FATAL(...) (((void) __ASSERT(FALSE, (const PCHAR) LOG_CLASS, ##__VA_ARGS__))) #endif #ifndef SANITIZED_FILE #define SANITIZED_FILE (STRRCHR(__FILE__, '/') ? STRRCHR(__FILE__, '/') + 1 : __FILE__) #endif #ifndef CRASH -#define CRASH(fmt, ...) LOG_ALWAYS_FATAL("%s::%s: " fmt, (PCHAR) LOG_CLASS, __FUNCTION__, ##__VA_ARGS__) +#define CRASH(fmt, ...) LOG_ALWAYS_FATAL("%s::%s: " fmt, (const PCHAR) LOG_CLASS, __FUNCTION__, ##__VA_ARGS__) #endif #ifndef CHECK -#define CHECK(x) LOG_ALWAYS_FATAL_IF(!(x), "%s::%s: ASSERTION FAILED at %s:%d: " #x, (PCHAR) LOG_CLASS, __FUNCTION__, SANITIZED_FILE, __LINE__) +#define CHECK(x) LOG_ALWAYS_FATAL_IF(!(x), "%s::%s: ASSERTION FAILED at %s:%d: " #x, (const PCHAR) LOG_CLASS, __FUNCTION__, SANITIZED_FILE, __LINE__) #endif #ifndef CHECK_EXT #define CHECK_EXT(x, fmt, ...) \ - LOG_ALWAYS_FATAL_IF(!(x), "%s::%s: ASSERTION FAILED at %s:%d: " fmt, (PCHAR) LOG_CLASS, __FUNCTION__, SANITIZED_FILE, __LINE__, ##__VA_ARGS__) + LOG_ALWAYS_FATAL_IF(!(x), "%s::%s: ASSERTION FAILED at %s:%d: " fmt, (const PCHAR) LOG_CLASS, __FUNCTION__, SANITIZED_FILE, __LINE__, \ + ##__VA_ARGS__) #endif #ifndef LOG_GIT_HASH diff --git a/src/duration/CMakeLists.txt b/src/duration/CMakeLists.txt index 6605f7817..073c2eca3 100644 --- a/src/duration/CMakeLists.txt +++ b/src/duration/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.6.3) -project(duration) +project(duration LANGUAGES C) kinesis_video_library_setup(${PROJECT_NAME}) kinesis_video_library_install() diff --git a/src/duration/tst/CMakeLists.txt b/src/duration/tst/CMakeLists.txt deleted file mode 100644 index a2ad22433..000000000 --- a/src/duration/tst/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -cmake_minimum_required (VERSION 2.6) - -project (durationTest) -file (GLOB DurationTestSources *.cpp) - -add_executable(${PROJECT_NAME} ${DurationTestSources}) - -target_link_libraries(${PROJECT_NAME} duration) -target_link_libraries(${PROJECT_NAME} utils) -target_link_libraries(${PROJECT_NAME} gtest gtest_main) - -add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/src/heap/CMakeLists.txt b/src/heap/CMakeLists.txt index c1d54419f..e218940ac 100644 --- a/src/heap/CMakeLists.txt +++ b/src/heap/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(heap) +project(heap LANGUAGES C) kinesis_video_library_setup(${PROJECT_NAME}) target_link_libraries(heap utils) kinesis_video_library_install() \ No newline at end of file diff --git a/src/heap/src/HybridFileHeap.c b/src/heap/src/HybridFileHeap.c index e401ae8d3..70cb89b55 100644 --- a/src/heap/src/HybridFileHeap.c +++ b/src/heap/src/HybridFileHeap.c @@ -186,6 +186,7 @@ DEFINE_HEAP_ALLOC(hybridFileHeapAlloc) CHAR filePath[MAX_PATH_LEN + 1]; ALLOCATION_HEADER allocationHeader; ALLOCATION_HANDLE handle; + INT32 retCode; // Call the base class for the accounting retStatus = commonHeapAlloc(pHeap, size, pHandle); @@ -213,7 +214,9 @@ DEFINE_HEAP_ALLOC(hybridFileHeapAlloc) DLOGS("Allocating from File heap"); // Try to allocate from file storage - SPRINTF(filePath, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, pHybridHeap->handleNum); + retCode = + SNPRINTF(filePath, MAX_PATH_LEN + 1, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, pHybridHeap->handleNum); + CHK(retCode <= MAX_PATH_LEN, STATUS_PATH_TOO_LONG); // Create a file with the overall size CHK_STATUS(createFile(filePath, allocationSize)); @@ -268,7 +271,8 @@ DEFINE_HEAP_FREE(hybridFileHeapFree) DLOGS("Indirect allocation"); // Convert the handle and create the file path fileHandle = TO_FILE_HANDLE(handle); - SPRINTF(filePath, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, fileHandle); + retCode = SNPRINTF(filePath, MAX_PATH_LEN + 1, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, fileHandle); + CHK(retCode <= MAX_PATH_LEN, STATUS_PATH_TOO_LONG); retCode = FREMOVE(filePath); @@ -303,6 +307,7 @@ DEFINE_HEAP_GET_ALLOC_SIZE(hybridFileHeapGetAllocSize) ALLOCATION_HEADER allocationHeader; CHAR filePath[MAX_PATH_LEN + 1]; UINT32 fileHandle; + INT32 retCode; // Call the base class to ensure the params are ok and set the default ret values CHK_STATUS(commonHeapGetAllocSize(pHeap, handle, pAllocSize)); @@ -320,7 +325,9 @@ DEFINE_HEAP_GET_ALLOC_SIZE(hybridFileHeapGetAllocSize) fileHandle = TO_FILE_HANDLE(handle); DLOGS("File heap allocation. Handle 0x%016" PRIx64 " File handle 0x%08x", handle, fileHandle); - SPRINTF(filePath, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, fileHandle); + retCode = SNPRINTF(filePath, MAX_PATH_LEN + 1, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, fileHandle); + CHK(retCode <= MAX_PATH_LEN, STATUS_PATH_TOO_LONG); + CHK_STATUS(readFileSegment(filePath, TRUE, (PBYTE) &allocationHeader, 0, FILE_ALLOCATION_HEADER_SIZE)); // Set the values and return @@ -344,6 +351,7 @@ DEFINE_HEAP_SET_ALLOC_SIZE(hybridFileHeapSetAllocSize) CHAR filePath[MAX_PATH_LEN + 1]; UINT32 fileHandle; UINT64 overallSize; + INT32 retCode; // Call the base class to ensure the params are ok and set the default ret values CHK_STATUS(commonHeapSetAllocSize(pHeap, pHandle, size, newSize)); @@ -369,8 +377,8 @@ DEFINE_HEAP_SET_ALLOC_SIZE(hybridFileHeapSetAllocSize) fileHandle = TO_FILE_HANDLE(handle); DLOGS("Sets new allocation size %\" PRIu64 \" for handle 0x%016" PRIx64, newSize, handle); - SPRINTF(filePath, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, fileHandle); - + retCode = SNPRINTF(filePath, MAX_PATH_LEN + 1, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, fileHandle); + CHK(retCode <= MAX_PATH_LEN, STATUS_PATH_TOO_LONG); // Set the file size CHK_STATUS(setFileLength(filePath, overallSize)); @@ -405,6 +413,7 @@ DEFINE_HEAP_MAP(hybridFileHeapMap) UINT32 fileHandle; PALLOCATION_HEADER pAllocation = NULL; UINT64 fileLength; + INT32 retCode; // Call the base class to ensure the params are ok and set the default ret values CHK_STATUS(commonHeapMap(pHeap, handle, ppAllocation, pSize)); @@ -422,7 +431,8 @@ DEFINE_HEAP_MAP(hybridFileHeapMap) fileHandle = TO_FILE_HANDLE(handle); DLOGS("File heap allocation. Handle 0x%016" PRIx64 " File handle 0x%08x", handle, fileHandle); - SPRINTF(filePath, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, fileHandle); + retCode = SNPRINTF(filePath, MAX_PATH_LEN + 1, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, fileHandle); + CHK(retCode <= MAX_PATH_LEN, STATUS_PATH_TOO_LONG); // Get the file size, allocate and read the entire file into memory CHK_STATUS(getFileLength(filePath, &fileLength)); @@ -457,6 +467,7 @@ DEFINE_HEAP_UNMAP(hybridFileHeapUnmap) PHybridFileHeap pHybridHeap = (PHybridFileHeap) pHeap; PALLOCATION_HEADER pHeader = (PALLOCATION_HEADER) pAllocation - 1; CHAR filePath[MAX_PATH_LEN + 1]; + INT32 retCode; // Call the base class to ensure the params are ok CHK_STATUS(commonHeapUnmap(pHeap, pAllocation)); @@ -473,7 +484,9 @@ DEFINE_HEAP_UNMAP(hybridFileHeapUnmap) } DLOGS("Indirect allocation"); - SPRINTF(filePath, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, pHeader->fileHandle); + retCode = + SNPRINTF(filePath, MAX_PATH_LEN + 1, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, pHeader->fileHandle); + CHK(retCode <= MAX_PATH_LEN, STATUS_PATH_TOO_LONG); // Un-maping in this case is simply writing the content into the file storage and releasing the mapped memory CHK_STATUS(writeFile(filePath, TRUE, FALSE, (PBYTE) pHeader, pHeader->size + FILE_ALLOCATION_HEADER_SIZE)); @@ -507,6 +520,7 @@ DEFINE_ALLOC_SIZE(hybridFileGetAllocationSize) UINT32 fileHandle; ALLOCATION_HEADER allocationHeader; UINT64 memSizes, fileSizes, memHeapAllocationSize; + INT32 retCode; CHECK_EXT(pHeap != NULL, "Internal error with file heap being null"); @@ -521,7 +535,10 @@ DEFINE_ALLOC_SIZE(hybridFileGetAllocationSize) // In case of File allocation we need to read the header and get the size fileHandle = TO_FILE_HANDLE(handle); - SPRINTF(filePath, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, fileHandle); + retCode = SNPRINTF(filePath, MAX_PATH_LEN + 1, "%s%c%u" FILE_HEAP_FILE_EXTENSION, pHybridHeap->rootDirectory, FPATHSEPARATOR, fileHandle); + if (retCode > MAX_PATH_LEN) { + DLOGW("filepath length has exceeded the maximum allowed"); + } // Read the header to get the size info so we can allocate enough storage if (STATUS_FAILED(readFileSegment(filePath, TRUE, (PBYTE) &allocationHeader, 0, FILE_ALLOCATION_HEADER_SIZE))) { diff --git a/src/heap/src/HybridHeap.h b/src/heap/src/HybridHeap.h index e549793ce..26d052647 100644 --- a/src/heap/src/HybridHeap.h +++ b/src/heap/src/HybridHeap.h @@ -43,7 +43,7 @@ typedef UINT32 (*VramGetMax)(VOID); #define ALIGNMENT_BITS (UINT64) 0x03 #define TO_VRAM_HANDLE(h) ((UINT32) ((UINT64) (h) >> 32)) #define FROM_VRAM_HANDLE(h) (ALLOCATION_HANDLE)(((UINT64) (h) << 32) | ALIGNMENT_BITS) -#define IS_DIRECT_ALLOCATION_HANDLE(h) (((UINT64) (h) &ALIGNMENT_BITS) == (UINT64) 0x00) +#define IS_DIRECT_ALLOCATION_HANDLE(h) (((UINT64) (h) & ALIGNMENT_BITS) == (UINT64) 0x00) /** * Hybrid heap struct diff --git a/src/heap/tst/CMakeLists.txt b/src/heap/tst/CMakeLists.txt deleted file mode 100644 index 470cb6e9f..000000000 --- a/src/heap/tst/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -cmake_minimum_required (VERSION 2.6) - -project (heapTest) -file (GLOB HeapTestSources *.cpp) - -add_executable(${PROJECT_NAME} ${HeapTestSources}) - -target_link_libraries(${PROJECT_NAME} heap) -target_link_libraries(${PROJECT_NAME} utils) -target_link_libraries(${PROJECT_NAME} gtest gtest_main) - -add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/src/mkvgen/CMakeLists.txt b/src/mkvgen/CMakeLists.txt index df3a42f0c..a1164533b 100644 --- a/src/mkvgen/CMakeLists.txt +++ b/src/mkvgen/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(mkvgen) +project(mkvgen LANGUAGES C) kinesis_video_library_setup(${PROJECT_NAME}) target_link_libraries(mkvgen utils) kinesis_video_library_install() diff --git a/src/mkvgen/include/com/amazonaws/kinesis/video/mkvgen/Include.h b/src/mkvgen/include/com/amazonaws/kinesis/video/mkvgen/Include.h index 2522da611..788f9e4d7 100644 --- a/src/mkvgen/include/com/amazonaws/kinesis/video/mkvgen/Include.h +++ b/src/mkvgen/include/com/amazonaws/kinesis/video/mkvgen/Include.h @@ -231,10 +231,10 @@ typedef enum { MKV_TREE_TAGS = 0, MKV_TREE_TAG, MKV_TREE_SIMPLE, MKV_TREE_LAST } /** * Macros checking for the frame flags */ -#define CHECK_FRAME_FLAG_KEY_FRAME(f) (((f) &FRAME_FLAG_KEY_FRAME) != FRAME_FLAG_NONE) -#define CHECK_FRAME_FLAG_DISCARDABLE_FRAME(f) (((f) &FRAME_FLAG_DISCARDABLE_FRAME) != FRAME_FLAG_NONE) -#define CHECK_FRAME_FLAG_INVISIBLE_FRAME(f) (((f) &FRAME_FLAG_INVISIBLE_FRAME) != FRAME_FLAG_NONE) -#define CHECK_FRAME_FLAG_END_OF_FRAGMENT(f) (((f) &FRAME_FLAG_END_OF_FRAGMENT) != FRAME_FLAG_NONE) +#define CHECK_FRAME_FLAG_KEY_FRAME(f) (((f) & FRAME_FLAG_KEY_FRAME) != FRAME_FLAG_NONE) +#define CHECK_FRAME_FLAG_DISCARDABLE_FRAME(f) (((f) & FRAME_FLAG_DISCARDABLE_FRAME) != FRAME_FLAG_NONE) +#define CHECK_FRAME_FLAG_INVISIBLE_FRAME(f) (((f) & FRAME_FLAG_INVISIBLE_FRAME) != FRAME_FLAG_NONE) +#define CHECK_FRAME_FLAG_END_OF_FRAGMENT(f) (((f) & FRAME_FLAG_END_OF_FRAGMENT) != FRAME_FLAG_NONE) #define SET_FRAME_FLAG_KEY_FRAME(f) ((f) = (FRAME_FLAGS) (f | FRAME_FLAG_KEY_FRAME)) #define SET_FRAME_FLAG_DISCARDABLE_FRAME(f) ((f) = (FRAME_FLAGS) (f | FRAME_FLAG_DISCARDABLE_FRAME)) diff --git a/src/mkvgen/src/Include_i.h b/src/mkvgen/src/Include_i.h index 27e8dd4cf..928d3e763 100644 --- a/src/mkvgen/src/Include_i.h +++ b/src/mkvgen/src/Include_i.h @@ -284,7 +284,7 @@ extern UINT32 gMkvTagStringBitsSize; /** * To and from MKV timestamp conversion factoring in the timecode */ -#define TIMESTAMP_TO_MKV_TIMECODE(ts, tcs) ((ts) *DEFAULT_TIME_UNIT_IN_NANOS / (tcs)) +#define TIMESTAMP_TO_MKV_TIMECODE(ts, tcs) ((ts) * DEFAULT_TIME_UNIT_IN_NANOS / (tcs)) #define MKV_TIMECODE_TO_TIMESTAMP(tc, tcs) ((tc) * ((tcs) / DEFAULT_TIME_UNIT_IN_NANOS)) /** diff --git a/src/mkvgen/tst/CMakeLists.txt b/src/mkvgen/tst/CMakeLists.txt deleted file mode 100644 index afac93411..000000000 --- a/src/mkvgen/tst/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required (VERSION 2.6) -project (mkvgenTest) -file (GLOB MkvgenTestSources *.cpp) -file(COPY samples DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) - -add_executable(${PROJECT_NAME} ${MkvgenTestSources}) -target_link_libraries(${PROJECT_NAME} mkvgen) -target_link_libraries(${PROJECT_NAME} gtest gtest_main) - -add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/src/state/CMakeLists.txt b/src/state/CMakeLists.txt index 9e38df1fa..4aa7edea0 100644 --- a/src/state/CMakeLists.txt +++ b/src/state/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(state) +project(state LANGUAGES C) kinesis_video_library_setup(${PROJECT_NAME}) target_link_libraries(state mkvgen) kinesis_video_library_install() diff --git a/src/state/include/com/amazonaws/kinesis/video/state/Include.h b/src/state/include/com/amazonaws/kinesis/video/state/Include.h index d4d35fcce..84d92c9d1 100644 --- a/src/state/include/com/amazonaws/kinesis/video/state/Include.h +++ b/src/state/include/com/amazonaws/kinesis/video/state/Include.h @@ -22,9 +22,10 @@ extern "C" { //////////////////////////////////////////////////// // Status return codes //////////////////////////////////////////////////// -#define STATUS_STATE_BASE 0x52000000 -#define STATUS_INVALID_STREAM_STATE STATUS_STATE_BASE + 0x0000000e -#define STATUS_STATE_MACHINE_STATE_NOT_FOUND STATUS_STATE_BASE + 0x00000056 +#define STATUS_STATE_BASE 0x52000000 +#define STATUS_INVALID_STREAM_STATE STATUS_STATE_BASE + 0x0000000e +#define STATUS_STATE_MACHINE_STATE_NOT_FOUND STATUS_STATE_BASE + 0x00000056 +#define STATUS_STATE_MACHINE_NAME_LEN_INVALID STATUS_STATE_BASE + 0x0000009a // 0x00000057 to 0x0000008f used with STATUS_CLIENT_BASE //////////////////////////////////////////////////// // Main structure declarations @@ -44,6 +45,11 @@ extern "C" { */ #define STATE_MACHINE_CURRENT_VERSION 0 +/** + * Maximum state machine name length + */ +#define MAX_STATE_MACHINE_NAME_LENGTH 32 + /** * State transition function definitions * @@ -104,15 +110,115 @@ typedef struct __StateMachine* PStateMachine; // Public functions //////////////////////////////////////////////////// +/** + * Creates the state machine object + * + * @param 1 PStateMachineState - IN - List of state machine details on states, state transition functions and valid state transition list (mandatory) + * @param 2 UINT32 - IN - stateCount - Number of entries in the PStateMachineState list (mandatory) + * @param 3 UINT64 - IN - customData - application specific data that needs to be passed around (optional) + * @param 4 GetCurrentTimeFunc - IN - custom get time function that the state machine should use to state transition wait time (optional) + * @param 5 UINT64 - IN - getCurrentTimeFuncCustomData - custom data for getCurrentTimeFunc (optional) + * @param 6 PStateMachine - OUT - Allocated state machine object + * + * @return - STATUS code of the execution + */ PUBLIC_API STATUS createStateMachine(PStateMachineState, UINT32, UINT64, GetCurrentTimeFunc, UINT64, PStateMachine*); + +/** + * Creates a state machine with a state machine name string to identify the state machine. State machine name is mandatory + * if using this API and cannot exceed 32 characters and cannot be an empty string + * + * @param 1 PStateMachineState - IN - List of state machine details on states, state transition functions and valid state transition list (mandatory) + * @param 2 UINT32 - IN - stateCount - Number of entries in the PStateMachineState list (mandatory) + * @param 3 UINT64 - IN - customData - application specific data that needs to be passed around (optional) + * @param 4 GetCurrentTimeFunc - IN - custom get time function that the state machine should use to state transition wait time (optional) + * @param 5 UINT64 - IN - getCurrentTimeFuncCustomData - custom data for getCurrentTimeFunc (optional) + * @param 6 PCHAR - IN - pStateMachineName - name for the state machine. This will be used in the logs to uniquely identify a state machine (useful + * when there are multiple state machines running simultaneously + * @param 6 PStateMachine - OUT - Allocated state machine object + * + * @return - STATUS code of the execution + */ +PUBLIC_API STATUS createStateMachineWithName(PStateMachineState, UINT32, UINT64, GetCurrentTimeFunc, UINT64, PCHAR, PStateMachine*); + +/** + * Free the state machine object + * @param 1 PStateMachine - IN - State machine object to be deallocated + * + * @return - STATUS code of the execution + */ PUBLIC_API STATUS freeStateMachine(PStateMachine); + +/** + * Step to next valid state in a state machine. + * @param 1 PStateMachine - IN - State machine object + * + * @return - STATUS code of the execution + */ PUBLIC_API STATUS stepStateMachine(PStateMachine); + +/** + * Checks whether the next state machine state is in the list of accepted states + * @param 1 PStateMachine - IN - State machine object + * @param 2 UINT64 - IN - requiredStates - ORed list of allowed states to transition to a particular state + * + * @return - STATUS code of the execution + */ PUBLIC_API STATUS acceptStateMachineState(PStateMachine, UINT64); + +/** + * Validate if the next state can accept the current state before transitioning + * @param 1 PStateMachine - IN - State machine object + * @param 2 UINT64 - IN - state - The next state to transition to + * @param 3 PStateMachineState - OUT - Pointer to the state object given it's state + * + * @return - STATUS code of the execution + */ PUBLIC_API STATUS getStateMachineState(PStateMachine, UINT64, PStateMachineState*); + +/** + * Gets a pointer to the current state object + * @param 1 PStateMachine - IN - State machine object + * @param 3 PStateMachineState - OUT - Pointer to the state object for the current state + * + * @return - STATUS code of the execution + */ PUBLIC_API STATUS getStateMachineCurrentState(PStateMachine, PStateMachineState*); + +/** + * Force sets a pointer to the current state object + * @param 1 PStateMachine - IN - State machine object + * @param 3 UINT64 - IN - state - Set state machine to a particular state + * + * @return - STATUS code of the execution + */ PUBLIC_API STATUS setStateMachineCurrentState(PStateMachine, UINT64); + +/** + * Resets the state machine retry count + * @param 1 PStateMachine - IN - State machine object to be deallocated + * + * @return - STATUS code of the execution + */ PUBLIC_API STATUS resetStateMachineRetryCount(PStateMachine); +/** + * Calls the from function of the current state to determine if the state machine is ready to + * move on to another state. + * @param 1 PStateMachine - IN - State machine object to be deallocated + * @param 2 PBOOL - OUT - Returns TRUE if state machine is ready to transition, else false + * + * @return - STATUS code of the execution + */ +PUBLIC_API STATUS checkForStateTransition(PStateMachine, PBOOL); + +/** + * Return state machine name set up + * @param 1 PStateMachine - IN - State machine object to be deallocated + * @return - Returns the name set for the state machine + */ +PUBLIC_API PCHAR getStateMachineName(PStateMachine); + static const ExponentialBackoffRetryStrategyConfig DEFAULT_STATE_MACHINE_EXPONENTIAL_BACKOFF_RETRY_CONFIGURATION = { /* Exponential wait times with this config will look like following - ************************************ diff --git a/src/state/src/Include_i.h b/src/state/src/Include_i.h index 2373c4fe1..243cace06 100644 --- a/src/state/src/Include_i.h +++ b/src/state/src/Include_i.h @@ -62,6 +62,8 @@ struct __StateMachineImpl { // State machine state count UINT32 stateCount; + CHAR stateMachineName[MAX_STATE_MACHINE_NAME_LENGTH + 1]; + // State machine states following the main structure PStateMachineState states; }; diff --git a/src/state/src/State.c b/src/state/src/State.c index b5f5fe63b..2adf8ee9a 100644 --- a/src/state/src/State.c +++ b/src/state/src/State.c @@ -54,6 +54,26 @@ STATUS createStateMachine(PStateMachineState pStates, UINT32 stateCount, UINT64 return retStatus; } +STATUS createStateMachineWithName(PStateMachineState pStates, UINT32 stateCount, UINT64 customData, GetCurrentTimeFunc getCurrentTimeFunc, + UINT64 getCurrentTimeFuncCustomData, PCHAR pStateMachineName, PStateMachine* ppStateMachine) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PStateMachineImpl pStateMachineImpl; + CHK_ERR(pStateMachineName != NULL, STATUS_NULL_ARG, "State machine name is NULL"); + CHK_ERR(!IS_EMPTY_STRING(pStateMachineName), STATUS_STATE_MACHINE_NAME_LEN_INVALID, "State machine name is empty"); + CHK(STRNLEN(pStateMachineName, MAX_STATE_MACHINE_NAME_LENGTH + 1) <= MAX_STATE_MACHINE_NAME_LENGTH, STATUS_STATE_MACHINE_NAME_LEN_INVALID); + CHK_STATUS(createStateMachine(pStates, stateCount, customData, getCurrentTimeFunc, getCurrentTimeFuncCustomData, ppStateMachine)); + pStateMachineImpl = (PStateMachineImpl) *ppStateMachine; + CHK(pStateMachineImpl != NULL, STATUS_NULL_ARG); + STRNCPY(pStateMachineImpl->stateMachineName, pStateMachineName, MAX_STATE_MACHINE_NAME_LENGTH); + pStateMachineImpl->stateMachineName[MAX_STATE_MACHINE_NAME_LENGTH] = '\0'; + *ppStateMachine = (PStateMachine) pStateMachineImpl; +CleanUp: + LEAVES(); + return retStatus; +} + /** * Frees the state machine object */ @@ -126,7 +146,7 @@ STATUS getStateMachineCurrentState(PStateMachine pStateMachine, PStateMachineSta } /** - * Transition the state machine given it's context + * Transition the state machine given its context */ STATUS stepStateMachine(PStateMachine pStateMachine) { @@ -161,7 +181,7 @@ STATUS stepStateMachine(PStateMachine pStateMachine) // Check if we are changing the state if (pState->state != pStateMachineImpl->context.pCurrentState->state) { - // Since we're transitioning to a different state from this state, reset the local state retry count to0 + // Since we're transitioning to a different state from this state, reset the local state retry count to 0 pStateMachineImpl->context.localStateRetryCount = 0; } else { // Increment the local state retry count. @@ -169,10 +189,18 @@ STATUS stepStateMachine(PStateMachine pStateMachine) pStateMachineImpl->context.localStateRetryCount++; } - DLOGV("State Machine - Current state: 0x%016" PRIx64 ", Next state: 0x%016" PRIx64 ", " - "Current local state retry count [%u], Max local state retry count [%u], State transition wait time [%u] ms", - pStateMachineImpl->context.pCurrentState->state, nextState, pStateMachineImpl->context.localStateRetryCount, - pState->maxLocalStateRetryCount, errorStateTransitionWaitTime / HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + if (IS_EMPTY_STRING(pStateMachineImpl->stateMachineName)) { + DLOGV("State Machine - Current state: 0x%016" PRIx64 ", Next state: 0x%016" PRIx64 ", " + "Current local state retry count [%u], Max local state retry count [%u], State transition wait time [%u] ms", + pStateMachineImpl->context.pCurrentState->state, nextState, pStateMachineImpl->context.localStateRetryCount, + pState->maxLocalStateRetryCount, errorStateTransitionWaitTime / HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + } else { + DLOGV("[%s] State Machine - Current state: 0x%016" PRIx64 ", Next state: 0x%016" PRIx64 ", " + "Current local state retry count [%u], Max local state retry count [%u], State transition wait time [%u] ms", + pStateMachineImpl->stateMachineName, pStateMachineImpl->context.pCurrentState->state, nextState, + pStateMachineImpl->context.localStateRetryCount, pState->maxLocalStateRetryCount, + errorStateTransitionWaitTime / HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + } // Check if we have tried enough times within the same state if (pState->maxLocalStateRetryCount != INFINITE_RETRY_COUNT_SENTINEL) { @@ -258,3 +286,56 @@ STATUS resetStateMachineRetryCount(PStateMachine pStateMachine) LEAVES(); return retStatus; } + +/** + * Calls the from function of the current state to determine if the state machine is ready to + * move on to another state. + */ +STATUS checkForStateTransition(PStateMachine pStateMachine, PBOOL pTransitionReady) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PStateMachineState pState = NULL; + UINT64 nextState, time; + UINT64 customData; + PStateMachineImpl pStateMachineImpl = (PStateMachineImpl) pStateMachine; + UINT64 errorStateTransitionWaitTime = 0; + BOOL transitionReady = FALSE; + UINT32 i; + + CHK(pStateMachineImpl != NULL && pTransitionReady != NULL, STATUS_NULL_ARG); + customData = pStateMachineImpl->customData; + + // Get the next state + CHK(pStateMachineImpl->context.pCurrentState->getNextStateFn != NULL, STATUS_NULL_ARG); + CHK_STATUS(pStateMachineImpl->context.pCurrentState->getNextStateFn(pStateMachineImpl->customData, &nextState)); + + // Iterate over and find the first state + for (i = 0; pState == NULL && i < pStateMachineImpl->stateCount; i++) { + if (pStateMachineImpl->states[i].state == nextState) { + if (pStateMachineImpl->context.pCurrentState->state != nextState) { + transitionReady = TRUE; + } + break; + } + } + + *pTransitionReady = transitionReady; + +CleanUp: + + LEAVES(); + return retStatus; +} + +// This function is useful for unit tests +PCHAR getStateMachineName(PStateMachine pStateMachine) +{ + PStateMachineImpl pStateMachineImpl = (PStateMachineImpl) pStateMachine; + if (pStateMachineImpl == NULL) { + DLOGW("State machine object not created. Cannot retrieve name"); + return NULL; + } else { + return pStateMachineImpl->stateMachineName; + } +} \ No newline at end of file diff --git a/src/state/tst/CMakeLists.txt b/src/state/tst/CMakeLists.txt deleted file mode 100644 index 31c9a761c..000000000 --- a/src/state/tst/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required (VERSION 2.6) -project (stateTest) -file (GLOB StateTestSources *.cpp) - -add_executable(${PROJECT_NAME} ${StateTestSources}) -target_link_libraries(${PROJECT_NAME} state) -target_link_libraries(${PROJECT_NAME} client) -target_link_libraries(${PROJECT_NAME} gtest gtest_main) - -add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/src/trace/CMakeLists.txt b/src/trace/CMakeLists.txt index 8d2a7bcbb..6506e5707 100644 --- a/src/trace/CMakeLists.txt +++ b/src/trace/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(trace) +project(trace LANGUAGES C) kinesis_video_library_setup(${PROJECT_NAME}) target_link_libraries(trace utils) kinesis_video_library_install() diff --git a/src/trace/tst/CMakeLists.txt b/src/trace/tst/CMakeLists.txt deleted file mode 100644 index 46c56028c..000000000 --- a/src/trace/tst/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -cmake_minimum_required (VERSION 2.6) -project (traceTest) -file (GLOB TraceTestSources *.cpp) - -add_executable(${PROJECT_NAME} ${TraceTestSources}) -target_link_libraries(${PROJECT_NAME} trace) -target_link_libraries(${PROJECT_NAME} gtest gtest_main) - -add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index 13697d1e7..14800f8d1 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(utils) +project(utils LANGUAGES C) kinesis_video_library_setup(${PROJECT_NAME}) target_link_libraries(${PROJECT_NAME} ${CMAKE_DL_LIBS}) kinesis_video_library_install() diff --git a/src/utils/include/com/amazonaws/kinesis/video/utils/Include.h b/src/utils/include/com/amazonaws/kinesis/video/utils/Include.h index 05b4c81f2..a96d6f5b5 100644 --- a/src/utils/include/com/amazonaws/kinesis/video/utils/Include.h +++ b/src/utils/include/com/amazonaws/kinesis/video/utils/Include.h @@ -68,6 +68,7 @@ extern "C" { #define STATUS_EXPONENTIAL_BACKOFF_INVALID_STATE STATUS_UTILS_BASE + 0x0000002a #define STATUS_EXPONENTIAL_BACKOFF_RETRIES_EXHAUSTED STATUS_UTILS_BASE + 0x0000002b #define STATUS_THREADPOOL_MAX_COUNT STATUS_UTILS_BASE + 0x0000002c +#define STATUS_THREADPOOL_INTERNAL_ERROR STATUS_UTILS_BASE + 0x0000002d /** * Base64 encode/decode functionality @@ -1247,6 +1248,16 @@ PUBLIC_API STATUS timerQueueGetTimersWithCustomData(TIMER_QUEUE_HANDLE, UINT64, */ PUBLIC_API STATUS timerQueueUpdateTimerPeriod(TIMER_QUEUE_HANDLE, UINT64, UINT32, UINT64); +/* + * Kick timer id's timer to invoke immediately. Do nothing if timer not found. + * + * @param - TIMER_QUEUE_HANDLE - IN - Timer queue handle + * @param - UINT32 - IN - Timer id to update + * + * @return - STATUS code of the execution + */ +PUBLIC_API STATUS timerQueueKick(TIMER_QUEUE_HANDLE, UINT32); + /* * stop the timer. Once stopped timer can't be restarted. There will be no more timer callback invocation after * timerQueueShutdown returns. @@ -1454,8 +1465,8 @@ PUBLIC_API SIZE_T getInstrumentedTotalAllocationSize(); /** * File logger error values starting from 0x41300000 */ -#define STATUS_FILE_LOGGER_BASE STATUS_UTILS_BASE + 0x01200000 -#define STATUS_FILE_LOGGER_INDEX_FILE_INVALID_SIZE STATUS_SEMAPHORE_BASE + 0x00000001 +#define STATUS_FILE_LOGGER_BASE STATUS_UTILS_BASE + 0x01300000 +#define STATUS_FILE_LOGGER_INDEX_FILE_INVALID_SIZE STATUS_FILE_LOGGER_BASE + 0x00000001 /** * File based logger limit constants diff --git a/src/utils/src/Allocators.c b/src/utils/src/Allocators.c index f402a9b98..7530fc7e9 100644 --- a/src/utils/src/Allocators.c +++ b/src/utils/src/Allocators.c @@ -53,7 +53,7 @@ VOID dumpMemoryHex(PVOID pMem, UINT32 size) PCHAR pCur = buf; PBYTE pByte = (PBYTE) pMem; for (UINT32 i = 0; i < size; i++) { - SPRINTF(pCur, "%02x ", *pByte++); + SNPRINTF(pCur, 4, "%02x ", *pByte++); pCur += 3; if ((i + 1) % 16 == 0) { DLOGS("%s", buf); diff --git a/src/utils/src/CustomAssert.c b/src/utils/src/CustomAssert.c new file mode 100644 index 000000000..26a672e0e --- /dev/null +++ b/src/utils/src/CustomAssert.c @@ -0,0 +1,9 @@ +#include "Include_i.h" + +VOID customAssert(INT64 condition, const CHAR* fileName, INT64 lineNumber, const CHAR* functionName) +{ + if (!condition) { + fprintf(stderr, "Assertion failed in file %s, function %s, line %lld \n", fileName, functionName, lineNumber); + abort(); // C function call + } +} diff --git a/src/utils/src/FileIo.c b/src/utils/src/FileIo.c index 1c156977b..55b385e63 100644 --- a/src/utils/src/FileIo.c +++ b/src/utils/src/FileIo.c @@ -23,6 +23,11 @@ STATUS readFile(PCHAR filePath, BOOL binMode, PBYTE pBuffer, PUINT64 pSize) CHK(fp != NULL, STATUS_OPEN_FILE_FAILED); // Get the size of the file + + // Use Windows-specific _fseeki64 and _ftelli64 as the traditional fseek and ftell are non-compliant on systems that do not provide + // the same guarantees as POSIX. On these systems, setting the file position indicator to the + // end of the file using fseek() is not guaranteed to work for a binary stream, and consequently, + // the amount of memory allocated may be incorrect, leading to a potential vulnerability. FSEEK(fp, 0, SEEK_END); fileLen = FTELL(fp); @@ -37,7 +42,14 @@ STATUS readFile(PCHAR filePath, BOOL binMode, PBYTE pBuffer, PUINT64 pSize) // Read the file into memory buffer FSEEK(fp, 0, SEEK_SET); - CHK(FREAD(pBuffer, (SIZE_T) fileLen, 1, fp) == 1, STATUS_READ_FILE_FAILED); + + // fread would either return 1, i.e, the number of objects we've requested it to read + // or it would run into end-of-file / error. + // fread does not distinguish between end-of-file and error, + // and callers must use feof and ferror to determine which occurred. + if (FREAD(pBuffer, (SIZE_T) fileLen, 1, fp) != 1) { + CHK(FEOF(fp), STATUS_READ_FILE_FAILED); + } CleanUp: @@ -75,6 +87,11 @@ STATUS readFileSegment(PCHAR filePath, BOOL binMode, PBYTE pBuffer, UINT64 offse CHK(fp != NULL, STATUS_OPEN_FILE_FAILED); // Get the size of the file + + // Use Windows-specific _fseeki64 and _ftelli64 as the traditional fseek and ftell are non-compliant on systems that do not provide + // the same guarantees as POSIX. On these systems, setting the file position indicator to the + // end of the file using fseek() is not guaranteed to work for a binary stream, and consequently, + // the amount of memory allocated may be incorrect, leading to a potential vulnerability. FSEEK(fp, 0, SEEK_END); fileLen = FTELL(fp); @@ -83,7 +100,16 @@ STATUS readFileSegment(PCHAR filePath, BOOL binMode, PBYTE pBuffer, UINT64 offse // Set the offset and read the file content result = FSEEK(fp, (UINT32) offset, SEEK_SET); - CHK(result == 0 && (FREAD(pBuffer, (SIZE_T) readSize, 1, fp) == 1), STATUS_READ_FILE_FAILED); + + CHK(result == 0, STATUS_READ_FILE_FAILED); + + // fread would either return 1, i.e, the number of objects we've requested it to read + // or it would run into end-of-file / error. + // fread does not distinguish between end-of-file and error, + // and callers must use feof and ferror to determine which occurred. + if (FREAD(pBuffer, (SIZE_T) readSize, 1, fp) != 1) { + CHK(FEOF(fp), STATUS_READ_FILE_FAILED); + } CleanUp: diff --git a/src/utils/src/FileLogger.c b/src/utils/src/FileLogger.c index 4cb9f8ace..856410cfe 100644 --- a/src/utils/src/FileLogger.c +++ b/src/utils/src/FileLogger.c @@ -57,14 +57,15 @@ STATUS flushLogToFile(PFileLoggerParameters loggerParameters) VOID fileLoggerLogPrintFn(UINT32 level, PCHAR tag, PCHAR fmt, ...) { + UNUSED_PARAM(tag); CHAR logFmtString[MAX_LOG_FORMAT_LENGTH + 1]; INT32 offset = 0; STATUS status = STATUS_SUCCESS; FileLoggerParameters* levelLoggerParameters = NULL; va_list valist; + UINT32 logLevel = GET_LOGGER_LOG_LEVEL(); - UNUSED_PARAM(tag); - if (level >= GET_LOGGER_LOG_LEVEL() && gFileLogger != NULL) { + if (logLevel != LOG_LEVEL_SILENT && level >= logLevel && gFileLogger != NULL) { MUTEX_LOCK(gFileLogger->lock); addLogMetadata(logFmtString, (UINT32) ARRAY_SIZE(logFmtString), fmt, level); diff --git a/src/utils/src/Logger.c b/src/utils/src/Logger.c index 059923058..a09952090 100644 --- a/src/utils/src/Logger.c +++ b/src/utils/src/Logger.c @@ -2,7 +2,7 @@ static volatile SIZE_T gLoggerLogLevel = LOG_LEVEL_WARN; -PCHAR getLogLevelStr(UINT32 loglevel) +const PCHAR getLogLevelStr(UINT32 loglevel) { switch (loglevel) { case LOG_LEVEL_VERBOSE: @@ -24,7 +24,7 @@ PCHAR getLogLevelStr(UINT32 loglevel) } } -VOID addLogMetadata(PCHAR buffer, UINT32 bufferLen, PCHAR fmt, UINT32 logLevel) +VOID addLogMetadata(const PCHAR buffer, UINT32 bufferLen, const PCHAR fmt, UINT32 logLevel) { UINT32 timeStrLen = 0; /* space for "yyyy-mm-dd HH:MM:SS.MMMMMM" + space + null */ @@ -56,12 +56,12 @@ VOID addLogMetadata(PCHAR buffer, UINT32 bufferLen, PCHAR fmt, UINT32 logLevel) // // Default logger function // -VOID defaultLogPrint(UINT32 level, PCHAR tag, PCHAR fmt, ...) +VOID defaultLogPrint(UINT32 level, const PCHAR tag, const PCHAR fmt, ...) { CHAR logFmtString[MAX_LOG_FORMAT_LENGTH + 1]; UINT32 logLevel = GET_LOGGER_LOG_LEVEL(); UNUSED_PARAM(tag); - if (level >= logLevel) { + if (logLevel != LOG_LEVEL_SILENT && level >= logLevel) { addLogMetadata(logFmtString, (UINT32) ARRAY_SIZE(logFmtString), fmt, level); va_list valist; diff --git a/src/utils/src/Thread.c b/src/utils/src/Thread.c index 0cfe68942..b4fa7f2ec 100644 --- a/src/utils/src/Thread.c +++ b/src/utils/src/Thread.c @@ -122,7 +122,7 @@ PUBLIC_API STATUS defaultDetachThread(TID threadId) PUBLIC_API STATUS defaultGetThreadName(TID thread, PCHAR name, UINT32 len) { - UINT32 retValue; + INT32 retValue; if (NULL == name) { return STATUS_NULL_ARG; diff --git a/src/utils/src/Threadpool.c b/src/utils/src/Threadpool.c index 6457a04ff..ae6ea805b 100644 --- a/src/utils/src/Threadpool.c +++ b/src/utils/src/Threadpool.c @@ -2,8 +2,12 @@ // per thread in threadpool typedef struct __ThreadData { + // Informs us the state of the threadpool object volatile ATOMIC_BOOL terminate; + // The threadpool we belong to (may have been deleted) PThreadpool pThreadpool; + // Must be locked before changing terminate, as a result this ensures us + // that the threadpool as not been deleted while we hold this lock. MUTEX dataMutex; } ThreadData, *PThreadData; @@ -12,6 +16,26 @@ typedef struct TaskData { PVOID customData; } TaskData, *PTaskData; +typedef struct TerminationTask { + MUTEX mutex; + PSIZE_T pCount; + SEMAPHORE_HANDLE semaphore; +} TerminationTask, *PTerminationTask; + +PVOID threadpoolTermination(PVOID data) +{ + PTerminationTask task = (PTerminationTask) data; + PSIZE_T pCount = NULL; + if (task != NULL) { + pCount = task->pCount; + MUTEX_LOCK(task->mutex); + semaphoreRelease(task->semaphore); + (*pCount)++; + MUTEX_UNLOCK(task->mutex); + } + return 0; +} + PVOID threadpoolActor(PVOID data) { PThreadData pThreadData = (PThreadData) data; @@ -25,29 +49,38 @@ PVOID threadpoolActor(PVOID data) if (pThreadData == NULL) { DLOGE("Threadpool actor unable to start, threaddata is NULL"); - return 0; + return NULL; } // attempt to acquire thread mutex, if we cannot it means the threadpool has already been // destroyed. Quickly exit if (MUTEX_TRYLOCK(pThreadData->dataMutex)) { - pThreadpool = pThreadData->pThreadpool; + if (!ATOMIC_LOAD_BOOL(&pThreadData->terminate)) { + pThreadpool = pThreadData->pThreadpool; - if (pThreadpool == NULL) { - DLOGE("Threadpool actor unable to start, threadpool is NULL"); - return 0; - } + if (pThreadpool == NULL) { + DLOGE("Threadpool actor unable to start, threadpool is NULL"); + return NULL; + } - pQueue = pThreadpool->taskQueue; + pQueue = pThreadpool->taskQueue; + } else { + finished = TRUE; + } MUTEX_UNLOCK(pThreadData->dataMutex); } else { finished = TRUE; } // This actor will now wait for a task to be added to the queue, and then execute that task - // when the task is complete it will check if the we're beyond our min threshhold of threads + // when the task is complete it will check if the we're beyond our min threshold of threads // to determine whether it should exit or wait for another task. while (!finished) { + // This lock exists to protect the atomic increment after the terminate check. + // There is a data-race condition that can result in an increment after the Threadpool + // has been deleted + MUTEX_LOCK(pThreadData->dataMutex); + // ThreadData is allocated separately from the Threadpool. // The Threadpool will set terminate to false before the threadpool is free. // This way the thread actors can avoid accessing the Threadpool after termination. @@ -56,20 +89,37 @@ PVOID threadpoolActor(PVOID data) if (safeBlockingQueueDequeue(pQueue, &item) == STATUS_SUCCESS) { pTask = (PTaskData) item; ATOMIC_DECREMENT(&pThreadData->pThreadpool->availableThreads); + MUTEX_UNLOCK(pThreadData->dataMutex); if (pTask != NULL) { pTask->function(pTask->customData); SAFE_MEMFREE(pTask); } - // got an error, but not terminating, so fixup available thread count - } else if (!ATOMIC_LOAD_BOOL(&pThreadData->terminate)) { + } else { ATOMIC_DECREMENT(&pThreadData->pThreadpool->availableThreads); + MUTEX_UNLOCK(pThreadData->dataMutex); } + } else { + finished = TRUE; + MUTEX_UNLOCK(pThreadData->dataMutex); + break; } - if (MUTEX_TRYLOCK(pThreadData->dataMutex)) { + MUTEX_LOCK(pThreadData->dataMutex); + if (ATOMIC_LOAD_BOOL(&pThreadData->terminate)) { + MUTEX_UNLOCK(pThreadData->dataMutex); + } else { // Threadpool is active - lock its mutex MUTEX_LOCK(pThreadpool->listMutex); + // if threadpool is in teardown, release this mutex and go to queue. + // We don't want to be the one to remove this actor from the list in the event + // of teardown. + if (ATOMIC_LOAD_BOOL(&pThreadpool->terminate)) { + MUTEX_UNLOCK(pThreadpool->listMutex); + MUTEX_UNLOCK(pThreadData->dataMutex); + continue; + } + // Check that there aren't any pending tasks. if (safeBlockingQueueIsEmpty(pQueue, &taskQueueEmpty) == STATUS_SUCCESS) { if (taskQueueEmpty) { @@ -78,39 +128,26 @@ PVOID threadpoolActor(PVOID data) if (stackQueueGetCount(pThreadpool->threadList, &count) == STATUS_SUCCESS) { if (count > pThreadpool->minThreads) { finished = TRUE; - if (stackQueueRemoveItem(pThreadpool->threadList, pThreadData) != STATUS_SUCCESS) { + if (stackQueueRemoveItem(pThreadpool->threadList, (UINT64) pThreadData) != STATUS_SUCCESS) { DLOGE("Failed to remove thread data from threadpool"); } } } } } - // this is a redundant safety check. To get here the threadpool must be being - // actively being destroyed, but it was unable to acquire our lock so entered a - // sleep spin check to allow this thread to finish. However somehow the task queue - // was not empty, so we ended up here. This check forces us to still exit gracefully - // in the event somehow the queue is not empty. - else if (ATOMIC_LOAD_BOOL(&pThreadData->terminate)) { - finished = TRUE; - if (stackQueueRemoveItem(pThreadpool->threadList, pThreadData) != STATUS_SUCCESS) { - DLOGE("Failed to remove thread data from threadpool"); - } - } MUTEX_UNLOCK(pThreadpool->listMutex); MUTEX_UNLOCK(pThreadData->dataMutex); - } else { - // couldn't lock our mutex, which means Threadpool locked this mutex to indicate - // Threadpool has been deleted. - // - // Unlock mutex and free ThreadData - MUTEX_UNLOCK(pThreadData->dataMutex); - finished = TRUE; } } + // now that we've released the listMutex, we can do an actual MUTEX_LOCK to ensure the + // threadpool has finished using pThreadData + MUTEX_LOCK(pThreadData->dataMutex); + MUTEX_UNLOCK(pThreadData->dataMutex); + // we assume we've already been removed from the threadList MUTEX_FREE(pThreadData->dataMutex); SAFE_MEMFREE(pThreadData); - return 0; + return NULL; } /** @@ -121,25 +158,20 @@ STATUS threadpoolCreate(PThreadpool* ppThreadpool, UINT32 minThreads, UINT32 max STATUS retStatus = STATUS_SUCCESS; UINT32 i = 0; PThreadpool pThreadpool = NULL; - BOOL poolCreated = FALSE, mutexCreated = FALSE, listCreated = FALSE, queueCreated = FALSE; CHK(ppThreadpool != NULL, STATUS_NULL_ARG); CHK(minThreads <= maxThreads && minThreads > 0 && maxThreads > 0, STATUS_INVALID_ARG); pThreadpool = (PThreadpool) MEMCALLOC(1, SIZEOF(Threadpool)); CHK(pThreadpool != NULL, STATUS_NOT_ENOUGH_MEMORY); - poolCreated = TRUE; ATOMIC_STORE_BOOL(&pThreadpool->terminate, FALSE); ATOMIC_STORE(&pThreadpool->availableThreads, 0); pThreadpool->listMutex = MUTEX_CREATE(FALSE); - mutexCreated = TRUE; CHK_STATUS(safeBlockingQueueCreate(&pThreadpool->taskQueue)); - queueCreated = TRUE; CHK_STATUS(stackQueueCreate(&pThreadpool->threadList)); - listCreated = TRUE; pThreadpool->minThreads = minThreads; pThreadpool->maxThreads = maxThreads; @@ -151,18 +183,7 @@ STATUS threadpoolCreate(PThreadpool* ppThreadpool, UINT32 minThreads, UINT32 max CleanUp: if (STATUS_FAILED(retStatus)) { - if (mutexCreated) { - MUTEX_FREE(pThreadpool->listMutex); - } - if (listCreated) { - stackQueueFree(pThreadpool->threadList); - } - if (queueCreated) { - safeBlockingQueueFree(pThreadpool->taskQueue); - } - if (poolCreated) { - SAFE_MEMFREE(pThreadpool); - } + threadpoolFree(pThreadpool); } return retStatus; } @@ -175,7 +196,7 @@ STATUS threadpoolInternalCreateThread(PThreadpool pThreadpool) { STATUS retStatus = STATUS_SUCCESS; PThreadData data = NULL; - BOOL locked = FALSE; + BOOL locked = FALSE, dataCreated = FALSE, mutexCreated = FALSE; TID thread; CHK(pThreadpool != NULL, STATUS_NULL_ARG); @@ -186,23 +207,37 @@ STATUS threadpoolInternalCreateThread(PThreadpool pThreadpool) data = (PThreadData) MEMCALLOC(1, SIZEOF(ThreadData)); CHK(data != NULL, STATUS_NOT_ENOUGH_MEMORY); + dataCreated = TRUE; data->dataMutex = MUTEX_CREATE(FALSE); + mutexCreated = TRUE; data->pThreadpool = pThreadpool; + ATOMIC_STORE_BOOL(&data->terminate, FALSE); CHK_STATUS(stackQueueEnqueue(pThreadpool->threadList, (UINT64) data)); MUTEX_UNLOCK(pThreadpool->listMutex); locked = FALSE; - THREAD_CREATE(&thread, threadpoolActor, data); - THREAD_DETACH(thread); + CHK_STATUS(THREAD_CREATE(&thread, threadpoolActor, (PVOID) data)); + CHK_STATUS(THREAD_DETACH(thread)); CleanUp: if (locked) { MUTEX_UNLOCK(pThreadpool->listMutex); } + // If logic changes such that it's possible successfully enqueue data but not create the thread + // We may attempt a double free. Right now it's fine. + if (STATUS_FAILED(retStatus)) { + if (mutexCreated) { + MUTEX_FREE(data->dataMutex); + } + if (dataCreated) { + SAFE_MEMFREE(data); + } + } + return retStatus; } @@ -270,7 +305,14 @@ STATUS threadpoolFree(PThreadpool pThreadpool) STATUS retStatus = STATUS_SUCCESS; StackQueueIterator iterator; PThreadData item = NULL; - BOOL finished = FALSE, taskQueueEmpty = FALSE; + UINT64 data; + UINT32 threadCount, i = 0; + BOOL finished = FALSE, taskQueueEmpty = FALSE, listMutexLocked = FALSE, tempMutexLocked = FALSE; + SIZE_T sentTerminationTasks = 0, finishedTerminationTasks = 0; + MUTEX tempMutex; + SEMAPHORE_HANDLE tempSemaphore; + TerminationTask terminateTask; + PTaskData pTask = NULL; CHK(pThreadpool != NULL, STATUS_NULL_ARG); // Threads are not forced to finish their tasks. If the user has assigned @@ -282,6 +324,17 @@ STATUS threadpoolFree(PThreadpool pThreadpool) // set terminate flag of pool -- no new threads/items can be added now ATOMIC_STORE_BOOL(&pThreadpool->terminate, TRUE); + // This is used to block threadpool actors on a task so that they release all + // mutexes the destructor will need to acquire to teardown gracefully. + tempMutex = MUTEX_CREATE(FALSE); + MUTEX_LOCK(tempMutex); + CHK_STATUS(semaphoreEmptyCreate(pThreadpool->maxThreads, &tempSemaphore)); + tempMutexLocked = TRUE; + + terminateTask.mutex = tempMutex; + terminateTask.semaphore = tempSemaphore; + terminateTask.pCount = &finishedTerminationTasks; + CHK_STATUS(safeBlockingQueueIsEmpty(pThreadpool->taskQueue, &taskQueueEmpty)); if (!taskQueueEmpty) { CHK_STATUS(safeBlockingQueueClear(pThreadpool->taskQueue, TRUE)); @@ -290,6 +343,7 @@ STATUS threadpoolFree(PThreadpool pThreadpool) while (!finished) { // lock list mutex MUTEX_LOCK(pThreadpool->listMutex); + listMutexLocked = TRUE; do { // iterate on list @@ -298,22 +352,35 @@ STATUS threadpoolFree(PThreadpool pThreadpool) finished = TRUE; break; } - retStatus = stackQueueIteratorGetItem(iterator, &item); - // set terminate flag of item - ATOMIC_STORE_BOOL(&item->terminate, TRUE); + CHK_STATUS(stackQueueIteratorGetItem(iterator, &data)); + item = (PThreadData) data; + + if (item == NULL) { + DLOGW("NULL thread data present on threadpool."); + if (stackQueueRemoveItem(pThreadpool->threadList, data) != STATUS_SUCCESS) { + DLOGE("Failed to remove NULL thread data from threadpool"); + } + // We use trylock to avoid a potential deadlock with the actor. The destructor needs + // to lock listMutex and then dataMutex, but the actor locks dataMutex and then listMutex. + } else if (MUTEX_TRYLOCK(item->dataMutex)) { + // set terminate flag of item + ATOMIC_STORE_BOOL(&item->terminate, TRUE); - // attempt to lock mutex of item - if (MUTEX_TRYLOCK(item->dataMutex)) { // when we acquire the lock, remove the item from the list. Its thread will free it. - if (stackQueueRemoveItem(pThreadpool->threadList, item) != STATUS_SUCCESS) { + if (stackQueueRemoveItem(pThreadpool->threadList, data) != STATUS_SUCCESS) { DLOGE("Failed to remove thread data from threadpool"); } + MUTEX_UNLOCK(item->dataMutex); } else { - // if the mutex is taken, unlock list mutex, sleep 10 ms and 'start over' + // if the mutex is taken, give each thread a waiting task, unlock list mutex, sleep 10 ms and 'start over' + // + // The reasoning here is that the threadActors acquire their mutex during 2 operations: + // + // 1. While waiting on the queue, so we need to publish a sleep task to the queue to get the actors + // to release the mutex. // - // The reasoning here is that the threadActors only acquire their mutex to check - // for termination, after acquiring their mutex they need the list mutex to evaluate + // 2. to checkfor termination, after acquiring their mutex they need the list mutex to evaluate // the current count and determine if they should exit or wait on the taskQueue. // // Therefore if we currently have the list mutex, but cannot acquire the item mutex they @@ -322,18 +389,58 @@ STATUS threadpoolFree(PThreadpool pThreadpool) // the termination flag we set earlier and will exit. // // When we unlock and sleep we give them + CHK_STATUS(stackQueueGetCount(pThreadpool->threadList, &threadCount)); + + for (i = 0; i < threadCount; i++) { + CHK_STATUS(threadpoolInternalCreateTask(pThreadpool, threadpoolTermination, &terminateTask)); + sentTerminationTasks++; + } break; } } while (1); MUTEX_UNLOCK(pThreadpool->listMutex); + listMutexLocked = FALSE; if (!finished) { // the aforementioned sleep - THREAD_SLEEP(10 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + THREAD_SLEEP(5 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); } } + // in the event that we sent termination tasks, we now need to clean up all those + // tasks so we can safely free the associated mutex. + while (finishedTerminationTasks < sentTerminationTasks) { + MUTEX_UNLOCK(tempMutex); + tempMutexLocked = FALSE; + + // if there are still items in the queue, then we need to clear them + CHK_STATUS(safeBlockingQueueGetCount(pThreadpool->taskQueue, &i)); + + if (i > 0 && safeBlockingQueueDequeue(pThreadpool->taskQueue, &data) == STATUS_SUCCESS) { + pTask = (PTaskData) data; + if (pTask != NULL) { + pTask->function(pTask->customData); + SAFE_MEMFREE(pTask); + } + } + semaphoreAcquire(tempSemaphore, INFINITE_TIME_VALUE); + MUTEX_LOCK(tempMutex); + tempMutexLocked = TRUE; + } + +CleanUp: + + if (tempMutexLocked) { + MUTEX_UNLOCK(tempMutex); + } + + if (listMutexLocked) { + MUTEX_UNLOCK(pThreadpool->listMutex); + } + // now free all the memory + MUTEX_FREE(tempMutex); + semaphoreFree(&tempSemaphore); MUTEX_FREE(pThreadpool->listMutex); stackQueueFree(pThreadpool->threadList); @@ -341,8 +448,6 @@ STATUS threadpoolFree(PThreadpool pThreadpool) safeBlockingQueueFree(pThreadpool->taskQueue); SAFE_MEMFREE(pThreadpool); -CleanUp: - return retStatus; } @@ -386,7 +491,7 @@ STATUS threadpoolInternalInactiveThreadCount(PThreadpool pThreadpool, PSIZE_T pC CHK_STATUS(safeBlockingQueueGetCount(pThreadpool->taskQueue, &pendingTasks)); unblockedThreads = (SIZE_T) ATOMIC_LOAD(&pThreadpool->availableThreads); - *pCount = unblockedThreads - (SIZE_T) pendingTasks; + *pCount = unblockedThreads > (SIZE_T) pendingTasks ? (unblockedThreads - (SIZE_T) pendingTasks) : 0; CleanUp: return retStatus; diff --git a/src/utils/src/Time.c b/src/utils/src/Time.c index c2fe724ed..f0cc0ba20 100644 --- a/src/utils/src/Time.c +++ b/src/utils/src/Time.c @@ -3,6 +3,21 @@ getTime globalGetTime = defaultGetTime; getTime globalGetRealTime = defaultGetTime; +#if !(defined _WIN32 || defined _WIN64 || defined __CYGWIN__) + +getTmTime globalGetThreadSafeTmTime = defaultGetThreadSafeTmTime; +pthread_mutex_t globalGmTimeMutex = PTHREAD_MUTEX_INITIALIZER; + +struct tm* defaultGetThreadSafeTmTime(const time_t* timer) +{ + struct tm* retVal; + pthread_mutex_lock(&globalGmTimeMutex); + retVal = GMTIME(timer); + pthread_mutex_unlock(&globalGmTimeMutex); + return retVal; +} +#endif + STATUS generateTimestampStrInMilliseconds(PCHAR formatStr, PCHAR pDestBuffer, UINT32 destBufferLen, PUINT32 pFormattedStrLen) { STATUS retStatus = STATUS_SUCCESS; @@ -31,7 +46,7 @@ STATUS generateTimestampStrInMilliseconds(PCHAR formatStr, PCHAR pDestBuffer, UI time_t seconds = milliseconds100ns / 1000; // Convert time_t to UTC tm struct - timeinfo = GMTIME(&seconds); + timeinfo = GMTIME_THREAD_SAFE(&seconds); millisecondVal = milliseconds100ns % 1000; @@ -39,7 +54,7 @@ STATUS generateTimestampStrInMilliseconds(PCHAR formatStr, PCHAR pDestBuffer, UI struct timeval tv; gettimeofday(&tv, NULL); // Get current time - timeinfo = GMTIME(&(tv.tv_sec)); // Convert to broken-down time + timeinfo = GMTIME_THREAD_SAFE(&(tv.tv_sec)); // Convert to broken-down time millisecondVal = tv.tv_usec / 1000; // Convert microseconds to milliseconds @@ -47,7 +62,7 @@ STATUS generateTimestampStrInMilliseconds(PCHAR formatStr, PCHAR pDestBuffer, UI struct timespec nowTime; clock_gettime(CLOCK_REALTIME, &nowTime); - timeinfo = GMTIME(&(nowTime.tv_sec)); + timeinfo = GMTIME_THREAD_SAFE(&(nowTime.tv_sec)); millisecondVal = nowTime.tv_nsec / (HUNDREDS_OF_NANOS_IN_A_MILLISECOND * DEFAULT_TIME_UNIT_IN_NANOS); // Convert nanoseconds to milliseconds #endif @@ -79,7 +94,7 @@ STATUS generateTimestampStr(UINT64 timestamp, PCHAR formatStr, PCHAR pDestBuffer formattedStrLen = 0; *pFormattedStrLen = 0; - formattedStrLen = (UINT32) STRFTIME(pDestBuffer, destBufferLen, formatStr, GMTIME(×tampSeconds)); + formattedStrLen = (UINT32) STRFTIME(pDestBuffer, destBufferLen, formatStr, GMTIME_THREAD_SAFE(×tampSeconds)); CHK(formattedStrLen != 0, STATUS_STRFTIME_FALIED); pDestBuffer[formattedStrLen] = '\0'; diff --git a/src/utils/src/TimerQueue.c b/src/utils/src/TimerQueue.c index 94482c029..48c41de56 100644 --- a/src/utils/src/TimerQueue.c +++ b/src/utils/src/TimerQueue.c @@ -313,6 +313,33 @@ STATUS timerQueueUpdateTimerPeriod(TIMER_QUEUE_HANDLE handle, UINT64 customData, return retStatus; } +PUBLIC_API STATUS timerQueueKick(TIMER_QUEUE_HANDLE handle, UINT32 timerId) +{ + ENTERS(); + STATUS retStatus = STATUS_SUCCESS; + PTimerQueue pTimerQueue = FROM_TIMER_QUEUE_HANDLE(handle); + BOOL locked = FALSE; + + CHK(pTimerQueue != NULL, STATUS_NULL_ARG); + CHK(timerId < pTimerQueue->maxTimerCount, STATUS_INVALID_ARG); + + MUTEX_LOCK(pTimerQueue->executorLock); + locked = TRUE; + + // change invoke time & signal to trigger timer. + pTimerQueue->pTimers[timerId].invokeTime = 0; + CVAR_SIGNAL(pTimerQueue->executorCvar); + +CleanUp: + + if (locked) { + MUTEX_UNLOCK(pTimerQueue->executorLock); + } + + LEAVES(); + return retStatus; +} + PUBLIC_API STATUS timerQueueShutdown(TIMER_QUEUE_HANDLE handle) { ENTERS(); diff --git a/src/utils/tst/CMakeLists.txt b/src/utils/tst/CMakeLists.txt deleted file mode 100644 index 768217019..000000000 --- a/src/utils/tst/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required (VERSION 2.6) -project (utilsTest) -file (GLOB UtilsTestSources *.cpp) - -add_executable(${PROJECT_NAME} ${UtilsTestSources}) -if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - target_link_libraries(${PROJECT_NAME} utils gtest gtest_main rt) -else() - target_link_libraries(${PROJECT_NAME} utils gtest gtest_main) -endif() -add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/src/utils/tst/Threadpool.cpp b/src/utils/tst/Threadpool.cpp deleted file mode 100644 index 4431b4de5..000000000 --- a/src/utils/tst/Threadpool.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include "UtilTestFixture.h" -#include -#include - -class ThreadpoolFunctionalityTest : public UtilTestBase { -}; - -PVOID randomishTask(PVOID customData) { - THREAD_SLEEP((rand()%100 + 1) * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - return 0; -} - -PVOID exitOnTeardownTask(PVOID customData) { - BOOL * pTerminate = (BOOL*)customData; - while(!*pTerminate) { - THREAD_SLEEP(20 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - } - return 0; -} - -TEST_F(ThreadpoolFunctionalityTest, CreateDestroyTest) -{ - PThreadpool pThreadpool = NULL; - EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, 1, 1)); - EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); - - //wait for threads to exit before test ends - THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - - EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, 1, 2)); - EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); - - //wait for threads to exit before test ends - THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - - EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, 1, 1)); - EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); - - //wait for threads to exit before test ends - THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); -} - -TEST_F(ThreadpoolFunctionalityTest, BasicTryAddTest) -{ - PThreadpool pThreadpool = NULL; - BOOL terminate = FALSE; - srand(GETTIME()); - EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, 1, 1)); - - //sleep for a little. Create asynchronously detaches threads, so using TryAdd too soon can - //fail if the threads are not yet ready. - THREAD_SLEEP(10 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - - EXPECT_EQ(STATUS_SUCCESS, threadpoolTryAdd(pThreadpool, exitOnTeardownTask, &terminate)); - EXPECT_EQ(STATUS_THREADPOOL_MAX_COUNT, threadpoolTryAdd(pThreadpool, exitOnTeardownTask, &terminate)); - terminate = TRUE; - EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); - - //wait for threads to exit before test ends - THREAD_SLEEP(300 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); -} - -TEST_F(ThreadpoolFunctionalityTest, BasicPushTest) -{ - PThreadpool pThreadpool = NULL; - BOOL terminate = FALSE; - UINT32 count = 0; - srand(GETTIME()); - EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, 1, 2)); - EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); - EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); - EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); - EXPECT_EQ(STATUS_SUCCESS, threadpoolTotalThreadCount(pThreadpool, &count)); - EXPECT_EQ(count, 2); - terminate = TRUE; - EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); - - //wait for threads to exit before test ends - THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); -} - -TEST_F(ThreadpoolFunctionalityTest, GetThreadCountTest) -{ - PThreadpool pThreadpool = NULL; - UINT32 count = 0; - BOOL terminate = FALSE; - srand(GETTIME()); - const UINT32 max = 10; - //accepted race condition where min is 1, threadpoolPush can create a new thread - //before the first thread is ready to accept tasks - UINT32 min = rand()%(max/2) + 2; - EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, min, max)); - //let threads get ready so new threads aren't created for this task - THREAD_SLEEP(200 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - EXPECT_EQ(STATUS_SUCCESS, threadpoolTotalThreadCount(pThreadpool, &count)); - EXPECT_EQ(count, min); - - for(UINT32 i = 0; i < min; i++) { - EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); - } - //no new threads created - EXPECT_EQ(STATUS_SUCCESS, threadpoolTotalThreadCount(pThreadpool, &count)); - EXPECT_EQ(count, min); - - //another thread needed - EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); - EXPECT_EQ(STATUS_SUCCESS, threadpoolTotalThreadCount(pThreadpool, &count)); - EXPECT_EQ(count, min+1); - - terminate = TRUE; - EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); - - //wait for threads to exit before test ends - THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); -} - -TEST_F(ThreadpoolFunctionalityTest, ThreadsExitGracefullyAfterThreadpoolFreeTest) -{ - PThreadpool pThreadpool = NULL; - UINT32 count = 0; - BOOL terminate = FALSE; - srand(GETTIME()); - const UINT32 max = 10; - UINT32 min = rand()%(max/2) + 2; - EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, min, max)); - for(UINT32 i = 0; i < min; i++) { - EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); - } - for(UINT32 i = min; i < max; i++) { - EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, randomishTask, NULL)); - } - EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); - //now threads will exit after threadpoolFree - terminate = TRUE; - - //wait for threads to exit before test ends - THREAD_SLEEP(150 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); -} - -typedef struct ThreadpoolUser { - PThreadpool pThreadpool; - volatile ATOMIC_BOOL usable; -}; - -PVOID createTasks(PVOID customData) { - ThreadpoolUser * user = (ThreadpoolUser*)customData; - PThreadpool pThreadpool = user->pThreadpool; - auto iterations = rand()%20; - - for(auto i = 0; i < iterations; i++) { - THREAD_SLEEP((rand()%5 + 1) * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - //allowed to fail as we may delete the threadpool early - if (ATOMIC_LOAD_BOOL(&user->usable)) { - if (threadpoolPush(pThreadpool, randomishTask, NULL) != STATUS_SUCCESS) { - break; - } - } - else { - break; - } - } - return 0; -} - -TEST_F(ThreadpoolFunctionalityTest, MultithreadUseTest) -{ - PThreadpool pThreadpool = NULL; - ThreadpoolUser user; - UINT32 count = 0; - BOOL terminate = FALSE; - srand(GETTIME()); - const UINT32 max = 10; - UINT32 min = rand()%(max/2) + 2; - TID thread1, thread2; - EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, min, max)); - user.pThreadpool = pThreadpool; - ATOMIC_STORE_BOOL(&user.usable, TRUE); - THREAD_CREATE(&thread1, createTasks, &user); - THREAD_CREATE(&thread2, createTasks, &user); - - THREAD_SLEEP((rand()%50 + 50) * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); - ATOMIC_STORE_BOOL(&user.usable, FALSE); - EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); - - //wait for threads to exit before test ends to avoid false memory leak alarm - THREAD_SLEEP(250 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); -} diff --git a/src/view/CMakeLists.txt b/src/view/CMakeLists.txt index 7e21be9a9..05f62f549 100644 --- a/src/view/CMakeLists.txt +++ b/src/view/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.6.3) -project(view) +project(view LANGUAGES C) kinesis_video_library_setup(${PROJECT_NAME}) target_link_libraries(view heap utils) kinesis_video_library_install() diff --git a/src/view/include/com/amazonaws/kinesis/video/view/Include.h b/src/view/include/com/amazonaws/kinesis/video/view/Include.h index 407362495..4e2613007 100644 --- a/src/view/include/com/amazonaws/kinesis/video/view/Include.h +++ b/src/view/include/com/amazonaws/kinesis/video/view/Include.h @@ -68,14 +68,14 @@ extern "C" { /** * Macros for checking/setting/clearing for various flags */ -#define CHECK_ITEM_FRAGMENT_START(f) (((f) &ITEM_FLAG_FRAGMENT_START) != ITEM_FLAG_NONE) -#define CHECK_ITEM_BUFFERING_ACK(f) (((f) &ITEM_FLAG_BUFFERING_ACK) != ITEM_FLAG_NONE) -#define CHECK_ITEM_RECEIVED_ACK(f) (((f) &ITEM_FLAG_RECEIVED_ACK) != ITEM_FLAG_NONE) -#define CHECK_ITEM_STREAM_START(f) (((f) &ITEM_FLAG_STREAM_START) != ITEM_FLAG_NONE) -#define CHECK_ITEM_FRAGMENT_END(f) (((f) &ITEM_FLAG_FRAGMENT_END) != ITEM_FLAG_NONE) -#define CHECK_ITEM_PERSISTED_ACK(f) (((f) &ITEM_FLAG_PERSISTED_ACK) != ITEM_FLAG_NONE) -#define CHECK_ITEM_SKIP_ITEM(f) (((f) &ITEM_FLAG_SKIP_ITEM) != ITEM_FLAG_NONE) -#define CHECK_ITEM_STREAM_START_DEBUG(f) (((f) &ITEM_FLAG_STREAM_START_DEBUG) != ITEM_FLAG_NONE) +#define CHECK_ITEM_FRAGMENT_START(f) (((f) & ITEM_FLAG_FRAGMENT_START) != ITEM_FLAG_NONE) +#define CHECK_ITEM_BUFFERING_ACK(f) (((f) & ITEM_FLAG_BUFFERING_ACK) != ITEM_FLAG_NONE) +#define CHECK_ITEM_RECEIVED_ACK(f) (((f) & ITEM_FLAG_RECEIVED_ACK) != ITEM_FLAG_NONE) +#define CHECK_ITEM_STREAM_START(f) (((f) & ITEM_FLAG_STREAM_START) != ITEM_FLAG_NONE) +#define CHECK_ITEM_FRAGMENT_END(f) (((f) & ITEM_FLAG_FRAGMENT_END) != ITEM_FLAG_NONE) +#define CHECK_ITEM_PERSISTED_ACK(f) (((f) & ITEM_FLAG_PERSISTED_ACK) != ITEM_FLAG_NONE) +#define CHECK_ITEM_SKIP_ITEM(f) (((f) & ITEM_FLAG_SKIP_ITEM) != ITEM_FLAG_NONE) +#define CHECK_ITEM_STREAM_START_DEBUG(f) (((f) & ITEM_FLAG_STREAM_START_DEBUG) != ITEM_FLAG_NONE) #define SET_ITEM_FRAGMENT_START(f) ((f) |= ITEM_FLAG_FRAGMENT_START) #define SET_ITEM_BUFFERING_ACK(f) ((f) |= ITEM_FLAG_BUFFERING_ACK) @@ -96,7 +96,7 @@ extern "C" { #define CLEAR_ITEM_STREAM_START_DEBUG(f) ((f) &= ~ITEM_FLAG_STREAM_START_DEBUG) #define GET_ITEM_DATA_OFFSET(f) ((UINT16) ((f) >> 16)) -#define SET_ITEM_DATA_OFFSET(f, o) ((f) = ((f) &0x0000ffff) | (((UINT16) (o)) << 16)) +#define SET_ITEM_DATA_OFFSET(f, o) ((f) = ((f) & 0x0000ffff) | (((UINT16) (o)) << 16)) /** * This is a sentinel indicating an invalid index value diff --git a/src/view/src/ContentView.c b/src/view/src/ContentView.c index 9f035dc1a..9a3f2befb 100644 --- a/src/view/src/ContentView.c +++ b/src/view/src/ContentView.c @@ -153,6 +153,15 @@ STATUS contentViewGetNext(PContentView pContentView, PViewItem* ppItem) // Quick check if any items exist - early return CHK((pRollingView->head != pRollingView->tail) && (pRollingView->current != pRollingView->head), STATUS_CONTENT_VIEW_NO_MORE_ITEMS); + // current is greater than the header & head isn't currently in an overflow state. + if (pRollingView->current > pRollingView->head && pRollingView->head > pRollingView->tail) { + // current index has broken outside the rolling buffer + // reset the index, and return error + DLOGI("Current index overflow state discovered! Resetting"); + pRollingView->current = pRollingView->tail; + CHK(FALSE, STATUS_CONTENT_VIEW_INVALID_INDEX); + } + // Get the current item pCurrent = GET_VIEW_ITEM_FROM_INDEX(pRollingView, pRollingView->current); @@ -322,16 +331,16 @@ STATUS contentViewRollbackCurrent(PContentView pContentView, UINT64 duration, BO break; } + // Terminate the loop if we reached the head + if (curIndex == pRollingView->head) { + break; + } + // Iterate forward curIndex++; // Set the current pRollingView->current = curIndex; - - // Terminate the loop if we reached the head - if (curIndex == pRollingView->head) { - break; - } } CleanUp: diff --git a/src/view/tst/CMakeLists.txt b/src/view/tst/CMakeLists.txt deleted file mode 100644 index 729b65606..000000000 --- a/src/view/tst/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -cmake_minimum_required (VERSION 2.6) -project (viewTest) -file (GLOB ViewTestSources *.cpp) - -add_executable(${PROJECT_NAME} ${ViewTestSources}) -target_link_libraries(${PROJECT_NAME} view) -target_link_libraries(${PROJECT_NAME} gtest gtest_main) - -add_test(${PROJECT_NAME} ${PROJECT_NAME}) diff --git a/tst/CMakeLists.txt b/tst/CMakeLists.txt new file mode 100644 index 000000000..403c10629 --- /dev/null +++ b/tst/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.6.3) +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") +include(Utilities) +project(pic_project_tests LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +if (OPEN_SRC_INSTALL_PREFIX) + find_package(GTest REQUIRED PATHS ${OPEN_SRC_INSTALL_PREFIX}) +else() + find_package(GTest REQUIRED) +endif() + +SET(GTEST_LIBNAME GTest::gtest) +if (TARGET GTest::GTest) + SET(GTEST_LIBNAME GTest::GTest) +endif() + +file(GLOB PIC_TEST_SOURCE_FILES "*/*.cpp") + +# the root CMakeLists uses add_definitions if ALIGNED_MEMORY_MODEL or FIXUP_ANNEX_B_TRAILING_NALU_ZERO is set +# https://cmake.org/cmake/help/latest/command/add_definitions.html we build the test project by calling add_directory(tst) +# hence those definitions will be propagated to the test project as well to match the pic build, hence a rebuild +# of pic inside of the test project is not necessary + +add_executable(kvspic_test ${PIC_TEST_SOURCE_FILES}) +target_link_libraries(kvspic_test kvspic ${GTEST_LIBNAME} ${CMAKE_DL_LIBS} Threads::Threads) +if(UNIX AND NOT APPLE) + # rt needed for clock_gettime + target_link_libraries(kvspic_test rt) +endif() + +add_test(${PROJECT_NAME} ${PROJECT_NAME}) \ No newline at end of file diff --git a/src/client/tst/AcksFunctionalityTest.cpp b/tst/client/AcksFunctionalityTest.cpp similarity index 99% rename from src/client/tst/AcksFunctionalityTest.cpp rename to tst/client/AcksFunctionalityTest.cpp index d1e441810..3e048812d 100644 --- a/src/client/tst/AcksFunctionalityTest.cpp +++ b/tst/client/AcksFunctionalityTest.cpp @@ -21,7 +21,7 @@ class AcksFunctionalityTest : public ClientTestBase, mStreamInfo.streamCaps.replayDuration = (UINT64) replayDuration; } }; - +#ifdef ALIGNED_MEMORY_MODEL //Submit various types of error ACKs, Ensure the rollback is done from the ACK error time. TEST_P(AcksFunctionalityTest, CheckRollbackFromErrorAckTime) { @@ -306,3 +306,4 @@ TEST_P(AcksFunctionalityTest, CreateStreamSubmitACKsTerminatedUploadHandle) { INSTANTIATE_TEST_SUITE_P(PermutatedStreamInfo, AcksFunctionalityTest, Combine(Values(STREAMING_TYPE_REALTIME, STREAMING_TYPE_OFFLINE), Values(0, 10 * HUNDREDS_OF_NANOS_IN_AN_HOUR), Bool(), Values(0, TEST_REPLAY_DURATION))); +#endif \ No newline at end of file diff --git a/src/client/tst/CallbacksAndPressuresFunctionalityTest.cpp b/tst/client/CallbacksAndPressuresFunctionalityTest.cpp similarity index 99% rename from src/client/tst/CallbacksAndPressuresFunctionalityTest.cpp rename to tst/client/CallbacksAndPressuresFunctionalityTest.cpp index 3df3558f5..fd39e5f61 100644 --- a/src/client/tst/CallbacksAndPressuresFunctionalityTest.cpp +++ b/tst/client/CallbacksAndPressuresFunctionalityTest.cpp @@ -22,7 +22,7 @@ class CallbacksAndPressuresFunctionalityTest : public ClientTestBase, mStreamInfo.streamCaps.replayDuration = (UINT64) replayDuration; } }; - +#ifdef ALIGNED_MEMORY_MODEL TEST_P(CallbacksAndPressuresFunctionalityTest, CreateStreamLatencyPressureCallbackCalledSuccess) { BOOL didPutFrame; UINT64 currentTime, streamStopTime; @@ -213,3 +213,4 @@ TEST_P(CallbacksAndPressuresFunctionalityTest, CheckBlockedOfflinePutFrameReturn INSTANTIATE_TEST_SUITE_P(PermutatedStreamInfo, CallbacksAndPressuresFunctionalityTest, Combine(Values(STREAMING_TYPE_REALTIME, STREAMING_TYPE_OFFLINE), Values(0, 10 * HUNDREDS_OF_NANOS_IN_AN_HOUR), Bool(), Values(0, TEST_REPLAY_DURATION))); +#endif \ No newline at end of file diff --git a/src/client/tst/ClientApiFunctionalityTest.cpp b/tst/client/ClientApiFunctionalityTest.cpp similarity index 100% rename from src/client/tst/ClientApiFunctionalityTest.cpp rename to tst/client/ClientApiFunctionalityTest.cpp diff --git a/src/client/tst/ClientApiTest.cpp b/tst/client/ClientApiTest.cpp similarity index 99% rename from src/client/tst/ClientApiTest.cpp rename to tst/client/ClientApiTest.cpp index 74d697f6b..8ed78b387 100644 --- a/src/client/tst/ClientApiTest.cpp +++ b/tst/client/ClientApiTest.cpp @@ -419,13 +419,19 @@ TEST_F(ClientApiTest, kinesisVideoClientCreateSync_Store_Alloc) mClientSyncMode = TRUE; mDeviceInfo.clientInfo.createClientTimeout = 20 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND; +#ifdef ALIGNED_MEMORY_MODEL mDeviceInfo.storageInfo.storageType = DEVICE_STORAGE_TYPE_IN_MEM_CONTENT_STORE_ALLOC; +#else + mDeviceInfo.storageInfo.storageType = DEVICE_STORAGE_TYPE_IN_MEM; +#endif EXPECT_EQ(STATUS_SUCCESS, createKinesisVideoClientSync(&mDeviceInfo, &mClientCallbacks, &clientHandle)); EXPECT_TRUE(IS_VALID_CLIENT_HANDLE(clientHandle)); +#ifdef ALIGNED_MEMORY_MODEL // Allocating another should fail EXPECT_NE(STATUS_SUCCESS, createKinesisVideoClientSync(&mDeviceInfo, &mClientCallbacks, &failedClientHandle)); EXPECT_FALSE(IS_VALID_CLIENT_HANDLE(failedClientHandle)); +#endif // Free the client and re-try EXPECT_EQ(STATUS_SUCCESS, freeKinesisVideoClient(&clientHandle)); diff --git a/src/client/tst/ClientFunctionalityTest.cpp b/tst/client/ClientFunctionalityTest.cpp similarity index 99% rename from src/client/tst/ClientFunctionalityTest.cpp rename to tst/client/ClientFunctionalityTest.cpp index af902c051..98367d8a7 100644 --- a/src/client/tst/ClientFunctionalityTest.cpp +++ b/tst/client/ClientFunctionalityTest.cpp @@ -20,6 +20,11 @@ class ClientFunctionalityTest : public ClientTestBase, mStreamInfo.streamCaps.streamingType = streamingType; mStreamInfo.streamCaps.fragmentAcks = enableAck; mStreamInfo.streamCaps.replayDuration = (UINT64) replayDuration; +#ifndef ALIGNED_MEMORY_MODEL + if (storageType == DEVICE_STORAGE_TYPE_IN_MEM_CONTENT_STORE_ALLOC) { + storageType = DEVICE_STORAGE_TYPE_IN_MEM; + } +#endif mDeviceInfo.storageInfo.storageType = storageType; } }; @@ -123,7 +128,7 @@ TEST_P(ClientFunctionalityTest, CreateClientCreateStreamSyncStopStreamFreeClient EXPECT_EQ(STATUS_SUCCESS, freeKinesisVideoClient(&mClientHandle)); EXPECT_TRUE(!IS_VALID_CLIENT_HANDLE(mClientHandle)); } - +#ifdef ALIGNED_MEMORY_MODEL //Create Producer, Create Streams, Await Ready, Put Frame, Free Producer TEST_P(ClientFunctionalityTest, CreateClientCreateStreamPutFrameFreeClient) { @@ -257,7 +262,7 @@ TEST_P(ClientFunctionalityTest, CreateClientCreateStreamSyncPutFrameStopStreamFr EXPECT_EQ(STATUS_SUCCESS, freeKinesisVideoClient(&mClientHandle)); EXPECT_TRUE(!IS_VALID_CLIENT_HANDLE(mClientHandle)); } - +#endif //Create producer, create streams sync, create same stream and fail, free client TEST_P(ClientFunctionalityTest, CreateClientCreateStreamSyncCreateSameStreamAndFailFreeClient) { diff --git a/src/client/tst/ClientTestFixture.cpp b/tst/client/ClientTestFixture.cpp similarity index 100% rename from src/client/tst/ClientTestFixture.cpp rename to tst/client/ClientTestFixture.cpp diff --git a/src/client/tst/ClientTestFixture.h b/tst/client/ClientTestFixture.h similarity index 99% rename from src/client/tst/ClientTestFixture.h rename to tst/client/ClientTestFixture.h index 95b23dab3..6ee6fcf0d 100644 --- a/src/client/tst/ClientTestFixture.h +++ b/tst/client/ClientTestFixture.h @@ -372,9 +372,11 @@ class ClientTestBase : public ::testing::Test { void initTestMembers() { UINT32 logLevel = 0; + STATUS retStatus = STATUS_SUCCESS; auto logLevelStr = GETENV("AWS_KVS_LOG_LEVEL"); if (logLevelStr != NULL) { - assert(STRTOUI32(logLevelStr, NULL, 10, &logLevel) == STATUS_SUCCESS); + retStatus = STRTOUI32(logLevelStr, NULL, 10, &logLevel); + ASSERT_EQ(retStatus, STATUS_SUCCESS); } // Zero things out @@ -923,9 +925,11 @@ class ClientTestBase : public ::testing::Test { virtual void SetUpWithoutClientCreation() { UINT32 logLevel = 0; + STATUS retStatus = STATUS_SUCCESS; auto logLevelStr = GETENV("AWS_KVS_LOG_LEVEL"); if (logLevelStr != NULL) { - assert(STRTOUI32(logLevelStr, NULL, 10, &logLevel) == STATUS_SUCCESS); + retStatus = STRTOUI32(logLevelStr, NULL, 10, &logLevel); + ASSERT_EQ(retStatus, STATUS_SUCCESS); SET_LOGGER_LOG_LEVEL(logLevel); } diff --git a/src/client/tst/FrameOrderCoordinatorTest.cpp b/tst/client/FrameOrderCoordinatorTest.cpp similarity index 100% rename from src/client/tst/FrameOrderCoordinatorTest.cpp rename to tst/client/FrameOrderCoordinatorTest.cpp diff --git a/src/client/tst/IntermittentProducerAutomaticStreamingTest.cpp b/tst/client/IntermittentProducerAutomaticStreamingTest.cpp similarity index 99% rename from src/client/tst/IntermittentProducerAutomaticStreamingTest.cpp rename to tst/client/IntermittentProducerAutomaticStreamingTest.cpp index d2f6824d1..b32b56f37 100644 --- a/src/client/tst/IntermittentProducerAutomaticStreamingTest.cpp +++ b/tst/client/IntermittentProducerAutomaticStreamingTest.cpp @@ -76,6 +76,8 @@ STATUS timerCallbackPreHook(UINT64 hookCustomData) return retStatus; }; +#ifdef ALIGNED_MEMORY_MODEL + TEST_P(IntermittentProducerAutomaticStreamingTest, ValidateTimerInvokedBeforeTime) { // Create new client so param value of callbackPeriod can be applied ASSERT_EQ(STATUS_SUCCESS, CreateClient()); @@ -138,7 +140,7 @@ TEST_P(IntermittentProducerAutomaticStreamingTest, ValidateTimerInvokedAfterFirs frame.frameData = temp; EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFrame(mStreamHandle, &frame)); - THREAD_SLEEP(INTERMITTENT_PRODUCER_TIMER_START_DELAY + 1.20 * mDeviceInfo.clientInfo.reservedCallbackPeriod); + THREAD_SLEEP(INTERMITTENT_PRODUCER_TIMER_START_DELAY + 1.50 * mDeviceInfo.clientInfo.reservedCallbackPeriod); EXPECT_EQ(2, ATOMIC_LOAD(&mTimerCallbackFuncCount)); } @@ -705,3 +707,5 @@ TEST_P(IntermittentProducerAutomaticStreamingTest, ValidateMultiStream) { INSTANTIATE_TEST_SUITE_P(PermutatedStreamInfo, IntermittentProducerAutomaticStreamingTest, Combine(Values(1000,2000,3000), Values(0, CLIENT_INFO_CURRENT_VERSION))); + +#endif \ No newline at end of file diff --git a/src/client/tst/IntermittentProducerFunctionalityTest.cpp b/tst/client/IntermittentProducerFunctionalityTest.cpp similarity index 99% rename from src/client/tst/IntermittentProducerFunctionalityTest.cpp rename to tst/client/IntermittentProducerFunctionalityTest.cpp index 823711111..8a5f172d0 100644 --- a/src/client/tst/IntermittentProducerFunctionalityTest.cpp +++ b/tst/client/IntermittentProducerFunctionalityTest.cpp @@ -21,7 +21,7 @@ class IntermittentProducerFunctionalityTest : public ClientTestBase, mStreamInfo.streamCaps.replayDuration = (UINT64) replayDuration; } }; - +#ifdef ALIGNED_MEMORY_MODEL TEST_P(IntermittentProducerFunctionalityTest, CreateSyncStreamWithLargeBufferAwaitForLastAckStopSyncFreeSuccess) { UINT64 currentTime, testTerminationTime, endPutFrameTime; BOOL didPutFrame, gotStreamData, submittedAck; @@ -132,3 +132,4 @@ TEST_P(IntermittentProducerFunctionalityTest, RepeatedCreateSyncStopSyncFree) INSTANTIATE_TEST_SUITE_P(PermutatedStreamInfo, IntermittentProducerFunctionalityTest, Combine(Values(STREAMING_TYPE_REALTIME, STREAMING_TYPE_OFFLINE), Values(0, 10 * HUNDREDS_OF_NANOS_IN_AN_HOUR), Bool(), Values(0, TEST_REPLAY_DURATION))); +#endif \ No newline at end of file diff --git a/src/client/tst/MockConsumer.cpp b/tst/client/MockConsumer.cpp similarity index 100% rename from src/client/tst/MockConsumer.cpp rename to tst/client/MockConsumer.cpp diff --git a/src/client/tst/MockConsumer.h b/tst/client/MockConsumer.h similarity index 100% rename from src/client/tst/MockConsumer.h rename to tst/client/MockConsumer.h diff --git a/src/client/tst/MockProducer.cpp b/tst/client/MockProducer.cpp similarity index 100% rename from src/client/tst/MockProducer.cpp rename to tst/client/MockProducer.cpp diff --git a/src/client/tst/MockProducer.h b/tst/client/MockProducer.h similarity index 100% rename from src/client/tst/MockProducer.h rename to tst/client/MockProducer.h diff --git a/src/client/tst/StateTransitionFunctionalityTest.cpp b/tst/client/StateTransitionFunctionalityTest.cpp similarity index 99% rename from src/client/tst/StateTransitionFunctionalityTest.cpp rename to tst/client/StateTransitionFunctionalityTest.cpp index b396c02d9..f717dd603 100644 --- a/src/client/tst/StateTransitionFunctionalityTest.cpp +++ b/tst/client/StateTransitionFunctionalityTest.cpp @@ -247,7 +247,7 @@ TEST_P(StateTransitionFunctionalityTest, ControlPlaneServiceCallExhaustRetry) TEST_AUTH_EXPIRATION)); freeKinesisVideoStream(&mStreamHandle); } - +#ifdef ALIGNED_MEMORY_MODEL // check that kinesisVideoStreamTerminated with certain service call results should move to getEndpoint state TEST_P(StateTransitionFunctionalityTest, StreamTerminatedAndGoToGetEndpointState) { @@ -573,6 +573,7 @@ TEST_P(StateTransitionFunctionalityTest, basicResetConnectionTest) { VerifyStopStreamSyncAndFree(); } +#endif TEST_P(StateTransitionFunctionalityTest, TestExecutionOfStreamStateMachineErrorHandlerOnErrors) { diff --git a/src/client/tst/StreamApiFunctionalityScenarioTest.cpp b/tst/client/StreamApiFunctionalityScenarioTest.cpp similarity index 99% rename from src/client/tst/StreamApiFunctionalityScenarioTest.cpp rename to tst/client/StreamApiFunctionalityScenarioTest.cpp index 329f62295..db63b94e8 100644 --- a/src/client/tst/StreamApiFunctionalityScenarioTest.cpp +++ b/tst/client/StreamApiFunctionalityScenarioTest.cpp @@ -22,6 +22,8 @@ class StreamApiFunctionalityScenarioTest : public ClientTestBase, } }; +#ifdef ALIGNED_MEMORY_MODEL + /* * Testing basic token rotation. The test was given enough time for 1 token rotations plus 2 seconds extra. * When the given time ran out, we should have observed 3 token rotations taken place. if timedGetStreamData returns @@ -515,3 +517,5 @@ TEST_P(StreamApiFunctionalityScenarioTest, TokenRotationBasicMultiTrackPassThrou INSTANTIATE_TEST_SUITE_P(PermutatedStreamInfo, StreamApiFunctionalityScenarioTest, Combine(Values(STREAMING_TYPE_REALTIME, STREAMING_TYPE_OFFLINE), Values(0, 10 * HUNDREDS_OF_NANOS_IN_AN_HOUR), Bool(), Values(0, TEST_REPLAY_DURATION))); + +#endif \ No newline at end of file diff --git a/src/client/tst/StreamApiFunctionalityTest.cpp b/tst/client/StreamApiFunctionalityTest.cpp similarity index 99% rename from src/client/tst/StreamApiFunctionalityTest.cpp rename to tst/client/StreamApiFunctionalityTest.cpp index f510cff22..43e3161b8 100644 --- a/src/client/tst/StreamApiFunctionalityTest.cpp +++ b/tst/client/StreamApiFunctionalityTest.cpp @@ -134,6 +134,7 @@ TEST_F(StreamApiFunctionalityTest, streamFormatChange_stateCheck) // Ensure we can successfully set the CPD EXPECT_EQ(STATUS_SUCCESS, kinesisVideoStreamFormatChanged(mStreamHandle, SIZEOF(cpd), cpd, TEST_TRACKID)); +#ifdef ALIGNED_MEMORY_MODEL for (i = 0, timestamp = 0; i < 20; timestamp += TEST_FRAME_DURATION, i++) { frame.index = i; frame.decodingTs = timestamp; @@ -155,6 +156,7 @@ TEST_F(StreamApiFunctionalityTest, streamFormatChange_stateCheck) // Setting CPD should fail EXPECT_NE(STATUS_SUCCESS, kinesisVideoStreamFormatChanged(mStreamHandle, SIZEOF(cpd), cpd, TEST_TRACKID)); } +#endif } TEST_F(StreamApiFunctionalityTest, setNalAdaptionFlags_stateCheck) @@ -211,6 +213,7 @@ TEST_F(StreamApiFunctionalityTest, setNalAdaptionFlags_stateCheck) EXPECT_EQ(nalFlags, pKinesisVideoStream->streamInfo.streamCaps.nalAdaptationFlags); EXPECT_EQ(MKV_NALS_ADAPT_NONE, ((PStreamMkvGenerator) pKinesisVideoStream->pMkvGenerator)->nalsAdaptation); +#ifdef ALIGNED_MEMORY_MODEL for (i = 0, timestamp = 0; i < 20; timestamp += TEST_FRAME_DURATION, i++) { frame.index = i; frame.decodingTs = timestamp; @@ -232,8 +235,10 @@ TEST_F(StreamApiFunctionalityTest, setNalAdaptionFlags_stateCheck) // Setting NAL flags should fail EXPECT_NE(STATUS_SUCCESS, kinesisVideoStreamSetNalAdaptationFlags(mStreamHandle, nalFlags)); } +#endif } +#ifdef ALIGNED_MEMORY_MODEL TEST_F(StreamApiFunctionalityTest, putFrame_BasicPutTestItemLimit) { UINT32 i, maxIteration; @@ -1747,29 +1752,6 @@ TEST_F(StreamApiFunctionalityTest, submitAck_shouldBeInWindowAfterErrorAck) MEMFREE(getDataBuffer); } -TEST_F(StreamApiFunctionalityTest, putFrame_AdaptAnnexB) -{ - BYTE frameData[] = {0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, - 0xa9, 0x50, 0x14, 0x07, 0xb4, 0x20, 0x00, 0x00, - 0x7d, 0x00, 0x00, 0x1d, 0x4c, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x01, 0x68, 0xce, 0x3c, 0x80}; - UINT32 frameDataSize = SIZEOF(frameData); - Frame frame; - - // Create and ready a stream - ReadyStream(); - - frame.index = 0; - frame.decodingTs = 0; - frame.presentationTs = 0; - frame.duration = TEST_FRAME_DURATION; - frame.size = frameDataSize; - frame.trackId = TEST_TRACKID; - frame.frameData = frameData; - frame.flags = FRAME_FLAG_KEY_FRAME; - EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFrame(mStreamHandle, &frame)); -} - TEST_F(StreamApiFunctionalityTest, PutGet_ConnectionStaleNotification) { UINT32 i, filledSize; @@ -1827,6 +1809,8 @@ TEST_F(StreamApiFunctionalityTest, PutGet_ConnectionStaleNotification) } } +#endif + extern UINT64 gPresetCurrentTime; TEST_F(StreamApiFunctionalityTest, streamingTokenJitter_none) { @@ -1985,3 +1969,26 @@ TEST_F(StreamApiFunctionalityTest, streamingTokenJitter_preset_max) EXPECT_EQ(STATUS_SUCCESS, freeKinesisVideoClient(&clientHandle)); } + +TEST_F(StreamApiFunctionalityTest, putFrame_AdaptAnnexB) +{ + BYTE frameData[] = {0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, + 0xa9, 0x50, 0x14, 0x07, 0xb4, 0x20, 0x00, 0x00, + 0x7d, 0x00, 0x00, 0x1d, 0x4c, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x01, 0x68, 0xce, 0x3c, 0x80}; + UINT32 frameDataSize = SIZEOF(frameData); + Frame frame; + + // Create and ready a stream + ReadyStream(); + + frame.index = 0; + frame.decodingTs = 0; + frame.presentationTs = 0; + frame.duration = TEST_FRAME_DURATION; + frame.size = frameDataSize; + frame.trackId = TEST_TRACKID; + frame.frameData = frameData; + frame.flags = FRAME_FLAG_KEY_FRAME; + EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFrame(mStreamHandle, &frame)); +} \ No newline at end of file diff --git a/src/client/tst/StreamApiServiceCallsTest.cpp b/tst/client/StreamApiServiceCallsTest.cpp similarity index 100% rename from src/client/tst/StreamApiServiceCallsTest.cpp rename to tst/client/StreamApiServiceCallsTest.cpp diff --git a/src/client/tst/StreamApiTest.cpp b/tst/client/StreamApiTest.cpp similarity index 99% rename from src/client/tst/StreamApiTest.cpp rename to tst/client/StreamApiTest.cpp index d5e68c386..9c8ff78b6 100644 --- a/src/client/tst/StreamApiTest.cpp +++ b/tst/client/StreamApiTest.cpp @@ -384,6 +384,7 @@ TEST_F(StreamApiTest, insertKinesisVideoEvent_NULL_Invalid) EXPECT_EQ(STATUS_INVALID_ARG, putKinesisVideoEventMetadata(mStreamHandle, STREAM_EVENT_TYPE_LAST + rand() % STREAM_EVENT_TYPE_LAST, &Meta)); } +#ifdef ALIGNED_MEMORY_MODEL TEST_F(StreamApiTest, insertKinesisVideoEvent_Invalid_Length) { StreamEventMetadata Meta{STREAM_EVENT_METADATA_CURRENT_VERSION, NULL, 1, {}, {}}; @@ -447,26 +448,6 @@ TEST_F(StreamApiTest, insertKinesisVideoEvent_Invalid_Length) } -TEST_F(StreamApiTest, insertKinesisVideoTag_Invalid_Name) -{ - // Create and ready stream - ReadyStream(); - - EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS", (PCHAR) "Tag Value", FALSE)); - EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS", (PCHAR) "Tag Value", TRUE)); - EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS ", (PCHAR) "Tag Value", FALSE)); - EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS ", (PCHAR) "Tag Value", TRUE)); - EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWSTag", (PCHAR) "Tag Value", FALSE)); - EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWSTag", (PCHAR) "Tag Value", TRUE)); - EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS:", (PCHAR) "Tag Value", FALSE)); - EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS:", (PCHAR) "Tag Value", TRUE)); - - EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "aWS", (PCHAR) "Tag Value", FALSE)); - EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "aWS", (PCHAR) "Tag Value", TRUE)); - EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "aws", (PCHAR) "Tag Value", FALSE)); - EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "aws", (PCHAR) "Tag Value", TRUE)); -} - TEST_F(StreamApiTest, insertKinesisVideoEvent_Invalid_Name) { StreamEventMetadata Meta{STREAM_EVENT_METADATA_CURRENT_VERSION, NULL, 1, {}, {}}; @@ -512,6 +493,27 @@ TEST_F(StreamApiTest, insertKinesisVideoEvent_Invalid_Name) MEMCPY(tagName, (PCHAR) "aws", STRLEN("aws")); EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoEventMetadata(mStreamHandle, STREAM_EVENT_TYPE_IMAGE_GENERATION, &Meta)); } +#endif + +TEST_F(StreamApiTest, insertKinesisVideoTag_Invalid_Name) +{ + // Create and ready stream + ReadyStream(); + + EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS", (PCHAR) "Tag Value", FALSE)); + EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS", (PCHAR) "Tag Value", TRUE)); + EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS ", (PCHAR) "Tag Value", FALSE)); + EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS ", (PCHAR) "Tag Value", TRUE)); + EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWSTag", (PCHAR) "Tag Value", FALSE)); + EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWSTag", (PCHAR) "Tag Value", TRUE)); + EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS:", (PCHAR) "Tag Value", FALSE)); + EXPECT_EQ(STATUS_INVALID_METADATA_NAME, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "AWS:", (PCHAR) "Tag Value", TRUE)); + + EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "aWS", (PCHAR) "Tag Value", FALSE)); + EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "aWS", (PCHAR) "Tag Value", TRUE)); + EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "aws", (PCHAR) "Tag Value", FALSE)); + EXPECT_EQ(STATUS_SUCCESS, putKinesisVideoFragmentMetadata(mStreamHandle, (PCHAR) "aws", (PCHAR) "Tag Value", TRUE)); +} TEST_F(StreamApiTest, insertKinesisVideoTag_Stream_State_Error) { @@ -748,6 +750,7 @@ PVOID streamStopNotifier(PVOID arg) return NULL; } +#ifdef ALIGNED_MEMORY_MODEL TEST_F(StreamApiTest, kinesisVideoStreamCreateSync_Valid) { freeKinesisVideoStream(&mStreamHandle); @@ -777,26 +780,6 @@ TEST_F(StreamApiTest, kinesisVideoStreamCreateSync_Valid) EXPECT_EQ(STATUS_SUCCESS, stopKinesisVideoStreamSync(mStreamHandle)); } -TEST_F(StreamApiTest, kinesisVideoStreamCreateSync_Valid_Timeout) -{ - CLIENT_HANDLE clientHandle; - - // Create a client with appropriate timeout so we don't block on test. - mClientSyncMode = TRUE; - mDeviceInfo.clientInfo.createStreamTimeout = 20 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND; - EXPECT_EQ(STATUS_SUCCESS, createKinesisVideoClientSync(&mDeviceInfo, &mClientCallbacks, &clientHandle)); - - // Create synchronously - EXPECT_EQ(STATUS_OPERATION_TIMED_OUT, createKinesisVideoStreamSync(clientHandle, &mStreamInfo, &mStreamHandle)); - - EXPECT_FALSE(IS_VALID_STREAM_HANDLE(mStreamHandle)); - - // Stop synchronously - will fail as we should have invalid handle - EXPECT_NE(STATUS_SUCCESS, stopKinesisVideoStreamSync(mStreamHandle)); - - EXPECT_EQ(STATUS_SUCCESS, freeKinesisVideoClient(&clientHandle)); -} - TEST_F(StreamApiTest, kinesisVideoStreamCreateSyncStopSync_Valid_Timeout) { CLIENT_HANDLE clientHandle; @@ -827,3 +810,25 @@ TEST_F(StreamApiTest, kinesisVideoStreamCreateSyncStopSync_Valid_Timeout) EXPECT_EQ(STATUS_SUCCESS, freeKinesisVideoClient(&clientHandle)); } + +#endif + +TEST_F(StreamApiTest, kinesisVideoStreamCreateSync_Valid_Timeout) +{ + CLIENT_HANDLE clientHandle; + + // Create a client with appropriate timeout so we don't block on test. + mClientSyncMode = TRUE; + mDeviceInfo.clientInfo.createStreamTimeout = 20 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND; + EXPECT_EQ(STATUS_SUCCESS, createKinesisVideoClientSync(&mDeviceInfo, &mClientCallbacks, &clientHandle)); + + // Create synchronously + EXPECT_EQ(STATUS_OPERATION_TIMED_OUT, createKinesisVideoStreamSync(clientHandle, &mStreamInfo, &mStreamHandle)); + + EXPECT_FALSE(IS_VALID_STREAM_HANDLE(mStreamHandle)); + + // Stop synchronously - will fail as we should have invalid handle + EXPECT_NE(STATUS_SUCCESS, stopKinesisVideoStreamSync(mStreamHandle)); + + EXPECT_EQ(STATUS_SUCCESS, freeKinesisVideoClient(&clientHandle)); +} \ No newline at end of file diff --git a/src/client/tst/StreamDeviceTagsTest.cpp b/tst/client/StreamDeviceTagsTest.cpp similarity index 100% rename from src/client/tst/StreamDeviceTagsTest.cpp rename to tst/client/StreamDeviceTagsTest.cpp diff --git a/src/client/tst/StreamFunctionalityTest.cpp b/tst/client/StreamFunctionalityTest.cpp similarity index 99% rename from src/client/tst/StreamFunctionalityTest.cpp rename to tst/client/StreamFunctionalityTest.cpp index d25f656f2..d894c481f 100644 --- a/src/client/tst/StreamFunctionalityTest.cpp +++ b/tst/client/StreamFunctionalityTest.cpp @@ -58,7 +58,7 @@ TEST_P(StreamFunctionalityTest, CreateSyncStopSyncFreePutFrameFail) { MockProducer mockProducer(mMockProducerConfig, mStreamHandle); EXPECT_NE(STATUS_SUCCESS, mockProducer.putFrame()); } - +#ifdef ALIGNED_MEMORY_MODEL TEST_P(StreamFunctionalityTest, CreateAwaitReadyPutFrameFree) { PASS_TEST_FOR_ZERO_RETENTION_AND_OFFLINE(); @@ -298,6 +298,7 @@ TEST_P(StreamFunctionalityTest, CreateSyncPutFrameEoFRFirstForceNotSkipping) EXPECT_EQ(0, streamMetrics.skippedFrames); // There should be no mem leaks } +#endif // Validate StreamDescription_V0 structure handling TEST_P(StreamFunctionalityTest, StreamDescription_V0_Test) diff --git a/src/client/tst/StreamParallelTest.cpp b/tst/client/StreamParallelTest.cpp similarity index 99% rename from src/client/tst/StreamParallelTest.cpp rename to tst/client/StreamParallelTest.cpp index 3fa31a9bc..5b99c37c8 100644 --- a/src/client/tst/StreamParallelTest.cpp +++ b/tst/client/StreamParallelTest.cpp @@ -98,7 +98,7 @@ PVOID ClientTestBase::basicConsumerRoutine(UINT64 streamId) return NULL; } - +#ifdef ALIGNED_MEMORY_MODEL TEST_F(StreamParallelTest, putFrame_BasicParallelPutGet) { UINT32 index; @@ -188,3 +188,4 @@ TEST_F(StreamParallelTest, putFrame_BasicParallelPutGet) EXPECT_EQ(STATUS_SUCCESS, THREAD_JOIN(mConsumerThreads[index], NULL)); } } +#endif diff --git a/src/client/tst/StreamPutGetTest.cpp b/tst/client/StreamPutGetTest.cpp similarity index 99% rename from src/client/tst/StreamPutGetTest.cpp rename to tst/client/StreamPutGetTest.cpp index 6d6122dd3..ff84b9581 100644 --- a/src/client/tst/StreamPutGetTest.cpp +++ b/tst/client/StreamPutGetTest.cpp @@ -2,7 +2,7 @@ class StreamPutGetTest : public ClientTestBase { }; - +#ifdef ALIGNED_MEMORY_MODEL TEST_F(StreamPutGetTest, putFrame_PutGetFrameBoundary) { UINT32 i, j, filledSize, offset, bufferSize; @@ -1735,3 +1735,4 @@ TEST_F(StreamPutGetTest, putFrame_PutGetPersistentTagsStoreData) MEMFREE(getDataBuffer); } +#endif \ No newline at end of file diff --git a/src/client/tst/StreamRecoveryFunctionalityTest.cpp b/tst/client/StreamRecoveryFunctionalityTest.cpp similarity index 99% rename from src/client/tst/StreamRecoveryFunctionalityTest.cpp rename to tst/client/StreamRecoveryFunctionalityTest.cpp index 0057eabdd..426a27de1 100644 --- a/src/client/tst/StreamRecoveryFunctionalityTest.cpp +++ b/tst/client/StreamRecoveryFunctionalityTest.cpp @@ -22,7 +22,7 @@ class StreamRecoveryFunctionalityTest : public ClientTestBase, mStreamInfo.streamCaps.replayDuration = (UINT64) replayDuration; } }; - +#ifdef ALIGNED_MEMORY_MODEL TEST_P(StreamRecoveryFunctionalityTest, CreateStreamThenStreamResetConnectionEnsureRecovery) { std::vector currentUploadHandles; @@ -1349,3 +1349,5 @@ TEST_P(StreamRecoveryFunctionalityTest, EventMetadataStartStreamFailRecovery) { INSTANTIATE_TEST_SUITE_P(PermutatedStreamInfo, StreamRecoveryFunctionalityTest, Combine(Values(STREAMING_TYPE_REALTIME, STREAMING_TYPE_OFFLINE), Values(0, 10 * HUNDREDS_OF_NANOS_IN_AN_HOUR), Bool(), Values(0, TEST_REPLAY_DURATION))); + +#endif \ No newline at end of file diff --git a/src/client/tst/StreamStateTransitionsTest.cpp b/tst/client/StreamStateTransitionsTest.cpp similarity index 99% rename from src/client/tst/StreamStateTransitionsTest.cpp rename to tst/client/StreamStateTransitionsTest.cpp index 937210170..ba3ba8167 100644 --- a/src/client/tst/StreamStateTransitionsTest.cpp +++ b/tst/client/StreamStateTransitionsTest.cpp @@ -71,7 +71,7 @@ TEST_F(StreamStateTransitionsTest, stopStateFromGetToken) EXPECT_EQ(STATUS_SUCCESS, kinesisVideoStreamTerminated(mCallContext.customData, TEST_UPLOAD_HANDLE, SERVICE_CALL_RESULT_OK)); } - +#ifdef ALIGNED_MEMORY_MODEL TEST_F(StreamStateTransitionsTest, stopStateFromStreamingSuccessRecovery) { UINT32 i, remaining; @@ -1047,3 +1047,4 @@ TEST_F(StreamStateTransitionsTest, stopStateFromStreamingOtherNoRecovery) EXPECT_EQ(STATUS_SERVICE_CALL_DEVICE_NOT_FOND_ERROR, kinesisVideoStreamTerminated(mCallContext.customData, TEST_UPLOAD_HANDLE, SERVICE_CALL_DEVICE_NOT_FOUND)); } +#endif \ No newline at end of file diff --git a/src/client/tst/StreamStoppingFunctionalityTest.cpp b/tst/client/StreamStoppingFunctionalityTest.cpp similarity index 98% rename from src/client/tst/StreamStoppingFunctionalityTest.cpp rename to tst/client/StreamStoppingFunctionalityTest.cpp index 38e111570..b21f801db 100644 --- a/src/client/tst/StreamStoppingFunctionalityTest.cpp +++ b/tst/client/StreamStoppingFunctionalityTest.cpp @@ -23,6 +23,27 @@ class StreamStoppingFunctionalityTest : public ClientTestBase, } }; +TEST_P(StreamStoppingFunctionalityTest, CreateSyncResetConnectionSuccess) +{ + PStateMachineState pState; + CreateScenarioTestClient(); + + PASS_TEST_FOR_ZERO_RETENTION_AND_OFFLINE(); + + CreateStreamSync(); + + // stream state should be STREAM_STATE_READY at this point. + PKinesisVideoStream pKinesisVideoStream = fromStreamHandle(mStreamHandle); + + // kinesisVideoStreamTerminated should succeed but have no effect on stream state because stream state is STREAM_STATE_READY + kinesisVideoStreamTerminated(mStreamHandle, INVALID_UPLOAD_HANDLE_VALUE, SERVICE_CALL_RESULT_OK); + + EXPECT_EQ(STATUS_SUCCESS, getStateMachineCurrentState(pKinesisVideoStream->base.pStateMachine, &pState)); + EXPECT_TRUE(pState->state == STREAM_STATE_READY); +} + +#ifdef ALIGNED_MEMORY_MODEL + TEST_P(StreamStoppingFunctionalityTest, CreateSyncStreamWithTwoUploadHandlesStopSyncFreeSuccess) { UINT64 currentTime, testTerminationTime; @@ -38,7 +59,7 @@ TEST_P(StreamStoppingFunctionalityTest, CreateSyncStreamWithTwoUploadHandlesStop CreateStreamSync(); testTerminationTime = mClientCallbacks.getCurrentTimeFn((UINT64) this) - + 2 * MIN_STREAMING_TOKEN_EXPIRATION_DURATION + 15 * HUNDREDS_OF_NANOS_IN_A_SECOND; + + 2 * MIN_STREAMING_TOKEN_EXPIRATION_DURATION + 15 * HUNDREDS_OF_NANOS_IN_A_SECOND; MockProducer mockProducer(mMockProducerConfig, mStreamHandle); @@ -67,24 +88,6 @@ TEST_P(StreamStoppingFunctionalityTest, CreateSyncStreamWithTwoUploadHandlesStop VerifyStopStreamSyncAndFree(); } -TEST_P(StreamStoppingFunctionalityTest, CreateSyncResetConnectionSuccess) -{ - PStateMachineState pState; - CreateScenarioTestClient(); - - PASS_TEST_FOR_ZERO_RETENTION_AND_OFFLINE(); - - CreateStreamSync(); - - // stream state should be STREAM_STATE_READY at this point. - PKinesisVideoStream pKinesisVideoStream = fromStreamHandle(mStreamHandle); - - // kinesisVideoStreamTerminated should succeed but have no effect on stream state because stream state is STREAM_STATE_READY - kinesisVideoStreamTerminated(mStreamHandle, INVALID_UPLOAD_HANDLE_VALUE, SERVICE_CALL_RESULT_OK); - - EXPECT_EQ(STATUS_SUCCESS, getStateMachineCurrentState(pKinesisVideoStream->base.pStateMachine, &pState)); - EXPECT_TRUE(pState->state == STREAM_STATE_READY); -} TEST_P(StreamStoppingFunctionalityTest, CreateSyncStreamHighDensityStopSyncTimeoutFreeSuccess) { std::vector currentUploadHandles; @@ -242,5 +245,7 @@ TEST_P(StreamStoppingFunctionalityTest, CreateSyncStreamStopSyncErrorAckWhileStr EXPECT_EQ(0, ATOMIC_LOAD(&mDroppedFrameReportFuncCount)); } +#endif + INSTANTIATE_TEST_SUITE_P(PermutatedStreamInfo, StreamStoppingFunctionalityTest, Combine(Values(STREAMING_TYPE_REALTIME, STREAMING_TYPE_OFFLINE), Values(0, 10 * HUNDREDS_OF_NANOS_IN_AN_HOUR), Bool(), Values(0, TEST_REPLAY_DURATION))); diff --git a/src/client/tst/StreamTokenRotationTest.cpp b/tst/client/StreamTokenRotationTest.cpp similarity index 99% rename from src/client/tst/StreamTokenRotationTest.cpp rename to tst/client/StreamTokenRotationTest.cpp index 4be03d609..5cd1a773e 100644 --- a/src/client/tst/StreamTokenRotationTest.cpp +++ b/tst/client/StreamTokenRotationTest.cpp @@ -48,6 +48,8 @@ STATUS testPutStream(UINT64 customData, return STATUS_SUCCESS; } +#ifdef ALIGNED_MEMORY_MODEL + TEST_F(StreamTokenRotationTest, basicTokenRotationNonPersistAwait) { UINT32 i, filledSize, rotation, lastRotation; @@ -351,3 +353,4 @@ TEST_F(StreamTokenRotationTest, rotationWithAwaitingCheck) } MEMFREE(emptyTagValue); } +#endif \ No newline at end of file diff --git a/src/client/tst/StreamingSession.cpp b/tst/client/StreamingSession.cpp similarity index 100% rename from src/client/tst/StreamingSession.cpp rename to tst/client/StreamingSession.cpp diff --git a/src/client/tst/StreamingSession.h b/tst/client/StreamingSession.h similarity index 100% rename from src/client/tst/StreamingSession.h rename to tst/client/StreamingSession.h diff --git a/src/client/tst/TokenRotationFunctionalityTest.cpp b/tst/client/TokenRotationFunctionalityTest.cpp similarity index 99% rename from src/client/tst/TokenRotationFunctionalityTest.cpp rename to tst/client/TokenRotationFunctionalityTest.cpp index 3445bd6dc..f801e2307 100644 --- a/src/client/tst/TokenRotationFunctionalityTest.cpp +++ b/tst/client/TokenRotationFunctionalityTest.cpp @@ -23,6 +23,7 @@ class TokenRotationFunctionalityTest : public ClientTestBase, } }; +#ifdef ALIGNED_MEMORY_MODEL TEST_P(TokenRotationFunctionalityTest, CreateSyncStreamWithResultEventAfterGracePeriodStopFreeSuccess) { UINT64 currentTime, testTerminationTime, startTestTime, stopPutFrameTime, @@ -319,3 +320,4 @@ TEST_P(TokenRotationFunctionalityTest, CreateSyncStreamAtTokenRotationLongDelayF INSTANTIATE_TEST_SUITE_P(PermutatedStreamInfo, TokenRotationFunctionalityTest, Combine(Values(STREAMING_TYPE_REALTIME, STREAMING_TYPE_OFFLINE), Values(0, 10 * HUNDREDS_OF_NANOS_IN_AN_HOUR), Bool(), Values(0, TEST_REPLAY_DURATION))); +#endif \ No newline at end of file diff --git a/src/duration/tst/DurationTest.cpp b/tst/duration/DurationTest.cpp similarity index 100% rename from src/duration/tst/DurationTest.cpp rename to tst/duration/DurationTest.cpp diff --git a/src/heap/tst/HeapApiFunctionalityTest.cpp b/tst/heap/HeapApiFunctionalityTest.cpp similarity index 99% rename from src/heap/tst/HeapApiFunctionalityTest.cpp rename to tst/heap/HeapApiFunctionalityTest.cpp index c74950e0e..7c44afd8b 100644 --- a/src/heap/tst/HeapApiFunctionalityTest.cpp +++ b/tst/heap/HeapApiFunctionalityTest.cpp @@ -358,7 +358,7 @@ TEST_F(HeapApiFunctionalityTest, SingleLargeAlloc) EXPECT_TRUE(STATUS_SUCCEEDED(heapInitialize(MIN_HEAP_SIZE, 20, FLAGS_USE_SYSTEM_HEAP, NULL, &pHeap))); singleLargeAlloc(pHeap); } - +#ifdef ALIGNED_MEMORY_MODEL TEST_F(HeapApiFunctionalityTest, MultipleLargeAlloc) { PHeap pHeap; @@ -370,14 +370,6 @@ TEST_F(HeapApiFunctionalityTest, MultipleLargeAlloc) multipleLargeAlloc(pHeap); } -TEST_F(HeapApiFunctionalityTest, DefragmentationAlloc) -{ - PHeap pHeap; - - EXPECT_TRUE(STATUS_SUCCEEDED(heapInitialize(MIN_HEAP_SIZE, 20, FLAGS_USE_AIV_HEAP, NULL, &pHeap))); - defragmentationAlloc(pHeap); -} - TEST_F(HeapApiFunctionalityTest, SingleByteAlloc) { PHeap pHeap; @@ -415,11 +407,7 @@ TEST_F(HeapApiFunctionalityTest, AivHeapUnalignedHeapLimit) PHeap pHeap; EXPECT_TRUE(STATUS_SUCCEEDED(heapInitialize(MIN_HEAP_SIZE + 1, 20, FLAGS_USE_AIV_HEAP, NULL, &pHeap))); -#ifdef ALIGNED_MEMORY_MODEL EXPECT_EQ(MIN_HEAP_SIZE + 8, pHeap->heapLimit); -#else - EXPECT_EQ(MIN_HEAP_SIZE + 1, pHeap->heapLimit); -#endif EXPECT_TRUE(STATUS_SUCCEEDED(heapRelease(pHeap))); } @@ -677,6 +665,15 @@ TEST_F(HeapApiFunctionalityTest, AivHeapResizeUpDownTopDown) EXPECT_TRUE(STATUS_SUCCEEDED(heapRelease(pHeap))); } +#endif + +TEST_F(HeapApiFunctionalityTest, DefragmentationAlloc) +{ + PHeap pHeap; + + EXPECT_TRUE(STATUS_SUCCEEDED(heapInitialize(MIN_HEAP_SIZE, 20, FLAGS_USE_AIV_HEAP, NULL, &pHeap))); + defragmentationAlloc(pHeap); +} TEST_F(HeapApiFunctionalityTest, MultipleMapUnmapByteAlloc) { diff --git a/src/heap/tst/HeapApiTest.cpp b/tst/heap/HeapApiTest.cpp similarity index 100% rename from src/heap/tst/HeapApiTest.cpp rename to tst/heap/HeapApiTest.cpp diff --git a/src/heap/tst/HeapPerfTest.cpp b/tst/heap/HeapPerfTest.cpp similarity index 100% rename from src/heap/tst/HeapPerfTest.cpp rename to tst/heap/HeapPerfTest.cpp diff --git a/src/heap/tst/HeapTestFixture.cpp b/tst/heap/HeapTestFixture.cpp similarity index 100% rename from src/heap/tst/HeapTestFixture.cpp rename to tst/heap/HeapTestFixture.cpp diff --git a/src/heap/tst/HeapTestFixture.h b/tst/heap/HeapTestFixture.h similarity index 100% rename from src/heap/tst/HeapTestFixture.h rename to tst/heap/HeapTestFixture.h diff --git a/src/heap/tst/HybridFileHeapTest.cpp b/tst/heap/HybridFileHeapTest.cpp similarity index 97% rename from src/heap/tst/HybridFileHeapTest.cpp rename to tst/heap/HybridFileHeapTest.cpp index 3bc3bb7fe..d28265723 100644 --- a/src/heap/tst/HybridFileHeapTest.cpp +++ b/tst/heap/HybridFileHeapTest.cpp @@ -14,6 +14,11 @@ class HybridFileHeapTest : public HeapTestBase, HEAP_BEHAVIOR_FLAGS primaryHeapType; std::tie(primaryHeapType) = GetParam(); +#ifndef ALIGNED_MEMORY_MODEL + if (primaryHeapType == FLAGS_USE_AIV_HEAP) { + primaryHeapType = FLAGS_USE_SYSTEM_HEAP; + } +#endif mHeapType = primaryHeapType | FLAGS_USE_HYBRID_FILE_HEAP; } diff --git a/src/heap/tst/HybridHeapTest.cpp b/tst/heap/HybridHeapTest.cpp similarity index 99% rename from src/heap/tst/HybridHeapTest.cpp rename to tst/heap/HybridHeapTest.cpp index 345af348a..41e01d2ed 100644 --- a/src/heap/tst/HybridHeapTest.cpp +++ b/tst/heap/HybridHeapTest.cpp @@ -16,6 +16,11 @@ class HybridHeapTest : public HeapTestBase, HEAP_BEHAVIOR_FLAGS primaryHeapType; std::tie(primaryHeapType) = GetParam(); +#ifndef ALIGNED_MEMORY_MODEL + if (primaryHeapType == FLAGS_USE_AIV_HEAP) { + primaryHeapType = FLAGS_USE_SYSTEM_HEAP; + } +#endif mHeapType = primaryHeapType | FLAGS_USE_HYBRID_VRAM_HEAP; // Set the invalid allocation handles diff --git a/src/mkvgen/tst/AnnexBCpdNalAdapterTest.cpp b/tst/mkvgen/AnnexBCpdNalAdapterTest.cpp similarity index 96% rename from src/mkvgen/tst/AnnexBCpdNalAdapterTest.cpp rename to tst/mkvgen/AnnexBCpdNalAdapterTest.cpp index d7f78f22e..5acc32c69 100644 --- a/src/mkvgen/tst/AnnexBCpdNalAdapterTest.cpp +++ b/tst/mkvgen/AnnexBCpdNalAdapterTest.cpp @@ -3,6 +3,7 @@ class AnnexBCpdNalAdapterTest : public MkvgenTestBase { }; +#ifdef FIXUP_ANNEX_B_TRAILING_NALU_ZERO TEST_F(AnnexBCpdNalAdapterTest, nalAdapter_InvalidInput) { PBYTE pCpd = (PBYTE) 100; @@ -18,6 +19,7 @@ TEST_F(AnnexBCpdNalAdapterTest, nalAdapter_InvalidInput) EXPECT_NE(STATUS_SUCCESS, adaptH264CpdNalsFromAnnexBToAvcc(pCpd, MIN_H264_ANNEXB_CPD_SIZE - 1, pAdaptedCpd, &adaptedCpdSize)); } +#endif TEST_F(AnnexBCpdNalAdapterTest, nalAdapter_InvalidNoStartCode) { @@ -104,8 +106,12 @@ TEST_F(AnnexBCpdNalAdapterTest, nalAdapter_FixedUpValidSpsPps) BYTE adaptedCpd[1000]; UINT32 adaptedCpdSize = SIZEOF(adaptedCpd); - EXPECT_EQ(STATUS_SUCCESS, adaptH264CpdNalsFromAnnexBToAvcc(cpd, cpdSize, NULL, &adaptedCpdSize)); - EXPECT_EQ(STATUS_SUCCESS, adaptH264CpdNalsFromAnnexBToAvcc(cpd, cpdSize, adaptedCpd, &adaptedCpdSize)); + ASSERT_EQ(STATUS_SUCCESS, adaptH264CpdNalsFromAnnexBToAvcc(cpd, cpdSize, NULL, &adaptedCpdSize)); +#ifdef FIXUP_ANNEX_B_TRAILING_NALU_ZERO + ASSERT_EQ(STATUS_SUCCESS, adaptH264CpdNalsFromAnnexBToAvcc(cpd, cpdSize, adaptedCpd, &adaptedCpdSize)); +#else + ASSERT_EQ(STATUS_MKV_INVALID_ANNEXB_NALU_IN_FRAME_DATA, adaptH264CpdNalsFromAnnexBToAvcc(cpd, cpdSize, adaptedCpd, &adaptedCpdSize)); +#endif } TEST_F(AnnexBCpdNalAdapterTest, nalAdapter_ValidH265) { diff --git a/src/mkvgen/tst/AnnexBNalAdapterTest.cpp b/tst/mkvgen/AnnexBNalAdapterTest.cpp similarity index 99% rename from src/mkvgen/tst/AnnexBNalAdapterTest.cpp rename to tst/mkvgen/AnnexBNalAdapterTest.cpp index 3073a705e..18b9b35b1 100644 --- a/src/mkvgen/tst/AnnexBNalAdapterTest.cpp +++ b/tst/mkvgen/AnnexBNalAdapterTest.cpp @@ -3,6 +3,7 @@ class AnnexBNalAdapterTest : public MkvgenTestBase { }; +#ifdef FIXUP_ANNEX_B_TRAILING_NALU_ZERO TEST_F(AnnexBNalAdapterTest, nalAdapter_InvalidInput) { PBYTE pFrameData = (PBYTE) 100; @@ -20,6 +21,7 @@ TEST_F(AnnexBNalAdapterTest, nalAdapter_InvalidInput) EXPECT_NE(STATUS_SUCCESS, adaptFrameNalsFromAnnexBToAvcc(pFrameData, frameDataSize, FALSE, pAdaptedFrameData, NULL)); EXPECT_NE(STATUS_SUCCESS, adaptFrameNalsFromAnnexBToAvcc(pFrameData, frameDataSize, TRUE, pAdaptedFrameData, NULL)); } +#endif TEST_F(AnnexBNalAdapterTest, nalAdapter_ValidOneByteNonZero) { @@ -176,6 +178,7 @@ TEST_F(AnnexBNalAdapterTest, nalAdapter_ValidTrailingZeros) EXPECT_EQ(frameData4Size, adaptedFrameDataSize); EXPECT_EQ(0, MEMCMP(adaptedFrameData, frameData, frameData4Size)); +#ifdef FIXUP_ANNEX_B_TRAILING_NALU_ZERO BYTE frameData5[] = {0, 0, 0, 0, 1}; UINT32 frameData5Size = SIZEOF(frameData5); EXPECT_EQ(STATUS_SUCCESS, adaptFrameNalsFromAnnexBToAvcc(frameData5, frameData5Size, TRUE, NULL, &adaptedFrameDataSize)); @@ -184,7 +187,7 @@ TEST_F(AnnexBNalAdapterTest, nalAdapter_ValidTrailingZeros) // Should set the size larger due to extra 0 removal and checking for at least the same size for EPB adaptedFrameDataSize += 1; EXPECT_EQ(STATUS_SUCCESS, adaptFrameNalsFromAnnexBToAvcc(frameData5, frameData5Size, TRUE, adaptedFrameData, &adaptedFrameDataSize)); - +#endif BYTE frameData6[] = {0, 0, 0, 1, 0}; UINT32 frameData6Size = SIZEOF(frameData6); @@ -372,6 +375,7 @@ TEST_F(AnnexBNalAdapterTest, nalAdapter_ValidEPB) } } +#ifdef FIXUP_ANNEX_B_TRAILING_NALU_ZERO TEST_F(AnnexBNalAdapterTest, nalAdapter_badRealLifeEncoderSampleWithFix) { // I-frame from a real-life encoder output which is actually invalid Annex-B format (shortened after a few bytes of the actual frame) @@ -423,3 +427,4 @@ TEST_F(AnnexBNalAdapterTest, nalAdapter_badRealLifeEncoderSampleWithFix) EXPECT_TRUE(pPps != NULL); EXPECT_EQ(4, ppsSize); } +#endif \ No newline at end of file diff --git a/src/mkvgen/tst/AudioCpdParserTest.cpp b/tst/mkvgen/AudioCpdParserTest.cpp similarity index 100% rename from src/mkvgen/tst/AudioCpdParserTest.cpp rename to tst/mkvgen/AudioCpdParserTest.cpp diff --git a/src/mkvgen/tst/AvccNalAdapterTest.cpp b/tst/mkvgen/AvccNalAdapterTest.cpp similarity index 100% rename from src/mkvgen/tst/AvccNalAdapterTest.cpp rename to tst/mkvgen/AvccNalAdapterTest.cpp diff --git a/src/mkvgen/tst/MkvgenApiFunctionalityTest.cpp b/tst/mkvgen/MkvgenApiFunctionalityTest.cpp similarity index 99% rename from src/mkvgen/tst/MkvgenApiFunctionalityTest.cpp rename to tst/mkvgen/MkvgenApiFunctionalityTest.cpp index 8f6178bd5..66c735c0c 100644 --- a/src/mkvgen/tst/MkvgenApiFunctionalityTest.cpp +++ b/tst/mkvgen/MkvgenApiFunctionalityTest.cpp @@ -192,7 +192,7 @@ TEST_F(MkvgenApiFunctionalityTest, mkvgenPackageFrame_CreateStoreMkvFromJpegAsFo mTrackInfoCount, MKV_TEST_CLIENT_ID, NULL, 0, &mkvGenerator)); for (i = 0, index = 0; i < 100; i++) { - SPRINTF(fileName, (PCHAR) "samples" FPATHSEPARATOR_STR "gif%03d.jpg", i); + SNPRINTF(fileName, 24, (PCHAR) "samples" FPATHSEPARATOR_STR "gif%03d.jpg", i); fileSize = MKV_TEST_BUFFER_SIZE; if (STATUS_FAILED(readFile(fileName, TRUE, NULL, &fileSize))) { break; @@ -215,8 +215,8 @@ TEST_F(MkvgenApiFunctionalityTest, mkvgenPackageFrame_CreateStoreMkvFromJpegAsFo // Add tags for (i = 0; i < MKV_TEST_TAG_COUNT; i++) { packagedSize = size; - SPRINTF(tagName, (PCHAR) "testTag_%d", i); - SPRINTF(tagVal, (PCHAR) "testTag_%d_Value", i); + SNPRINTF(tagName, 16, (PCHAR) "testTag_%d", i); + SNPRINTF(tagVal, 16, (PCHAR) "testTag_%d_Value", i); EXPECT_EQ(STATUS_SUCCESS, mkvgenGenerateTag(mkvGenerator, mkvBuffer + index, tagName, @@ -259,8 +259,8 @@ TEST_F(MkvgenApiFunctionalityTest, mkvgenPackageFrame_CreateStoreMkvTagsOnly) // Add tags for (i = 0; i < MKV_TEST_TAG_COUNT; i++) { packagedSize = size; - SPRINTF(tagName, (PCHAR) "testTag_%d", i); - SPRINTF(tagVal, (PCHAR) "testTag_%d_Value", i); + SNPRINTF(tagName, 16, (PCHAR) "testTag_%d", i); + SNPRINTF(tagVal, 24, (PCHAR) "testTag_%d_Value", i); EXPECT_EQ(STATUS_SUCCESS, mkvgenGenerateTag(mkvGenerator, mkvBuffer + index, tagName, @@ -316,8 +316,8 @@ TEST_F(MkvgenApiFunctionalityTest, mkvgenPackageFrame_CreateStoreMkvMixedTags) { if ((frame.flags & FRAME_FLAG_KEY_FRAME) == FRAME_FLAG_KEY_FRAME) { for (i = 0; i < MKV_TEST_TAG_COUNT; i++) { packagedSize = size; - SPRINTF(tagName, (PCHAR) "testTag_%d", i); - SPRINTF(tagVal, "(PCHAR) testTag_%d_Value", i); + SNPRINTF(tagName, 16, (PCHAR) "testTag_%d", i); + SNPRINTF(tagVal, 24, "(PCHAR) testTag_%d_Value", i); EXPECT_EQ(STATUS_SUCCESS, mkvgenGenerateTag(mkvGenerator, mkvBuffer + index, tagName, @@ -343,8 +343,8 @@ TEST_F(MkvgenApiFunctionalityTest, mkvgenPackageFrame_CreateStoreMkvMixedTags) { // Insert tags after the last cluster for (i = 0; i < MKV_TEST_TAG_COUNT; i++) { packagedSize = size; - SPRINTF(tagName, (PCHAR) "testTag_%d", i); - SPRINTF(tagVal, (PCHAR) "testTag_%d_Value", i); + SNPRINTF(tagName, 16, (PCHAR) "testTag_%d", i); + SNPRINTF(tagVal, 24, (PCHAR) "testTag_%d_Value", i); EXPECT_EQ(STATUS_SUCCESS, mkvgenGenerateTag(mkvGenerator, mkvBuffer + index, tagName, @@ -1660,6 +1660,7 @@ TEST_F(MkvgenApiFunctionalityTest, mkvgenExtractCpd_Variations) MEMCPY(frame.frameData, cpdH264AudSeiExtra0, frame.size); size = SIZEOF(frameBuf); +#ifdef FIXUP_ANNEX_B_TRAILING_NALU_ZERO EXPECT_EQ(STATUS_SUCCESS, mkvgenPackageFrame(pMkvGenerator, &frame, pTrackInfo, mBuffer, &size, &encodedFrameInfo)); // Ensure we have no width/height or CPD @@ -1667,7 +1668,7 @@ TEST_F(MkvgenApiFunctionalityTest, mkvgenExtractCpd_Variations) EXPECT_NE((PBYTE) NULL, pStreamMkvGenerator->trackInfoList[0].codecPrivateData); EXPECT_EQ(704, pStreamMkvGenerator->trackInfoList[0].trackCustomData.trackVideoConfig.videoWidth); EXPECT_EQ(480, pStreamMkvGenerator->trackInfoList[0].trackCustomData.trackVideoConfig.videoHeight); - +#endif // Free the generator EXPECT_EQ(STATUS_SUCCESS, freeMkvGenerator(pMkvGenerator)); @@ -1700,14 +1701,14 @@ TEST_F(MkvgenApiFunctionalityTest, mkvgenExtractCpd_Variations) MEMCPY(frame.frameData, cpdH264AudSeiExtra0, frame.size); size = SIZEOF(frameBuf); +#ifdef FIXUP_ANNEX_B_TRAILING_NALU_ZERO EXPECT_EQ(STATUS_SUCCESS, mkvgenPackageFrame(pMkvGenerator, &frame, pTrackInfo, mBuffer, &size, &encodedFrameInfo)); - // Ensure we have no width/height or CPD EXPECT_NE(0, pStreamMkvGenerator->trackInfoList[0].codecPrivateDataSize); EXPECT_NE((PBYTE) NULL, pStreamMkvGenerator->trackInfoList[0].codecPrivateData); EXPECT_EQ(704, pStreamMkvGenerator->trackInfoList[0].trackCustomData.trackVideoConfig.videoWidth); EXPECT_EQ(480, pStreamMkvGenerator->trackInfoList[0].trackCustomData.trackVideoConfig.videoHeight); - +#endif // Free the generator EXPECT_EQ(STATUS_SUCCESS, freeMkvGenerator(pMkvGenerator)); diff --git a/src/mkvgen/tst/MkvgenApiTest.cpp b/tst/mkvgen/MkvgenApiTest.cpp similarity index 99% rename from src/mkvgen/tst/MkvgenApiTest.cpp rename to tst/mkvgen/MkvgenApiTest.cpp index 652a52f6b..86c6b9e45 100644 --- a/src/mkvgen/tst/MkvgenApiTest.cpp +++ b/tst/mkvgen/MkvgenApiTest.cpp @@ -535,7 +535,7 @@ TEST_F(MkvgenApiTest, mkvgenIncreaseTagsTagSize_FunctionalityTest) /*** RANDOMIZATION TESTING ***/ randomSize = rand()%(0x000fffffffffffff); //UINT64, first byte is occupied, - EXPECT_EQ(STATUS_SUCCESS, mkvgenGenerateTagsChain(tagsMkvHolder, "TEST_NAME", "TEST_VALUE", &size, MKV_TREE_TAGS)); + EXPECT_EQ(STATUS_SUCCESS, mkvgenGenerateTagsChain(tagsMkvHolder, (PCHAR) "TEST_NAME", (PCHAR) "TEST_VALUE", &size, MKV_TREE_TAGS)); EXPECT_EQ(STATUS_SUCCESS, mkvgenIncreaseTagsTagSize(tagsMkvHolder, randomSize)); //grab size, and verify it increase by the expected amount. diff --git a/src/mkvgen/tst/MkvgenTestFixture.cpp b/tst/mkvgen/MkvgenTestFixture.cpp similarity index 100% rename from src/mkvgen/tst/MkvgenTestFixture.cpp rename to tst/mkvgen/MkvgenTestFixture.cpp diff --git a/src/mkvgen/tst/MkvgenTestFixture.h b/tst/mkvgen/MkvgenTestFixture.h similarity index 100% rename from src/mkvgen/tst/MkvgenTestFixture.h rename to tst/mkvgen/MkvgenTestFixture.h diff --git a/src/mkvgen/tst/SpsParserTest.cpp b/tst/mkvgen/SpsParserTest.cpp similarity index 100% rename from src/mkvgen/tst/SpsParserTest.cpp rename to tst/mkvgen/SpsParserTest.cpp diff --git a/src/mkvgen/tst/samples/gif000.jpg b/tst/mkvgen/samples/gif000.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif000.jpg rename to tst/mkvgen/samples/gif000.jpg diff --git a/src/mkvgen/tst/samples/gif001.jpg b/tst/mkvgen/samples/gif001.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif001.jpg rename to tst/mkvgen/samples/gif001.jpg diff --git a/src/mkvgen/tst/samples/gif002.jpg b/tst/mkvgen/samples/gif002.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif002.jpg rename to tst/mkvgen/samples/gif002.jpg diff --git a/src/mkvgen/tst/samples/gif003.jpg b/tst/mkvgen/samples/gif003.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif003.jpg rename to tst/mkvgen/samples/gif003.jpg diff --git a/src/mkvgen/tst/samples/gif004.jpg b/tst/mkvgen/samples/gif004.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif004.jpg rename to tst/mkvgen/samples/gif004.jpg diff --git a/src/mkvgen/tst/samples/gif005.jpg b/tst/mkvgen/samples/gif005.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif005.jpg rename to tst/mkvgen/samples/gif005.jpg diff --git a/src/mkvgen/tst/samples/gif006.jpg b/tst/mkvgen/samples/gif006.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif006.jpg rename to tst/mkvgen/samples/gif006.jpg diff --git a/src/mkvgen/tst/samples/gif007.jpg b/tst/mkvgen/samples/gif007.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif007.jpg rename to tst/mkvgen/samples/gif007.jpg diff --git a/src/mkvgen/tst/samples/gif008.jpg b/tst/mkvgen/samples/gif008.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif008.jpg rename to tst/mkvgen/samples/gif008.jpg diff --git a/src/mkvgen/tst/samples/gif009.jpg b/tst/mkvgen/samples/gif009.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif009.jpg rename to tst/mkvgen/samples/gif009.jpg diff --git a/src/mkvgen/tst/samples/gif010.jpg b/tst/mkvgen/samples/gif010.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif010.jpg rename to tst/mkvgen/samples/gif010.jpg diff --git a/src/mkvgen/tst/samples/gif011.jpg b/tst/mkvgen/samples/gif011.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif011.jpg rename to tst/mkvgen/samples/gif011.jpg diff --git a/src/mkvgen/tst/samples/gif012.jpg b/tst/mkvgen/samples/gif012.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif012.jpg rename to tst/mkvgen/samples/gif012.jpg diff --git a/src/mkvgen/tst/samples/gif013.jpg b/tst/mkvgen/samples/gif013.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif013.jpg rename to tst/mkvgen/samples/gif013.jpg diff --git a/src/mkvgen/tst/samples/gif014.jpg b/tst/mkvgen/samples/gif014.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif014.jpg rename to tst/mkvgen/samples/gif014.jpg diff --git a/src/mkvgen/tst/samples/gif015.jpg b/tst/mkvgen/samples/gif015.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif015.jpg rename to tst/mkvgen/samples/gif015.jpg diff --git a/src/mkvgen/tst/samples/gif016.jpg b/tst/mkvgen/samples/gif016.jpg similarity index 100% rename from src/mkvgen/tst/samples/gif016.jpg rename to tst/mkvgen/samples/gif016.jpg diff --git a/src/state/tst/StateApiFunctionalityTest.cpp b/tst/state/StateApiFunctionalityTest.cpp similarity index 96% rename from src/state/tst/StateApiFunctionalityTest.cpp rename to tst/state/StateApiFunctionalityTest.cpp index 1d1f27508..ac9e9e64e 100644 --- a/src/state/tst/StateApiFunctionalityTest.cpp +++ b/tst/state/StateApiFunctionalityTest.cpp @@ -236,4 +236,16 @@ TEST_F(StateApiFunctionalityTest, checkStateErrorHandlerOnStateTransitions) // State's local retry count should be zero since no retries are // happening within this state. EXPECT_EQ(0, pStateMachineImpl->context.localStateRetryCount); -} \ No newline at end of file +} + +TEST_F(StateApiFunctionalityTest, checkForStateMachineTransitionBasicTest) +{ + PStateMachineState pStateMachineState = NULL; + PStateMachineImpl pStateMachineImpl = (PStateMachineImpl) mStateMachine; + BOOL transition = FALSE; + + EXPECT_EQ(STATUS_SUCCESS, stepStateMachine(mStateMachine)); + EXPECT_EQ(STATUS_SUCCESS, checkForStateTransition(mStateMachine, &transition)); + EXPECT_EQ(TRUE, transition); +} + diff --git a/src/state/tst/StateApiTest.cpp b/tst/state/StateApiTest.cpp similarity index 52% rename from src/state/tst/StateApiTest.cpp rename to tst/state/StateApiTest.cpp index 6eb0759e1..320aac3c4 100644 --- a/src/state/tst/StateApiTest.cpp +++ b/tst/state/StateApiTest.cpp @@ -71,3 +71,47 @@ TEST_F(StateApiTest, resetStateMachineRetryCount_InvalidInput) EXPECT_EQ(STATUS_SUCCESS, resetStateMachineRetryCount(mStateMachine)); } + +TEST_F(StateApiTest, checkForStateTransition_InvalidInput) +{ + BOOL testBool = FALSE; + EXPECT_NE(STATUS_SUCCESS, checkForStateTransition(NULL,NULL)); + EXPECT_NE(STATUS_SUCCESS, checkForStateTransition(NULL,&testBool)); + EXPECT_NE(STATUS_SUCCESS, checkForStateTransition(mStateMachine,NULL)); + + EXPECT_EQ(STATUS_SUCCESS, checkForStateTransition(mStateMachine, &testBool)); +} + +TEST_F(StateApiTest, createStateMachineWithName_InvalidArg) +{ + PStateMachine pStateMachine = NULL; + CHAR longRandomName[34] = "abcdefghijklmnopqrstuvwxyzABCDEFG"; + EXPECT_EQ(STATUS_NULL_ARG, createStateMachineWithName(TEST_STATE_MACHINE_STATES, TEST_STATE_MACHINE_STATE_COUNT, + (UINT64) this, kinesisVideoStreamDefaultGetCurrentTime, + (UINT64) this, NULL, &pStateMachine)); + EXPECT_EQ(NULL, getStateMachineName(pStateMachine)); + EXPECT_EQ(STATUS_STATE_MACHINE_NAME_LEN_INVALID, createStateMachineWithName(TEST_STATE_MACHINE_STATES, TEST_STATE_MACHINE_STATE_COUNT, + (UINT64) this, kinesisVideoStreamDefaultGetCurrentTime, + (UINT64) this, (PCHAR)"", &pStateMachine)); + EXPECT_EQ(NULL, getStateMachineName(pStateMachine)); + EXPECT_EQ(STATUS_STATE_MACHINE_NAME_LEN_INVALID, createStateMachineWithName(TEST_STATE_MACHINE_STATES, TEST_STATE_MACHINE_STATE_COUNT, + (UINT64) this, kinesisVideoStreamDefaultGetCurrentTime, + (UINT64) this, longRandomName, &pStateMachine)); + EXPECT_EQ(STATUS_SUCCESS, freeStateMachine(pStateMachine)); +} + +TEST_F(StateApiTest, createStateMachineWithName_ValidArg) +{ + PStateMachine pStateMachine; + CHAR maxLengthRandomName[33] = "abcdefghijklmnopqrstuvwxyzABCDEF"; + EXPECT_EQ(STATUS_SUCCESS, createStateMachineWithName(TEST_STATE_MACHINE_STATES, TEST_STATE_MACHINE_STATE_COUNT, + (UINT64) this, kinesisVideoStreamDefaultGetCurrentTime, + (UINT64) this, (PCHAR) "Test", &pStateMachine)); + EXPECT_STREQ("Test", getStateMachineName(pStateMachine)); + EXPECT_EQ(STATUS_SUCCESS, freeStateMachine(pStateMachine)); + EXPECT_EQ(STATUS_SUCCESS, createStateMachineWithName(TEST_STATE_MACHINE_STATES, TEST_STATE_MACHINE_STATE_COUNT, + (UINT64) this, kinesisVideoStreamDefaultGetCurrentTime, + (UINT64) this, maxLengthRandomName, &pStateMachine)); + EXPECT_STREQ(maxLengthRandomName, getStateMachineName(pStateMachine)); + EXPECT_EQ(STATUS_SUCCESS, freeStateMachine(pStateMachine)); +} \ No newline at end of file diff --git a/src/state/tst/StateTestFixture.cpp b/tst/state/StateTestFixture.cpp similarity index 100% rename from src/state/tst/StateTestFixture.cpp rename to tst/state/StateTestFixture.cpp diff --git a/src/state/tst/StateTestFixture.h b/tst/state/StateTestFixture.h similarity index 100% rename from src/state/tst/StateTestFixture.h rename to tst/state/StateTestFixture.h diff --git a/src/utils/tst/suppressions/TSAN.supp b/tst/suppressions/TSAN.supp similarity index 100% rename from src/utils/tst/suppressions/TSAN.supp rename to tst/suppressions/TSAN.supp diff --git a/src/utils/tst/suppressions/UBSAN.supp b/tst/suppressions/UBSAN.supp similarity index 100% rename from src/utils/tst/suppressions/UBSAN.supp rename to tst/suppressions/UBSAN.supp diff --git a/src/trace/tst/TraceApiFunctionalityTest.cpp b/tst/trace/TraceApiFunctionalityTest.cpp similarity index 100% rename from src/trace/tst/TraceApiFunctionalityTest.cpp rename to tst/trace/TraceApiFunctionalityTest.cpp diff --git a/src/trace/tst/TraceApiTest.cpp b/tst/trace/TraceApiTest.cpp similarity index 100% rename from src/trace/tst/TraceApiTest.cpp rename to tst/trace/TraceApiTest.cpp diff --git a/src/trace/tst/TraceTestFixture.cpp b/tst/trace/TraceTestFixture.cpp similarity index 100% rename from src/trace/tst/TraceTestFixture.cpp rename to tst/trace/TraceTestFixture.cpp diff --git a/src/trace/tst/TraceTestFixture.h b/tst/trace/TraceTestFixture.h similarity index 100% rename from src/trace/tst/TraceTestFixture.h rename to tst/trace/TraceTestFixture.h diff --git a/src/utils/tst/BitField.cpp b/tst/utils/BitField.cpp similarity index 100% rename from src/utils/tst/BitField.cpp rename to tst/utils/BitField.cpp diff --git a/src/utils/tst/BitReader.cpp b/tst/utils/BitReader.cpp similarity index 100% rename from src/utils/tst/BitReader.cpp rename to tst/utils/BitReader.cpp diff --git a/src/utils/tst/Crc32Test.cpp b/tst/utils/Crc32Test.cpp similarity index 100% rename from src/utils/tst/Crc32Test.cpp rename to tst/utils/Crc32Test.cpp diff --git a/src/utils/tst/Directory.cpp b/tst/utils/Directory.cpp similarity index 97% rename from src/utils/tst/Directory.cpp rename to tst/utils/Directory.cpp index de55a664c..8ffa70974 100644 --- a/src/utils/tst/Directory.cpp +++ b/tst/utils/Directory.cpp @@ -67,7 +67,7 @@ STATUS createSubDirStruct(PCHAR dirPath, UINT32 depth, UINT32 numberOfFiles, UIN // Create the files first for (UINT32 i = 0; i < numberOfFiles; i++) { // Create the file path - SPRINTF(temp, FPATHSEPARATOR_STR "file_%03d.tmp", i); + SNPRINTF(temp, 32, FPATHSEPARATOR_STR "file_%03d.tmp", i); STRCAT(dirPath, temp); // Create/write the file @@ -83,7 +83,7 @@ STATUS createSubDirStruct(PCHAR dirPath, UINT32 depth, UINT32 numberOfFiles, UIN // Create the directories and iterate for (UINT32 i = 0; i < numberOfDirectories; i++) { // Create the file path - SPRINTF(temp, FPATHSEPARATOR_STR "tmp_dir_%03d", i); + SNPRINTF(temp, 32, FPATHSEPARATOR_STR "tmp_dir_%03d", i); STRCAT(dirPath, temp); // Create the dir diff --git a/src/utils/tst/DoubleLinkedList.cpp b/tst/utils/DoubleLinkedList.cpp similarity index 100% rename from src/utils/tst/DoubleLinkedList.cpp rename to tst/utils/DoubleLinkedList.cpp diff --git a/src/utils/tst/Endianness.cpp b/tst/utils/Endianness.cpp similarity index 100% rename from src/utils/tst/Endianness.cpp rename to tst/utils/Endianness.cpp diff --git a/src/utils/tst/ExponentialBackoffUtilsTest.cpp b/tst/utils/ExponentialBackoffUtilsTest.cpp similarity index 100% rename from src/utils/tst/ExponentialBackoffUtilsTest.cpp rename to tst/utils/ExponentialBackoffUtilsTest.cpp diff --git a/tst/utils/FileIo.cpp b/tst/utils/FileIo.cpp new file mode 100644 index 000000000..f8514e4f5 --- /dev/null +++ b/tst/utils/FileIo.cpp @@ -0,0 +1,159 @@ +#include "UtilTestFixture.h" + +#ifdef _WIN32 +#define TEST_TEMP_DIR_PATH (PCHAR) "C:\\Windows\\Temp\\" +#define TEST_TEMP_DIR_PATH_NO_ENDING_SEPARTOR (PCHAR) "C:\\Windows\\Temp" +#else +#define TEST_TEMP_DIR_PATH (PCHAR) "/tmp/" +#define TEST_TEMP_DIR_PATH_NO_ENDING_SEPARTOR (PCHAR) "/tmp" +#endif + +class FileIoFunctionalityTest : public UtilTestBase { +}; + +class FileIoUnitTest : public UtilTestBase { +}; + +TEST_F(FileIoUnitTest, readFile_filePathNull) { + EXPECT_EQ(STATUS_NULL_ARG, readFile(NULL, TRUE, NULL, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, readFile(NULL, FALSE, NULL, NULL)); +} + +TEST_F(FileIoUnitTest, readFile_sizeNull) { + EXPECT_EQ(STATUS_NULL_ARG, readFile((PCHAR) (TEST_TEMP_DIR_PATH "test"), TRUE, NULL, NULL)); + EXPECT_EQ(STATUS_NULL_ARG, readFile((PCHAR) (TEST_TEMP_DIR_PATH "test"), FALSE, NULL, NULL)); +} + +TEST_F(FileIoUnitTest, readFile_asciiBufferTooSmall) { + FILE *file = FOPEN((PCHAR) (TEST_TEMP_DIR_PATH "asciitest"), "w"); + UINT64 fileSize = 43; + PCHAR fileBuffer = NULL; + + fprintf(file, "This is line 1\nThis is line 2\nThis is line 3\n"); + FCLOSE(file); + + EXPECT_EQ(STATUS_SUCCESS, readFile((PCHAR) (TEST_TEMP_DIR_PATH "asciitest"), FALSE, NULL, &fileSize)); + +// In Windows systems, the newline character is represented by a combination of +// two characters: Carriage Return (CR) followed by Line Feed (LF), written as \r\n. +// So, each newline character in a text file on Windows contributes two bytes to the file size. +#if defined _WIN32 || defined _WIN64 + EXPECT_EQ(48, fileSize); +#else + EXPECT_EQ(45, fileSize); +#endif + + fileSize = 43; + + fileBuffer = (PCHAR) MEMCALLOC(1, (fileSize + 1) * SIZEOF(CHAR)); + EXPECT_EQ(STATUS_BUFFER_TOO_SMALL, readFile((PCHAR) (TEST_TEMP_DIR_PATH "asciitest"), FALSE, (PBYTE) fileBuffer, &fileSize)); + + remove((PCHAR) (TEST_TEMP_DIR_PATH "asciitest")); + SAFE_MEMFREE(fileBuffer); +} + +TEST_F(FileIoUnitTest, readFile_binaryBufferTooSmall) { + FILE *file = fopen((PCHAR) (TEST_TEMP_DIR_PATH "binarytest"), "wb"); + UINT64 fileSize = 43; + PCHAR fileBuffer = NULL; + CHAR data[100] = "This is line 1\nThis is line 2\nThis is line 3\n"; + + FWRITE(data, SIZEOF(CHAR), STRLEN(data), file); + FCLOSE(file); + + EXPECT_EQ(STATUS_SUCCESS, readFile((PCHAR) (TEST_TEMP_DIR_PATH "binarytest"), TRUE, NULL, &fileSize)); + EXPECT_EQ(45, fileSize); + + fileSize = 43; + + fileBuffer = (PCHAR) MEMCALLOC(1, (fileSize + 1) * SIZEOF(CHAR)); + EXPECT_EQ(STATUS_BUFFER_TOO_SMALL, readFile((PCHAR) (TEST_TEMP_DIR_PATH "binarytest"), TRUE, (PBYTE) fileBuffer, &fileSize)); + + remove((PCHAR) (TEST_TEMP_DIR_PATH "binarytest")); + SAFE_MEMFREE(fileBuffer); +} + +TEST_F(FileIoFunctionalityTest, readFile_asciiFileSize) { + FILE *file = FOPEN((PCHAR) (TEST_TEMP_DIR_PATH "asciitest"), "w"); + CHAR fileBuffer[256]; + UINT64 fileSize = ARRAY_SIZE(fileBuffer); + + fprintf(file, "This is line 1\nThis is line 2\nThis is line 3\n"); + FCLOSE(file); + + EXPECT_EQ(STATUS_SUCCESS, readFile((PCHAR) (TEST_TEMP_DIR_PATH "asciitest"), FALSE, NULL, &fileSize)); + +// In Windows systems, the newline character is represented by a combination of +// two characters: Carriage Return (CR) followed by Line Feed (LF), written as \r\n. +// So, each newline character in a text file on Windows contributes two bytes to the file size. +#if defined _WIN32 || defined _WIN64 + EXPECT_EQ(48, fileSize); +#else + EXPECT_EQ(45, fileSize); +#endif + + remove((PCHAR) (TEST_TEMP_DIR_PATH "asciitest")); +} + +TEST_F(FileIoFunctionalityTest, readFile_binaryFileSize) { + FILE *file = fopen((PCHAR) (TEST_TEMP_DIR_PATH "binarytest"), "wb"); + CHAR fileBuffer[256]; + CHAR data[100] = "This is line 1\nThis is line 2\nThis is line 3\n"; + UINT64 fileSize = ARRAY_SIZE(fileBuffer); + + FWRITE(data, SIZEOF(CHAR), STRLEN(data), file); + FCLOSE(file); + + EXPECT_EQ(STATUS_SUCCESS, readFile((PCHAR)(TEST_TEMP_DIR_PATH "binarytest"), TRUE, NULL, &fileSize)); + EXPECT_EQ(45, fileSize); + + remove((PCHAR) (TEST_TEMP_DIR_PATH "binarytest")); +} + +TEST_F(FileIoFunctionalityTest, readFile_binary) { + FILE *file = fopen((PCHAR) (TEST_TEMP_DIR_PATH "binarytest"), "wb"); + UINT64 fileSize; + PCHAR fileBuffer = NULL; + CHAR data[100] = "This is line 1\nThis is line 2\nThis is line 3\n"; + + FWRITE(data, SIZEOF(CHAR), STRLEN(data), file); + FCLOSE(file); + + EXPECT_EQ(STATUS_SUCCESS, readFile((PCHAR) (TEST_TEMP_DIR_PATH "binarytest"), TRUE, NULL, &fileSize)); + EXPECT_EQ(45, fileSize); + + fileBuffer = (PCHAR) MEMCALLOC(1, (fileSize + 1) * SIZEOF(CHAR)); + EXPECT_EQ(STATUS_SUCCESS, readFile((PCHAR) (TEST_TEMP_DIR_PATH "binarytest"), TRUE, (PBYTE) fileBuffer, &fileSize)); + EXPECT_STREQ(data, fileBuffer); + + remove((PCHAR) (TEST_TEMP_DIR_PATH "binarytest")); + SAFE_MEMFREE(fileBuffer); +} + +TEST_F(FileIoFunctionalityTest, readFile_ascii) { + FILE *file = fopen((PCHAR) (TEST_TEMP_DIR_PATH "asciitest"), "w"); + UINT64 fileSize; + PCHAR fileBuffer = NULL; + CHAR data[100] = "This is line 1\nThis is line 2\nThis is line 3\n"; + + FWRITE(data, SIZEOF(CHAR), STRLEN(data), file); + FCLOSE(file); + + EXPECT_EQ(STATUS_SUCCESS, readFile((PCHAR) (TEST_TEMP_DIR_PATH "asciitest"), FALSE, NULL, &fileSize)); + +// In Windows systems, the newline character is represented by a combination of +// two characters: Carriage Return (CR) followed by Line Feed (LF), written as \r\n. +// So, each newline character in a text file on Windows contributes two bytes to the file size. +#if defined _WIN32 || defined _WIN64 + EXPECT_EQ(48, fileSize); +#else + EXPECT_EQ(45, fileSize); +#endif + + fileBuffer = (PCHAR) MEMCALLOC(1, (fileSize + 1) * SIZEOF(CHAR)); + EXPECT_EQ(STATUS_SUCCESS, readFile((PCHAR) (TEST_TEMP_DIR_PATH "asciitest"), FALSE, (PBYTE) fileBuffer, &fileSize)); + EXPECT_STREQ(data, fileBuffer); + + remove((PCHAR) (TEST_TEMP_DIR_PATH "asciitest")); + SAFE_MEMFREE(fileBuffer); +} \ No newline at end of file diff --git a/src/utils/tst/FileLogger.cpp b/tst/utils/FileLogger.cpp similarity index 99% rename from src/utils/tst/FileLogger.cpp rename to tst/utils/FileLogger.cpp index b877a6009..2f0d62434 100644 --- a/src/utils/tst/FileLogger.cpp +++ b/tst/utils/FileLogger.cpp @@ -98,7 +98,7 @@ TEST_F(FileLoggerTest, checkFileRotation) // make sure the files dont exist FREMOVE(TEST_TEMP_DIR_PATH "kvsFileLogIndex"); for(; i < logIterationCount; ++i) { - SPRINTF(filePath, TEST_TEMP_DIR_PATH "kvsFileLog.%u", i); + SNPRINTF(filePath, 1024, TEST_TEMP_DIR_PATH "kvsFileLog.%u", i); FREMOVE(filePath); } @@ -115,7 +115,7 @@ TEST_F(FileLoggerTest, checkFileRotation) RELEASE_FILE_LOGGER(); for(i = 0; i < logIterationCount; ++i) { - SPRINTF(filePath, TEST_TEMP_DIR_PATH "kvsFileLog.%u", i); + SNPRINTF(filePath, 1024, TEST_TEMP_DIR_PATH "kvsFileLog.%u", i); EXPECT_EQ(STATUS_SUCCESS, fileExists(filePath, &fileFound)); // only log file with index from 6 to 11 should remain. The rest are rotated out. if (i < logIterationCount && i >= (logIterationCount - maxLogFileCount)) { diff --git a/src/utils/tst/FindStringTest.cpp b/tst/utils/FindStringTest.cpp similarity index 100% rename from src/utils/tst/FindStringTest.cpp rename to tst/utils/FindStringTest.cpp diff --git a/src/utils/tst/HashTable.cpp b/tst/utils/HashTable.cpp similarity index 100% rename from src/utils/tst/HashTable.cpp rename to tst/utils/HashTable.cpp diff --git a/src/utils/tst/InstrumentedAllocators.cpp b/tst/utils/InstrumentedAllocators.cpp similarity index 100% rename from src/utils/tst/InstrumentedAllocators.cpp rename to tst/utils/InstrumentedAllocators.cpp diff --git a/src/utils/tst/IntegerToString.cpp b/tst/utils/IntegerToString.cpp similarity index 100% rename from src/utils/tst/IntegerToString.cpp rename to tst/utils/IntegerToString.cpp diff --git a/src/utils/tst/MathTest.cpp b/tst/utils/MathTest.cpp similarity index 100% rename from src/utils/tst/MathTest.cpp rename to tst/utils/MathTest.cpp diff --git a/src/utils/tst/Semaphore.cpp b/tst/utils/Semaphore.cpp similarity index 100% rename from src/utils/tst/Semaphore.cpp rename to tst/utils/Semaphore.cpp diff --git a/src/utils/tst/SingleLinkedList.cpp b/tst/utils/SingleLinkedList.cpp similarity index 100% rename from src/utils/tst/SingleLinkedList.cpp rename to tst/utils/SingleLinkedList.cpp diff --git a/src/utils/tst/StackQueue.cpp b/tst/utils/StackQueue.cpp similarity index 100% rename from src/utils/tst/StackQueue.cpp rename to tst/utils/StackQueue.cpp diff --git a/src/utils/tst/StringSearch.cpp b/tst/utils/StringSearch.cpp similarity index 100% rename from src/utils/tst/StringSearch.cpp rename to tst/utils/StringSearch.cpp diff --git a/src/utils/tst/StringToInteger.cpp b/tst/utils/StringToInteger.cpp similarity index 100% rename from src/utils/tst/StringToInteger.cpp rename to tst/utils/StringToInteger.cpp diff --git a/src/utils/tst/Tags.cpp b/tst/utils/Tags.cpp similarity index 100% rename from src/utils/tst/Tags.cpp rename to tst/utils/Tags.cpp diff --git a/src/utils/tst/Thread.cpp b/tst/utils/Thread.cpp similarity index 59% rename from src/utils/tst/Thread.cpp rename to tst/utils/Thread.cpp index 440751e06..196d28811 100644 --- a/src/utils/tst/Thread.cpp +++ b/tst/utils/Thread.cpp @@ -5,33 +5,37 @@ class ThreadFunctionalityTest : public UtilTestBase { #define TEST_THREAD_COUNT 500 -UINT64 gThreadCurrentCount = 0; -UINT64 gThreadSleepTimes[TEST_THREAD_COUNT]; -BOOL gThreadVisited[TEST_THREAD_COUNT]; -BOOL gThreadCleared[TEST_THREAD_COUNT]; MUTEX gThreadMutex; +UINT64 gThreadCount; + +struct sleep_times { + BOOL threadSleepTime; + BOOL threadVisited; + BOOL threadCleared; +}; PVOID testThreadRoutine(PVOID arg) { MUTEX_LOCK(gThreadMutex); - gThreadCurrentCount++; - MUTEX_UNLOCK(gThreadMutex); - - UINT64 index = (UINT64) arg; + gThreadCount++; + struct sleep_times* st = (struct sleep_times*) arg; // Mark as visited - gThreadVisited[index] = TRUE; + st->threadVisited = TRUE; + MUTEX_UNLOCK(gThreadMutex); + + UINT64 sleepTime = st->threadSleepTime; // Just sleep for some time - THREAD_SLEEP(gThreadSleepTimes[index]); + THREAD_SLEEP(sleepTime); + + MUTEX_LOCK(gThreadMutex); // Mark as cleared - gThreadCleared[index] = TRUE; + st->threadCleared = TRUE; - MUTEX_LOCK(gThreadMutex); - gThreadCurrentCount--; + gThreadCount--; MUTEX_UNLOCK(gThreadMutex); - return NULL; } @@ -40,13 +44,14 @@ TEST_F(ThreadFunctionalityTest, ThreadCreateAndReleaseSimpleCheck) UINT64 index; TID threads[TEST_THREAD_COUNT]; gThreadMutex = MUTEX_CREATE(FALSE); + struct sleep_times st[TEST_THREAD_COUNT]; // Create the threads for (index = 0; index < TEST_THREAD_COUNT; index++) { - gThreadVisited[index] = FALSE; - gThreadCleared[index] = FALSE; - gThreadSleepTimes[index] = index * HUNDREDS_OF_NANOS_IN_A_MILLISECOND; - EXPECT_EQ(STATUS_SUCCESS, THREAD_CREATE(&threads[index], testThreadRoutine, (PVOID)index)); + st[index].threadVisited = FALSE; + st[index].threadCleared = FALSE; + st[index].threadSleepTime = index * HUNDREDS_OF_NANOS_IN_A_MILLISECOND; + EXPECT_EQ(STATUS_SUCCESS, THREAD_CREATE(&threads[index], testThreadRoutine, (PVOID)&st[index])); } // Await for the threads to finish @@ -54,11 +59,13 @@ TEST_F(ThreadFunctionalityTest, ThreadCreateAndReleaseSimpleCheck) EXPECT_EQ(STATUS_SUCCESS, THREAD_JOIN(threads[index], NULL)); } - EXPECT_EQ(0, gThreadCurrentCount); + MUTEX_LOCK(gThreadMutex); + EXPECT_EQ(0, gThreadCount); + MUTEX_UNLOCK(gThreadMutex); for (index = 0; index < TEST_THREAD_COUNT; index++) { - EXPECT_TRUE(gThreadVisited[index]) << "Thread didn't visit index " << index; - EXPECT_TRUE(gThreadCleared[index]) << "Thread didn't clear index " << index; + EXPECT_TRUE(st[index].threadVisited) << "Thread didn't visit index " << index; + EXPECT_TRUE(st[index].threadCleared) << "Thread didn't clear index " << index; } MUTEX_FREE(gThreadMutex); @@ -69,14 +76,17 @@ TEST_F(ThreadFunctionalityTest, ThreadCreateAndCancel) UINT64 index; TID threads[TEST_THREAD_COUNT]; gThreadMutex = MUTEX_CREATE(FALSE); + struct sleep_times st[TEST_THREAD_COUNT]; // Create the threads for (index = 0; index < TEST_THREAD_COUNT; index++) { - gThreadVisited[index] = FALSE; - gThreadCleared[index] = FALSE; + + st[index].threadVisited = FALSE; + st[index].threadCleared = FALSE; // Long sleep - gThreadSleepTimes[index] = 20 * HUNDREDS_OF_NANOS_IN_A_SECOND; - EXPECT_EQ(STATUS_SUCCESS, THREAD_CREATE(&threads[index], testThreadRoutine, (PVOID)index)); + st[index].threadSleepTime = 20 * HUNDREDS_OF_NANOS_IN_A_SECOND; + + EXPECT_EQ(STATUS_SUCCESS, THREAD_CREATE(&threads[index], testThreadRoutine, (PVOID)&st[index])); #if !(defined _WIN32 || defined _WIN64 || defined __CYGWIN__) // We should detach thread for non-windows platforms only // Windows implementation would cancel the handle and the @@ -93,14 +103,17 @@ TEST_F(ThreadFunctionalityTest, ThreadCreateAndCancel) EXPECT_EQ(STATUS_SUCCESS, THREAD_CANCEL(threads[index])); } + // Validate that threads have been killed and didn't finish successfully - EXPECT_EQ(TEST_THREAD_COUNT, gThreadCurrentCount); + MUTEX_LOCK(gThreadMutex); + EXPECT_EQ(TEST_THREAD_COUNT, gThreadCount); for (index = 0; index < TEST_THREAD_COUNT; index++) { - EXPECT_TRUE(gThreadVisited[index]) << "Thread didn't visit index " << index; - EXPECT_FALSE(gThreadCleared[index]) << "Thread shouldn't have cleared index " << index; + EXPECT_TRUE(st[index].threadVisited) << "Thread didn't visit index " << index; + EXPECT_FALSE(st[index].threadCleared) << "Thread shouldn't have cleared index " << index; } - gThreadCurrentCount = 0; + MUTEX_UNLOCK(gThreadMutex); + MUTEX_FREE(gThreadMutex); } diff --git a/tst/utils/Threadpool.cpp b/tst/utils/Threadpool.cpp new file mode 100644 index 000000000..2b6959ccc --- /dev/null +++ b/tst/utils/Threadpool.cpp @@ -0,0 +1,271 @@ +#include "UtilTestFixture.h" +#include +#include + +class ThreadpoolFunctionalityTest : public UtilTestBase { +}; + +MUTEX gTerminateMutex; +SEMAPHORE_HANDLE gTerminateSemaphore; +UINT8 gTerminateCount = 0; + + +PVOID randomishTask(PVOID customData) +{ + THREAD_SLEEP((rand() % 20 + 100) * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + return 0; +} + +PVOID exitOnTeardownTask(PVOID customData) +{ + BOOL* pTerminate = (BOOL*) customData; + BOOL terminate; + MUTEX_LOCK(gTerminateMutex); + gTerminateCount++; + MUTEX_UNLOCK(gTerminateMutex); + do { + THREAD_SLEEP(20 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + MUTEX_LOCK(gTerminateMutex); + terminate = *pTerminate; + MUTEX_UNLOCK(gTerminateMutex); + } while(!terminate); + + semaphoreRelease(gTerminateSemaphore); + + return 0; +} + +TEST_F(ThreadpoolFunctionalityTest, CreateDestroyTest) +{ + PThreadpool pThreadpool = NULL; + EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, 1, 1)); + EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); + + // wait for threads to exit before test ends + THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + + EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, 1, 2)); + EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); + + // wait for threads to exit before test ends + THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + + EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, 1, 1)); + EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); + + // wait for threads to exit before test ends + THREAD_SLEEP(1 * HUNDREDS_OF_NANOS_IN_A_SECOND); +} + +TEST_F(ThreadpoolFunctionalityTest, BasicTryAddTest) +{ + PThreadpool pThreadpool = NULL; + BOOL terminate = FALSE; + auto count = 0; + gTerminateCount = 0; + gTerminateMutex = MUTEX_CREATE(FALSE); + EXPECT_EQ(STATUS_SUCCESS, semaphoreEmptyCreate(10, &gTerminateSemaphore)); + srand(GETTIME()); + EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, 1, 1)); + + // sleep for a little. Create asynchronously detaches threads, so using TryAdd too soon can + // fail if the threads are not yet ready. + THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + + EXPECT_EQ(STATUS_SUCCESS, threadpoolTryAdd(pThreadpool, exitOnTeardownTask, &terminate)); + EXPECT_EQ(STATUS_THREADPOOL_MAX_COUNT, threadpoolTryAdd(pThreadpool, exitOnTeardownTask, &terminate)); + MUTEX_LOCK(gTerminateMutex); + terminate = TRUE; + MUTEX_UNLOCK(gTerminateMutex); + EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); + + MUTEX_LOCK(gTerminateMutex); + count = gTerminateCount; + MUTEX_UNLOCK(gTerminateMutex); + + // wait for threads to exit before test ends + for(auto i = 0; i < count; i++) { + EXPECT_EQ(STATUS_SUCCESS, semaphoreAcquire(gTerminateSemaphore, INFINITE_TIME_VALUE)); + } + EXPECT_EQ(STATUS_SUCCESS, semaphoreFree(&gTerminateSemaphore)); + MUTEX_FREE(gTerminateMutex); + THREAD_SLEEP(1 * HUNDREDS_OF_NANOS_IN_A_SECOND); +} + +TEST_F(ThreadpoolFunctionalityTest, BasicPushTest) +{ + gTerminateCount = 0; + gTerminateMutex = MUTEX_CREATE(FALSE); + EXPECT_EQ(STATUS_SUCCESS, semaphoreEmptyCreate(10, &gTerminateSemaphore)); + PThreadpool pThreadpool = NULL; + BOOL terminate = FALSE; + UINT32 count = 0; + srand(GETTIME()); + EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, 1, 2)); + EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); + EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); + EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); + EXPECT_EQ(STATUS_SUCCESS, threadpoolTotalThreadCount(pThreadpool, &count)); + EXPECT_EQ(count, 2); + MUTEX_LOCK(gTerminateMutex); + terminate = TRUE; + MUTEX_UNLOCK(gTerminateMutex); + EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); + + MUTEX_LOCK(gTerminateMutex); + auto enteredThreadCount = gTerminateCount; + MUTEX_UNLOCK(gTerminateMutex); + + // wait for threads to exit before test ends + for(auto i = 0; i < enteredThreadCount; i++) { + EXPECT_EQ(STATUS_SUCCESS, semaphoreAcquire(gTerminateSemaphore, INFINITE_TIME_VALUE)); + } + EXPECT_EQ(STATUS_SUCCESS, semaphoreFree(&gTerminateSemaphore)); + MUTEX_FREE(gTerminateMutex); + THREAD_SLEEP(1 * HUNDREDS_OF_NANOS_IN_A_SECOND); +} + +TEST_F(ThreadpoolFunctionalityTest, GetThreadCountTest) +{ + gTerminateCount = 0; + gTerminateMutex = MUTEX_CREATE(FALSE); + EXPECT_EQ(STATUS_SUCCESS, semaphoreEmptyCreate(10, &gTerminateSemaphore)); + PThreadpool pThreadpool = NULL; + UINT32 count = 0; + BOOL terminate = FALSE; + srand(GETTIME()); + const UINT32 max = 10; + // accepted race condition where min is 1, threadpoolPush can create a new thread + // before the first thread is ready to accept tasks + UINT32 min = rand() % (max / 2) + 2; + EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, min, max)); + // let threads get ready so new threads aren't created for this task + THREAD_SLEEP(200 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + EXPECT_EQ(STATUS_SUCCESS, threadpoolTotalThreadCount(pThreadpool, &count)); + EXPECT_EQ(count, min); + + for (UINT32 i = 0; i < min; i++) { + EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); + } + // Threads have to wake up and accept tasks. Put a small sleep here so that we know the tasks + // have started before the next push. + THREAD_SLEEP(20 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + + // no new threads created + EXPECT_EQ(STATUS_SUCCESS, threadpoolTotalThreadCount(pThreadpool, &count)); + EXPECT_EQ(count, min); + + // another thread needed + EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); + EXPECT_EQ(STATUS_SUCCESS, threadpoolTotalThreadCount(pThreadpool, &count)); + EXPECT_EQ(count, min + 1); + + MUTEX_LOCK(gTerminateMutex); + terminate = TRUE; + MUTEX_UNLOCK(gTerminateMutex); + + EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); + + MUTEX_LOCK(gTerminateMutex); + auto enteredThreadCount = gTerminateCount; + MUTEX_UNLOCK(gTerminateMutex); + + // wait for threads to exit before test ends + for(auto i = 0; i < enteredThreadCount; i++) { + EXPECT_EQ(STATUS_SUCCESS, semaphoreAcquire(gTerminateSemaphore, INFINITE_TIME_VALUE)); + } + EXPECT_EQ(STATUS_SUCCESS, semaphoreFree(&gTerminateSemaphore)); + MUTEX_FREE(gTerminateMutex); + THREAD_SLEEP(1 * HUNDREDS_OF_NANOS_IN_A_SECOND); +} + +TEST_F(ThreadpoolFunctionalityTest, ThreadsExitGracefullyAfterThreadpoolFreeTest) +{ + gTerminateCount = 0; + gTerminateMutex = MUTEX_CREATE(FALSE); + EXPECT_EQ(STATUS_SUCCESS, semaphoreEmptyCreate(10, &gTerminateSemaphore)); + PThreadpool pThreadpool = NULL; + UINT32 count = 0; + BOOL terminate = FALSE; + srand(GETTIME()); + const UINT32 max = 10; + UINT32 min = rand() % (max / 2) + 2; + EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, min, max)); + for (UINT32 i = 0; i < min; i++) { + EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, exitOnTeardownTask, &terminate)); + } + for (UINT32 i = min; i < max; i++) { + EXPECT_EQ(STATUS_SUCCESS, threadpoolPush(pThreadpool, randomishTask, NULL)); + } + THREAD_SLEEP(110 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); + // now threads will exit after threadpoolFree + MUTEX_LOCK(gTerminateMutex); + terminate = TRUE; + MUTEX_UNLOCK(gTerminateMutex); + + MUTEX_LOCK(gTerminateMutex); + auto enteredThreadCount = gTerminateCount; + MUTEX_UNLOCK(gTerminateMutex); + + // wait for threads to exit before test ends + for(auto i = 0; i < enteredThreadCount; i++) { + EXPECT_EQ(STATUS_SUCCESS, semaphoreAcquire(gTerminateSemaphore, INFINITE_TIME_VALUE)); + } + EXPECT_EQ(STATUS_SUCCESS, semaphoreFree(&gTerminateSemaphore)); + MUTEX_FREE(gTerminateMutex); + THREAD_SLEEP(1 * HUNDREDS_OF_NANOS_IN_A_SECOND); +} + + + +typedef struct __ThreadpoolUser { + PThreadpool pThreadpool; + volatile ATOMIC_BOOL usable; +} ThreadpoolUser; + +PVOID createTasks(PVOID customData) +{ + ThreadpoolUser* user = (ThreadpoolUser*) customData; + PThreadpool pThreadpool = user->pThreadpool; + auto iterations = rand() % 20; + + for (auto i = 0; i < iterations; i++) { + THREAD_SLEEP((rand() % 5 + 1) * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + // allowed to fail as we may delete the threadpool early + if (ATOMIC_LOAD_BOOL(&user->usable)) { + if (threadpoolPush(pThreadpool, randomishTask, NULL) != STATUS_SUCCESS) { + break; + } + } else { + break; + } + } + return 0; +} + +TEST_F(ThreadpoolFunctionalityTest, MultithreadUseTest) +{ + PThreadpool pThreadpool = NULL; + ThreadpoolUser user; + UINT32 count = 0; + srand(GETTIME()); + const UINT32 max = 10; + UINT32 min = rand() % (max / 2) + 2; + TID thread1, thread2; + EXPECT_EQ(STATUS_SUCCESS, threadpoolCreate(&pThreadpool, min, max)); + user.pThreadpool = pThreadpool; + ATOMIC_STORE_BOOL(&user.usable, TRUE); + THREAD_CREATE(&thread1, createTasks, &user); + THREAD_CREATE(&thread2, createTasks, &user); + + THREAD_SLEEP((rand() % 50 + 50) * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + ATOMIC_STORE_BOOL(&user.usable, FALSE); + EXPECT_EQ(STATUS_SUCCESS, threadpoolFree(pThreadpool)); + + THREAD_JOIN(thread1, NULL); + THREAD_JOIN(thread2, NULL); + + // wait for threads to exit before test ends to avoid false memory leak alarm + THREAD_SLEEP(1 * HUNDREDS_OF_NANOS_IN_A_SECOND); +} diff --git a/src/utils/tst/ThreadsafeBlockingQueue.cpp b/tst/utils/ThreadsafeBlockingQueue.cpp similarity index 92% rename from src/utils/tst/ThreadsafeBlockingQueue.cpp rename to tst/utils/ThreadsafeBlockingQueue.cpp index a6e0a2dfc..b2579e3aa 100644 --- a/src/utils/tst/ThreadsafeBlockingQueue.cpp +++ b/tst/utils/ThreadsafeBlockingQueue.cpp @@ -89,10 +89,10 @@ TEST_F(ThreadsafeBlockingQueueFunctionalityTest, queueCountCorrectTest) } #define STATIC_NUMBER_OF_ITEMS 50 -typedef struct SafeQueueUser { +typedef struct __SafeQueueUser { PSafeBlockingQueue pSafeQueue; volatile ATOMIC_BOOL usable; -}; +} SafeQueueUser; void* writingThread(void* ptr) { SafeQueueUser * user = (SafeQueueUser*)ptr; @@ -144,13 +144,13 @@ TEST_F(ThreadsafeBlockingQueueFunctionalityTest, multithreadQueueDequeueTest) user.pSafeQueue = pSafeQueue; ATOMIC_STORE_BOOL(&user.usable, TRUE); for(UINT64 i = 0; i < totalThreads/2; i++) { - THREAD_CREATE(&threads[threadCount++], readingThread, &user); + EXPECT_EQ(STATUS_SUCCESS, THREAD_CREATE(&threads[threadCount++], readingThread, &user)); } for(UINT64 i = 0; i < totalThreads/2; i++) { - THREAD_CREATE(&threads[threadCount++], writingThread, &user); + EXPECT_EQ(STATUS_SUCCESS, THREAD_CREATE(&threads[threadCount++], writingThread, &user)); } for(UINT64 i = 0; i < totalThreads; i++) { - THREAD_JOIN(threads[i], NULL); + EXPECT_EQ(STATUS_SUCCESS, THREAD_JOIN(threads[i], NULL)); } ATOMIC_STORE_BOOL(&user.usable, FALSE); EXPECT_EQ(STATUS_SUCCESS, safeBlockingQueueFree(pSafeQueue)); @@ -171,12 +171,12 @@ TEST_F(ThreadsafeBlockingQueueFunctionalityTest, multithreadTeardownTest) user.pSafeQueue = pSafeQueue; ATOMIC_STORE_BOOL(&user.usable, TRUE); for(UINT64 i = 0; i < totalThreads; i++) { - THREAD_CREATE(&threads[threadCount++], readingThread, &user); + EXPECT_EQ(STATUS_SUCCESS, THREAD_CREATE(&threads[threadCount++], readingThread, &user)); } THREAD_SLEEP(125 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); ATOMIC_STORE_BOOL(&user.usable, FALSE); EXPECT_EQ(STATUS_SUCCESS, safeBlockingQueueFree(pSafeQueue)); for(UINT64 i = 0; i < totalThreads; i++) { - THREAD_JOIN(threads[i], NULL); + EXPECT_EQ(STATUS_SUCCESS, THREAD_JOIN(threads[i], NULL)); } } diff --git a/src/utils/tst/TimerQueue.cpp b/tst/utils/TimerQueue.cpp similarity index 96% rename from src/utils/tst/TimerQueue.cpp rename to tst/utils/TimerQueue.cpp index 223e9e13f..8d918b554 100644 --- a/src/utils/tst/TimerQueue.cpp +++ b/tst/utils/TimerQueue.cpp @@ -789,3 +789,36 @@ TEST_F(TimerQueueFunctionalityTest, shutdownTimerQueueTest) EXPECT_EQ(STATUS_SUCCESS, timerQueueFree(&handle)); } + +TEST_F(TimerQueueFunctionalityTest, kickTimerQueueTest) +{ + TIMER_QUEUE_HANDLE handle = INVALID_TIMER_QUEUE_HANDLE_VALUE; + UINT32 timerId; + UINT64 curTime; + + // Make sure we don't check for the timer in the test callback + checkTimerId = FALSE; + + // Create a valid timer queue + EXPECT_EQ(STATUS_SUCCESS, timerQueueCreate(&handle)); + + EXPECT_EQ(STATUS_SUCCESS, timerQueueAddTimer(handle, 0, 200 * HUNDREDS_OF_NANOS_IN_A_SECOND, testTimerCallback, (UINT64) this, &timerId)); + + //let timer start + THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + EXPECT_EQ(1, ATOMIC_LOAD(&invokeCount)); + + EXPECT_EQ(STATUS_SUCCESS, timerQueueKick(handle, timerId)); + EXPECT_NE(STATUS_SUCCESS, timerQueueKick(INVALID_TIMER_QUEUE_HANDLE_VALUE, timerId)); + EXPECT_NE(STATUS_SUCCESS, timerQueueKick(handle, 0)); + + //let kick happen + THREAD_SLEEP(100 * HUNDREDS_OF_NANOS_IN_A_MILLISECOND); + //check kick occurred + EXPECT_EQ(2, ATOMIC_LOAD(&invokeCount)); + + // Calling again has no effect + EXPECT_EQ(STATUS_SUCCESS, timerQueueShutdown(handle)); + + EXPECT_EQ(STATUS_SUCCESS, timerQueueFree(&handle)); +} diff --git a/src/utils/tst/TrimStringTest.cpp b/tst/utils/TrimStringTest.cpp similarity index 100% rename from src/utils/tst/TrimStringTest.cpp rename to tst/utils/TrimStringTest.cpp diff --git a/src/utils/tst/UtilTestFixture.cpp b/tst/utils/UtilTestFixture.cpp similarity index 100% rename from src/utils/tst/UtilTestFixture.cpp rename to tst/utils/UtilTestFixture.cpp diff --git a/src/utils/tst/UtilTestFixture.h b/tst/utils/UtilTestFixture.h similarity index 89% rename from src/utils/tst/UtilTestFixture.h rename to tst/utils/UtilTestFixture.h index 47499a549..15dc5a49b 100644 --- a/src/utils/tst/UtilTestFixture.h +++ b/tst/utils/UtilTestFixture.h @@ -108,9 +108,25 @@ class UtilTestBase : public ::testing::Test { DLOGI("\nTearing down test: %s\n", GetTestName()); // Validate the allocations cleanup + if (allocatorsSet) { + MUTEX_LOCK(gUtilityMemMutex); + } DLOGI("Final remaining allocation size is %llu\n", gTotalUtilsMemoryUsage); + while (gTotalUtilsMemoryUsage != 0) { + if (allocatorsSet) { + MUTEX_UNLOCK(gUtilityMemMutex); + } + THREAD_SLEEP(1 * HUNDREDS_OF_NANOS_IN_A_SECOND); + if (allocatorsSet) { + MUTEX_LOCK(gUtilityMemMutex); + } + } + EXPECT_EQ((UINT64) 0, gTotalUtilsMemoryUsage); + if (allocatorsSet) { + MUTEX_UNLOCK(gUtilityMemMutex); + } if (allocatorsSet) { globalMemAlloc = storedMemAlloc; diff --git a/src/utils/tst/VersionsTest.cpp b/tst/utils/VersionsTest.cpp similarity index 100% rename from src/utils/tst/VersionsTest.cpp rename to tst/utils/VersionsTest.cpp diff --git a/tst/utils/suppressions/TSAN.supp b/tst/utils/suppressions/TSAN.supp new file mode 100644 index 000000000..f6c441f0f --- /dev/null +++ b/tst/utils/suppressions/TSAN.supp @@ -0,0 +1,25 @@ +# Every exception in this file should be heavily justified + +# Suppressing lock order reports from TSAN. Our tests and the PIC codebase is based on differnet layer of locks which are re-entrant. +deadlock:StreamApiTest_kinesisVideoStreamCreateSync_Valid_Test::TestBody +deadlock:StreamApiTest_kinesisVideoStreamCreateSyncStopSync_Valid_Timeout_Test +deadlock:clientReadyFunc +deadlock:describeStreamResult + +# For some reason TSAN has a false positive flagging the global logging function race condition where as we use create stream SYNC API which should have set the logger +race:globalCustomLogPrintFn + +# False-positive hit on createsync and the handle. This one is really bizarre so disabling TSAN on this particular test which simulates a network stack async stop notification +race:StreamApiTest_kinesisVideoStreamCreateSync_Valid_Test + + +# Weird failure with data race in Semaphore tests with the gtest nternal contructor and the spun threads which simulate multi-thread access. SHould not have contention due to the atomics. This is a false positive, +race:acquireThreadRoutine +race:drainThreadRoutine + +# Time-sensitive tests for the semaphore teardown without awaiting which is synchronized by thread sleeps in the test. This is OK, +race:freeWithoutReleaseAllThreadsTest + +# Another false positive on a single-threaded execution of a test. This seems to be an issue with TSAN as the test in question has no threads +deadlock:ClientTestBase::lockMutexFunc + diff --git a/tst/utils/suppressions/UBSAN.supp b/tst/utils/suppressions/UBSAN.supp new file mode 100644 index 000000000..510734b67 --- /dev/null +++ b/tst/utils/suppressions/UBSAN.supp @@ -0,0 +1,2 @@ +# Some of these suppressions are heavy handed, but can't trace them down to a single cause +signed-integer-overflow:String.c \ No newline at end of file diff --git a/src/view/tst/ViewApiFunctionalityTest.cpp b/tst/view/ViewApiFunctionalityTest.cpp similarity index 100% rename from src/view/tst/ViewApiFunctionalityTest.cpp rename to tst/view/ViewApiFunctionalityTest.cpp diff --git a/src/view/tst/ViewApiTest.cpp b/tst/view/ViewApiTest.cpp similarity index 100% rename from src/view/tst/ViewApiTest.cpp rename to tst/view/ViewApiTest.cpp diff --git a/src/view/tst/ViewDropPolicyFunctionalityTest.cpp b/tst/view/ViewDropPolicyFunctionalityTest.cpp similarity index 100% rename from src/view/tst/ViewDropPolicyFunctionalityTest.cpp rename to tst/view/ViewDropPolicyFunctionalityTest.cpp diff --git a/src/view/tst/ViewTestFixture.cpp b/tst/view/ViewTestFixture.cpp similarity index 100% rename from src/view/tst/ViewTestFixture.cpp rename to tst/view/ViewTestFixture.cpp diff --git a/src/view/tst/ViewTestFixture.h b/tst/view/ViewTestFixture.h similarity index 100% rename from src/view/tst/ViewTestFixture.h rename to tst/view/ViewTestFixture.h