kinemotion 0.21.0__tar.gz → 0.22.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kinemotion might be problematic. Click here for more details.

Files changed (95) hide show
  1. {kinemotion-0.21.0 → kinemotion-0.22.1}/CHANGELOG.md +21 -0
  2. kinemotion-0.22.1/CLAUDE.md +247 -0
  3. {kinemotion-0.21.0 → kinemotion-0.22.1}/PKG-INFO +1 -1
  4. kinemotion-0.22.1/docs/development/testing.md +311 -0
  5. kinemotion-0.22.1/docs/development/type-hints.md +294 -0
  6. kinemotion-0.22.1/docs/technical/implementation-details.md +80 -0
  7. {kinemotion-0.21.0 → kinemotion-0.22.1}/pyproject.toml +1 -1
  8. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_api.py +99 -0
  9. kinemotion-0.22.1/tests/test_cli_cmj.py +369 -0
  10. kinemotion-0.22.1/tests/test_cli_dropjump.py +367 -0
  11. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_cmj_analysis.py +187 -1
  12. kinemotion-0.22.1/tests/test_contact_detection.py +222 -0
  13. {kinemotion-0.21.0 → kinemotion-0.22.1}/uv.lock +1 -1
  14. kinemotion-0.21.0/CLAUDE.md +0 -404
  15. kinemotion-0.21.0/tests/test_contact_detection.py +0 -70
  16. {kinemotion-0.21.0 → kinemotion-0.22.1}/.dockerignore +0 -0
  17. {kinemotion-0.21.0 → kinemotion-0.22.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  18. {kinemotion-0.21.0 → kinemotion-0.22.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  19. {kinemotion-0.21.0 → kinemotion-0.22.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  20. {kinemotion-0.21.0 → kinemotion-0.22.1}/.github/pull_request_template.md +0 -0
  21. {kinemotion-0.21.0 → kinemotion-0.22.1}/.github/workflows/docs.yml +0 -0
  22. {kinemotion-0.21.0 → kinemotion-0.22.1}/.github/workflows/release.yml +0 -0
  23. {kinemotion-0.21.0 → kinemotion-0.22.1}/.github/workflows/test.yml +0 -0
  24. {kinemotion-0.21.0 → kinemotion-0.22.1}/.gitignore +0 -0
  25. {kinemotion-0.21.0 → kinemotion-0.22.1}/.pre-commit-config.yaml +0 -0
  26. {kinemotion-0.21.0 → kinemotion-0.22.1}/.readthedocs.yml +0 -0
  27. {kinemotion-0.21.0 → kinemotion-0.22.1}/.tool-versions +0 -0
  28. {kinemotion-0.21.0 → kinemotion-0.22.1}/CODE_OF_CONDUCT.md +0 -0
  29. {kinemotion-0.21.0 → kinemotion-0.22.1}/CONTRIBUTING.md +0 -0
  30. {kinemotion-0.21.0 → kinemotion-0.22.1}/Dockerfile +0 -0
  31. {kinemotion-0.21.0 → kinemotion-0.22.1}/GEMINI.md +0 -0
  32. {kinemotion-0.21.0 → kinemotion-0.22.1}/LICENSE +0 -0
  33. {kinemotion-0.21.0 → kinemotion-0.22.1}/README.md +0 -0
  34. {kinemotion-0.21.0 → kinemotion-0.22.1}/SECURITY.md +0 -0
  35. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/README.md +0 -0
  36. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/api/cmj.md +0 -0
  37. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/api/core.md +0 -0
  38. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/api/dropjump.md +0 -0
  39. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/api/overview.md +0 -0
  40. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/development/errors-findings.md +0 -0
  41. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/development/validation-plan.md +0 -0
  42. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/development/wallball-norep-detection.md +0 -0
  43. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/guides/bulk-processing.md +0 -0
  44. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/guides/camera-setup.md +0 -0
  45. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/guides/cmj-guide.md +0 -0
  46. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/index.md +0 -0
  47. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/reference/parameters.md +0 -0
  48. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/reference/pose-systems.md +0 -0
  49. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/research/sports-biomechanics-pose-estimation.md +0 -0
  50. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/technical/framerate.md +0 -0
  51. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/technical/imu-metadata.md +0 -0
  52. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/technical/real-time-analysis.md +0 -0
  53. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/technical/triple-extension.md +0 -0
  54. {kinemotion-0.21.0 → kinemotion-0.22.1}/docs/translations/es/camera-setup.md +0 -0
  55. {kinemotion-0.21.0 → kinemotion-0.22.1}/examples/bulk/README.md +0 -0
  56. {kinemotion-0.21.0 → kinemotion-0.22.1}/examples/bulk/bulk_processing.py +0 -0
  57. {kinemotion-0.21.0 → kinemotion-0.22.1}/examples/bulk/simple_example.py +0 -0
  58. {kinemotion-0.21.0 → kinemotion-0.22.1}/examples/programmatic_usage.py +0 -0
  59. {kinemotion-0.21.0 → kinemotion-0.22.1}/mkdocs.yml +0 -0
  60. {kinemotion-0.21.0 → kinemotion-0.22.1}/requirements-docs.txt +0 -0
  61. {kinemotion-0.21.0 → kinemotion-0.22.1}/samples/cmjs/README.md +0 -0
  62. {kinemotion-0.21.0 → kinemotion-0.22.1}/sonar-project.properties +0 -0
  63. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/__init__.py +0 -0
  64. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/api.py +0 -0
  65. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/cli.py +0 -0
  66. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/cmj/__init__.py +0 -0
  67. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/cmj/analysis.py +0 -0
  68. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/cmj/cli.py +0 -0
  69. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/cmj/debug_overlay.py +0 -0
  70. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/cmj/joint_angles.py +0 -0
  71. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/cmj/kinematics.py +0 -0
  72. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/core/__init__.py +0 -0
  73. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/core/auto_tuning.py +0 -0
  74. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/core/cli_utils.py +0 -0
  75. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/core/debug_overlay_utils.py +0 -0
  76. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/core/filtering.py +0 -0
  77. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/core/pose.py +0 -0
  78. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/core/smoothing.py +0 -0
  79. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/core/video_io.py +0 -0
  80. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/dropjump/__init__.py +0 -0
  81. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/dropjump/analysis.py +0 -0
  82. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/dropjump/cli.py +0 -0
  83. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/dropjump/debug_overlay.py +0 -0
  84. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/dropjump/kinematics.py +0 -0
  85. {kinemotion-0.21.0 → kinemotion-0.22.1}/src/kinemotion/py.typed +0 -0
  86. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/__init__.py +0 -0
  87. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_adaptive_threshold.py +0 -0
  88. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_aspect_ratio.py +0 -0
  89. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_cli_imports.py +0 -0
  90. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_cmj_kinematics.py +0 -0
  91. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_com_estimation.py +0 -0
  92. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_filtering.py +0 -0
  93. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_joint_angles.py +0 -0
  94. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_kinematics.py +0 -0
  95. {kinemotion-0.21.0 → kinemotion-0.22.1}/tests/test_polyorder.py +0 -0
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  <!-- version list -->
9
9
 
10
+ ## v0.22.1 (2025-11-10)
11
+
12
+ ### Bug Fixes
13
+
14
+ - Skip batch mode tests in CI to prevent MediaPipe multiprocessing crashes
15
+ ([`05dd796`](https://github.com/feniix/kinemotion/commit/05dd796b36252323c36f8d503c372d96e4108381))
16
+
17
+
18
+ ## v0.22.0 (2025-11-10)
19
+
20
+ ### Bug Fixes
21
+
22
+ - Make CLI batch tests resilient to processing failures in CI
23
+ ([`1f3dfed`](https://github.com/feniix/kinemotion/commit/1f3dfedbe88a2c9be21c907053e549ee2431c500))
24
+
25
+ ### Features
26
+
27
+ - Comprehensive test coverage expansion and documentation refactoring
28
+ ([`dc3cda4`](https://github.com/feniix/kinemotion/commit/dc3cda4e022b61f635e537784aafc08e0f6e78fe))
29
+
30
+
10
31
  ## v0.21.0 (2025-11-10)
11
32
 
12
33
  ### Features
@@ -0,0 +1,247 @@
1
+ # CLAUDE.md
2
+
3
+ ## Repository Purpose
4
+
5
+ Kinemotion: Video-based kinematic analysis for athletic performance using MediaPipe pose tracking.
6
+
7
+ **Supported Jump Types:**
8
+
9
+ - **Drop Jump**: Ground contact time, flight time, reactive strength index
10
+ - **Counter Movement Jump (CMJ)**: Jump height, flight time, countermovement depth, triple extension
11
+
12
+ ## Quick Setup
13
+
14
+ ```bash
15
+ asdf install # Install Python 3.12.7 + uv
16
+ uv sync # Install dependencies
17
+ uv run kinemotion dropjump-analyze video.mp4
18
+ uv run kinemotion cmj-analyze video.mp4
19
+ ```
20
+
21
+ **Development:**
22
+
23
+ ```bash
24
+ uv run pytest # Run all 206 tests with coverage (73.03%)
25
+ uv run pytest --cov-report=html # Generate HTML coverage report
26
+ uv run ruff check --fix && uv run pyright # Lint + type check
27
+ ```
28
+
29
+ **Coverage Reports:**
30
+
31
+ - Terminal: Automatic with `uv run pytest`
32
+ - HTML: `htmlcov/index.html` (open in browser)
33
+ - XML: `coverage.xml` (for CI integration)
34
+
35
+ **SonarQube Cloud Integration:**
36
+
37
+ The project integrates with SonarQube Cloud for continuous code quality and coverage tracking.
38
+
39
+ Setup (one-time):
40
+
41
+ 1. Visit [SonarCloud](https://sonarcloud.io/) and sign in with GitHub
42
+ 2. Import the `feniix/kinemotion` repository
43
+ 3. Generate a token: My Account > Security > Generate Tokens
44
+ 4. Add token to GitHub: Repository > Settings > Secrets and variables > Actions
45
+ - Name: `SONAR_TOKEN`
46
+ - Value: Your generated token
47
+
48
+ Configuration files:
49
+
50
+ - `sonar-project.properties` - SonarQube project configuration
51
+ - `.github/workflows/test.yml` - CI workflow with SonarQube scan
52
+
53
+ The workflow automatically:
54
+
55
+ - Runs tests with coverage on every PR and push to main
56
+ - Uploads coverage.xml to SonarQube Cloud
57
+ - Runs quality gate checks
58
+
59
+ View results: <https://sonarcloud.io/project/overview?id=feniix_kinemotion>
60
+
61
+ ## Architecture
62
+
63
+ ### Module Structure
64
+
65
+ ```text
66
+ src/kinemotion/
67
+ ├── cli.py # Main CLI (registers subcommands)
68
+ ├── api.py # Python API (process_video, process_cmj_video, bulk)
69
+ ├── core/ # Shared: pose, smoothing, filtering, auto_tuning, video_io
70
+ ├── dropjump/ # Drop jump: cli, analysis, kinematics, debug_overlay
71
+ └── cmj/ # CMJ: cli, analysis, kinematics, joint_angles, debug_overlay
72
+
73
+ tests/ # 206 tests total (comprehensive coverage across all modules)
74
+ docs/ # CMJ_GUIDE, TRIPLE_EXTENSION, REAL_TIME_ANALYSIS, etc.
75
+ ```
76
+
77
+ **Design**: Each jump type is a sibling module with its own CLI command, metrics, and visualization.
78
+
79
+ ### Key Differences: Drop Jump vs CMJ
80
+
81
+ | Feature | Drop Jump | CMJ |
82
+ |---------|-----------|-----|
83
+ | Starting | Elevated box | Floor level |
84
+ | Algorithm | Forward search | Backward search from peak |
85
+ | Velocity | Absolute (magnitude) | Signed (direction matters) |
86
+ | Parameters | Auto-tuned quality presets | Auto-tuned quality presets |
87
+ | Key Metric | Ground contact time | Jump height from flight time |
88
+
89
+ ## Critical Gotchas
90
+
91
+ **Video Processing:**
92
+
93
+ - Read first frame for dimensions (not OpenCV properties)
94
+ - Handle rotation metadata (mobile videos)
95
+ - Convert NumPy types for JSON: `int()`, `float()`
96
+
97
+ **CMJ Specific:**
98
+
99
+ - Use signed velocity (not absolute)
100
+ - Backward search algorithm (find peak first)
101
+ - Lateral view required
102
+
103
+ See [Implementation Details](docs/technical/implementation-details.md) for complete technical reference.
104
+
105
+ ## Testing & Quality
106
+
107
+ ### Before Commit
108
+
109
+ ```bash
110
+ uv run ruff check --fix # Auto-fix linting
111
+ uv run pyright # Type check (strict)
112
+ uv run pytest # All 206 tests with coverage
113
+ ```
114
+
115
+ ### Standards
116
+
117
+ - Pyright strict mode (all functions typed)
118
+ - Ruff (100 char lines)
119
+ - Conventional Commits (see below)
120
+ - **Code duplication target: < 3%**
121
+ - **Test coverage: ≥ 50% (current: 73.03% with branch coverage)**
122
+
123
+ ### Coverage Summary
124
+
125
+ **Current:** 73.03% (206 tests, 2225 statements, 752 branches)
126
+
127
+ **Coverage by tier:**
128
+
129
+ - Core algorithms: 85-100% ✅ (analysis, kinematics, filtering, pose)
130
+ - API/Integration: 63% ✅ (api.py)
131
+ - CLI modules: 62-89% ✅ (dropjump: 88.75%, cmj: 62.27%)
132
+ - Visualization: 10-36% ✅ (debug overlays - appropriate)
133
+
134
+ **Key metrics:**
135
+
136
+ - All 206 tests pass
137
+ - 0 type errors (pyright strict)
138
+ - 0 linting errors (ruff)
139
+
140
+ See [Testing Guide](docs/development/testing.md) for:
141
+
142
+ - Detailed coverage breakdown by module
143
+ - Test file organization
144
+ - CLI testing strategy (maintainable patterns)
145
+ - Test breakdown by category
146
+
147
+ View HTML report: `uv run pytest --cov-report=html && open htmlcov/index.html`
148
+
149
+ ### Code Quality
150
+
151
+ - **Duplication target:** < 3% (current: 2.96%)
152
+ - **Check:** `npx jscpd src/kinemotion`
153
+
154
+ **Principles:**
155
+
156
+ 1. Extract common logic to shared utilities
157
+ 2. Use inheritance for shared behavior
158
+ 3. Create helper functions (testable, reusable)
159
+ 4. Use function composition (pass functions as parameters)
160
+
161
+ See [Testing Guide](docs/development/testing.md) for detailed duplication avoidance strategies.
162
+
163
+ ## Quick Reference
164
+
165
+ ### CLI
166
+
167
+ ```bash
168
+ # Drop jump (auto-tuned parameters)
169
+ kinemotion dropjump-analyze video.mp4
170
+
171
+ # CMJ with debug video
172
+ kinemotion cmj-analyze video.mp4 --output debug.mp4
173
+
174
+ # Batch processing
175
+ kinemotion cmj-analyze videos/*.mp4 --batch --workers 4
176
+ ```
177
+
178
+ ### Python API
179
+
180
+ ```python
181
+ # Drop jump
182
+ from kinemotion import process_dropjump_video
183
+ metrics = process_dropjump_video("video.mp4", quality="balanced")
184
+
185
+ # CMJ
186
+ from kinemotion import process_cmj_video
187
+ metrics = process_cmj_video("video.mp4", quality="balanced")
188
+ ```
189
+
190
+ ## Type Safety & Dependencies
191
+
192
+ **Type hints:** Use TypedDict, type aliases, NDArray[dtype]. See [Type Hints Guide](docs/development/type-hints.md).
193
+
194
+ **Key versions:**
195
+
196
+ - Python: 3.12.7
197
+ - NumPy: 2.3.4
198
+ - pytest: 9.0.0
199
+ - MediaPipe: 0.10.14
200
+
201
+ ## Documentation
202
+
203
+ Documentation follows the [Diátaxis framework](https://diataxis.fr/):
204
+
205
+ - **guides/** - How-to tutorials
206
+ - **reference/** - Technical specs
207
+ - **technical/** - Implementation details
208
+ - **development/** - Testing, typing, contribution guides
209
+ - **research/** - Background theory
210
+
211
+ See [docs/README.md](docs/README.md) for complete navigation.
212
+
213
+ ## Commit Format
214
+
215
+ **Required**: [Conventional Commits](https://www.conventionalcommits.org/) - enforced by pre-commit hook
216
+
217
+ **Format**: `<type>(<scope>): <description>`
218
+
219
+ **Types** (triggers version bumps):
220
+
221
+ - `feat`: New feature → minor version bump (0.x.0)
222
+ - `fix`: Bug fix → patch version bump (0.0.x)
223
+ - `perf`: Performance improvement → patch
224
+ - `docs`, `test`, `refactor`, `chore`, `style`, `ci`, `build` → no version bump
225
+
226
+ **Examples:**
227
+
228
+ ```bash
229
+ feat: add CMJ analysis with triple extension tracking
230
+ fix: correct takeoff detection in backward search algorithm
231
+ docs: add triple extension biomechanics guide
232
+ test: add CMJ phase detection tests
233
+ refactor: extract signed velocity to separate function
234
+ chore(release): 0.11.0 [skip ci]
235
+ ```
236
+
237
+ **Breaking changes**: Add `!` or `BREAKING CHANGE:` footer
238
+
239
+ ```bash
240
+ feat!: change API signature for process_video
241
+ ```
242
+
243
+ **Important**: Commit messages must never reference Claude or AI assistance. Keep messages professional and focused on the technical changes.
244
+
245
+ ## MCP Servers
246
+
247
+ Configured in `.mcp.json`: web-search, sequential-thinking, context7, etc.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kinemotion
3
- Version: 0.21.0
3
+ Version: 0.22.1
4
4
  Summary: Video-based kinematic analysis for athletic performance
5
5
  Project-URL: Homepage, https://github.com/feniix/kinemotion
6
6
  Project-URL: Repository, https://github.com/feniix/kinemotion
@@ -0,0 +1,311 @@
1
+ # Testing Guide
2
+
3
+ Comprehensive guide to testing in the Kinemotion project.
4
+
5
+ ## Current Coverage
6
+
7
+ **Overall:** 73.03% (2225 statements, 752 branches, 206 tests)
8
+
9
+ ### Coverage by Module Category
10
+
11
+ #### Perfect Coverage (100%)
12
+
13
+ - Init files (`__init__.py` modules)
14
+ - CMJ joint angles: 100.00%
15
+
16
+ #### Excellent Coverage (85-99%)
17
+
18
+ - Drop jump CLI: 88.75%
19
+ - CMJ analysis: 88.24%
20
+ - Drop jump analysis: 86.26%
21
+ - CMJ kinematics: 95.65%
22
+ - Drop jump kinematics: 85.71%
23
+ - Core video I/O: 91.09%
24
+ - Main CLI: 88.89%
25
+ - Core pose tracking: 88.46%
26
+ - Core filtering: 87.80%
27
+
28
+ #### Good Coverage (60-84%)
29
+
30
+ - Core smoothing: 73.29%
31
+ - Core auto-tuning: 69.66%
32
+ - CMJ CLI: 62.27%
33
+ - Core CLI utils: 64.23%
34
+ - API module: 62.89%
35
+ - Core debug overlay utils: 80.43%
36
+
37
+ #### Expected Lower Coverage (Visualization/UI)
38
+
39
+ - Debug overlays: 10-36% (visualization code - appropriate for UI layer)
40
+
41
+ ### Coverage Targets by Module Type
42
+
43
+ | Module Type | Target Coverage | Current | Status |
44
+ | --------------- | --------------- | ------- | -------------- |
45
+ | Core algorithms | 80%+ | 85-100% | ✅ Exceeded |
46
+ | API/Integration | 60-70% | 63% | ✅ Met |
47
+ | CLI modules | 40-60% | 62-89% | ✅ Exceeded |
48
+ | Visualization | 20-40% | 10-36% | ✅ Appropriate |
49
+
50
+ ## Test File Organization
51
+
52
+ ```
53
+ tests/
54
+ ├── test_cli_dropjump.py # Drop jump CLI integration (17 tests)
55
+ ├── test_cli_cmj.py # CMJ CLI integration (17 tests)
56
+ ├── test_cli_imports.py # CLI import verification (5 tests)
57
+ ├── test_api.py # Public API tests (19 tests)
58
+ ├── test_cmj_analysis.py # CMJ phase detection (31 tests)
59
+ ├── test_contact_detection.py # Drop jump detection (12 tests)
60
+ ├── test_cmj_kinematics.py # CMJ metrics (4 tests)
61
+ ├── test_kinematics.py # Drop jump metrics (2 tests)
62
+ ├── test_joint_angles.py # Triple extension (48 tests)
63
+ ├── test_adaptive_threshold.py # Auto-tuning (10 tests)
64
+ ├── test_filtering.py # Signal filtering (15 tests)
65
+ ├── test_aspect_ratio.py # Video I/O (13 tests)
66
+ ├── test_com_estimation.py # Center of mass (6 tests)
67
+ └── test_polyorder.py # Savitzky-Golay (5 tests)
68
+ ```
69
+
70
+ ### Test Breakdown by Category
71
+
72
+ - **Analysis module tests:** 43 tests (edge cases, helper functions, debug modes)
73
+ - **API tests:** 19 tests (helper functions, verbose mode, outputs)
74
+ - **CLI integration tests:** 34 tests (CliRunner-based, Tier 1 + Tier 2)
75
+ - **Kinematics tests:** 8 tests
76
+ - **Joint angles tests:** 48 tests
77
+ - **Other core tests:** 54 tests
78
+
79
+ ## CLI Testing Strategy
80
+
81
+ The project uses **maintainable CLI testing** with Click's CliRunner to achieve 62-89% coverage on CLI modules without brittle hardcoded strings.
82
+
83
+ ### Maintainable Test Patterns
84
+
85
+ #### Pattern 1: Test Exit Codes (Most Stable)
86
+
87
+ ```python
88
+ from click.testing import CliRunner
89
+
90
+ def test_command_succeeds(cli_runner, minimal_video):
91
+ result = cli_runner.invoke(command, [str(minimal_video), '--quality', 'fast'])
92
+
93
+ # ✅ STABLE: Exit codes rarely change
94
+ assert result.exit_code == 0
95
+ ```
96
+
97
+ #### Pattern 2: Test Behavior, Not Output
98
+
99
+ ```python
100
+ def test_json_output_created(cli_runner, minimal_video, tmp_path):
101
+ json_output = tmp_path / "metrics.json"
102
+
103
+ result = cli_runner.invoke(
104
+ command,
105
+ [str(minimal_video), '--json-output', str(json_output)]
106
+ )
107
+
108
+ # ✅ STABLE: Test file creation
109
+ if result.exit_code == 0:
110
+ assert json_output.exists()
111
+
112
+ # ✅ STABLE: Test structure, not values
113
+ with open(json_output) as f:
114
+ data = json.load(f)
115
+ assert 'ground_contact_time_ms' in data # Key exists
116
+ # ❌ DON'T: assert data['ground_contact_time_ms'] == 250.0
117
+ ```
118
+
119
+ #### Pattern 3: Test CSV Structure
120
+
121
+ ```python
122
+ def test_csv_summary_created(cli_runner, minimal_video, tmp_path):
123
+ csv_path = tmp_path / "summary.csv"
124
+
125
+ result = cli_runner.invoke(
126
+ command,
127
+ [str(minimal_video), '--batch', '--csv-summary', str(csv_path)]
128
+ )
129
+
130
+ if result.exit_code == 0 and csv_path.exists():
131
+ import csv
132
+
133
+ with open(csv_path, newline="") as f:
134
+ reader = csv.DictReader(f)
135
+ rows = list(reader)
136
+
137
+ # ✅ STABLE: Test structure
138
+ assert reader.fieldnames is not None # Has headers
139
+ assert len(rows) >= 1 # Has data
140
+ # ❌ DON'T: Check specific column names or cell values
141
+ ```
142
+
143
+ #### Pattern 4: Parametrized Options
144
+
145
+ ```python
146
+ @pytest.mark.parametrize("quality", ["fast", "balanced", "accurate"])
147
+ def test_quality_presets(cli_runner, minimal_video, quality):
148
+ result = cli_runner.invoke(
149
+ command,
150
+ [str(minimal_video), '--quality', quality]
151
+ )
152
+
153
+ # ✅ STABLE: Just verify option accepted
154
+ assert "Invalid quality" not in result.output
155
+ ```
156
+
157
+ ### What TO Test (CLI)
158
+
159
+ #### Tier 1: Must Have (High Priority)
160
+
161
+ - ✅ Help text displays (`--help`)
162
+ - ✅ Missing video file error
163
+ - ✅ Invalid quality preset error
164
+ - ✅ JSON output file created
165
+ - ✅ Debug video output created
166
+ - ✅ All quality presets accepted
167
+ - ✅ Expert parameters accepted
168
+ - ✅ Command runs without crash
169
+
170
+ #### Tier 2: Nice to Have (Medium Priority)
171
+
172
+ - ✅ Batch mode with multiple videos
173
+ - ✅ Output directory creation
174
+ - ✅ CSV summary creation
175
+ - ✅ Workers option accepted
176
+
177
+ ### What NOT to Test (CLI)
178
+
179
+ - ❌ Exact output text (too fragile)
180
+ - ❌ Progress bar appearance
181
+ - ❌ Specific metric values (tested in core modules)
182
+ - ❌ Output formatting details
183
+ - ❌ Color codes in terminal
184
+
185
+ ### CLI Test Results
186
+
187
+ #### Tier 1 Tests (12 per CLI = 24 total)
188
+
189
+ **Coverage improvement:**
190
+
191
+ - dropjump/cli.py: 23.33% → 52.08% (+28.75%)
192
+ - cmj/cli.py: 22.73% → 51.82% (+29.09%)
193
+
194
+ #### Tier 2 Tests (5 per CLI = 10 total)
195
+
196
+ **Final coverage:**
197
+
198
+ - dropjump/cli.py: 52.08% → 88.75% (+36.67%)
199
+ - cmj/cli.py: 51.82% → 62.27% (+10.45%)
200
+
201
+ **Total CLI tests:** 34 tests (all passing)
202
+
203
+ ## Avoiding Code Duplication
204
+
205
+ When writing new code, follow these principles to maintain duplication below 3%:
206
+
207
+ ### 1. Extract Common Logic
208
+
209
+ If you find yourself copying code between modules, extract it to a shared utility.
210
+
211
+ **Examples:**
212
+
213
+ - `core/smoothing.py` uses `_smooth_landmarks_core()` shared by both standard and advanced smoothing
214
+ - `core/debug_overlay_utils.py` provides `BaseDebugOverlayRenderer` base class
215
+
216
+ ### 2. Use Inheritance for Shared Behavior
217
+
218
+ When classes share common initialization or methods.
219
+
220
+ **Example:**
221
+
222
+ - `DebugOverlayRenderer` and `CMJDebugOverlayRenderer` inherit from `BaseDebugOverlayRenderer`
223
+ - Avoids duplicating `__init__()`, `write_frame()`, `close()`, and context manager methods
224
+
225
+ ### 3. Create Helper Functions
226
+
227
+ Break down complex functions into smaller, reusable pieces.
228
+
229
+ **Examples:**
230
+
231
+ - `_extract_landmark_coordinates()`, `_get_landmark_names()`, `_fill_missing_frames()`
232
+ - Makes code more testable and reusable
233
+
234
+ ### 4. Use Function Composition
235
+
236
+ Pass functions as parameters to share control flow logic.
237
+
238
+ **Example:**
239
+
240
+ - `_smooth_landmarks_core()` accepts a `smoother_fn` parameter
241
+ - Allows different smoothing strategies without duplicating iteration logic
242
+
243
+ ### 5. Check Duplication
244
+
245
+ Run `npx jscpd src/kinemotion` to verify duplication stays below 3%.
246
+
247
+ **Current:** 2.96% (206 duplicated lines out of 6952)
248
+
249
+ **Acceptable duplicates:**
250
+
251
+ - CLI option definitions
252
+ - Small wrapper functions for type safety
253
+
254
+ ## Running Tests
255
+
256
+ ### Quick Commands
257
+
258
+ ```bash
259
+ # Run all tests with coverage
260
+ uv run pytest
261
+
262
+ # Run specific test file
263
+ uv run pytest tests/test_cmj_analysis.py
264
+
265
+ # Run tests matching pattern
266
+ uv run pytest -k "test_edge"
267
+
268
+ # Run with verbose output
269
+ uv run pytest -v
270
+
271
+ # Generate HTML coverage report
272
+ uv run pytest --cov-report=html
273
+ open htmlcov/index.html
274
+ ```
275
+
276
+ ### Coverage Reports
277
+
278
+ **Terminal:** Automatic with `uv run pytest`
279
+
280
+ **HTML:** `htmlcov/index.html` (open in browser)
281
+
282
+ **XML:** `coverage.xml` (for CI integration)
283
+
284
+ ### Test Configuration
285
+
286
+ Pytest 9 native TOML configuration in `pyproject.toml`:
287
+
288
+ ```toml
289
+ [tool.pytest]
290
+ minversion = "9.0"
291
+ testpaths = ["tests"]
292
+ console_output_style = "times" # Per-test execution time
293
+ strict = true # All strictness options enabled
294
+ addopts = [
295
+ "--cov=src/kinemotion",
296
+ "--cov-report=term-missing",
297
+ "--cov-report=html",
298
+ "--cov-report=xml",
299
+ "--cov-branch",
300
+ ]
301
+ ```
302
+
303
+ ### Before Commit Checklist
304
+
305
+ ```bash
306
+ uv run ruff check --fix # Auto-fix linting
307
+ uv run pyright # Type check (strict)
308
+ uv run pytest # All 206 tests with coverage (73.03%)
309
+ ```
310
+
311
+ All checks must pass before committing.