Skip to content

Add unit tests for SharedFrame (zero-copy, concurrency, edge cases) #18

@CREVIOS

Description

@CREVIOS

Problem

`SharedFrame` is the most critical data structure in Drift — it sits between the RDP session actor and the GPU renderer. It has zero tests.

Tests to write

Basic correctness

```rust
#[test]
fn test_update_full_and_publish() {
let sf = SharedFrame::new(2, 2);
let data = vec![255u8; 2 * 2 * 4]; // 2x2 white RGBA
sf.update_full(2, 2, &data);
let snapshot = sf.publish().expect("should be dirty");
assert_eq!(snapshot.width, 2);
assert_eq!(snapshot.height, 2);
assert_eq!(snapshot.data, data);
}

#[test]
fn test_publish_returns_none_when_clean() {
let sf = SharedFrame::new(2, 2);
assert!(sf.publish().is_none()); // not dirty after creation... wait, it IS dirty
// Actually: new() sets dirty = false, so first publish returns None
// Need to verify this behavior
}
```

Dirty rect update

```rust
#[test]
fn test_update_rect_partial() {
let sf = SharedFrame::new(4, 4); // 4x4 black frame
// Write a 2x2 red square at (1, 1)
let red = vec![255, 0, 0, 255, 255, 0, 0, 255,
255, 0, 0, 255, 255, 0, 0, 255];
sf.update_rect(1, 1, 2, 2, &red, 2 * 4);
let snap = sf.publish().unwrap();
// Verify pixel at (1,1) is red
let offset = (1 * 4 + 1) * 4; // row 1, col 1
assert_eq!(&snap.data[offset..offset+4], &[255, 0, 0, 255]);
// Verify pixel at (0,0) is still black
assert_eq!(&snap.data[0..4], &[0, 0, 0, 0]);
}
```

Batched writes

```rust
#[test]
fn test_begin_write_batches() {
let sf = SharedFrame::new(4, 4);
{
let mut guard = sf.begin_write();
guard.update_rect(0, 0, 2, 2, &vec![1u8; 16], 8);
guard.update_rect(2, 2, 2, 2, &vec![2u8; 16], 8);
} // guard drops, lock released
sf.mark_dirty();
let snap = sf.publish().unwrap();
assert_eq!(snap.data[0], 1); // first rect
assert_eq!(snap.data[(2*4+2)*4], 2); // second rect
}
```

Zero-copy verification

```rust
#[test]
fn test_publish_reuses_allocation() {
let sf = SharedFrame::new(2, 2);
sf.update_full(2, 2, &vec![1u8; 16]);
let snap1 = sf.publish().unwrap();
let ptr1 = snap1.data.as_ptr();
drop(snap1); // release the Arc

sf.update_full(2, 2, &vec![2u8; 16]);
let snap2 = sf.publish().unwrap();
let ptr2 = snap2.data.as_ptr();
// The allocation should be reused (same pointer)
assert_eq!(ptr1, ptr2, "should reuse allocation via Arc::try_unwrap");

}
```

Concurrent access

```rust
#[test]
fn test_concurrent_writer_and_publisher() {
let sf = SharedFrame::new(100, 100);
let sf_writer = sf.clone();
let sf_reader = sf.clone();

let writer = std::thread::spawn(move || {
    for i in 0..1000u8 {
        sf_writer.update_full(100, 100, &vec![i; 100 * 100 * 4]);
    }
});

let reader = std::thread::spawn(move || {
    let mut count = 0;
    for _ in 0..2000 {
        if sf_reader.publish().is_some() {
            count += 1;
        }
    }
    count
});

writer.join().unwrap();
let published = reader.join().unwrap();
assert!(published > 0, "should have published at least some frames");

}
```

Edge cases

  • `update_rect` with out-of-bounds coordinates (should clamp, not crash)
  • `update_full` with wrong-sized data (should skip, not crash)
  • Resolution change mid-stream (old snapshot kept, new buffer allocated)
  • `wait_for_frame` timeout behavior

Files to create

  • `src-tauri/src/renderer/shared_frame_test.rs` or `#[cfg(test)] mod tests` in `shared_frame.rs`

Priority: P1 — critical data structure with zero test coverage

Metadata

Metadata

Assignees

No one assigned

    Labels

    good first issueGood for newcomersgpuGPU rendering pipeline

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions