Skip to content

Commit 529227a

Browse files
CodeRabbit Generated Unit Tests: Add GoogleTest ADL file tests and IMidiChannelVoice test suite
1 parent e6bb4a9 commit 529227a

File tree

2 files changed

+282
-0
lines changed

2 files changed

+282
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
#include <cstdint>
2+
#include <limits>
3+
#include <type_traits>
4+
5+
// Prefer the project's established test framework.
6+
// Defaulting to GoogleTest. If your project uses Catch2 or doctest,
7+
// replace the includes and TEST/EXPECT macros accordingly.
8+
#include <gtest/gtest.h>
9+
10+
#include <HyperSonicDrivers/drivers/midi/IMidiChannelVoice.hpp>
11+
#include <HyperSonicDrivers/drivers/midi/IMidiChannel.hpp>
12+
13+
namespace HSD = HyperSonicDrivers::drivers::midi;
14+
15+
// A minimal concrete test double for IMidiChannelVoice in case the interface
16+
// has other pure virtual functions. We expose accessors to observe
17+
// effective (real) volume for assertions, relying only on base class behavior.
18+
class TestMidiChannelVoice : public HSD::IMidiChannelVoice {
19+
public:
20+
// Provide a constructor that sets the channel pointer and initial volume state if needed.
21+
explicit TestMidiChannelVoice(HSD::IMidiChannel* ch) {
22+
m_channel = ch;
23+
// Ensure deterministic initial values
24+
m_volume = 0;
25+
m_real_volume = 0;
26+
}
27+
28+
// If IMidiChannelVoice declares other pure virtuals, stub them here.
29+
// For example:
30+
// void noteOn(uint8_t, uint8_t) override {}
31+
// void noteOff(uint8_t, uint8_t) override {}
32+
// ... (No-ops for unit testing of base functionality.)
33+
34+
// Test-only helpers to read protected/private state computed by base logic.
35+
uint8_t test_getRealVolume() const noexcept { return m_real_volume; }
36+
uint8_t test_getRawVolume() const noexcept { return m_volume; }
37+
};
38+
39+
static_assert(std::is_same_v<uint8_t, unsigned char> || std::numeric_limits<uint8_t>::digits == 8,
40+
"Assumption: uint8_t is 8-bit.");
41+
42+
TEST(IMidiChannelVoiceTests, GetChannelNumReflectsUnderlyingChannel) {
43+
HSD::IMidiChannel channel{};
44+
channel.channel = 9; // Typical percussion channel in MIDI (0-based 9 == channel 10)
45+
channel.volume = 100; // Arbitrary
46+
47+
TestMidiChannelVoice voice(&channel);
48+
EXPECT_EQ(voice.getChannelNum(), 9u);
49+
50+
// Mutate underlying channel object and ensure reflection
51+
channel.channel = 2;
52+
EXPECT_EQ(voice.getChannelNum(), 2u);
53+
}
54+
55+
TEST(IMidiChannelVoiceTests, SetVolumesUpdatesRawAndRealVolume_WithChannelAtMax127) {
56+
HSD::IMidiChannel channel{};
57+
channel.channel = 1;
58+
channel.volume = 127; // Max channel volume
59+
60+
TestMidiChannelVoice voice(&channel);
61+
62+
// When channel volume = 127, real volume should equal requested volume, clamped to 127.
63+
voice.setVolumes(0);
64+
EXPECT_EQ(voice.test_getRawVolume(), 0u);
65+
EXPECT_EQ(voice.test_getRealVolume(), 0u);
66+
67+
voice.setVolumes(64);
68+
EXPECT_EQ(voice.test_getRawVolume(), 64u);
69+
EXPECT_EQ(voice.test_getRealVolume(), 64u);
70+
71+
voice.setVolumes(127);
72+
EXPECT_EQ(voice.test_getRawVolume(), 127u);
73+
EXPECT_EQ(voice.test_getRealVolume(), 127u);
74+
75+
// Values >127 can appear due to uint8_t domain (0..255). Base code clamps via std::min(..., 127).
76+
voice.setVolumes(static_cast<uint8_t>(200));
77+
EXPECT_EQ(voice.test_getRawVolume(), static_cast<uint8_t>(200));
78+
EXPECT_EQ(voice.test_getRealVolume(), 127u) << "Should saturate at 127";
79+
}
80+
81+
TEST(IMidiChannelVoiceTests, RealVolumeScalesByChannelVolume_AndUsesIntegerDivision) {
82+
HSD::IMidiChannel channel{};
83+
channel.channel = 3;
84+
// Use a mid channel volume to test scaling and truncation
85+
channel.volume = 64; // approx 50% of 127
86+
87+
TestMidiChannelVoice voice(&channel);
88+
89+
// raw=64, channel=64 => (64 * 64) / 127 = 4096/127 = 32 (integer truncation)
90+
voice.setVolumes(64);
91+
EXPECT_EQ(voice.test_getRawVolume(), 64u);
92+
EXPECT_EQ(voice.test_getRealVolume(), 32u) << "Expected truncated integer division result";
93+
94+
// raw=127, channel=64 => (127 * 64) / 127 = 64
95+
voice.setVolumes(127);
96+
EXPECT_EQ(voice.test_getRealVolume(), 64u);
97+
98+
// raw=1, channel=64 => (1 * 64) / 127 = 0
99+
voice.setVolumes(1);
100+
EXPECT_EQ(voice.test_getRealVolume(), 0u) << "Small products should truncate to zero";
101+
}
102+
103+
TEST(IMidiChannelVoiceTests, RealVolumeBecomesZeroWhenChannelVolumeIsZero) {
104+
HSD::IMidiChannel channel{};
105+
channel.channel = 7;
106+
channel.volume = 0;
107+
108+
TestMidiChannelVoice voice(&channel);
109+
110+
for (uint16_t v = 0; v <= 255; ++v) {
111+
voice.setVolumes(static_cast<uint8_t>(v));
112+
EXPECT_EQ(voice.test_getRealVolume(), 0u) << "Channel volume zero should mute all";
113+
}
114+
}
115+
116+
TEST(IMidiChannelVoiceTests, SaturatesAt127ForLargeProducts) {
117+
HSD::IMidiChannel channel{};
118+
channel.channel = 4;
119+
channel.volume = 127;
120+
121+
TestMidiChannelVoice voice(&channel);
122+
123+
// Any raw volume >= 127 should clamp to 127 when channel volume is max
124+
for (uint16_t v = 127; v <= 255; ++v) {
125+
voice.setVolumes(static_cast<uint8_t>(v));
126+
EXPECT_EQ(voice.test_getRealVolume(), 127u);
127+
}
128+
129+
// Also verify saturation when channel volume < 127 but product still exceeds 127.
130+
channel.volume = 100; // Change the same channel instance (voice references it)
131+
// raw=200, ch=100 -> 20000/127 = 157 (floor) -> min(157, 127) = 127
132+
voice.setVolumes(static_cast<uint8_t>(200));
133+
EXPECT_EQ(voice.test_getRealVolume(), 127u);
134+
}
135+
136+
TEST(IMidiChannelVoiceTests, ChangingChannelVolumeRecalculatesOnNextSetVolumesCall) {
137+
HSD::IMidiChannel channel{};
138+
channel.channel = 5;
139+
channel.volume = 100;
140+
141+
TestMidiChannelVoice voice(&channel);
142+
143+
voice.setVolumes(50); // 50*100/127 = 39
144+
EXPECT_EQ(voice.test_getRealVolume(), 39u);
145+
146+
// Increase channel volume; real volume should only update when setVolumes is called again
147+
channel.volume = 127;
148+
// Real volume remains unchanged until we call setVolumes (contract inferred from impl)
149+
EXPECT_EQ(voice.test_getRealVolume(), 39u);
150+
151+
// Trigger recompute by setting the same value again
152+
voice.setVolumes(50);
153+
EXPECT_EQ(voice.test_getRealVolume(), 50u);
154+
}
155+
156+
//
157+
// Notes:
158+
// - Testing library/framework: GoogleTest (gtest) assumed based on conventional C++ repos.
159+
// Please adapt includes/macros if your repository uses Catch2 (TEST_CASE/REQUIRE) or doctest (TEST_CASE/CHECK).
160+
// - External dependencies are not invoked; this is a pure computation test.
161+
// - We use a minimal derived class to expose computed internal state for validation,
162+
// avoiding white-box access to private members in production code.
163+
//

sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,125 @@ namespace HyperSonicDrivers::files::westwood
120120
}
121121
}
122122

123+
// -----------------------------------------------------------------------------
124+
// Additional unit tests generated by CodeRabbit Inc. using GoogleTest/GoogleMock
125+
// Focus: strengthen coverage for ADLv1/2 and especially the newly added ADLv3.
126+
// -----------------------------------------------------------------------------
127+
128+
using ADLFile_T = HyperSonicDrivers::files::westwood::ADLFile;
129+
130+
131+
132+
TEST(ADLFile, ADLv2_Dune0_ProgramOffsetsWithinBounds) {
133+
134+
ADLFile_T f("../fixtures/DUNE0.ADL");
135+
136+
EXPECT_EQ(f.getVersion(), 2);
137+
138+
int indices[] = {2, 4, 5, 10};
139+
140+
for (int idx : indices) {
141+
142+
int tr = f.getTrack(idx);
143+
144+
if (tr == 0xFF) continue; // absent/sentinel track
145+
146+
auto trackOff = f.getTrackOffset(tr);
147+
148+
auto instOff = f.getInstrumentOffset(tr);
149+
150+
EXPECT_GE(trackOff, 0);
151+
152+
EXPECT_GE(instOff, 0);
153+
154+
EXPECT_LT(trackOff, f.getDataSize());
155+
156+
EXPECT_LT(instOff, f.getDataSize());
157+
158+
EXPECT_EQ(f.getProgramOffset(tr, ADLFile_T::PROG_TYPE::Track), trackOff);
159+
160+
EXPECT_EQ(f.getProgramOffset(tr, ADLFile_T::PROG_TYPE::Instrument), instOff);
161+
162+
}
163+
164+
}
165+
166+
167+
168+
TEST(ADLFile, ADLv1_OffsetsWithinBounds_Track2) {
169+
170+
ADLFile_T f("../fixtures/EOBSOUND.ADL");
171+
172+
EXPECT_EQ(f.getVersion(), 1);
173+
174+
int tr = f.getTrack(2);
175+
176+
auto trackOff = f.getTrackOffset(tr);
177+
178+
auto instOff = f.getInstrumentOffset(tr);
179+
180+
EXPECT_LT(trackOff, f.getDataSize());
181+
182+
EXPECT_LT(instOff, f.getDataSize());
183+
184+
EXPECT_EQ(f.getProgramOffset(tr, ADLFile_T::PROG_TYPE::Track), trackOff);
185+
186+
EXPECT_EQ(f.getProgramOffset(tr, ADLFile_T::PROG_TYPE::Instrument), instOff);
187+
188+
}
189+
190+
191+
192+
TEST(ADLFile, ADLv3_OffsetsWithinBounds_SampleIndices) {
193+
194+
ADLFile_T f("../fixtures/LOREINTR.ADL");
195+
196+
EXPECT_EQ(f.getVersion(), 3);
197+
198+
int indices[] = {0, 1, 2, 3, 4, 5};
199+
200+
for (int idx : indices) {
201+
202+
int tr = f.getTrack(idx);
203+
204+
if (tr == 0xFF) continue; // skip missing entries
205+
206+
auto trackOff = f.getTrackOffset(tr);
207+
208+
auto instOff = f.getInstrumentOffset(tr);
209+
210+
EXPECT_GE(trackOff, 0);
211+
212+
EXPECT_GE(instOff, 0);
213+
214+
EXPECT_LT(trackOff, f.getDataSize());
215+
216+
EXPECT_LT(instOff, f.getDataSize());
217+
218+
EXPECT_EQ(f.getProgramOffset(tr, ADLFile_T::PROG_TYPE::Track), trackOff);
219+
220+
EXPECT_EQ(f.getProgramOffset(tr, ADLFile_T::PROG_TYPE::Instrument), instOff);
221+
222+
}
223+
224+
}
225+
226+
227+
228+
TEST(ADLFile, ADLv3_InvalidTrackAndInstrument) {
229+
230+
ADLFile_T f("../fixtures/LOREINTR.ADL");
231+
232+
// Track index far beyond the reported number of tracks should throw
233+
234+
EXPECT_THROW(f.getTrack(9999), std::out_of_range);
235+
236+
// Instrument/program index beyond available offsets should throw
237+
238+
EXPECT_THROW(f.getInstrumentOffset(200), std::out_of_range);
239+
240+
}
241+
123242
int main(int argc, char** argv)
124243
{
125244
::testing::InitGoogleTest(&argc, argv);

0 commit comments

Comments
 (0)