# How to Test Keyframe Functionality in Palmier Pro: A Swift Testing Guide

> Easily test keyframe functionality in Palmier Pro with Swift unit tests. Learn to exercise KeyframeTrack mutations and verify interpolation sampling for robust video editing.

- Repository: [Palmier/palmier-pro](https://github.com/palmier-io/palmier-pro)
- Tags: testing
- Published: 2026-06-24

---

**You test keyframe functionality in Palmier Pro by writing unit tests that exercise `KeyframeTrack` mutations and verify interpolation sampling using Swift’s Testing framework.**

Testing animation keyframes is critical for video editor reliability. In the `palmier-io/palmier-pro` repository, the keyframe system centers on generic `KeyframeTrack<Value>` types that manage timeline mutations and mathematical interpolation. This guide demonstrates how to validate track operations, interpolation modes, and clip-level integrations using the concrete implementation found in [`Sources/PalmierPro/Models/Keyframe.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Models/Keyframe.swift).

## Understanding the Core Architecture

Before writing tests, familiarize yourself with the three fundamental types defined in [`Sources/PalmierPro/Models/Keyframe.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Models/Keyframe.swift):

- **`Keyframe<Value>`** – Stores a timeline frame index, a generic value (e.g., opacity, position), and an outgoing interpolation mode (`linear`, `hold`, or `smooth`).
- **`KeyframeTrack<Value>`** – A sorted array of keyframes (lines 7–25) that exposes core mutating operations (`upsert`, `remove`, `move`) and guarantees that frames remain sorted and duplicates collapse to the latest write.
- **`KeyframeInterpolatable`** – A protocol implemented by `Double`, `AnimPair`, and `Crop` extensions that enables the `sample(at:fallback:)` method to calculate interpolated values between keyframes (lines 31–50).

The `Clip` model in [`Sources/PalmierPro/Models/Timeline.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Models/Timeline.swift) attaches these tracks to video clips, while [`Sources/PalmierPro/Inspector/Keyframes/KeyframesLane.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Inspector/Keyframes/KeyframesLane.swift) provides the UI layer that reads the same public APIs.

## Setting Up the Swift Testing Framework

The test suite lives in [`Tests/PalmierProTests/Timeline/KeyframeTests.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Tests/PalmierProTests/Timeline/KeyframeTests.swift). Palmier Pro uses Swift Testing (the modern successor to XCTest) to validate behavior. Import the `PalmierPro` module and create focused test functions that isolate specific track operations, mutation invariants, and interpolation accuracy.

## Testing Track Mutations

Verify that `upsert`, `remove`, and `move` maintain sorted order and enforce the "last-write-wins" rule. When you call `upsert` on an existing frame, the new value must replace the old one without breaking the sort order.

```swift
import Testing
@testable import PalmierPro

@Test func trackMutationMaintainsSortOrder() {
    var track = KeyframeTrack<Double>()
    
    // Insert out-of-order
    track.upsert(Keyframe(frame: 20, value: 2.0))
    track.upsert(Keyframe(frame: 5, value: 0.5))
    track.upsert(Keyframe(frame: 10, value: 1.0))
    
    // Track automatically sorts by frame
    #expect(track.keyframes.map(\.frame) == [5, 10, 20])
    
    // Move keyframe while preserving order
    track.move(from: 5, to: 15)
    #expect(track.keyframes.map(\.frame) == [10, 15, 20])
    #expect(track.keyframes[1].value == 0.5)
}

```

## Validating Interpolation Logic

Test the `sample(at:fallback:)` method to ensure empty tracks return the fallback value, single keyframes clamp across the entire timeline, and interpolation modes produce mathematically correct results.

```swift
@Test func linearInterpolationSampling() {
    let linear = KeyframeTrack<Double>()
    linear.upsert(Keyframe(frame: 0, value: 0, interpolationOut: .linear))
    linear.upsert(Keyframe(frame: 10, value: 10))
    
    #expect(linear.sample(at: 3, fallback: 0) == 3)  // Linear lerp
    #expect(linear.sample(at: 5, fallback: 0) == 5)
}

@Test func holdInterpolationBehavior() {
    // Hold interpolation keeps the left keyframe's value until the next frame
    let hold = KeyframeTrack<Double>()
    hold.upsert(Keyframe(frame: 0, value: 0, interpolationOut: .hold))
    hold.upsert(Keyframe(frame: 10, value: 10))
    
    #expect(hold.sample(at: 1, fallback: 0) == 0)
    #expect(hold.sample(at: 9, fallback: 0) == 0)
    #expect(hold.sample(at: 10, fallback: 0) == 10)
}

```

## Testing Clip-Level Frame Translation

Clip extensions (lines 94–128 in [`Keyframe.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Keyframe.swift)) provide helpers like `upsertKeyframe`, `moveKeyframe`, and `keyframeFrames` that translate between absolute timeline frames and clip-relative offsets. Test these to ensure frame calculations remain accurate when a clip starts at a non-zero timeline position.

```swift
@Test func clipKeyframeFrameTranslation() {
    var clip = Clip(startFrame: 100, endFrame: 200)
    
    // Insert at absolute timeline frame 110
    clip.upsertKeyframe(in: \.opacityTrack, frame: 110, value: 0.8)
    
    // Helper converts to clip-relative frame 10
    #expect(clip.opacityTrack?.keyframes.first?.frame == 10)
    
    // Move using absolute coordinates
    clip.moveKeyframe(for: .opacity, from: 110, to: 150)
    #expect(clip.keyframeFrames(for: .opacity) == [150])
}

```

## Summary

- **Keyframe testing** validates both data structure integrity (sorted order, duplicate handling) and mathematical interpolation accuracy.
- **Test location**: Place unit tests in [`Tests/PalmierProTests/Timeline/KeyframeTests.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Tests/PalmierProTests/Timeline/KeyframeTests.swift) using Swift Testing syntax.
- **Track mutations**: Verify that `upsert`, `remove`, and `move` maintain sorted order and enforce last-write-wins semantics.
- **Interpolation**: Confirm that `sample(at:fallback:)` correctly handles empty tracks, single keyframes, and `linear`/`hold`/`smooth` modes.
- **Clip integration**: Test absolute-to-relative frame translation via `Clip` helper methods to ensure timeline accuracy.

## Frequently Asked Questions

### What interpolation modes does Palmier Pro support?

Palmier Pro supports `linear`, `hold`, and `smooth` interpolation modes, controlled by the `interpolationOut` property on each `Keyframe`. The `sample(at:fallback:)` method in `KeyframeTrack` uses the left-hand keyframe’s interpolation setting to calculate values between frames, as implemented in [`Sources/PalmierPro/Models/Keyframe.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Models/Keyframe.swift) (lines 31–50).

### How does KeyframeTrack handle duplicate frames?

When you call `upsert` with a keyframe at an existing frame index, the track enforces a "last-write-wins" rule. The new value replaces the existing one at that index, and the internal array remains sorted. This invariant is guaranteed by the `KeyframeTrack` implementation (lines 7–25) and should be verified in mutation tests.

### Can I test custom animation properties beyond opacity and position?

Yes. Because `KeyframeTrack` is generic, adding a new property like `blur` only requires adding a `KeyframeTrack<BlurValue>?` field to the `Clip` model in [`Sources/PalmierPro/Models/Timeline.swift`](https://github.com/palmier-io/palmier-pro/blob/main/Sources/PalmierPro/Models/Timeline.swift). The existing generic tests in [`KeyframeTests.swift`](https://github.com/palmier-io/palmier-pro/blob/main/KeyframeTests.swift) will automatically validate the new track’s behavior without requiring additional test code.

### How do I test frame translation between timeline and clip coordinates?

Use the `Clip` extension methods such as `upsertKeyframe` and `moveKeyframe`, which automatically convert absolute timeline frames to clip-relative offsets. Verify the conversion by asserting that the underlying track’s frame values match the expected relative offsets after performing operations on absolute timeline positions.