-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_VisualAudioSync.cpp
More file actions
331 lines (267 loc) · 10.1 KB
/
test_VisualAudioSync.cpp
File metadata and controls
331 lines (267 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/*
==============================================================================
test_VisualAudioSync.cpp
Created: 30 Jul 2025
Author: Epic 5 Story 5.1 Implementation
Unit tests for Epic 4 Story 4.3 Visual-Audio Synchronization.
Tests pattern visualization, playback cursor, and active note highlighting.
==============================================================================
*/
#include <gtest/gtest.h>
#include <juce_gui_basics/juce_gui_basics.h>
#include "../PatternVisualizationComponent.h"
#include "../MIDIPattern.h"
#include "../Note.h"
//==============================================================================
class VisualAudioSyncTest : public ::testing::Test
{
protected:
void SetUp() override
{
// Initialize JUCE message manager for GUI testing
juce::MessageManager::getInstance();
// Create pattern visualization component
visualization = std::make_unique<PatternVisualizationComponent>();
visualization->setBounds(0, 0, 800, 400);
visualization->setVisible(true); // Explicitly set visible for testing
// Create test pattern with some notes
createTestPattern();
}
void TearDown() override
{
visualization.reset();
}
void createTestPattern()
{
testPattern.clear();
testPattern.lengthInBeats = 16.0;
// Add test notes at different times
Note note1;
note1.pitch = 60; // C4
note1.velocity = 100;
note1.startTime = 0.0;
note1.duration = 1.0;
testPattern.notes.push_back(note1);
Note note2;
note2.pitch = 64; // E4
note2.velocity = 80;
note2.startTime = 2.0;
note2.duration = 1.5;
testPattern.notes.push_back(note2);
Note note3;
note3.pitch = 67; // G4
note3.velocity = 90;
note3.startTime = 8.0;
note3.duration = 2.0;
testPattern.notes.push_back(note3);
}
std::unique_ptr<PatternVisualizationComponent> visualization;
MIDIPattern testPattern;
};
//==============================================================================
// Epic 5 Story 5.1: Visual Synchronization Tests
TEST_F(VisualAudioSyncTest, InitialVisualizationState)
{
// Test initial state
EXPECT_TRUE(visualization->isVisible());
// Component should have reasonable bounds
auto bounds = visualization->getBounds();
EXPECT_GT(bounds.getWidth(), 0);
EXPECT_GT(bounds.getHeight(), 0);
}
TEST_F(VisualAudioSyncTest, PatternDisplayBasics)
{
// Set test pattern
visualization->setPattern(testPattern);
// Force a repaint to ensure pattern is processed
visualization->repaint();
// Test pattern clearing
visualization->clearPattern();
visualization->repaint();
}
TEST_F(VisualAudioSyncTest, PlaybackModeToggling)
{
// Test playback mode changes
visualization->setPlaybackMode(false);
// Initially not in playback mode
visualization->setPlaybackMode(true);
// Now in playback mode - should enable sync features
visualization->setPlaybackMode(false);
// Back to normal mode
}
TEST_F(VisualAudioSyncTest, PlaybackPositionControl)
{
visualization->setPattern(testPattern);
visualization->setPlaybackMode(true);
// Test various playback positions
visualization->setPlaybackPosition(0.0);
visualization->repaint();
visualization->setPlaybackPosition(0.25);
visualization->repaint();
visualization->setPlaybackPosition(0.5);
visualization->repaint();
visualization->setPlaybackPosition(0.75);
visualization->repaint();
visualization->setPlaybackPosition(1.0);
visualization->repaint();
// Test position clamping
visualization->setPlaybackPosition(-0.1); // Should clamp to 0.0
visualization->setPlaybackPosition(1.1); // Should clamp to 1.0
}
TEST_F(VisualAudioSyncTest, ZoomAndGridControls)
{
// Test zoom functionality
visualization->setZoomLevel(1.0f);
visualization->setZoomLevel(2.0f);
visualization->setZoomLevel(0.5f);
// Test grid visibility
visualization->setShowGrid(true);
visualization->setShowGrid(false);
// Test velocity display
visualization->setShowVelocity(true);
visualization->setShowVelocity(false);
}
//==============================================================================
// Epic 5 Story 5.1: Active Note Detection Tests
class ActiveNoteDetectionTest : public ::testing::Test
{
protected:
void SetUp() override
{
createDetailedTestPattern();
}
void createDetailedTestPattern()
{
testPattern.clear();
testPattern.lengthInBeats = 8.0;
// Note 1: Plays from 0.0 to 1.0 beats
Note note1;
note1.pitch = 60;
note1.velocity = 100;
note1.startTime = 0.0;
note1.duration = 1.0;
testPattern.notes.push_back(note1);
// Note 2: Plays from 1.0 to 3.0 beats
Note note2;
note2.pitch = 64;
note2.velocity = 80;
note2.startTime = 1.0;
note2.duration = 2.0;
testPattern.notes.push_back(note2);
// Note 3: Plays from 4.0 to 6.0 beats
Note note3;
note3.pitch = 67;
note3.velocity = 90;
note3.startTime = 4.0;
note3.duration = 2.0;
testPattern.notes.push_back(note3);
}
bool isNoteActiveAtTime(const Note& note, double timeInBeats)
{
return timeInBeats >= note.startTime &&
timeInBeats <= (note.startTime + note.duration);
}
MIDIPattern testPattern;
};
TEST_F(ActiveNoteDetectionTest, NoteActiveTimeCalculations)
{
// Test note 1 activity (0.0 to 1.0 beats)
EXPECT_TRUE(isNoteActiveAtTime(testPattern.notes[0], 0.0));
EXPECT_TRUE(isNoteActiveAtTime(testPattern.notes[0], 0.5));
EXPECT_TRUE(isNoteActiveAtTime(testPattern.notes[0], 1.0));
EXPECT_FALSE(isNoteActiveAtTime(testPattern.notes[0], 1.1));
// Test note 2 activity (1.0 to 3.0 beats)
EXPECT_FALSE(isNoteActiveAtTime(testPattern.notes[1], 0.9));
EXPECT_TRUE(isNoteActiveAtTime(testPattern.notes[1], 1.0));
EXPECT_TRUE(isNoteActiveAtTime(testPattern.notes[1], 2.0));
EXPECT_TRUE(isNoteActiveAtTime(testPattern.notes[1], 3.0));
EXPECT_FALSE(isNoteActiveAtTime(testPattern.notes[1], 3.1));
// Test note 3 activity (4.0 to 6.0 beats)
EXPECT_FALSE(isNoteActiveAtTime(testPattern.notes[2], 3.9));
EXPECT_TRUE(isNoteActiveAtTime(testPattern.notes[2], 4.0));
EXPECT_TRUE(isNoteActiveAtTime(testPattern.notes[2], 5.0));
EXPECT_TRUE(isNoteActiveAtTime(testPattern.notes[2], 6.0));
EXPECT_FALSE(isNoteActiveAtTime(testPattern.notes[2], 6.1));
}
TEST_F(ActiveNoteDetectionTest, MultipleNotesActiveSimultaneously)
{
// Add overlapping note for testing
Note overlappingNote;
overlappingNote.pitch = 72;
overlappingNote.velocity = 85;
overlappingNote.startTime = 0.5;
overlappingNote.duration = 3.0; // Extends to 3.5 beats
testPattern.notes.push_back(overlappingNote);
// At time 1.5, notes 1, 2, and the overlapping note should be active
int activeCount = 0;
double testTime = 1.5;
for (const auto& note : testPattern.notes)
{
if (isNoteActiveAtTime(note, testTime))
activeCount++;
}
EXPECT_EQ(activeCount, 2); // Note 2 and overlapping note
}
//==============================================================================
// Epic 5 Story 5.1: Performance Tests
class VisualizationPerformanceTest : public ::testing::Test
{
protected:
void SetUp() override
{
juce::MessageManager::getInstance();
visualization = std::make_unique<PatternVisualizationComponent>();
visualization->setBounds(0, 0, 1920, 1080); // Large size for stress testing
createLargeTestPattern();
}
void createLargeTestPattern()
{
testPattern.clear();
testPattern.lengthInBeats = 64.0; // Longer pattern
// Create many notes for performance testing
for (int i = 0; i < 200; ++i)
{
Note note;
note.pitch = 36 + (i % 48); // Range from C2 to B5
note.velocity = 60 + (i % 68); // Velocity range 60-127
note.startTime = (i % 64) * 0.25; // Notes every quarter beat
note.duration = 0.5 + (i % 4) * 0.25; // Varying durations
testPattern.notes.push_back(note);
}
}
std::unique_ptr<PatternVisualizationComponent> visualization;
MIDIPattern testPattern;
};
TEST_F(VisualizationPerformanceTest, LargePatternRenderingPerformance)
{
visualization->setPattern(testPattern);
visualization->setPlaybackMode(true);
// Measure rendering performance with many notes
auto startTime = std::chrono::high_resolution_clock::now();
// Simulate multiple playback position updates
for (int i = 0; i <= 100; ++i)
{
double position = i / 100.0;
visualization->setPlaybackPosition(position);
visualization->repaint();
}
auto endTime = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
// Performance expectation: Should complete in reasonable time
EXPECT_LT(duration.count(), 5000); // Less than 5 seconds for 100 updates
}
TEST_F(VisualizationPerformanceTest, RepaintEfficiency)
{
visualization->setPattern(testPattern);
// Test that repaints don't cause excessive processing
auto startTime = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 60; ++i) // Simulate 1 second at 60 FPS
{
visualization->repaint();
std::this_thread::sleep_for(std::chrono::milliseconds(16)); // ~60 FPS
}
auto endTime = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
// Should maintain near real-time performance
EXPECT_LT(duration.count(), 2000); // Allow some overhead but keep it reasonable
}