diff --git a/.github/workflows/engines-cross-ci.yml b/.github/workflows/engines-cross-ci.yml new file mode 100644 index 00000000000..1500d130bb5 --- /dev/null +++ b/.github/workflows/engines-cross-ci.yml @@ -0,0 +1,230 @@ +# Cross-platform lanes for the engines (parakeet-cpp/, tts-cpp/ — engines/* +# after the repo reorg QIP; update the path filters + SRC map when it lands). +# +# Complements parakeet-ci.yml / tts-ci.yml (linux+mac build+test): +# - windows: full build + non-GPU ctest (MSVC, static ggml) +# - android: compile smoke, arm64-v8a via NDK (no device to run tests on) +# - ios: compile smoke, arm64 device slice, Metal embedded (flags +# mirror build-xcframework.sh) +# On-device e2e stays downstream in tetherto/qvac; these lanes exist to +# catch cross-compile/link breaks *before* the registry pin chain. +name: engines cross CI + +on: + push: + branches: [master] + paths: + - 'parakeet-cpp/**' + - 'tts-cpp/**' + - '.github/workflows/engines-cross-ci.yml' + pull_request: + paths: + - 'parakeet-cpp/**' + - 'tts-cpp/**' + - '.github/workflows/engines-cross-ci.yml' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: engines-cross-ci-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + GGML_REPO: https://github.com/tetherto/qvac-ext-ggml.git + GGML_BRANCH: speech # single ggml pin shared with parakeet-ci.yml / tts-ci.yml + +jobs: + windows: + strategy: + fail-fast: false + matrix: + engine: [parakeet, tts] + include: + - engine: parakeet + src: parakeet-cpp + cmake_flags: -DPARAKEET_USE_SYSTEM_GGML=ON -DPARAKEET_BUILD_TESTS=ON -DPARAKEET_BUILD_EXAMPLES=OFF + - engine: tts + src: tts-cpp + cmake_flags: -DTTS_CPP_BUILD_TESTS=ON + runs-on: windows-2022 + timeout-minutes: 90 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + + - name: Resolve ggml pin (tip of ${{ env.GGML_BRANCH }}) + id: ggml + run: echo "sha=$(git ls-remote "$GGML_REPO" "refs/heads/$GGML_BRANCH" | cut -f1)" >> "$GITHUB_OUTPUT" + + - name: Cache ggml install + id: ggml-cache + uses: actions/cache@v4 + with: + path: ggml-install + key: ggml-install-windows-2022-msvc-static-${{ steps.ggml.outputs.sha }} + + # Static ggml on Windows sidesteps DLL discovery for the test exes. + - name: Build ggml (MSVC, static, CPU-only) + if: steps.ggml-cache.outputs.cache-hit != 'true' + run: | + # Fetch the exact SHA the cache key was derived from (the branch + # tip may advance between ls-remote and this step). + git init -q ggml-src + git -C ggml-src remote add origin "$GGML_REPO" + git -C ggml-src fetch --depth 1 origin ${{ steps.ggml.outputs.sha }} + git -C ggml-src checkout -q FETCH_HEAD + cmake -S ggml-src -B ggml-src/build -A x64 \ + -DBUILD_SHARED_LIBS=OFF \ + -DGGML_BUILD_TESTS=OFF \ + -DGGML_BUILD_EXAMPLES=OFF \ + -DCMAKE_INSTALL_PREFIX="$PWD/ggml-install" + cmake --build ggml-src/build --config Release -j4 + cmake --install ggml-src/build --config Release + + - name: Configure + run: | + cmake -S ${{ matrix.src }} -B build -A x64 \ + ${{ matrix.cmake_flags }} \ + -DCMAKE_PREFIX_PATH="$PWD/ggml-install" + + - name: Build + run: cmake --build build --config Release -j4 + + - name: Test (non-GPU) + run: ctest --test-dir build -C Release -LE 'gpu|perf' --output-on-failure --timeout 600 + + android: + strategy: + fail-fast: false + matrix: + engine: [parakeet, tts] + include: + - engine: parakeet + src: parakeet-cpp + cmake_flags: -DPARAKEET_USE_SYSTEM_GGML=ON -DPARAKEET_BUILD_TESTS=OFF -DPARAKEET_BUILD_EXAMPLES=OFF + - engine: tts + src: tts-cpp + cmake_flags: -DTTS_CPP_BUILD_TESTS=OFF + runs-on: ubuntu-24.04 + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + + - name: Resolve ggml pin (tip of ${{ env.GGML_BRANCH }}) + id: ggml + run: echo "sha=$(git ls-remote "$GGML_REPO" "refs/heads/$GGML_BRANCH" | cut -f1)" >> "$GITHUB_OUTPUT" + + - name: Cache ggml install + id: ggml-cache + uses: actions/cache@v4 + with: + path: ggml-install + key: ggml-install-android-arm64-${{ steps.ggml.outputs.sha }} + + - name: Build ggml (NDK arm64-v8a, static, CPU-only) + if: steps.ggml-cache.outputs.cache-hit != 'true' + run: | + # Fetch the exact SHA the cache key was derived from (the branch + # tip may advance between ls-remote and this step). + git init -q ggml-src + git -C ggml-src remote add origin "$GGML_REPO" + git -C ggml-src fetch --depth 1 origin ${{ steps.ggml.outputs.sha }} + git -C ggml-src checkout -q FETCH_HEAD + cmake -S ggml-src -B ggml-src/build \ + -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake" \ + -DANDROID_ABI=arm64-v8a \ + -DANDROID_PLATFORM=android-28 \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DGGML_OPENMP=OFF \ + -DGGML_BUILD_TESTS=OFF \ + -DGGML_BUILD_EXAMPLES=OFF \ + -DCMAKE_INSTALL_PREFIX="$PWD/ggml-install" + cmake --build ggml-src/build -j4 + cmake --install ggml-src/build + + # Compile smoke only: hosted runners can't execute arm64 Android + # binaries. CMAKE_FIND_ROOT_PATH (not PREFIX_PATH) because the NDK + # toolchain restricts find_package() to the find-root. + - name: Configure + build engine (compile smoke) + run: | + cmake -S ${{ matrix.src }} -B build \ + -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_LATEST_HOME/build/cmake/android.toolchain.cmake" \ + -DANDROID_ABI=arm64-v8a \ + -DANDROID_PLATFORM=android-28 \ + -DCMAKE_BUILD_TYPE=Release \ + ${{ matrix.cmake_flags }} \ + -DCMAKE_FIND_ROOT_PATH="$PWD/ggml-install" + cmake --build build -j4 + + ios: + strategy: + fail-fast: false + matrix: + engine: [parakeet, tts] + include: + - engine: parakeet + src: parakeet-cpp + cmake_flags: -DPARAKEET_USE_SYSTEM_GGML=ON -DPARAKEET_BUILD_TESTS=OFF -DPARAKEET_BUILD_EXAMPLES=OFF + - engine: tts + src: tts-cpp + cmake_flags: -DTTS_CPP_BUILD_TESTS=OFF + runs-on: macos-14 + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + + - name: Resolve ggml pin (tip of ${{ env.GGML_BRANCH }}) + id: ggml + run: echo "sha=$(git ls-remote "$GGML_REPO" "refs/heads/$GGML_BRANCH" | cut -f1)" >> "$GITHUB_OUTPUT" + + - name: Cache ggml install + id: ggml-cache + uses: actions/cache@v4 + with: + path: ggml-install + key: ggml-install-ios-arm64-${{ steps.ggml.outputs.sha }} + + # Flags mirror build-xcframework.sh's iOS-device slice (Metal embedded). + - name: Build ggml (iOS device arm64, static, Metal embedded) + if: steps.ggml-cache.outputs.cache-hit != 'true' + run: | + # Fetch the exact SHA the cache key was derived from (the branch + # tip may advance between ls-remote and this step). + git init -q ggml-src + git -C ggml-src remote add origin "$GGML_REPO" + git -C ggml-src fetch --depth 1 origin ${{ steps.ggml.outputs.sha }} + git -C ggml-src checkout -q FETCH_HEAD + cmake -S ggml-src -B ggml-src/build \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DCMAKE_OSX_SYSROOT=iphoneos \ + -DCMAKE_OSX_ARCHITECTURES=arm64 \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=16.4 \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=OFF \ + -DGGML_METAL=ON \ + -DGGML_METAL_EMBED_LIBRARY=ON \ + -DGGML_METAL_USE_BF16=ON \ + -DGGML_OPENMP=OFF \ + -DGGML_BUILD_TESTS=OFF \ + -DGGML_BUILD_EXAMPLES=OFF \ + -DCMAKE_INSTALL_PREFIX="$PWD/ggml-install" + cmake --build ggml-src/build -j4 + cmake --install ggml-src/build + + # Compile smoke only (no iOS device on hosted runners). + - name: Configure + build engine (compile smoke) + run: | + cmake -S ${{ matrix.src }} -B build \ + -DCMAKE_SYSTEM_NAME=iOS \ + -DCMAKE_OSX_SYSROOT=iphoneos \ + -DCMAKE_OSX_ARCHITECTURES=arm64 \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=16.4 \ + -DCMAKE_BUILD_TYPE=Release \ + ${{ matrix.cmake_flags }} \ + -DCMAKE_FIND_ROOT_PATH="$PWD/ggml-install" + cmake --build build -j4 diff --git a/.github/workflows/parakeet-ci.yml b/.github/workflows/parakeet-ci.yml new file mode 100644 index 00000000000..02ce8ee9d49 --- /dev/null +++ b/.github/workflows/parakeet-ci.yml @@ -0,0 +1,146 @@ +# CI for the parakeet engine (parakeet-cpp/ — engines/parakeet/ after the +# repo reorg QIP lands; update the path filters below when it does). +# +# Scope (QIP "PR 0" lane): build the engine + every test harness against +# system ggml (qvac-ext-ggml @ speech, the same fork the ggml-speech vcpkg +# port ships), then run the non-GPU ctest suites. Tests whose model/reference +# fixtures aren't present are registered as DISABLED by the CMakeLists and +# show up as "Not Run" — the lane stays green with the committed fixtures +# only. GPU suites (`ctest -L gpu`) need real GPUs → self-hosted lane, see +# the gpu job stub at the bottom. +name: parakeet CI + +on: + push: + branches: [master] + paths: + - 'parakeet-cpp/**' + - '.github/workflows/parakeet-ci.yml' + pull_request: + paths: + - 'parakeet-cpp/**' + - '.github/workflows/parakeet-ci.yml' + workflow_dispatch: + inputs: + run_gpu: + description: 'Also run the GPU ctest lane (needs a [self-hosted, gpu] runner)' + type: boolean + default: false + +permissions: + contents: read + +concurrency: + group: parakeet-ci-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + GGML_REPO: https://github.com/tetherto/qvac-ext-ggml.git + GGML_BRANCH: speech # keep in sync with parakeet-cpp/scripts/setup-ggml.sh + +jobs: + build-test: + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, macos-14] + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + + - name: Install ccache + run: | + if [ "$RUNNER_OS" = "macOS" ]; then brew install ccache; else sudo apt-get update -q && sudo apt-get install -y ccache; fi + echo "CCACHE_DIR=${{ github.workspace }}/.ccache" >> "$GITHUB_ENV" + + - name: Resolve ggml pin (tip of ${{ env.GGML_BRANCH }}) + id: ggml + run: echo "sha=$(git ls-remote "$GGML_REPO" "refs/heads/$GGML_BRANCH" | cut -f1)" >> "$GITHUB_OUTPUT" + + - name: Cache ggml install + id: ggml-cache + uses: actions/cache@v4 + with: + path: ggml-install + key: ggml-install-${{ matrix.os }}-${{ steps.ggml.outputs.sha }} + + - name: Cache ccache + uses: actions/cache@v4 + with: + path: .ccache + key: ccache-parakeet-${{ matrix.os }}-${{ github.sha }} + restore-keys: ccache-parakeet-${{ matrix.os }}- + + # CPU-only ggml: this lane runs `-LE gpu` tests, and hosted runners + # have no Vulkan/OpenCL; Metal is exercised by the gpu lane instead. + - name: Build ggml (system ggml, qvac-ext-ggml@${{ env.GGML_BRANCH }}) + if: steps.ggml-cache.outputs.cache-hit != 'true' + run: | + # Fetch the exact SHA the cache key was derived from — the branch + # tip may have advanced since the ls-remote resolution, and a + # tip-only shallow clone would then silently build (and cache) + # a different commit than the key claims. + git init -q ggml-src + git -C ggml-src remote add origin "$GGML_REPO" + git -C ggml-src fetch --depth 1 origin ${{ steps.ggml.outputs.sha }} + git -C ggml-src checkout -q FETCH_HEAD + cmake -S ggml-src -B ggml-src/build \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=ON \ + -DGGML_METAL=OFF \ + -DGGML_BUILD_TESTS=OFF \ + -DGGML_BUILD_EXAMPLES=OFF \ + -DCMAKE_INSTALL_PREFIX="$PWD/ggml-install" + cmake --build ggml-src/build -j4 + cmake --install ggml-src/build + + - name: Configure + run: | + cmake -S parakeet-cpp -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DPARAKEET_USE_SYSTEM_GGML=ON \ + -DPARAKEET_BUILD_TESTS=ON \ + -DCMAKE_PREFIX_PATH="$PWD/ggml-install" + + - name: Build + run: cmake --build build -j4 + + # Excludes gpu (no GPU on hosted runners) and perf (timing gates are + # meaningless on shared runners). Model-dependent tests are DISABLED + # at configure time when fixtures are absent and report as "Not Run". + # LD_LIBRARY_PATH: the ggml install ships libqvac-speech-ggml.so whose + # own dependency on the ggml-cpu/base siblings is resolved transitively + # — Linux RUNPATH doesn't apply to transitive loads, so the loader + # needs the directory explicitly (macOS @rpath chains handle it). + - name: Test (non-GPU) + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/ggml-install/lib + run: ctest --test-dir build -LE 'gpu|perf' --output-on-failure --timeout 600 + + # GPU lane stub: opt-in until self-hosted GPU runners exist. Trial note: + # hosted macos-14 exposes Metal, so moving `-L gpu` into the macOS + # build-test job is a cheap first experiment. + gpu: + if: github.event_name == 'workflow_dispatch' && inputs.run_gpu + runs-on: [self-hosted, gpu] + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + # GGML_VULKAN=ON is the backend the -L gpu suites primarily target + # (test_vk_vs_cpu et al); Metal is on by default on Apple hosts. + # Revisit the flag set (OpenCL? CUDA?) when the GPU runner fleet is + # provisioned — a CPU-only ggml here would skip or falsely pass the + # GPU suites. + - name: Build ggml + engine (native backends) and run GPU suites + run: | + git clone --depth 1 --branch "$GGML_BRANCH" "$GGML_REPO" ggml-src + cmake -S ggml-src -B ggml-src/build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON \ + -DGGML_VULKAN=ON \ + -DCMAKE_INSTALL_PREFIX="$PWD/ggml-install" + cmake --build ggml-src/build -j && cmake --install ggml-src/build + cmake -S parakeet-cpp -B build -DCMAKE_BUILD_TYPE=Release \ + -DPARAKEET_USE_SYSTEM_GGML=ON -DPARAKEET_BUILD_TESTS=ON \ + -DCMAKE_PREFIX_PATH="$PWD/ggml-install" + cmake --build build -j + ctest --test-dir build -L gpu --output-on-failure --timeout 1200 diff --git a/.github/workflows/tts-ci.yml b/.github/workflows/tts-ci.yml new file mode 100644 index 00000000000..00234903010 --- /dev/null +++ b/.github/workflows/tts-ci.yml @@ -0,0 +1,146 @@ +# CI for the TTS engines (tts-cpp/ — engines/tts/ after the repo reorg QIP +# lands; update the path filters below when it does). +# +# Scope (QIP "PR 0" lane): build chatterbox + supertonic + lavasr and every +# test harness against system ggml (qvac-ext-ggml @ speech — mandatory here: +# TTS_CPP_USE_SYSTEM_GGML defaults ON and the bundled path is rejected in +# this tree), then run the non-GPU ctest suites (~68 pass with committed +# fixtures; model-dependent ones auto-DISABLE to "Not Run"). GPU suites need +# real GPUs → self-hosted lane, see the gpu job stub at the bottom. +name: tts CI + +on: + push: + branches: [master] + paths: + - 'tts-cpp/**' + - '.github/workflows/tts-ci.yml' + pull_request: + paths: + - 'tts-cpp/**' + - '.github/workflows/tts-ci.yml' + workflow_dispatch: + inputs: + run_gpu: + description: 'Also run the GPU ctest lane (needs a [self-hosted, gpu] runner)' + type: boolean + default: false + +permissions: + contents: read + +concurrency: + group: tts-ci-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + GGML_REPO: https://github.com/tetherto/qvac-ext-ggml.git + GGML_BRANCH: speech # single ggml pin shared with parakeet-ci.yml + +jobs: + build-test: + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, macos-14] + runs-on: ${{ matrix.os }} + timeout-minutes: 60 + steps: + - uses: actions/checkout@v4 + + - name: Install ccache + run: | + if [ "$RUNNER_OS" = "macOS" ]; then brew install ccache; else sudo apt-get update -q && sudo apt-get install -y ccache; fi + echo "CCACHE_DIR=${{ github.workspace }}/.ccache" >> "$GITHUB_ENV" + + - name: Resolve ggml pin (tip of ${{ env.GGML_BRANCH }}) + id: ggml + run: echo "sha=$(git ls-remote "$GGML_REPO" "refs/heads/$GGML_BRANCH" | cut -f1)" >> "$GITHUB_OUTPUT" + + - name: Cache ggml install + id: ggml-cache + uses: actions/cache@v4 + with: + path: ggml-install + key: ggml-install-${{ matrix.os }}-${{ steps.ggml.outputs.sha }} + + - name: Cache ccache + uses: actions/cache@v4 + with: + path: .ccache + key: ccache-tts-${{ matrix.os }}-${{ github.sha }} + restore-keys: ccache-tts-${{ matrix.os }}- + + # CPU-only ggml: this lane runs `-LE gpu` tests, and hosted runners + # have no Vulkan/OpenCL; Metal is exercised by the gpu lane instead. + # Cache key is shared with parakeet-ci.yml so whichever lane builds + # first serves both. + - name: Build ggml (system ggml, qvac-ext-ggml@${{ env.GGML_BRANCH }}) + if: steps.ggml-cache.outputs.cache-hit != 'true' + run: | + # Fetch the exact SHA the cache key was derived from — the branch + # tip may have advanced since the ls-remote resolution, and a + # tip-only shallow clone would then silently build (and cache) + # a different commit than the key claims. + git init -q ggml-src + git -C ggml-src remote add origin "$GGML_REPO" + git -C ggml-src fetch --depth 1 origin ${{ steps.ggml.outputs.sha }} + git -C ggml-src checkout -q FETCH_HEAD + cmake -S ggml-src -B ggml-src/build \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=ON \ + -DGGML_METAL=OFF \ + -DGGML_BUILD_TESTS=OFF \ + -DGGML_BUILD_EXAMPLES=OFF \ + -DCMAKE_INSTALL_PREFIX="$PWD/ggml-install" + cmake --build ggml-src/build -j4 + cmake --install ggml-src/build + + - name: Configure + run: | + cmake -S tts-cpp -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DTTS_CPP_BUILD_TESTS=ON \ + -DCMAKE_PREFIX_PATH="$PWD/ggml-install" + + - name: Build + run: cmake --build build -j4 + + # Excludes gpu (no GPU on hosted runners) and perf (timing gates are + # meaningless on shared runners). Model-dependent tests are DISABLED + # at configure time when fixtures are absent and report as "Not Run". + # LD_LIBRARY_PATH: the ggml install ships libqvac-speech-ggml.so whose + # own dependency on the ggml-cpu/base siblings is resolved transitively + # — Linux RUNPATH doesn't apply to transitive loads, so the loader + # needs the directory explicitly (macOS @rpath chains handle it). + - name: Test (non-GPU) + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/ggml-install/lib + run: ctest --test-dir build -LE 'gpu|perf' --output-on-failure --timeout 600 + + # GPU lane stub: opt-in until self-hosted GPU runners exist. Trial note: + # hosted macos-14 exposes Metal, so moving `-L gpu` / the mtl-* suites + # into the macOS build-test job is a cheap first experiment. + gpu: + if: github.event_name == 'workflow_dispatch' && inputs.run_gpu + runs-on: [self-hosted, gpu] + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + # GGML_VULKAN=ON is the backend the -L gpu suites primarily target + # (test_vk_vs_cpu et al); Metal is on by default on Apple hosts. + # Revisit the flag set (OpenCL? CUDA?) when the GPU runner fleet is + # provisioned — a CPU-only ggml here would skip or falsely pass the + # GPU suites. + - name: Build ggml + engine (native backends) and run GPU suites + run: | + git clone --depth 1 --branch "$GGML_BRANCH" "$GGML_REPO" ggml-src + cmake -S ggml-src -B ggml-src/build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON \ + -DGGML_VULKAN=ON \ + -DCMAKE_INSTALL_PREFIX="$PWD/ggml-install" + cmake --build ggml-src/build -j && cmake --install ggml-src/build + cmake -S tts-cpp -B build -DCMAKE_BUILD_TYPE=Release \ + -DTTS_CPP_BUILD_TESTS=ON \ + -DCMAKE_PREFIX_PATH="$PWD/ggml-install" + cmake --build build -j + ctest --test-dir build -L gpu --output-on-failure --timeout 1200 diff --git a/tts-cpp/CMakeLists.txt b/tts-cpp/CMakeLists.txt index ae2a3a11e4f..86ddb0e6a6e 100644 --- a/tts-cpp/CMakeLists.txt +++ b/tts-cpp/CMakeLists.txt @@ -755,7 +755,8 @@ if (TTS_CPP_BUILD_TESTS) add_executable(test-campplus test/test_campplus.cpp - src/campplus.cpp) + src/campplus.cpp + src/backend_selection.cpp) target_link_libraries(test-campplus PRIVATE ggml) target_include_directories(test-campplus PRIVATE ggml/include src) if (OpenMP_CXX_FOUND) @@ -1331,7 +1332,8 @@ if (TTS_CPP_BUILD_TESTS) add_executable(test-campplus-backward-parity test/test_campplus_backward_parity.cpp src/campplus_backward.cpp - src/campplus.cpp) + src/campplus.cpp + src/backend_selection.cpp) target_link_libraries(test-campplus-backward-parity PRIVATE ggml) target_include_directories(test-campplus-backward-parity PRIVATE ggml/include src) if (OpenMP_CXX_FOUND) diff --git a/tts-cpp/test/test_env_portable.h b/tts-cpp/test/test_env_portable.h new file mode 100644 index 00000000000..1eee6f3dfa6 --- /dev/null +++ b/tts-cpp/test/test_env_portable.h @@ -0,0 +1,30 @@ +#pragma once +// Portable setenv/unsetenv for the test harnesses. MSVC's CRT has no +// POSIX setenv/unsetenv (only _putenv_s), so provide same-named shims +// there; every other platform gets the real functions from . +#include +#include +#include +#ifdef _MSC_VER +inline int setenv(const char * name, const char * value, int overwrite) { + // POSIX: with overwrite=0 an existing variable must be left untouched + // (and 0 returned) — don't let the shim diverge from that silently. + if (!overwrite && std::getenv(name) != nullptr) { + return 0; + } + return _putenv_s(name, value); +} +inline int unsetenv(const char * name) { + return _putenv_s(name, ""); // empty value removes the variable +} +#endif + +// Writable scratch directory for tests that emit temp fixtures. POSIX +// runners set TMPDIR; Windows sets TEMP/TMP and has no /tmp, so fall +// through the whole chain before defaulting. +inline std::string test_tmpdir() { + for (const char * var : { "TMPDIR", "TEMP", "TMP" }) { + if (const char * v = std::getenv(var); v && *v) return v; + } + return "/tmp"; +} diff --git a/tts-cpp/test/test_gguf_stream.cpp b/tts-cpp/test/test_gguf_stream.cpp index 1dd5f90f6db..113fea1a276 100644 --- a/tts-cpp/test/test_gguf_stream.cpp +++ b/tts-cpp/test/test_gguf_stream.cpp @@ -8,6 +8,7 @@ // chunk boundary) into a temp file, loads it through both paths, and // compares. +#include "test_env_portable.h" #include "ggml.h" #include "ggml-backend.h" #include "ggml-cpu.h" @@ -47,8 +48,7 @@ static const spec SPECS[] = { static const size_t N_SPECS = sizeof(SPECS) / sizeof(SPECS[0]); static std::string write_fixture() { - const char * tmpdir = getenv("TMPDIR"); - std::string path = std::string(tmpdir ? tmpdir : "/tmp") + "/test-gguf-stream-fixture.gguf"; + std::string path = test_tmpdir() + "/test-gguf-stream-fixture.gguf"; size_t total = 0; for (size_t i = 0; i < N_SPECS; ++i) { diff --git a/tts-cpp/test/test_lavasr_enhancer_ggml.cpp b/tts-cpp/test/test_lavasr_enhancer_ggml.cpp index a25cd443441..8eb962a586b 100644 --- a/tts-cpp/test/test_lavasr_enhancer_ggml.cpp +++ b/tts-cpp/test/test_lavasr_enhancer_ggml.cpp @@ -17,6 +17,7 @@ // is present it additionally compares the ggml forward against the onnxruntime // golden real/imag with the real model weights. +#include "test_env_portable.h" #include "backend_selection.h" #include "lavasr/enhancer.h" // scalar enhance() + enhance_with() #include "lavasr/enhancer_core.h" @@ -505,9 +506,7 @@ static std::string write_enhancer_gguf(const EnhancerWeights & w) { add_gguf_tensor(ctx, g, w, e.name, e.ne); } - const char * tmpdir = std::getenv("TMPDIR"); - std::string path = - std::string(tmpdir ? tmpdir : "/tmp") + "/test-lavasr-enhancer-load.gguf"; + std::string path = test_tmpdir() + "/test-lavasr-enhancer-load.gguf"; const bool ok = gguf_write_to_file(g, path.c_str(), /*only_meta=*/false); gguf_free(g); ggml_free(ctx); diff --git a/tts-cpp/test/test_s3gen_sched_equivalence.cpp b/tts-cpp/test/test_s3gen_sched_equivalence.cpp index 31b7dfb7c9a..90cf0a7102e 100644 --- a/tts-cpp/test/test_s3gen_sched_equivalence.cpp +++ b/tts-cpp/test/test_s3gen_sched_equivalence.cpp @@ -23,6 +23,7 @@ // // usage: test-s3gen-sched-equivalence MODEL.gguf [n_gpu_layers] +#include "test_env_portable.h" #include "tts-cpp/chatterbox/s3gen_pipeline.h" #include diff --git a/tts-cpp/test/test_supertonic_profile_csv.cpp b/tts-cpp/test/test_supertonic_profile_csv.cpp index 780fc376e76..d07565d8f18 100644 --- a/tts-cpp/test/test_supertonic_profile_csv.cpp +++ b/tts-cpp/test/test_supertonic_profile_csv.cpp @@ -37,6 +37,7 @@ // Registered with `LABEL "unit"` in CMakeLists.txt — no GGUF // required. +#include "test_env_portable.h" #include "supertonic_internal.h" #include diff --git a/tts-cpp/test/test_supertonic_sched_equivalence.cpp b/tts-cpp/test/test_supertonic_sched_equivalence.cpp index 169aa081c6e..918b490164e 100644 --- a/tts-cpp/test/test_supertonic_sched_equivalence.cpp +++ b/tts-cpp/test/test_supertonic_sched_equivalence.cpp @@ -25,6 +25,7 @@ // // usage: test-supertonic-sched-equivalence MODEL.gguf [n_gpu_layers] +#include "test_env_portable.h" #include "tts-cpp/supertonic/engine.h" #include diff --git a/tts-cpp/test/test_supertonic_vulkan_env_overrides.cpp b/tts-cpp/test/test_supertonic_vulkan_env_overrides.cpp index c578c096eab..516c94293ff 100644 --- a/tts-cpp/test/test_supertonic_vulkan_env_overrides.cpp +++ b/tts-cpp/test/test_supertonic_vulkan_env_overrides.cpp @@ -51,6 +51,7 @@ // Whole TU MUST fail to compile before the symbols are added, // then pass after. +#include "test_env_portable.h" #include "tts-cpp/supertonic/engine.h" #include "supertonic_internal.h" diff --git a/tts-cpp/test/test_t3_alignment_analyzer.cpp b/tts-cpp/test/test_t3_alignment_analyzer.cpp index af35ef36f76..65c18372e71 100644 --- a/tts-cpp/test/test_t3_alignment_analyzer.cpp +++ b/tts-cpp/test/test_t3_alignment_analyzer.cpp @@ -11,6 +11,7 @@ // - token repetition only forces EOS once complete (avoids #519/#587 early cut) // - short text / empty row / disabled => no force +#include "test_env_portable.h" #include "t3_alignment_analyzer.h" #include diff --git a/tts-cpp/test/test_t3_caches.cpp b/tts-cpp/test/test_t3_caches.cpp index ee079d4eb0b..85cb5f7ee02 100644 --- a/tts-cpp/test/test_t3_caches.cpp +++ b/tts-cpp/test/test_t3_caches.cpp @@ -28,6 +28,7 @@ // Without arguments, runs only the lightweight default-state // invariants (no model load required). +#include "test_env_portable.h" #include "chatterbox_t3_internal.h" #include "chatterbox_tts_test_hooks.h" diff --git a/tts-cpp/test/test_t3_sched_dispatch.cpp b/tts-cpp/test/test_t3_sched_dispatch.cpp index 5bc3119a0ea..ba242c19b5b 100644 --- a/tts-cpp/test/test_t3_sched_dispatch.cpp +++ b/tts-cpp/test/test_t3_sched_dispatch.cpp @@ -12,6 +12,7 @@ // Registered with `LABEL "unit"` in CMakeLists.txt so a fresh // checkout's `ctest` exercises this without needing any fixture. +#include "test_env_portable.h" #include "backend_selection.h" #include "sched_dispatch.h" diff --git a/tts-cpp/test/test_t3_sched_equivalence.cpp b/tts-cpp/test/test_t3_sched_equivalence.cpp index 9cdcdc98305..c19cd043f4a 100644 --- a/tts-cpp/test/test_t3_sched_equivalence.cpp +++ b/tts-cpp/test/test_t3_sched_equivalence.cpp @@ -23,6 +23,7 @@ // it, phase B must BYPASS the cached graphs, and phase A' proves they // were left intact. +#include "test_env_portable.h" #include "chatterbox_t3_internal.h" #include "ggml.h" diff --git a/tts-cpp/test/test_t3_stop_controller.cpp b/tts-cpp/test/test_t3_stop_controller.cpp index 55a28c44844..c0fdff90c45 100644 --- a/tts-cpp/test/test_t3_stop_controller.cpp +++ b/tts-cpp/test/test_t3_stop_controller.cpp @@ -16,6 +16,7 @@ // EOS, no post-check stop. // - make_mtl_stop_params budget scaling + env overrides. +#include "test_env_portable.h" #include "t3_stop_controller.h" #include