Skip to content

05_14_04 - Validation System (Extended)

Overview

The Validation System extends the base SchemaValidator from the Preset Schemas subsystem with advanced validation capabilities:

  • Cross-preset validation - Validate relationships between multiple presets
  • Dependency validation - Verify resource dependencies and module dependencies
  • Rule engine - Custom validation rules with severity levels
  • Batch validation - Validate preset banks efficiently
  • Report generation - Detailed validation reports with actionable insights

This subsystem builds on top of 05_14_00_preset_schemas and integrates with 05_14_03_resource_management.


Architecture

┌─────────────────────────────────────────────────────────────┐
│                    Validation System                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────────┐      ┌─────────────────┐             │
│  │  Base Validator │──────│ Schema Validator │             │
│  │  (from TAREA 1) │      │  (structure)     │             │
│  └─────────────────┘      └─────────────────┘             │
│           │                                                 │
│           ├──────────────────────────────────┐             │
│           │                                  │             │
│  ┌────────▼────────┐              ┌─────────▼──────────┐  │
│  │  Cross-Preset   │              │   Dependency       │  │
│  │   Validator     │              │    Validator       │  │
│  │                 │              │                    │  │
│  │ • Bank checks   │              │ • Resources exist  │  │
│  │ • Duplicates    │              │ • Modules compat   │  │
│  │ • Conflicts     │              │ • Circular deps    │  │
│  └─────────────────┘              └────────────────────┘  │
│           │                                  │             │
│           └──────────┬───────────────────────┘             │
│                      │                                     │
│            ┌─────────▼──────────┐                          │
│            │   Rules Engine     │                          │
│            │                    │                          │
│            │ • Custom rules     │                          │
│            │ • Severity levels  │                          │
│            │ • Auto-fix hints   │                          │
│            └────────────────────┘                          │
│                      │                                     │
│            ┌─────────▼──────────┐                          │
│            │ Validation Report  │                          │
│            │                    │                          │
│            │ • Errors/Warnings  │                          │
│            │ • Statistics       │                          │
│            │ • Recommendations  │                          │
│            └────────────────────┘                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Components

1. CrossPresetValidator

Validates relationships and consistency across multiple presets:

Checks: - Duplicate detection - Find presets with identical content - Name conflicts - Detect name collisions in same bank - Category consistency - Verify category/tag usage - Version compatibility - Check version mismatches in bank - Dependency chains - Validate preset dependencies

Example:

CrossPresetValidator validator;

std::vector<PresetSchema> bank = loadPresetBank("my_bank.json");

// Validate entire bank
auto result = validator.validateBank(bank);

if (!result.isValid()) {
    for (const auto& error : result.errors) {
        std::cout << "Error: " << error.message << "\n";
    }
}

// Check for duplicates
auto duplicates = validator.findDuplicates(bank);
std::cout << "Found " << duplicates.size() << " duplicate groups\n";

// Check for conflicts
auto conflicts = validator.findConflicts(bank);

2. DependencyValidator

Validates resource and module dependencies:

Resource Validation: - All referenced resources exist or have fallbacks - Resource types match declared types - Checksums match if specified - No circular resource dependencies

Module Validation: - Required modules are available - Module versions are compatible - Module dependencies are satisfied - No circular module dependencies

Example:

DependencyValidator validator(resource_manager);

// Validate single preset dependencies
auto result = validator.validatePresetDependencies(preset);

if (result.missing_resources.size() > 0) {
    std::cout << "Missing resources:\n";
    for (const auto& res : result.missing_resources) {
        std::cout << "  - " << res.path << "\n";

        // Find alternatives
        auto alternatives = validator.findResourceAlternatives(res);
        for (const auto& alt : alternatives) {
            std::cout << "    → " << alt.path << " (similarity: "
                      << alt.similarity << ")\n";
        }
    }
}

// Check circular dependencies
if (validator.hasCircularDependencies(presets)) {
    auto cycles = validator.findCircularDependencies(presets);
    // ...
}

3. ValidationRulesEngine

Extensible rule-based validation system:

Built-in Rules: - Parameter range validation - Parameter consistency checks (e.g., filter cutoff vs resonance) - Naming conventions - Metadata completeness - Performance characteristics (e.g., CPU usage estimates)

Custom Rules:

class MyCustomRule : public ValidationRule {
public:
    ValidationResult validate(const PresetSchema& preset) override {
        ValidationResult result;

        // Custom validation logic
        if (preset.header.name.length() > 50) {
            result.addWarning("Name too long (max 50 chars)");
        }

        return result;
    }

    std::string getName() const override { return "NameLengthRule"; }
    Severity getSeverity() const override { return Severity::Warning; }
};

// Register rule
ValidationRulesEngine engine;
engine.registerRule(std::make_unique<MyCustomRule>());

// Validate with all rules
auto result = engine.validate(preset);

Rule Severity Levels: - Critical - Prevents preset from loading (e.g., corrupt data) - Error - Invalid preset, may cause issues (e.g., missing required field) - Warning - Valid but potentially problematic (e.g., unusual values) - Info - Informational message (e.g., optimization suggestion)

4. BatchValidator

Efficient validation of large preset collections:

Features: - Parallel validation (multi-threaded) - Progress callbacks - Incremental validation (only changed presets) - Validation caching - Summary reports

Example:

BatchValidator validator;
validator.setThreadCount(8);  // Use 8 threads

// Progress callback
validator.setProgressCallback([](size_t current, size_t total) {
    std::cout << "Validating: " << current << "/" << total << "\r";
});

// Validate large bank
std::vector<PresetSchema> bank = loadLargeBank("factory.json");  // 1000 presets
auto report = validator.validateBatch(bank);

std::cout << "\nValidation Summary:\n";
std::cout << "  Total: " << report.total_presets << "\n";
std::cout << "  Valid: " << report.valid_presets << "\n";
std::cout << "  Warnings: " << report.warning_count << "\n";
std::cout << "  Errors: " << report.error_count << "\n";
std::cout << "  Time: " << report.validation_time_ms << "ms\n";

5. ValidationReport

Structured validation results with actionable insights:

Report Contents: - Errors - Issues that must be fixed - Warnings - Issues that should be reviewed - Info - Informational messages - Statistics - Validation metrics - Recommendations - Auto-fix suggestions

Report Formats: - Console - Human-readable text output - JSON - Machine-readable structured data - HTML - Rich formatted report with navigation - Markdown - Documentation-friendly format

Example:

ValidationReport report = validator.validate(preset);

// Console output
std::cout << report.toConsole();

// JSON export
std::ofstream json_file("validation_report.json");
json_file << report.toJSON();

// HTML report
std::ofstream html_file("validation_report.html");
html_file << report.toHTML();

// Get auto-fix suggestions
for (const auto& fix : report.getAutoFixSuggestions()) {
    std::cout << "Suggestion: " << fix.description << "\n";

    if (fix.can_auto_fix) {
        std::cout << "  Can auto-fix: Yes\n";
        // Apply fix
        fix.apply(preset);
    }
}


Validation Levels

The system supports multiple validation levels that can be configured:

Level 1: Quick (Structure Only)

  • JSON/Binary structure valid
  • Required fields present
  • Type checking
  • Performance: <1ms per preset

Level 2: Standard (+ Basic Validation)

  • Level 1 checks
  • Parameter ranges
  • Basic consistency
  • Performance: ~5ms per preset

Level 3: Extended (+ Cross-Preset)

  • Level 2 checks
  • Duplicate detection
  • Name conflicts
  • Performance: ~10ms per preset

Level 4: Full (+ Dependencies)

  • Level 3 checks
  • Resource validation
  • Module compatibility
  • Performance: ~20ms per preset

Level 5: Comprehensive (+ Rules Engine)

  • Level 4 checks
  • All custom rules
  • Auto-fix suggestions
  • Performance: ~50ms per preset

Configuration:

ValidationConfig config;
config.level = ValidationLevel::Full;
config.enable_auto_fix = false;
config.parallel_validation = true;
config.cache_results = true;

Validator validator(config);


Integration with Other Subsystems

With Resource Management (TAREA 4)

// Create validator with resource manager integration
ResourceManager resource_manager;
DependencyValidator dep_validator(resource_manager);

// Validate preset - will check if resources exist
auto result = dep_validator.validatePresetDependencies(preset);

// Auto-fix: Use resource manager to find alternatives
if (!result.missing_resources.empty()) {
    for (const auto& missing : result.missing_resources) {
        auto similar = resource_manager.findSimilarResources(missing);
        if (!similar.empty()) {
            preset.replaceResource(missing, similar[0]);
        }
    }
}

With Version Management (TAREA 3)

// Check version compatibility in bank
CrossPresetValidator validator;

auto bank = loadPresetBank("mixed_versions.json");
auto version_issues = validator.findVersionMismatches(bank);

if (!version_issues.empty()) {
    VersionManager& vm = VersionManager::getInstance();

    // Migrate all to latest version
    for (auto& preset : bank) {
        if (preset.header.version != vm.getCurrentVersion()) {
            preset = vm.migrate(preset, vm.getCurrentVersion());
        }
    }
}

With Serialization (TAREA 2)

// Validate before serialization
PresetSchema preset = createPreset();

Validator validator;
auto result = validator.validate(preset);

if (result.isValid()) {
    // Safe to serialize
    JSONSerializer serializer;
    serializer.serializeToFile(preset, "preset.json");
} else {
    // Try auto-fix
    result.applyAutoFixes(preset);

    // Re-validate
    result = validator.validate(preset);
    if (result.isValid()) {
        serializer.serializeToFile(preset, "preset.json");
    } else {
        std::cerr << "Cannot save invalid preset\n";
    }
}

API Reference

CrossPresetValidator

class CrossPresetValidator {
public:
    // Bank validation
    ValidationResult validateBank(const std::vector<PresetSchema>& bank);

    // Duplicate detection
    std::vector<DuplicateGroup> findDuplicates(
        const std::vector<PresetSchema>& bank);

    // Conflict detection
    std::vector<Conflict> findConflicts(
        const std::vector<PresetSchema>& bank);

    // Version consistency
    std::vector<VersionMismatch> findVersionMismatches(
        const std::vector<PresetSchema>& bank);

    // Category validation
    ValidationResult validateCategories(
        const std::vector<PresetSchema>& bank);
};

DependencyValidator

class DependencyValidator {
public:
    // Constructor with resource manager
    explicit DependencyValidator(ResourceManager& resource_manager);

    // Resource dependency validation
    DependencyResult validatePresetDependencies(const PresetSchema& preset);

    // Module dependency validation
    DependencyResult validateModuleDependencies(const PresetSchema& preset);

    // Circular dependency detection
    bool hasCircularDependencies(const std::vector<PresetSchema>& presets);
    std::vector<DependencyCircle> findCircularDependencies(
        const std::vector<PresetSchema>& presets);

    // Find alternatives for missing resources
    std::vector<ResourceAlternative> findResourceAlternatives(
        const PresetSchema::ResourceRef& resource);
};

ValidationRulesEngine

class ValidationRulesEngine {
public:
    // Rule management
    void registerRule(std::unique_ptr<ValidationRule> rule);
    void unregisterRule(const std::string& rule_name);

    // Validation
    ValidationResult validate(const PresetSchema& preset);
    ValidationResult validateWithRules(
        const PresetSchema& preset,
        const std::vector<std::string>& rule_names);

    // Rule queries
    std::vector<std::string> getRegisteredRules() const;
    const ValidationRule* getRule(const std::string& name) const;
};

// Base class for custom rules
class ValidationRule {
public:
    virtual ~ValidationRule() = default;

    virtual ValidationResult validate(const PresetSchema& preset) = 0;
    virtual std::string getName() const = 0;
    virtual Severity getSeverity() const = 0;
    virtual std::string getDescription() const = 0;
};

BatchValidator

class BatchValidator {
public:
    // Configuration
    void setThreadCount(size_t count);
    void setValidationLevel(ValidationLevel level);
    void setProgressCallback(ProgressCallback callback);

    // Batch validation
    BatchValidationReport validateBatch(
        const std::vector<PresetSchema>& presets);

    // Incremental validation (only changed presets)
    BatchValidationReport validateIncremental(
        const std::vector<PresetSchema>& presets,
        const std::vector<std::string>& changed_ids);

    // Cache management
    void clearCache();
    void setCacheEnabled(bool enabled);
};

ValidationReport

class ValidationReport {
public:
    // Status
    bool isValid() const;
    bool hasErrors() const;
    bool hasWarnings() const;

    // Counts
    size_t getErrorCount() const;
    size_t getWarningCount() const;
    size_t getInfoCount() const;

    // Messages
    const std::vector<ValidationMessage>& getErrors() const;
    const std::vector<ValidationMessage>& getWarnings() const;
    const std::vector<ValidationMessage>& getInfo() const;

    // Export
    std::string toConsole() const;
    std::string toJSON() const;
    std::string toHTML() const;
    std::string toMarkdown() const;

    // Auto-fix
    std::vector<AutoFixSuggestion> getAutoFixSuggestions() const;
    void applyAutoFixes(PresetSchema& preset);
};

Performance Targets

Operation Target Notes
Quick validation (L1) <1ms Structure only
Standard validation (L2) <5ms + Basic checks
Full validation (L4) <20ms + Dependencies
Batch validation (1000) <5s Parallel, 8 threads
Duplicate detection (1000) <100ms Hash-based
Circular dependency check O(n²) DFS algorithm

Usage Examples

Example 1: Validate Single Preset

#include <validation_system.hpp>

PresetSchema preset = loadPreset("my_preset.json");

// Create validator with standard level
ValidationConfig config;
config.level = ValidationLevel::Standard;
Validator validator(config);

// Validate
auto report = validator.validate(preset);

if (report.isValid()) {
    std::cout << "✓ Preset is valid\n";
} else {
    std::cout << "✗ Preset has issues:\n";
    for (const auto& error : report.getErrors()) {
        std::cout << "  [ERROR] " << error.message << "\n";
    }
    for (const auto& warning : report.getWarnings()) {
        std::cout << "  [WARN] " << warning.message << "\n";
    }
}

Example 2: Validate Preset Bank

// Load bank
std::vector<PresetSchema> bank = loadPresetBank("factory.json");

// Create cross-preset validator
CrossPresetValidator validator;

// Find duplicates
auto duplicates = validator.findDuplicates(bank);
std::cout << "Found " << duplicates.size() << " duplicate groups\n";

for (const auto& group : duplicates) {
    std::cout << "Duplicates:\n";
    for (const auto& preset : group.presets) {
        std::cout << "  - " << preset.header.name << "\n";
    }
}

// Find conflicts
auto conflicts = validator.findConflicts(bank);
for (const auto& conflict : conflicts) {
    std::cout << "Conflict: " << conflict.description << "\n";
    std::cout << "  Between: " << conflict.preset1_id
              << " and " << conflict.preset2_id << "\n";
}

Example 3: Custom Validation Rule

class CPUUsageRule : public ValidationRule {
public:
    ValidationResult validate(const PresetSchema& preset) override {
        ValidationResult result;

        // Estimate CPU usage
        float cpu_estimate = estimateCPU(preset);

        if (cpu_estimate > 80.0f) {
            result.addWarning(
                "High CPU usage estimated: " +
                std::to_string(cpu_estimate) + "%"
            );

            // Add auto-fix suggestion
            result.addAutoFix(
                "Consider reducing oversampling or filter order"
            );
        }

        return result;
    }

    std::string getName() const override { return "CPUUsageRule"; }
    Severity getSeverity() const override { return Severity::Warning; }

private:
    float estimateCPU(const PresetSchema& preset) {
        // Simple heuristic based on parameter values
        float cpu = 0.0f;

        // Check for expensive operations
        if (preset.parameters.find("oversampling") != preset.parameters.end()) {
            cpu += preset.parameters.at("oversampling").value * 10.0f;
        }

        if (preset.parameters.find("filter.order") != preset.parameters.end()) {
            cpu += preset.parameters.at("filter.order").value * 5.0f;
        }

        return cpu;
    }
};

// Use the rule
ValidationRulesEngine engine;
engine.registerRule(std::make_unique<CPUUsageRule>());

auto result = engine.validate(preset);

Best Practices

1. Choose Appropriate Validation Level

  • Use Quick during interactive editing
  • Use Standard for save operations
  • Use Full for bank imports
  • Use Comprehensive for quality assurance

2. Handle Validation Results Gracefully

auto result = validator.validate(preset);

if (!result.isValid()) {
    // Try auto-fix first
    if (result.hasAutoFixes()) {
        result.applyAutoFixes(preset);
        result = validator.validate(preset);
    }

    // If still invalid, notify user
    if (!result.isValid()) {
        showValidationErrorsDialog(result);
        return;
    }
}

// Proceed with valid preset
savePreset(preset);

3. Use Batch Validation for Large Collections

BatchValidator validator;
validator.setThreadCount(std::thread::hardware_concurrency());
validator.setValidationLevel(ValidationLevel::Standard);

auto report = validator.validateBatch(bank);

// Process results
for (const auto& preset_result : report.preset_results) {
    if (!preset_result.isValid()) {
        std::cout << preset_result.preset_id << ": "
                  << preset_result.error_count << " errors\n";
    }
}

4. Cache Validation Results

BatchValidator validator;
validator.setCacheEnabled(true);

// First validation (slow)
auto report1 = validator.validateBatch(bank);

// Modify some presets
bank[0].parameters["gain"].value = 0.5f;

// Incremental validation (fast - only changed presets)
auto report2 = validator.validateIncremental(bank, {"preset_0"});

Files

05_14_04_validation_system/
├── include/
│   ├── cross_preset_validator.hpp
│   ├── dependency_validator.hpp
│   ├── validation_rules_engine.hpp
│   ├── batch_validator.hpp
│   └── validation_report.hpp
├── src/
│   ├── cross_preset_validator.cpp
│   ├── dependency_validator.cpp
│   ├── validation_rules_engine.cpp
│   ├── batch_validator.cpp
│   └── validation_report.cpp
├── tests/
│   ├── test_cross_preset_validator.cpp
│   ├── test_dependency_validator.cpp
│   ├── test_validation_rules.cpp
│   └── test_batch_validator.cpp
├── examples/
│   ├── validate_preset_example.cpp
│   ├── validate_bank_example.cpp
│   ├── custom_rule_example.cpp
│   └── batch_validation_example.cpp
├── CMakeLists.txt
└── README.md (this file)

Dependencies

  • 05_14_00_preset_schemas - Base schemas and SchemaValidator
  • 05_14_03_resource_management - Resource validation
  • nlohmann/json - JSON handling
  • Catch2 - Testing framework

Notes

  • This subsystem extends but does not replace the base SchemaValidator
  • Focus is on higher-level validation (cross-preset, dependencies, rules)
  • Designed for extensibility - easy to add custom rules
  • Performance-conscious - parallel validation, caching
  • Integration-ready - works with all other subsystems