Skip to content

Commit 89f319a

Browse files
committed
Add comprehensive CLAUDE.md documentation for AI assistants
This document provides AI assistants with a complete guide to the pluginval codebase including: - Project overview and architecture - Directory structure explanation - Build system and configuration options - Test framework design and how to add new tests - Code conventions and style guidelines - Command line interface documentation - CI/CD integration guidance - Common tasks and workflows https://claude.ai/code/session_01HazUUyiepsFF8Eg9XevwVx
1 parent d9312d9 commit 89f319a

1 file changed

Lines changed: 364 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
# CLAUDE.md - AI Assistant Guide for pluginval
2+
3+
## Project Overview
4+
5+
**pluginval** is a cross-platform audio plugin validator and tester application developed by Tracktion Corporation. It tests VST, VST3, AU (Audio Unit), LV2, and LADSPA plugins for compatibility and stability with host applications.
6+
7+
- **Version**: 1.0.4 (see `VERSION` file)
8+
- **License**: GPLv3
9+
- **Framework**: Built on JUCE (v8.0.x)
10+
- **Language**: C++20
11+
12+
### Key Features
13+
- Tests VST/VST2/VST3/AU/LV2/LADSPA plugins
14+
- Cross-platform (macOS, Windows, Linux)
15+
- GUI and headless (CLI) operation modes
16+
- Validation runs in a separate process to prevent crashes from bringing down the app
17+
- Real-time safety checking via rtcheck (macOS only currently)
18+
- Integration with native validators (auval for AU, vstvalidator for VST3)
19+
20+
## Directory Structure
21+
22+
```
23+
pluginval/
24+
├── Source/ # Main application source code
25+
│ ├── Main.cpp # Application entry point
26+
│ ├── MainComponent.cpp/h # GUI main window component
27+
│ ├── Validator.cpp/h # Core validation orchestration
28+
│ ├── PluginTests.cpp/h # Test framework and base classes
29+
│ ├── CommandLine.cpp/h # CLI argument parsing
30+
│ ├── CrashHandler.cpp/h # Crash reporting utilities
31+
│ ├── TestUtilities.cpp/h # Helper functions for tests
32+
│ ├── RTCheck.h # Real-time safety checking macros
33+
│ ├── PluginvalLookAndFeel.h # Custom UI styling
34+
│ ├── StrictnessInfoPopup.h # Strictness level info UI
35+
│ ├── binarydata/ # Binary resources (icons)
36+
│ └── tests/ # Individual test implementations
37+
│ ├── BasicTests.cpp # Core plugin tests (info, state, audio)
38+
│ ├── BusTests.cpp # Audio bus configuration tests
39+
│ ├── EditorTests.cpp # Plugin editor/GUI tests
40+
│ ├── ParameterFuzzTests.cpp # Parameter fuzzing tests
41+
│ ├── LocaleTest.cpp # Locale handling tests
42+
│ └── ExtremeTests.cpp # Edge case tests
43+
├── modules/
44+
│ └── juce/ # JUCE framework (git submodule)
45+
├── cmake/
46+
│ └── CPM.cmake # CMake Package Manager
47+
├── tests/
48+
│ ├── AddPluginvalTests.cmake # CMake module for CTest integration
49+
│ ├── test_plugins/ # Test plugin files
50+
│ ├── mac_tests/ # macOS-specific tests
51+
│ └── windows_tests.bat # Windows test scripts
52+
├── docs/ # Documentation
53+
│ ├── Adding pluginval to CI.md
54+
│ ├── Command line options.md
55+
│ ├── Debugging a failed validation.md
56+
│ └── Testing plugins with pluginval.md
57+
├── CMakeLists.txt # Main build configuration
58+
├── VERSION # Version number file
59+
├── CHANGELIST.md # Release changelog
60+
└── ROADMAP.md # Future development plans
61+
```
62+
63+
## Build System
64+
65+
### Prerequisites
66+
- CMake 3.15+
67+
- C++20 compatible compiler
68+
- Git (for submodules)
69+
70+
### Building
71+
72+
```bash
73+
# Initialize JUCE submodule
74+
git submodule update --init
75+
76+
# Configure (Debug build)
77+
cmake -B Builds/Debug -DCMAKE_BUILD_TYPE=Debug .
78+
79+
# Build
80+
cmake --build Builds/Debug --config Debug
81+
```
82+
83+
### Build Options
84+
85+
| Option | Description | Default |
86+
|--------|-------------|---------|
87+
| `PLUGINVAL_FETCH_JUCE` | Fetch JUCE with pluginval | ON |
88+
| `WITH_ADDRESS_SANITIZER` | Enable AddressSanitizer | OFF |
89+
| `WITH_THREAD_SANITIZER` | Enable ThreadSanitizer | OFF |
90+
| `VST2_SDK_DIR` | Path to VST2 SDK (env var) | - |
91+
92+
### Enabling VST2 Support
93+
94+
VST2 SDK is not included. Set the environment variable before configuring:
95+
```bash
96+
VST2_SDK_DIR=/path/to/vst2sdk cmake -B Builds/Debug .
97+
```
98+
99+
### Target Platforms
100+
- **macOS**: 10.11+ (deployment target), supports Apple Silicon via universal binary
101+
- **Windows**: MSVC with static runtime linking
102+
- **Linux**: Ubuntu 22.04+, statically links libstdc++
103+
104+
## Architecture
105+
106+
### Core Components
107+
108+
1. **PluginValidatorApplication** (`Main.cpp`)
109+
- JUCE application entry point
110+
- Handles both GUI and CLI modes
111+
- Manages preferences and window lifecycle
112+
113+
2. **Validator** (`Validator.h/cpp`)
114+
- Orchestrates validation passes
115+
- Supports in-process and child-process validation
116+
- Listener interface for progress callbacks
117+
118+
3. **ValidationPass** (`Validator.h`)
119+
- Single async validation for one plugin
120+
- Can run in separate process for crash isolation
121+
122+
4. **PluginTests** (`PluginTests.h/cpp`)
123+
- UnitTest subclass that runs all registered tests
124+
- Manages plugin loading and test execution
125+
- Configurable via `Options` struct
126+
127+
5. **PluginTest** (`PluginTests.h`)
128+
- Base class for individual tests
129+
- Auto-registers via static instance pattern
130+
- Defines requirements (thread, GUI needs)
131+
132+
### Test Framework
133+
134+
Tests are self-registering. To find all tests, look for static instances:
135+
```cpp
136+
static MyTest myTest; // Registers automatically
137+
```
138+
139+
**Strictness Levels (1-10)**:
140+
- Level 1-4: Basic tests, quick execution
141+
- Level 5: Recommended minimum for host compatibility (default)
142+
- Level 6+: Extended tests, parameter fuzzing, longer duration
143+
- Level 10: Most thorough, includes real-time safety checks
144+
145+
**Test Requirements**:
146+
```cpp
147+
struct Requirements {
148+
Thread thread; // backgroundThread or messageThread
149+
GUI gui; // noGUI or requiresGUI
150+
};
151+
```
152+
153+
### Key Test Files by Category
154+
155+
| File | Tests Included |
156+
|------|----------------|
157+
| `BasicTests.cpp` | PluginInfo, Programs, Editor, AudioProcessing, PluginState, Automation, auval, VST3validator |
158+
| `BusTests.cpp` | Bus layout, channel configuration |
159+
| `EditorTests.cpp` | Editor creation, resizing |
160+
| `ParameterFuzzTests.cpp` | Random parameter value testing |
161+
| `LocaleTest.cpp` | Locale handling verification |
162+
| `ExtremeTests.cpp` | Edge cases, stress tests |
163+
164+
## Adding New Tests
165+
166+
1. Create a subclass of `PluginTest`:
167+
```cpp
168+
struct MyNewTest : public PluginTest
169+
{
170+
MyNewTest()
171+
: PluginTest ("My Test Name",
172+
5, // strictness level (1-10)
173+
{ Requirements::Thread::backgroundThread,
174+
Requirements::GUI::noGUI })
175+
{
176+
}
177+
178+
void runTest (PluginTests& ut, juce::AudioPluginInstance& instance) override
179+
{
180+
// Use ut.expect(), ut.expectEquals(), ut.logMessage()
181+
ut.logMessage ("Running my test...");
182+
ut.expect (someCondition, "Test failed because...");
183+
}
184+
185+
std::vector<TestDescription> getDescription (int strictnessLevel) const override
186+
{
187+
return { { name, "Description of what this test does" } };
188+
}
189+
};
190+
191+
// Register the test with a static instance
192+
static MyNewTest myNewTest;
193+
```
194+
195+
2. Add the source file to `CMakeLists.txt` in the `SourceFiles` list.
196+
197+
### Test Utilities
198+
199+
Located in `TestUtilities.h`:
200+
- `getNonBypassAutomatableParameters()` - Get automatable params
201+
- `fillNoise()` - Fill buffer with random audio
202+
- `countNaNs()`, `countInfs()`, `countSubnormals()` - Audio validation
203+
- `ScopedEditorShower` - RAII editor creation/destruction
204+
- `callPrepareToPlayOnMessageThreadIfVST3()` - VST3-safe lifecycle
205+
- `ScopedAllocationDisabler` - Detect allocations in audio thread
206+
207+
### Real-time Safety Checking
208+
209+
Use the `RTC_REALTIME_CONTEXT_IF_ENABLED` macro around `processBlock` calls:
210+
```cpp
211+
{
212+
RTC_REALTIME_CONTEXT_IF_ENABLED(ut.getOptions().realtimeCheck, blockNum)
213+
instance.processBlock(ab, mb);
214+
}
215+
```
216+
217+
## Code Conventions
218+
219+
### Style
220+
- JUCE coding style (CamelCase for types, camelCase for variables)
221+
- 4-space indentation
222+
- Braces on same line for control structures
223+
- Use JUCE types: `juce::String`, `juce::Array`, `juce::File`, etc.
224+
225+
### Header Guards
226+
Use `#pragma once` (not traditional include guards)
227+
228+
### JUCE Namespace
229+
Either use `juce::` prefix or have `using namespace juce;` in cpp files (not headers)
230+
231+
### Thread Safety
232+
- Tests may run on background or message thread (specify in Requirements)
233+
- VST3 plugins require certain operations on message thread (use `*OnMessageThreadIfVST3` helpers)
234+
- Use `juce::WaitableEvent` for thread synchronization
235+
236+
### Logging
237+
```cpp
238+
ut.logMessage("Important message"); // Always shown
239+
ut.logVerboseMessage("Detail message"); // Only with --verbose flag
240+
```
241+
242+
## Command Line Interface
243+
244+
Basic usage:
245+
```bash
246+
./pluginval --strictness-level 5 /path/to/plugin.vst3
247+
```
248+
249+
Key options:
250+
- `--validate [path]` - Validate plugin at path
251+
- `--strictness-level [1-10]` - Test thoroughness (default: 5)
252+
- `--skip-gui-tests` - Skip GUI tests (for headless CI)
253+
- `--validate-in-process` - Don't use child process (for debugging)
254+
- `--timeout-ms [ms]` - Test timeout (default: 30000, -1 for none)
255+
- `--verbose` - Enable verbose logging
256+
- `--output-dir [dir]` - Directory for log files
257+
- `--sample-rates [list]` - Comma-separated sample rates
258+
- `--block-sizes [list]` - Comma-separated block sizes
259+
- `--rtcheck [disabled|enabled|relaxed]` - Real-time safety checking
260+
261+
Environment variables can substitute CLI args:
262+
- `--skip-gui-tests` -> `SKIP_GUI_TESTS=1`
263+
- `--timeout-ms 30000` -> `TIMEOUT_MS=30000`
264+
265+
Exit codes: 0 = success, 1 = failure
266+
267+
## CI Integration
268+
269+
### CMake/CTest Integration
270+
271+
Use `tests/AddPluginvalTests.cmake`:
272+
```cmake
273+
include(AddPluginvalTests)
274+
add_pluginval_tests(MyPluginTarget
275+
TEST_PREFIX "MyPlugin.pluginval"
276+
LOG_DIR "${CMAKE_BINARY_DIR}/logs"
277+
)
278+
```
279+
280+
### GitHub Actions Example
281+
282+
```yaml
283+
- name: Download pluginval
284+
run: |
285+
curl -L "https://github.com/Tracktion/pluginval/releases/latest/download/pluginval_${{ runner.os }}.zip" -o pluginval.zip
286+
unzip pluginval.zip
287+
288+
- name: Validate Plugin
289+
run: |
290+
./pluginval --strictness-level 5 --skip-gui-tests ./build/MyPlugin.vst3
291+
```
292+
293+
## Dependencies
294+
295+
### External
296+
- **JUCE** (v8.0.x) - Audio application framework (git submodule)
297+
- **magic_enum** (v0.9.7) - Enum reflection (fetched via CPM)
298+
- **rtcheck** (optional, macOS) - Real-time safety checking (fetched via CPM)
299+
300+
### System
301+
- macOS: CoreAudio, AudioUnit frameworks
302+
- Linux: ALSA, X11
303+
- Windows: WASAPI, DirectSound
304+
305+
## Testing pluginval Itself
306+
307+
Debug unit tests run automatically in debug builds:
308+
```cpp
309+
#if JUCE_DEBUG
310+
juce::UnitTestRunner testRunner;
311+
testRunner.runTestsInCategory ("pluginval");
312+
#endif
313+
```
314+
315+
Run internal tests via CLI:
316+
```bash
317+
./pluginval --run-tests
318+
```
319+
320+
## Release Process
321+
322+
1. Update `VERSION` file
323+
2. Update `CHANGELIST.md`
324+
3. Commit: `git commit -am "Version X.Y.Z"`
325+
4. Tag: `git tag -a vX.Y.Z -m "X.Y.Z release"`
326+
5. Push: `git push --tags`
327+
328+
## Common Tasks for AI Assistants
329+
330+
### Finding Where Tests Are Defined
331+
- All test classes are in `Source/tests/*.cpp`
332+
- Search for `static.*Test.*Test;` to find registrations
333+
- Each test subclasses `PluginTest`
334+
335+
### Understanding Test Flow
336+
1. `Main.cpp` creates `Validator` or `CommandLineValidator`
337+
2. `Validator` creates `ValidationPass` for each plugin
338+
3. `ValidationPass` spawns child process or runs in-process
339+
4. `PluginTests::runTest()` iterates all registered `PluginTest` instances
340+
5. Each `PluginTest::runTest()` performs its specific validation
341+
342+
### Modifying Build Configuration
343+
- All in `CMakeLists.txt`
344+
- Source files listed in `SourceFiles` variable
345+
- JUCE modules linked via `target_link_libraries`
346+
347+
### Adding Platform-Specific Code
348+
```cpp
349+
#if JUCE_MAC
350+
// macOS specific
351+
#elif JUCE_WINDOWS
352+
// Windows specific
353+
#elif JUCE_LINUX
354+
// Linux specific
355+
#endif
356+
```
357+
358+
## Important Notes
359+
360+
- Always test changes on multiple platforms when possible
361+
- VST3 plugins have specific threading requirements - use the `*OnMessageThreadIfVST3` helpers
362+
- Child process validation is the default and recommended for production use
363+
- In-process validation (`--validate-in-process`) is useful for debugging but a crashing plugin will crash pluginval
364+
- Real-time safety checking is only available on macOS currently (uses rtcheck library)

0 commit comments

Comments
 (0)