Files
2025-11-29 18:28:12 +08:00

11 KiB

name, description
name description
gpui-test Generate comprehensive tests for GPUI components, views, state management, and user interactions

GPUI Test Generation

Generate comprehensive tests for GPUI components including unit tests, integration tests, state management tests, and user interaction tests.

Arguments

  • $1: Component path (required) - Path to the component file to generate tests for

Workflow

1. Analyze Component Structure

  • Read the component file
  • Identify component type (View, Model, Element)
  • Extract component struct and fields
  • Identify render method and UI structure
  • Find state management patterns
  • Locate event handlers and actions
  • Identify dependencies and injected services

2. Generate Unit Tests

Create unit tests for component logic:

Component Initialization Tests

#[cfg(test)]
mod tests {
    use super::*;
    use gpui::*;

    #[gpui::test]
    fn test_component_initialization() {
        App::test(|cx| {
            let state = cx.new_model(|_| AppState::default());
            let view = cx.new_view(|cx| MyComponent::new(state.clone(), cx));

            assert!(view.is_some());
        });
    }

    #[gpui::test]
    fn test_initial_state() {
        App::test(|cx| {
            let state = cx.new_model(|_| AppState {
                count: 0,
                items: vec![],
            });
            let view = cx.new_view(|cx| MyComponent::new(state.clone(), cx));

            view.update(cx, |view, cx| {
                let state = view.state.read(cx);
                assert_eq!(state.count, 0);
                assert_eq!(state.items.len(), 0);
            });
        });
    }
}

State Management Tests

#[gpui::test]
fn test_state_updates() {
    App::test(|cx| {
        let state = cx.new_model(|_| AppState { count: 0 });
        let view = cx.new_view(|cx| Counter::new(state.clone(), cx));

        // Update state
        state.update(cx, |state, cx| {
            state.count = 5;
            cx.notify();
        });

        // Verify view reflects change
        view.update(cx, |view, cx| {
            let state = view.state.read(cx);
            assert_eq!(state.count, 5);
        });
    });
}

#[gpui::test]
fn test_subscription_updates() {
    App::test(|cx| {
        let state = cx.new_model(|_| AppState { count: 0 });
        let view = cx.new_view(|cx| Counter::new(state.clone(), cx));

        let initial_render_count = view.render_count();

        // Update should trigger rerender via subscription
        state.update(cx, |state, cx| {
            state.count += 1;
            cx.notify();
        });

        assert_eq!(view.render_count(), initial_render_count + 1);
    });
}

3. Create Integration Tests

Generate integration tests for component interactions:

User Interaction Tests

#[gpui::test]
fn test_button_click() {
    App::test(|cx| {
        let state = cx.new_model(|_| AppState { count: 0 });
        let view = cx.new_view(|cx| Counter::new(state.clone(), cx));

        // Simulate button click
        view.update(cx, |view, cx| {
            view.handle_increment(cx);
        });

        // Verify state updated
        state.update(cx, |state, _| {
            assert_eq!(state.count, 1);
        });
    });
}

#[gpui::test]
fn test_input_change() {
    App::test(|cx| {
        let state = cx.new_model(|_| FormState::default());
        let view = cx.new_view(|cx| Form::new(state.clone(), cx));

        // Simulate input change
        view.update(cx, |view, cx| {
            view.handle_input_change("test value", cx);
        });

        // Verify state updated
        state.update(cx, |state, _| {
            assert_eq!(state.input_value, "test value");
        });
    });
}

Action Handling Tests

#[gpui::test]
fn test_action_dispatch() {
    App::test(|cx| {
        let state = cx.new_model(|_| AppState { count: 0 });
        let view = cx.new_view(|cx| Counter::new(state.clone(), cx));

        // Dispatch action
        view.update(cx, |view, cx| {
            cx.dispatch_action(Increment);
        });

        // Verify action handled
        state.update(cx, |state, _| {
            assert_eq!(state.count, 1);
        });
    });
}

4. Add Interaction Tests

Test complex user interactions:

Multi-Step Interactions

#[gpui::test]
fn test_complete_user_flow() {
    App::test(|cx| {
        let state = cx.new_model(|_| TodoState::default());
        let view = cx.new_view(|cx| TodoList::new(state.clone(), cx));

        view.update(cx, |view, cx| {
            // Add item
            view.handle_add_todo("Buy milk", cx);

            // Mark complete
            view.handle_toggle_todo(0, cx);

            // Delete item
            view.handle_delete_todo(0, cx);
        });

        state.update(cx, |state, _| {
            assert_eq!(state.todos.len(), 0);
        });
    });
}

Edge Cases

#[gpui::test]
fn test_empty_state() {
    App::test(|cx| {
        let state = cx.new_model(|_| AppState::default());
        let view = cx.new_view(|cx| MyComponent::new(state.clone(), cx));

        // Verify graceful handling of empty state
        view.update(cx, |view, cx| {
            let element = view.render(cx);
            // Assert renders without panic
        });
    });
}

#[gpui::test]
fn test_boundary_conditions() {
    App::test(|cx| {
        let state = cx.new_model(|_| CounterState { count: i32::MAX });
        let view = cx.new_view(|cx| Counter::new(state.clone(), cx));

        // Test overflow handling
        view.update(cx, |view, cx| {
            view.handle_increment(cx);
        });

        state.update(cx, |state, _| {
            // Should handle overflow gracefully
            assert!(state.count == i32::MAX || state.count == 0);
        });
    });
}

5. Generate Test Utilities

Create helper functions for testing:

// Test helpers
mod test_utils {
    use super::*;
    use gpui::*;

    pub fn create_test_state() -> AppState {
        AppState {
            count: 0,
            items: vec!["item1".to_string(), "item2".to_string()],
            is_loading: false,
        }
    }

    pub fn create_test_view(cx: &mut WindowContext) -> View<MyComponent> {
        let state = cx.new_model(|_| create_test_state());
        cx.new_view(|cx| MyComponent::new(state, cx))
    }

    pub fn assert_state_equals(state: &Model<AppState>, expected_count: i32, cx: &mut AppContext) {
        state.update(cx, |state, _| {
            assert_eq!(state.count, expected_count);
        });
    }
}

6. Provide Coverage Report

Generate overview of test coverage:

// Coverage targets:
// - Component initialization: ✓
// - State updates: ✓
// - User interactions: ✓
// - Action handling: ✓
// - Edge cases: ✓
// - Error handling: ⚠ (needs work)
// - Async operations: ⚠ (needs work)

7. Add Async Tests

For components with async operations:

#[gpui::test]
async fn test_async_data_loading() {
    App::test(|cx| async move {
        let state = cx.new_model(|_| DataState::default());
        let view = cx.new_view(|cx| DataView::new(state.clone(), cx));

        // Trigger async load
        view.update(cx, |view, cx| {
            view.load_data(cx);
        });

        // Wait for completion
        cx.run_until_parked();

        // Verify data loaded
        state.update(cx, |state, _| {
            assert!(state.is_loaded);
            assert!(!state.data.is_empty());
        });
    });
}

#[gpui::test]
async fn test_async_error_handling() {
    App::test(|cx| async move {
        let state = cx.new_model(|_| DataState::default());
        let view = cx.new_view(|cx| DataView::new(state.clone(), cx));

        // Trigger async operation that will fail
        view.update(cx, |view, cx| {
            view.load_data_with_error(cx);
        });

        cx.run_until_parked();

        // Verify error handled
        state.update(cx, |state, _| {
            assert!(state.error.is_some());
            assert!(!state.is_loaded);
        });
    });
}

8. Generate Property-Based Tests

For complex logic, generate property-based tests:

#[cfg(test)]
mod property_tests {
    use super::*;
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn test_counter_never_negative(increments in 0..100u32, decrements in 0..100u32) {
            App::test(|cx| {
                let state = cx.new_model(|_| CounterState { count: 0 });

                state.update(cx, |state, _| {
                    for _ in 0..increments {
                        state.count += 1;
                    }
                    for _ in 0..decrements {
                        state.count = state.count.saturating_sub(1);
                    }
                });

                state.update(cx, |state, _| {
                    prop_assert!(state.count >= 0);
                    Ok(())
                }).unwrap();
            });
        }
    }
}

9. Add Benchmark Tests

Create performance benchmarks:

// benches/component_bench.rs
use criterion::{black_box, criterion_group, criterion_main, Criterion};

fn render_benchmark(c: &mut Criterion) {
    c.bench_function("component render", |b| {
        App::test(|cx| {
            let state = cx.new_model(|_| create_large_state());
            let view = cx.new_view(|cx| MyComponent::new(state, cx));

            b.iter(|| {
                view.update(cx, |view, cx| {
                    black_box(view.render(cx));
                });
            });
        });
    });
}

criterion_group!(benches, render_benchmark);
criterion_main!(benches);

10. Generate Test Documentation

Create documentation for tests:

//! Component Tests
//!
//! This module contains comprehensive tests for MyComponent including:
//!
//! - Unit tests for component logic and state management
//! - Integration tests for user interactions
//! - Async tests for data loading
//! - Property-based tests for invariants
//! - Performance benchmarks
//!
//! ## Running Tests
//!
//! ```bash
//! # Run all tests
//! cargo test
//!
//! # Run specific test
//! cargo test test_state_updates
//!
//! # Run with output
//! cargo test -- --nocapture
//!
//! # Run benchmarks
//! cargo bench
//! ```

Test Categories

Unit Tests

  • Component initialization
  • State management
  • Helper functions
  • Pure logic

Integration Tests

  • User interactions
  • Component composition
  • Event propagation
  • Action handling

UI Tests

  • Render output
  • Layout calculations
  • Style application
  • Theme integration

Performance Tests

  • Render benchmarks
  • State update performance
  • Memory usage
  • Subscription efficiency

Example Usage

# Generate tests for specific component
/gpui-test src/ui/views/counter.rs

# Generate tests for all components in directory
/gpui-test src/ui/components/

# Generate tests with benchmarks
/gpui-test src/ui/views/data_view.rs --with-benchmarks

Best Practices

  • Test behavior, not implementation
  • Use descriptive test names
  • Test edge cases and error conditions
  • Keep tests focused and independent
  • Use test utilities for common setup
  • Document complex test scenarios
  • Maintain high coverage (>80%)
  • Run tests in CI/CD pipeline

Output Structure

tests/
├── component_name_test.rs
│   ├── Unit tests
│   ├── Integration tests
│   └── Test utilities
└── benches/
    └── component_name_bench.rs