@delorenj/claude-notifications 1.2.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DO.md +5 -0
- package/FIXES-APPLIED.md +195 -0
- package/INTEGRATION.md +445 -0
- package/LAYOUT-INTEGRATION.md +191 -0
- package/QUICK-REFERENCE.md +195 -0
- package/README.md +145 -14
- package/TASK.md +15 -0
- package/ZELLIJ-NOTIFY.md +523 -0
- package/_bmad-output/implementation-artifacts/spec-install-multi-cli-hooks.md +241 -0
- package/bin/claude-notifications.js +424 -305
- package/bin/claude-notify.js +47 -1
- package/bin/zellij-notify.js +346 -0
- package/bun.lock +35 -0
- package/diagnose-zellij.sh +105 -0
- package/examples/settings-with-zellij.json +18 -0
- package/examples/settings-zellij-only.json +18 -0
- package/examples/zellij-notify-examples.sh +143 -0
- package/lib/adapters/_stub.js +35 -0
- package/lib/adapters/auggie.js +10 -0
- package/lib/adapters/claude-code.js +181 -0
- package/lib/adapters/codex.js +10 -0
- package/lib/adapters/copilot.js +10 -0
- package/lib/adapters/gemini.js +10 -0
- package/lib/adapters/index.js +240 -0
- package/lib/adapters/kimi.js +10 -0
- package/lib/adapters/opencode.js +14 -0
- package/lib/adapters/vibe.js +10 -0
- package/lib/config.js +121 -5
- package/lib/tui.js +115 -0
- package/lib/zellij.js +248 -0
- package/package.json +6 -4
- package/postinstall.js +28 -25
- package/preuninstall.js +18 -9
- package/test/adapters/claude-code.test.js +144 -0
- package/test/adapters/patches.test.js +81 -0
- package/test/adapters/registry.test.js +89 -0
- package/test/adapters/stubs.test.js +46 -0
- package/test/cli-json.test.js +79 -0
- package/test/helpers/fake-fs.js +59 -0
- package/test-integration.sh +113 -0
- package/test-notification-plugin.kdl +34 -0
- package/test-updated-layout.sh +75 -0
- package/test-zellij-cli.sh +72 -0
- package/test.sh +1 -1
- package/zellij-plugin/.cargo/config.toml +5 -0
- package/zellij-plugin/.github/workflows/ci.yml +97 -0
- package/zellij-plugin/Cargo.lock +3558 -0
- package/zellij-plugin/Cargo.toml +40 -0
- package/zellij-plugin/README.md +290 -0
- package/zellij-plugin/build.sh +179 -0
- package/zellij-plugin/configs/examples/accessibility.kdl +31 -0
- package/zellij-plugin/configs/examples/catppuccin.kdl +32 -0
- package/zellij-plugin/configs/examples/default.kdl +34 -0
- package/zellij-plugin/configs/examples/minimal.kdl +22 -0
- package/zellij-plugin/docs/CONFIGURATION.md +191 -0
- package/zellij-plugin/docs/INTEGRATION.md +333 -0
- package/zellij-plugin/src/animation.rs +451 -0
- package/zellij-plugin/src/colors.rs +407 -0
- package/zellij-plugin/src/config.rs +664 -0
- package/zellij-plugin/src/event_bridge.rs +339 -0
- package/zellij-plugin/src/main.rs +420 -0
- package/zellij-plugin/src/notification.rs +466 -0
- package/zellij-plugin/src/queue.rs +399 -0
- package/zellij-plugin/src/renderer.rs +477 -0
- package/zellij-plugin/src/state.rs +338 -0
- package/zellij-plugin/src/tests.rs +413 -0
- package/ruv-swarm-mcp.db +0 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
//! Integration tests for Zellij Visual Notifications
|
|
2
|
+
//!
|
|
3
|
+
//! These tests verify the complete notification flow and component interactions.
|
|
4
|
+
|
|
5
|
+
#[cfg(test)]
|
|
6
|
+
mod integration_tests {
|
|
7
|
+
use crate::animation::{AnimationEngine, easing};
|
|
8
|
+
use crate::colors::{Color, ColorManager, generate_gradient, generate_pulse_gradient};
|
|
9
|
+
use crate::config::{AnimationConfig, AnimationStyle, Config, ThemeConfig};
|
|
10
|
+
use crate::event_bridge::{EventBridge, create_test_message};
|
|
11
|
+
use crate::notification::{Notification, NotificationBuilder, NotificationType, Priority};
|
|
12
|
+
use crate::queue::NotificationQueue;
|
|
13
|
+
use crate::renderer::Renderer;
|
|
14
|
+
use crate::state::{PluginState, VisualNotificationState, VisualState};
|
|
15
|
+
|
|
16
|
+
// ==================== Integration Tests ====================
|
|
17
|
+
|
|
18
|
+
#[test]
|
|
19
|
+
fn test_full_notification_flow() {
|
|
20
|
+
// Create components
|
|
21
|
+
let config = Config::default();
|
|
22
|
+
let mut queue = NotificationQueue::new(100, 300_000);
|
|
23
|
+
let mut event_bridge = EventBridge::new();
|
|
24
|
+
let animation_engine = AnimationEngine::new(&config.animation);
|
|
25
|
+
let color_manager = ColorManager::new(&config.theme);
|
|
26
|
+
|
|
27
|
+
// Simulate receiving a notification message
|
|
28
|
+
let json = r#"{
|
|
29
|
+
"type": "success",
|
|
30
|
+
"message": "Build completed in 5.2s",
|
|
31
|
+
"pane_id": 1
|
|
32
|
+
}"#;
|
|
33
|
+
|
|
34
|
+
// Parse the message
|
|
35
|
+
let result = event_bridge.parse_notification(json);
|
|
36
|
+
assert!(result.is_ok());
|
|
37
|
+
let notification = result.unwrap();
|
|
38
|
+
|
|
39
|
+
// Enqueue the notification
|
|
40
|
+
queue.enqueue(notification.clone());
|
|
41
|
+
assert_eq!(queue.len(), 1);
|
|
42
|
+
|
|
43
|
+
// Process the notification
|
|
44
|
+
let dequeued = queue.dequeue_ready();
|
|
45
|
+
assert!(dequeued.is_some());
|
|
46
|
+
let processed = dequeued.unwrap();
|
|
47
|
+
|
|
48
|
+
// Verify notification properties
|
|
49
|
+
assert_eq!(processed.notification_type, NotificationType::Success);
|
|
50
|
+
assert!(processed.message.contains("Build completed"));
|
|
51
|
+
assert_eq!(processed.pane_id, Some(1));
|
|
52
|
+
|
|
53
|
+
// Create visual state for the pane
|
|
54
|
+
let mut visual_state = VisualState::new();
|
|
55
|
+
let color = color_manager.get_notification_color(&processed.notification_type);
|
|
56
|
+
assert!(color.is_some());
|
|
57
|
+
|
|
58
|
+
visual_state.border_color = color;
|
|
59
|
+
visual_state.notification_type = Some(processed.notification_type.clone());
|
|
60
|
+
visual_state.notification_message = Some(processed.message.clone());
|
|
61
|
+
|
|
62
|
+
// Verify visual state
|
|
63
|
+
assert!(visual_state.has_notification());
|
|
64
|
+
assert!(visual_state.border_color.is_some());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#[test]
|
|
68
|
+
fn test_notification_priority_flow() {
|
|
69
|
+
let mut queue = NotificationQueue::new(100, 300_000);
|
|
70
|
+
|
|
71
|
+
// Enqueue notifications in different order
|
|
72
|
+
queue.enqueue(Notification::info("Low priority").with_priority(Priority::Low));
|
|
73
|
+
queue.enqueue(Notification::success("Normal priority").with_priority(Priority::Normal));
|
|
74
|
+
queue.enqueue(Notification::error("Critical priority").with_priority(Priority::Critical));
|
|
75
|
+
queue.enqueue(Notification::warning("High priority").with_priority(Priority::High));
|
|
76
|
+
|
|
77
|
+
// Should dequeue in priority order
|
|
78
|
+
let first = queue.dequeue_ready().unwrap();
|
|
79
|
+
assert_eq!(first.priority, Priority::Critical);
|
|
80
|
+
|
|
81
|
+
let second = queue.dequeue_ready().unwrap();
|
|
82
|
+
assert_eq!(second.priority, Priority::High);
|
|
83
|
+
|
|
84
|
+
let third = queue.dequeue_ready().unwrap();
|
|
85
|
+
assert_eq!(third.priority, Priority::Normal);
|
|
86
|
+
|
|
87
|
+
let fourth = queue.dequeue_ready().unwrap();
|
|
88
|
+
assert_eq!(fourth.priority, Priority::Low);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#[test]
|
|
92
|
+
fn test_animation_lifecycle() {
|
|
93
|
+
let config = AnimationConfig {
|
|
94
|
+
enabled: true,
|
|
95
|
+
style: AnimationStyle::Pulse,
|
|
96
|
+
speed: 50,
|
|
97
|
+
cycles: 2,
|
|
98
|
+
duration_ms: 1000,
|
|
99
|
+
};
|
|
100
|
+
let engine = AnimationEngine::new(&config);
|
|
101
|
+
|
|
102
|
+
let mut state = VisualState::new();
|
|
103
|
+
|
|
104
|
+
// Start animation
|
|
105
|
+
engine.start_animation(&mut state, 0, AnimationStyle::Pulse);
|
|
106
|
+
assert!(state.is_animating);
|
|
107
|
+
assert_eq!(state.animation_start_tick, 0);
|
|
108
|
+
|
|
109
|
+
// Update animation midway
|
|
110
|
+
engine.update_animation(&mut state, 50);
|
|
111
|
+
let brightness = engine.get_brightness(&state, 50);
|
|
112
|
+
assert!(brightness > 0.0 && brightness <= 1.0);
|
|
113
|
+
|
|
114
|
+
// Animation should continue
|
|
115
|
+
assert!(engine.should_continue(&state, 50));
|
|
116
|
+
|
|
117
|
+
// After total ticks, animation should stop
|
|
118
|
+
engine.update_animation(&mut state, 500);
|
|
119
|
+
assert!(!engine.should_continue(&state, 500));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#[test]
|
|
123
|
+
fn test_theme_color_consistency() {
|
|
124
|
+
let themes = vec![
|
|
125
|
+
"default", "dracula", "nord", "catppuccin", "gruvbox", "tokyo-night"
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
for theme_name in themes {
|
|
129
|
+
let theme = ThemeConfig::from_preset(theme_name);
|
|
130
|
+
let manager = ColorManager::new(&theme);
|
|
131
|
+
|
|
132
|
+
// All notification types should have colors
|
|
133
|
+
for notif_type in [
|
|
134
|
+
NotificationType::Success,
|
|
135
|
+
NotificationType::Error,
|
|
136
|
+
NotificationType::Warning,
|
|
137
|
+
NotificationType::Info,
|
|
138
|
+
NotificationType::Attention,
|
|
139
|
+
] {
|
|
140
|
+
let color = manager.get_notification_color(¬if_type);
|
|
141
|
+
assert!(color.is_some(), "Theme {} missing color for {:?}", theme_name, notif_type);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Colors should be different
|
|
145
|
+
let success = manager.get_notification_color(&NotificationType::Success).unwrap();
|
|
146
|
+
let error = manager.get_notification_color(&NotificationType::Error).unwrap();
|
|
147
|
+
assert_ne!(success, error, "Success and Error colors should differ");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
#[test]
|
|
152
|
+
fn test_state_machine_transitions() {
|
|
153
|
+
let mut state = VisualState::new();
|
|
154
|
+
assert_eq!(state.state, VisualNotificationState::Idle);
|
|
155
|
+
|
|
156
|
+
// Transition: Idle -> Active
|
|
157
|
+
state.set_notification(
|
|
158
|
+
NotificationType::Success,
|
|
159
|
+
"Test".to_string(),
|
|
160
|
+
"#22c55e".to_string(),
|
|
161
|
+
"+".to_string(),
|
|
162
|
+
);
|
|
163
|
+
assert_eq!(state.state, VisualNotificationState::Active);
|
|
164
|
+
assert!(state.has_notification());
|
|
165
|
+
|
|
166
|
+
// Transition: Active -> Fading
|
|
167
|
+
state.acknowledge();
|
|
168
|
+
assert_eq!(state.state, VisualNotificationState::Fading);
|
|
169
|
+
|
|
170
|
+
// Clear should go to Idle
|
|
171
|
+
state.clear();
|
|
172
|
+
assert_eq!(state.state, VisualNotificationState::Idle);
|
|
173
|
+
assert!(!state.has_notification());
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#[test]
|
|
177
|
+
fn test_color_interpolation_smooth() {
|
|
178
|
+
let start = Color::new(0, 0, 0);
|
|
179
|
+
let end = Color::new(255, 255, 255);
|
|
180
|
+
|
|
181
|
+
let gradient = generate_gradient(&start, &end, 11);
|
|
182
|
+
|
|
183
|
+
// Check gradient is smooth
|
|
184
|
+
for i in 0..gradient.len() - 1 {
|
|
185
|
+
let diff = (gradient[i + 1].r as i32 - gradient[i].r as i32).abs();
|
|
186
|
+
assert!(diff <= 30, "Gradient step too large at index {}", i);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Check endpoints
|
|
190
|
+
assert_eq!(gradient[0].r, 0);
|
|
191
|
+
assert_eq!(gradient[10].r, 255);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
#[test]
|
|
195
|
+
fn test_event_bridge_error_recovery() {
|
|
196
|
+
let mut bridge = EventBridge::new();
|
|
197
|
+
|
|
198
|
+
// Cause errors
|
|
199
|
+
for _ in 0..4 {
|
|
200
|
+
let _ = bridge.parse_notification("invalid json");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Should not be in error state yet
|
|
204
|
+
assert_ne!(
|
|
205
|
+
bridge.health_status().error_count,
|
|
206
|
+
5,
|
|
207
|
+
"Should not reach max errors"
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// One more error
|
|
211
|
+
let _ = bridge.parse_notification("invalid");
|
|
212
|
+
|
|
213
|
+
// Now in error state
|
|
214
|
+
let health = bridge.health_status();
|
|
215
|
+
assert_eq!(health.error_count, 5);
|
|
216
|
+
|
|
217
|
+
// Recovery
|
|
218
|
+
bridge.reset_errors();
|
|
219
|
+
let health = bridge.health_status();
|
|
220
|
+
assert_eq!(health.error_count, 0);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
#[test]
|
|
224
|
+
fn test_queue_expiry_cleanup() {
|
|
225
|
+
let mut queue = NotificationQueue::new(100, 1000); // 1 second TTL
|
|
226
|
+
queue.update_timestamp(0);
|
|
227
|
+
|
|
228
|
+
// Add notification
|
|
229
|
+
let mut notif = Notification::info("Expiring");
|
|
230
|
+
notif.timestamp = 0;
|
|
231
|
+
notif.ttl_ms = 1000;
|
|
232
|
+
queue.enqueue(notif);
|
|
233
|
+
|
|
234
|
+
assert_eq!(queue.len(), 1);
|
|
235
|
+
|
|
236
|
+
// Not expired yet
|
|
237
|
+
queue.update_timestamp(500);
|
|
238
|
+
queue.cleanup_expired();
|
|
239
|
+
assert_eq!(queue.len(), 1);
|
|
240
|
+
|
|
241
|
+
// Now expired
|
|
242
|
+
queue.update_timestamp(1500);
|
|
243
|
+
queue.cleanup_expired();
|
|
244
|
+
assert_eq!(queue.len(), 0);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#[test]
|
|
248
|
+
fn test_pane_specific_notifications() {
|
|
249
|
+
let mut queue = NotificationQueue::new(100, 300_000);
|
|
250
|
+
|
|
251
|
+
// Add notifications for different panes
|
|
252
|
+
queue.enqueue(Notification::success("Pane 1").for_pane(1));
|
|
253
|
+
queue.enqueue(Notification::error("Pane 2").for_pane(2));
|
|
254
|
+
queue.enqueue(Notification::info("Pane 1 again").for_pane(1));
|
|
255
|
+
queue.enqueue(Notification::warning("Pane 3").for_pane(3));
|
|
256
|
+
|
|
257
|
+
// Check pane-specific queries
|
|
258
|
+
assert!(queue.has_notifications_for_pane(1));
|
|
259
|
+
assert!(queue.has_notifications_for_pane(2));
|
|
260
|
+
assert!(queue.has_notifications_for_pane(3));
|
|
261
|
+
assert!(!queue.has_notifications_for_pane(4));
|
|
262
|
+
|
|
263
|
+
// Get notifications for pane 1
|
|
264
|
+
let pane1_notifs = queue.get_for_pane(1);
|
|
265
|
+
assert_eq!(pane1_notifs.len(), 2);
|
|
266
|
+
|
|
267
|
+
// Remove notifications for pane 1
|
|
268
|
+
queue.remove_for_pane(1);
|
|
269
|
+
assert!(!queue.has_notifications_for_pane(1));
|
|
270
|
+
assert_eq!(queue.len(), 2);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ==================== Component Tests ====================
|
|
274
|
+
|
|
275
|
+
#[test]
|
|
276
|
+
fn test_notification_builder_chain() {
|
|
277
|
+
let notif = NotificationBuilder::new()
|
|
278
|
+
.notification_type(NotificationType::Error)
|
|
279
|
+
.message("Build failed")
|
|
280
|
+
.title("CI")
|
|
281
|
+
.pane_id(5)
|
|
282
|
+
.tab_index(2)
|
|
283
|
+
.source("github-actions")
|
|
284
|
+
.command("cargo build")
|
|
285
|
+
.exit_code(1)
|
|
286
|
+
.duration(15000)
|
|
287
|
+
.build();
|
|
288
|
+
|
|
289
|
+
assert_eq!(notif.notification_type, NotificationType::Error);
|
|
290
|
+
assert_eq!(notif.message, "Build failed");
|
|
291
|
+
assert_eq!(notif.title, Some("CI".to_string()));
|
|
292
|
+
assert_eq!(notif.pane_id, Some(5));
|
|
293
|
+
assert_eq!(notif.tab_index, Some(2));
|
|
294
|
+
assert_eq!(notif.source, "github-actions");
|
|
295
|
+
assert_eq!(notif.metadata.command, Some("cargo build".to_string()));
|
|
296
|
+
assert_eq!(notif.metadata.exit_code, Some(1));
|
|
297
|
+
assert_eq!(notif.metadata.duration_ms, Some(15000));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
#[test]
|
|
301
|
+
fn test_easing_functions() {
|
|
302
|
+
// Test all easing functions at boundaries
|
|
303
|
+
assert_eq!(easing::linear(0.0), 0.0);
|
|
304
|
+
assert_eq!(easing::linear(1.0), 1.0);
|
|
305
|
+
|
|
306
|
+
assert_eq!(easing::ease_in(0.0), 0.0);
|
|
307
|
+
assert!((easing::ease_in(1.0) - 1.0).abs() < 0.001);
|
|
308
|
+
|
|
309
|
+
assert_eq!(easing::ease_out(0.0), 0.0);
|
|
310
|
+
assert!((easing::ease_out(1.0) - 1.0).abs() < 0.001);
|
|
311
|
+
|
|
312
|
+
assert_eq!(easing::ease_in_out(0.0), 0.0);
|
|
313
|
+
assert!((easing::ease_in_out(1.0) - 1.0).abs() < 0.001);
|
|
314
|
+
|
|
315
|
+
// Midpoint characteristics
|
|
316
|
+
assert!(easing::ease_in(0.5) < 0.5); // Slow start
|
|
317
|
+
assert!(easing::ease_out(0.5) > 0.5); // Slow end
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
#[test]
|
|
321
|
+
fn test_pulse_gradient() {
|
|
322
|
+
let base = Color::new(100, 100, 100);
|
|
323
|
+
let bright = Color::new(200, 200, 200);
|
|
324
|
+
|
|
325
|
+
let gradient = generate_pulse_gradient(&base, &bright, 10);
|
|
326
|
+
assert_eq!(gradient.len(), 10);
|
|
327
|
+
|
|
328
|
+
// Should start near base, go to bright, then back toward base
|
|
329
|
+
// First element should be at base (100)
|
|
330
|
+
assert!(gradient[0].r <= 110);
|
|
331
|
+
// Last element should be returning toward base (around 120 due to interpolation)
|
|
332
|
+
assert!(gradient[gradient.len() - 1].r <= 130);
|
|
333
|
+
// Middle should be brighter than ends
|
|
334
|
+
assert!(gradient[gradient.len() / 2].r > gradient[0].r);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
#[test]
|
|
338
|
+
fn test_config_validation() {
|
|
339
|
+
let mut config = Config::default();
|
|
340
|
+
assert!(config.validate().is_ok());
|
|
341
|
+
|
|
342
|
+
// Invalid timeout
|
|
343
|
+
config.notification_timeout_ms = 100;
|
|
344
|
+
assert!(config.validate().is_err());
|
|
345
|
+
|
|
346
|
+
// Reset and test queue size
|
|
347
|
+
config.notification_timeout_ms = 5000;
|
|
348
|
+
config.queue_max_size = 0;
|
|
349
|
+
assert!(config.validate().is_err());
|
|
350
|
+
|
|
351
|
+
// Reset and test animation speed
|
|
352
|
+
config.queue_max_size = 100;
|
|
353
|
+
config.animation.speed = 150;
|
|
354
|
+
assert!(config.validate().is_err());
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
#[test]
|
|
358
|
+
fn test_renderer_icon_mapping() {
|
|
359
|
+
let renderer = Renderer::default();
|
|
360
|
+
let config = Config::default();
|
|
361
|
+
let color_manager = ColorManager::new(&config.theme);
|
|
362
|
+
|
|
363
|
+
// All notification types should have distinct icons
|
|
364
|
+
let types = vec![
|
|
365
|
+
NotificationType::Success,
|
|
366
|
+
NotificationType::Error,
|
|
367
|
+
NotificationType::Warning,
|
|
368
|
+
NotificationType::Info,
|
|
369
|
+
NotificationType::Attention,
|
|
370
|
+
NotificationType::Progress,
|
|
371
|
+
];
|
|
372
|
+
|
|
373
|
+
let mut seen_icons = std::collections::HashSet::new();
|
|
374
|
+
for t in &types {
|
|
375
|
+
let icon = t.icon().unwrap();
|
|
376
|
+
assert!(!icon.is_empty());
|
|
377
|
+
// Icons should be unique (except Attention and Warning may share)
|
|
378
|
+
if *t != NotificationType::Attention && *t != NotificationType::Warning {
|
|
379
|
+
seen_icons.insert(icon);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// ==================== Performance Tests ====================
|
|
385
|
+
|
|
386
|
+
#[test]
|
|
387
|
+
fn test_queue_performance() {
|
|
388
|
+
let mut queue = NotificationQueue::new(1000, 300_000);
|
|
389
|
+
|
|
390
|
+
// Enqueue many notifications
|
|
391
|
+
for i in 0..1000 {
|
|
392
|
+
queue.enqueue(Notification::info(&format!("Message {}", i)));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
assert_eq!(queue.len(), 1000);
|
|
396
|
+
|
|
397
|
+
// Dequeue all
|
|
398
|
+
while queue.dequeue_ready().is_some() {}
|
|
399
|
+
|
|
400
|
+
assert!(queue.is_empty());
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
#[test]
|
|
404
|
+
fn test_color_interpolation_performance() {
|
|
405
|
+
let c1 = Color::from_hex("#ff0000");
|
|
406
|
+
let c2 = Color::from_hex("#00ff00");
|
|
407
|
+
|
|
408
|
+
// Many interpolations
|
|
409
|
+
for _ in 0..10000 {
|
|
410
|
+
let _ = c1.interpolate(&c2, 0.5);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
package/ruv-swarm-mcp.db
DELETED
|
Binary file
|