Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 14 additions & 18 deletions .claude/skills/build-compilation-dependencies/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ react-native-audio-api/
│ │ └── include_ffmpeg/ # Headers for FFmpeg
│ ├── common/cpp/test/
│ │ ├── CMakeLists.txt # Standalone test build (no Android/iOS)
│ │ ├── RunTests.sh # Test runner script
│ │ ├── RunTests.sh / filters.sh # smoke|extended|full (+ categories)
│ │ └── src/ # Google Test files
│ ├── RNAudioAPI.podspec # CocoaPods spec for iOS
│ └── scripts/
Expand Down Expand Up @@ -185,10 +185,12 @@ CI intentionally skips native Android/iOS builds (expensive). Use the tiered loc

```bash
yarn validate:fast # CI parity (format, lint, typecheck, enum sync, build, C++ + JS tests)
yarn validate:graph # graph tests + ASan (optional; graph path changes)
yarn validate:cpp # C++ smoke
yarn validate:cpp-extended # C++ extended (all categories)
yarn validate:graph # legacy alias: extended category graph only
yarn validate:android # yarn workspace … build:android
yarn validate:ios # yarn workspace … build:ios (macOS only)
yarn validate:full # --fast + --android + --ios
yarn validate:full # --fast + C++ extended + --android + --ios
```

Script: [`scripts/validate.sh`](../../../scripts/validate.sh) at monorepo root.
Expand All @@ -198,10 +200,10 @@ Script: [`scripts/validate.sh`](../../../scripts/validate.sh) at monorepo root.
| Layer | CI (`ci.yml` + `tests.yml`) | Local tiers |
|---|---|---|
| TS build (`bob build`) | Yes | `--fast` |
| C++ test subset (`RunTests.sh`) | Yes | `--fast` |
| C++ coverage (`RunCoverage.sh`, Clang) | Yes (`cpp-coverage` artifact) | `yarn test:cpp:coverage` |
| C++ smoke (`RunTests.sh`) | Yes | `--fast` |
| C++ coverage (`RunCoverage.sh`, smoke, Clang) | Yes (`cpp-coverage` artifact) | `yarn test:cpp:coverage` |
| Jest | Yes | `--fast` |
| Graph tests | No, path-filtered in `graph-tests.yml` | `--graph` |
| Extended C++ by category (e.g. graph) | Path change or manual dispatch in `tests.yml` | `--cpp-extended` / `--graph` |
| HostObjects (26 JSI `.cpp` files) | **No** | `--android` + `--ios` |
| Android JNI C++ + Kotlin | **No** | `--android` |
| iOS ObjC++ | **No** | `--ios` |
Expand All @@ -224,7 +226,7 @@ Android (NDK) and iOS (Clang) cannot share object files — reuse is at the preb

- `--ios` on Linux → skip with message (exit 0)
- `--android` without `ANDROID_HOME` → fail on explicit `--android`; skip with warning inside `--full`
- Graph tests are separate from `--full` (slow; CI path-filters them)
- `--full` includes C++ extended (all categories) after `--fast`’s smoke, so local full covers C++ full + native builds

### Which tier to run

Expand All @@ -239,18 +241,12 @@ See the decision table in [post-work-checks](../post-work-checks/SKILL.md).

### How to run
```bash
yarn test # from monorepo root — runs RunTests.sh
yarn test # Jest + C++ smoke
yarn workspace react-native-audio-api test:cpp:smoke|extended|full
yarn workspace react-native-audio-api test:cpp:extended -- graph
```

`RunTests.sh` does:
```bash
cd packages/react-native-audio-api/common/cpp/test
cmake -S . -B build -Wno-dev
cd build && make -j10
./tests --gtest_print_time=1
```

The `build/` directory is deleted after each run.
`RunTests.sh [smoke|extended|full] [category…] [--ubasan|--tsan|--no-ubasan]` uses filters from `filters.sh`. Docs: `common/cpp/test/TESTING.md`. `yarn test:graph` is a legacy alias for `extended graph`.

### Coverage (Clang / llvm-cov)

Expand All @@ -259,7 +255,7 @@ yarn workspace react-native-audio-api test:cpp:coverage
# open packages/react-native-audio-api/common/cpp/test/coverage-html/index.html
```

`RunCoverage.sh` configures a separate `build-coverage/` tree with `-DENABLE_COVERAGE=ON` (Clang-only LLVM source-based coverage: `-fprofile-instr-generate -fcoverage-mapping`), defaults `CC`/`CXX` to `clang`/`clang++` when unset, runs the same gtest filter as `RunTests.sh`, then prints `llvm-cov report` and writes HTML via `llvm-cov show -format=html`. When `GITHUB_STEP_SUMMARY` is set, the report is also appended there. Sanitizer targets are skipped when coverage is enabled. Requires Apple Clang / `xcrun llvm-profdata` and `xcrun llvm-cov` on macOS (or the same tools on PATH for Linux).
`RunCoverage.sh` configures a separate `build-coverage/` tree with `-DENABLE_COVERAGE=ON` (Clang-only LLVM source-based coverage: `-fprofile-instr-generate -fcoverage-mapping`), defaults `CC`/`CXX` to `clang`/`clang++` when unset, runs the **smoke** filter from `filters.sh`, then prints `llvm-cov report` and writes HTML via `llvm-cov show -format=html`. When `GITHUB_STEP_SUMMARY` is set, the report is also appended there. Sanitizer targets are skipped when coverage is enabled. Requires Apple Clang / `xcrun llvm-profdata` and `xcrun llvm-cov` on macOS (or the same tools on PATH for Linux).

CI runs a parallel `cpp-coverage` job via `.github/workflows/cpp-coverage-job.yml` (called from `tests.yml` on pull requests; Clang + LLVM apt packages, separate from the GCC `cpp-tests` job). It uploads the HTML tree as the `cpp-coverage-html` artifact (14-day retention); download the zip from the Actions run and open `index.html`. Manual `workflow_dispatch` on `tests.yml` accepts booleans `run_cpp_tests` / `run_cpp_coverage` / `run_js_tests` (default true); PRs always run all three.

Expand Down
3 changes: 2 additions & 1 deletion .claude/skills/build-compilation-dependencies/maintenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ Review this skill when `pre-push-update` reports changes in:
| `RNAudioAPI.podspec` | Subspecs table, `miniaudio_impl` workaround, `-force_load` list, xcframeworks list, `rnaa_utils.rb` dynamic paths |
| `apps/fabric-example/ios/Podfile` | New Architecture enablement, minimum iOS version helper |
| `common/cpp/test/CMakeLists.txt` | Excluded sources list, compile definitions, GoogleTest fetch URL, include paths, `ENABLE_COVERAGE` |
| `common/cpp/test/RunCoverage.sh` | Coverage build dir, Clang CC/CXX defaults, llvm-profdata/llvm-cov report+HTML, `GITHUB_STEP_SUMMARY`, ignore regexes |
| `common/cpp/test/RunCoverage.sh` | Coverage build dir, Clang CC/CXX defaults, llvm-profdata/llvm-cov report+HTML, `GITHUB_STEP_SUMMARY`, ignore regexes, smoke filter via `filters.sh` |
| `common/cpp/test/filters.sh` / `RunTests.sh` | Mode/category filters (smoke/extended/full) and sanitizer flags |
| `.github/workflows/cpp-coverage-job.yml` | Reusable coverage job (Clang/LLVM install, artifact `cpp-coverage-html`) |
| `.github/workflows/js-job.yml` | Reusable JS integration tests job (draft-PR skip) |
| `.github/workflows/tests.yml` | Calls cpp/js/coverage jobs; `workflow_dispatch` booleans select jobs |
Expand Down
34 changes: 20 additions & 14 deletions .claude/skills/post-work-checks/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ Run these checks after any code change and before opening a PR.

## Quick Reference — Local Validation Tiers

CI always runs the **fast** tier (format, lint, typecheck, enum sync, TS build, C++ + JS tests). Graph tests run in a separate path-filtered workflow (`graph-tests.yml`). CI does **not** build Android Gradle, iOS pods, or example apps. Use `validate.sh` locally to close that gap:
CI always runs the **fast** tier (format, lint, typecheck, enum sync, TS build, C++ smoke + JS tests). Extended C++ categories (e.g. graph) run from `tests.yml` when matching paths change, or via `workflow_dispatch` booleans. CI does **not** build Android Gradle, iOS pods, or example apps. Use `validate.sh` locally to close that gap:

```bash
yarn validate:fast # CI parity — always run before opening a PR
yarn validate:graph # graph tests (when graph/audio-thread code changes)
yarn validate:android # Android native build (requires ANDROID_HOME)
yarn validate:ios # iOS native build (macOS only)
yarn validate:full # --fast + --android + --ios (skips unavailable platforms)
yarn validate:fast # CI parity — always run before opening a PR
yarn validate:cpp # C++ smoke
yarn validate:cpp-extended # C++ extended (all categories)
yarn validate:graph # legacy alias: extended category graph only
yarn validate:android # Android native build (requires ANDROID_HOME)
yarn validate:ios # iOS native build (macOS only)
yarn validate:full # --fast + C++ extended + --android + --ios
```

Equivalent: `./scripts/validate.sh --fast` (etc.)
Expand All @@ -42,7 +44,7 @@ Equivalent: `./scripts/validate.sh --fast` (etc.)
| `ios/audioapi/` | `--fast` + `--ios` |
| `CMakeLists.txt`, `build.gradle`, `podspec` | `--full` |

Graph changes under `common/cpp/audioapi/core/utils/graph/` → also run `yarn validate:graph`.
Graph changes under `common/cpp/audioapi/core/utils/graph/` → also run `yarn validate:graph` or `yarn validate:cpp-extended` (extended category `graph`).

---

Expand Down Expand Up @@ -73,7 +75,7 @@ Hooks run when lefthook is installed (`lefthook install`).

**If a hook fails, the commit is aborted.** Fix the issue and re-commit — do NOT use `--no-verify`.

There is no pre-push hook — `yarn validate:fast` (and native/graph tiers) are run manually before opening a PR. Native builds (`validate:android`, `validate:ios`, `validate:full`) and graph tests (`validate:graph`) are never run by lefthook.
There is no pre-push hook — `yarn validate:fast` (and native/C++ extended tiers) are run manually before opening a PR. Native builds (`validate:android`, `validate:ios`, `validate:full`) and extended C++ (`validate:cpp-extended` / legacy `validate:graph`) are never run by lefthook.

---

Expand All @@ -92,24 +94,28 @@ yarn validate:fast
```bash
yarn validate:android # yarn workspace … build:android (~3–4 min)
yarn validate:ios # yarn workspace … build:ios (macOS only)
yarn validate:full # --fast + --android + --ios
yarn validate:full # --fast + C++ extended + --android + --ios
```

The Gradle project resolves through the `node_modules/react-native-audio-api` workspace symlink, so local edits in `packages/react-native-audio-api/` are picked up.

### Graph tests (when graph / audio-thread code changes)
### Extended graph (when graph / audio-thread code changes)

```bash
yarn validate:graph
yarn validate:graph # legacy alias: extended category graph
yarn validate:cpp-extended # all extended categories
# or: yarn workspace react-native-audio-api test:cpp:extended -- graph
```

### C++ tests only

```bash
yarn workspace react-native-audio-api run test:cpp
yarn validate:cpp # smoke via validate.sh
yarn workspace react-native-audio-api run test:cpp # smoke
yarn workspace react-native-audio-api run test:cpp:full # smoke + all extended
```

**When**: after any change to `common/cpp/audioapi/core/`, `dsp/`, or `utils/` C++ files. Prefer this for a fast C++-only loop without running Jest; run `yarn validate:fast` before opening a PR.
**When**: after any change to `common/cpp/audioapi/core/`, `dsp/`, or `utils/` C++ files. Prefer smoke for a fast C++-only loop; see `common/cpp/test/TESTING.md`. Run `yarn validate:fast` before opening a PR.

### Library unit tests (JS + C++)

Expand Down Expand Up @@ -162,7 +168,7 @@ Later steps may surface issues caused by earlier ones — run in this order:
2. `yarn lint` — catch remaining code issues
3. `yarn typecheck` — catch TypeScript errors
4. `yarn validate:fast` — full CI-parity gate (or `yarn test` / `test:cpp` for a quick local loop; always run `--fast` before opening a PR)
5. `yarn validate:graph` — when graph / audio-thread code changed
5. `yarn validate:cpp-extended` / `yarn validate:graph` — when graph / audio-thread code changed
6. `yarn validate:android` / `yarn validate:ios` / `yarn validate:full` — when native code or build files changed (see decision table above)

---
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/post-work-checks/maintenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ Review this skill when `pre-push-update` reports changes in:
| `lefthook.yml` | Pre-commit / commit-msg hook changes |
| `scripts/validate.sh` | Tier behavior (`--fast` / `--graph` / `--android` / `--ios` / `--full`), skip rules |
| `scripts/check-audio-enum-sync*` or `packages/react-native-audio-api/scripts/check-audio-events-sync.sh` | Enum sync check details |
| `.github/workflows/ci.yml`, `tests.yml`, `graph-tests.yml` | What CI covers vs local validation tiers |
| `.github/workflows/ci.yml`, `tests.yml`, `cpp-extended-job.yml` | What CI covers vs local validation tiers |
78 changes: 78 additions & 0 deletions .github/workflows/cpp-extended-job.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
name: C++ Extended Job

# Reusable runner for one (or more) C++ extended categories.
# Path filtering and setup live here so tests.yml only declares a thin per-category entry.

on:
workflow_call:
inputs:
name:
description: Job display name
required: true
type: string
categories:
description: Space-separated extended categories (e.g. graph)
required: true
type: string
force:
description: Skip path filter and always run (manual workflow_dispatch)
required: false
type: boolean
default: false
path_filters:
description: >
dorny/paths-filter YAML document with a single top-level key `run`
listing globs that should trigger this category on pull_request.
required: true
type: string

jobs:
run:
name: ${{ inputs.name }}
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
defaults:
run:
working-directory: packages/react-native-audio-api/common/cpp/test
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Path filter
if: ${{ inputs.force == false }}
uses: dorny/paths-filter@v3
id: filter
with:
filters: ${{ inputs.path_filters }}

- name: Decide whether to run
id: decide
working-directory: .
run: |
if [[ "${{ inputs.force }}" == "true" ]]; then
echo "run=true" >> "$GITHUB_OUTPUT"
elif [[ "${{ steps.filter.outputs.run }}" == "true" ]]; then
echo "run=true" >> "$GITHUB_OUTPUT"
else
echo "run=false" >> "$GITHUB_OUTPUT"
echo "No matching path changes; skipping ${{ inputs.name }}."
fi

- name: Checkout with Node
if: steps.decide.outputs.run == 'true'
uses: ./.github/actions/checkout-with-node

- name: Setup Yarn
if: steps.decide.outputs.run == 'true'
uses: ./.github/actions/setup-yarn

- name: Setup CMake
if: steps.decide.outputs.run == 'true'
uses: ./.github/actions/setup-cmake

- name: ${{ inputs.name }}
if: steps.decide.outputs.run == 'true'
run: bash RunTests.sh extended ${{ inputs.categories }}
15 changes: 0 additions & 15 deletions .github/workflows/graph-tests.yml

This file was deleted.

26 changes: 23 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,28 @@ on:
workflow_dispatch:
inputs:
run_cpp_tests:
description: Run C++ tests (GCC)
description: Run C++ smoke tests (GCC)
type: boolean
default: true
run_cpp_coverage:
description: Run C++ coverage (Clang / llvm-cov)
description: Run C++ coverage (Clang / llvm-cov, smoke)
type: boolean
default: true
run_js_tests:
description: Run JS integration tests
type: boolean
default: true
run_cpp_extended_graph:
description: Run C++ extended category graph
type: boolean
default: false

jobs:
cpp-tests:
if: github.event_name == 'pull_request' || inputs.run_cpp_tests
uses: ./.github/workflows/cpp-job.yml
with:
name: C++ tests
name: C++ smoke
working-directory: packages/react-native-audio-api
run: yarn test:cpp

Expand All @@ -33,3 +37,19 @@ jobs:
js-tests:
if: github.event_name == 'pull_request' || inputs.run_js_tests
uses: ./.github/workflows/js-job.yml

# One thin entry per extended category: paths + categories stay here;
# filter/setup/run logic lives in cpp-extended-job.yml.
cpp-extended-graph:
if: github.event_name == 'pull_request' || inputs.run_cpp_extended_graph
uses: ./.github/workflows/cpp-extended-job.yml
with:
name: C++ extended (graph)
categories: graph
force: ${{ github.event_name == 'workflow_dispatch' }}
path_filters: |
run:
- 'packages/react-native-audio-api/common/cpp/audioapi/core/utils/graph/**'
- 'packages/react-native-audio-api/common/cpp/test/src/graph/**'
- 'packages/react-native-audio-api/common/cpp/test/filters.sh'
- 'packages/react-native-audio-api/common/cpp/test/RunTests.sh'
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
"test": "yarn workspace react-native-audio-api run test",
"check-audio-enum-sync": "bash packages/react-native-audio-api/scripts/check-audio-events-sync.sh",
"validate:fast": "bash scripts/validate.sh --fast",
"validate:cpp": "bash scripts/validate.sh --cpp",
"validate:cpp-extended": "bash scripts/validate.sh --cpp-extended",
"validate:cpp-full": "bash scripts/validate.sh --cpp-full",
"validate:graph": "bash scripts/validate.sh --graph",
"validate:android": "bash scripts/validate.sh --android",
"validate:ios": "bash scripts/validate.sh --ios",
Expand Down
41 changes: 34 additions & 7 deletions packages/react-native-audio-api/common/cpp/test/RunCoverage.sh
Original file line number Diff line number Diff line change
@@ -1,21 +1,47 @@
#!/bin/bash

# Build the gtest suite with Clang LLVM source-based coverage and emit an
# llvm-cov HTML report. Uses Xcode llvm-profdata/llvm-cov on macOS (no gcovr).
#
# From packages/react-native-audio-api:
# yarn test:cpp:coverage
print_help() {
cat <<'EOF'
Usage: RunCoverage.sh

Build the C++ gtest suite with Clang LLVM source-based coverage and emit an
llvm-cov HTML report under coverage-html/. Always uses the smoke filter
(unsanitized). Override with GTEST_FILTER if needed.

yarn test:cpp:coverage

Options:
--help, -h Show this help.
EOF
}

set -euo pipefail

if [[ $# -gt 0 ]]; then
case "$1" in
--help|-h)
print_help
exit 0
;;
*)
echo "error: unexpected argument '$1'" >&2
print_help >&2
exit 1
;;
esac
fi

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"

# shellcheck source=filters.sh
source "${SCRIPT_DIR}/filters.sh"

readonly BUILD_DIR="${SCRIPT_DIR}/build-coverage"
readonly COVERAGE_HTML_DIR="${SCRIPT_DIR}/coverage-html"
readonly PROFDATA_FILE="${BUILD_DIR}/coverage.profdata"
readonly GRAPH_FILTER="AudioGraphTest.*:AudioGraphFuzzTest.*:GraphTest.*:GraphFuzzTest.*:GraphCycleDebugTest.*:HostGraphTest.*:Seeds/*"
readonly IGNORE_FILENAME_REGEX='(/common/cpp/test/|/_deps/|/googletest|/gmock|/audioapi/libs/|/r8brain/|/jsi/|/HostObjects/)'
readonly FILTER="${GTEST_FILTER:-$(cpp_test_smoke_filter)}"

resolve_llvm_tool() {
local tool_name="$1"
Expand Down Expand Up @@ -57,7 +83,8 @@ rm -rf "$COVERAGE_HTML_DIR"
(
cd "$BUILD_DIR"
export LLVM_PROFILE_FILE="${BUILD_DIR}/default-%p.profraw"
./tests --gtest_print_time=1 --gtest_filter="-${GRAPH_FILTER}"
echo "=== Coverage run (smoke filter=${FILTER}) ==="
./tests --gtest_print_time=1 --gtest_filter="${FILTER}"
)

shopt -s nullglob
Expand Down
Loading