#[cfg(test)]
mod tests {
use crate::clap::test_util::{self, StereoTestBuffers};
use clack_host::prelude::*;
use std::path::PathBuf;
use std::sync::atomic::Ordering;
#[test]
#[ignore]
fn test_gc_stress_1000_swaps() {
let (_entry, _host_info, mut plugin_instance) = test_util::make_test_plugin();
let state_ext = test_util::get_state_ext(&mut plugin_instance);
let audio_config = PluginAudioConfiguration {
sample_rate: 48000.0,
min_frames_count: 64,
max_frames_count: 64,
};
let stopped_processor = plugin_instance.activate(|_, _| (), audio_config).unwrap();
let mut started_processor = stopped_processor.start_processing().unwrap();
let mut model_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
model_dir.push("tests/fixtures/models");
let models = [
"BossWN-nano.nam",
"BossWN-feather.nam",
"BossWN-standard.nam",
"wavenet_a2_lite.nam",
];
let n = 64;
let mut bufs = StereoTestBuffers::new(n, 0.0, 0.0);
let shared = unsafe { &*test_util::extract_shared(&mut plugin_instance) };
let rt_status = &shared.cold.rt_status;
for i in 0..24 {
let model_name = models[i % models.len()];
let mut path = model_dir.clone();
path.push(model_name);
let params = test_util::make_default_params(Some(path));
let state_bytes = serde_json::to_vec(¶ms).unwrap();
let mut handle = plugin_instance.plugin_handle();
let prev_counter = shared.cold.model_load_counter.load(Ordering::Relaxed);
state_ext
.load(&mut handle, &mut state_bytes.as_slice())
.expect("Failed to load state");
let current_counter = shared.cold.model_load_counter.load(Ordering::Relaxed);
assert!(
current_counter > prev_counter,
"Model load counter did not increment after loading {}, indicating the load failed.",
model_name
);
let mut input_channels = [bufs.in_l.as_mut_slice(), bufs.in_r.as_mut_slice()];
let input_audio = bufs.input_ports.with_input_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_input_only(
input_channels.iter_mut().map(InputChannel::constant),
),
}]);
let output_channels = [bufs.out_l.as_mut_slice(), bufs.out_r.as_mut_slice()];
let mut output_audio = bufs.output_ports.with_output_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_output_only(output_channels.into_iter()),
}]);
let input_events = InputEvents::empty();
let mut output_events = OutputEvents::from_buffer(&mut bufs.output_events_buffer);
started_processor
.process(
&input_audio,
&mut output_audio,
&input_events,
&mut output_events,
None,
None,
)
.unwrap();
}
assert!(
!rt_status.check_flag(crate::common::spsc::RT_STATUS_GC_OVERFLOW),
"GC overflow flag was set prematurely!"
);
{
let model_name = models[24 % models.len()];
let mut path = model_dir.clone();
path.push(model_name);
let params = test_util::make_default_params(Some(path));
let state_bytes = serde_json::to_vec(¶ms).unwrap();
let mut handle = plugin_instance.plugin_handle();
let prev_counter = shared.cold.model_load_counter.load(Ordering::Relaxed);
state_ext
.load(&mut handle, &mut state_bytes.as_slice())
.expect("Failed to load state");
let current_counter = shared.cold.model_load_counter.load(Ordering::Relaxed);
assert!(
current_counter > prev_counter,
"Model load counter did not increment after loading {}, indicating the load failed.",
model_name
);
let mut input_channels = [bufs.in_l.as_mut_slice(), bufs.in_r.as_mut_slice()];
let input_audio = bufs.input_ports.with_input_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_input_only(
input_channels.iter_mut().map(InputChannel::constant),
),
}]);
let output_channels = [bufs.out_l.as_mut_slice(), bufs.out_r.as_mut_slice()];
let mut output_audio = bufs.output_ports.with_output_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_output_only(output_channels.into_iter()),
}]);
let input_events = InputEvents::empty();
let mut output_events = OutputEvents::from_buffer(&mut bufs.output_events_buffer);
started_processor
.process(
&input_audio,
&mut output_audio,
&input_events,
&mut output_events,
None,
None,
)
.unwrap();
}
assert!(
!rt_status.check_flag(crate::common::spsc::RT_STATUS_GC_OVERFLOW),
"GC overflow flag was set prematurely — only 1 item entered the 64-slot overflow buffer!"
);
plugin_instance.call_on_main_thread_callback();
{
let mut input_channels = [bufs.in_l.as_mut_slice(), bufs.in_r.as_mut_slice()];
let input_audio = bufs.input_ports.with_input_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_input_only(
input_channels.iter_mut().map(InputChannel::constant),
),
}]);
let output_channels = [bufs.out_l.as_mut_slice(), bufs.out_r.as_mut_slice()];
let mut output_audio = bufs.output_ports.with_output_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_output_only(output_channels.into_iter()),
}]);
let input_events = InputEvents::empty();
let mut output_events = OutputEvents::from_buffer(&mut bufs.output_events_buffer);
started_processor
.process(
&input_audio,
&mut output_audio,
&input_events,
&mut output_events,
None,
None,
)
.unwrap();
}
plugin_instance.call_on_main_thread_callback();
rt_status.clear_flag(crate::common::spsc::RT_STATUS_GC_OVERFLOW);
for i in 25..1000 {
let model_name = models[i % models.len()];
let mut path = model_dir.clone();
path.push(model_name);
let params = test_util::make_default_params(Some(path));
let state_bytes = serde_json::to_vec(¶ms).unwrap();
let mut handle = plugin_instance.plugin_handle();
let prev_counter = shared.cold.model_load_counter.load(Ordering::Relaxed);
state_ext
.load(&mut handle, &mut state_bytes.as_slice())
.expect("Failed to load state");
let current_counter = shared.cold.model_load_counter.load(Ordering::Relaxed);
assert!(
current_counter > prev_counter,
"Model load counter did not increment after loading {}, indicating the load failed.",
model_name
);
let mut input_channels = [bufs.in_l.as_mut_slice(), bufs.in_r.as_mut_slice()];
let input_audio = bufs.input_ports.with_input_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_input_only(
input_channels.iter_mut().map(InputChannel::constant),
),
}]);
let output_channels = [bufs.out_l.as_mut_slice(), bufs.out_r.as_mut_slice()];
let mut output_audio = bufs.output_ports.with_output_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_output_only(output_channels.into_iter()),
}]);
let input_events = InputEvents::empty();
let mut output_events = OutputEvents::from_buffer(&mut bufs.output_events_buffer);
started_processor
.process(
&input_audio,
&mut output_audio,
&input_events,
&mut output_events,
None,
None,
)
.unwrap();
if i % 10 == 0 {
plugin_instance.call_on_main_thread_callback();
}
}
for _ in 0..5 {
plugin_instance.call_on_main_thread_callback();
let mut input_channels = [bufs.in_l.as_mut_slice(), bufs.in_r.as_mut_slice()];
let input_audio = bufs.input_ports.with_input_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_input_only(
input_channels.iter_mut().map(InputChannel::constant),
),
}]);
let output_channels = [bufs.out_l.as_mut_slice(), bufs.out_r.as_mut_slice()];
let mut output_audio = bufs.output_ports.with_output_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_output_only(output_channels.into_iter()),
}]);
let input_events = InputEvents::empty();
let mut output_events = OutputEvents::from_buffer(&mut bufs.output_events_buffer);
started_processor
.process(
&input_audio,
&mut output_audio,
&input_events,
&mut output_events,
None,
None,
)
.unwrap();
}
plugin_instance.call_on_main_thread_callback();
assert!(
!rt_status.check_flag(crate::common::spsc::RT_STATUS_GC_OVERFLOW),
"GC overflow / leak occurred during the remaining model swaps!"
);
}
#[test]
#[ignore]
fn test_gc_drain_on_destroy_no_leak() {
let (_entry, _host_info, mut plugin_instance) = test_util::make_test_plugin();
let state_ext = test_util::get_state_ext(&mut plugin_instance);
let audio_config = PluginAudioConfiguration {
sample_rate: 48000.0,
min_frames_count: 64,
max_frames_count: 64,
};
let stopped_processor = plugin_instance.activate(|_, _| (), audio_config).unwrap();
let mut started_processor = stopped_processor.start_processing().unwrap();
let mut model_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
model_dir.push("tests/fixtures/models");
let models = [
"BossWN-nano.nam",
"BossWN-feather.nam",
"BossWN-standard.nam",
];
let n = 64;
let mut bufs = StereoTestBuffers::new(n, 0.0, 0.0);
let _shared = unsafe { &*test_util::extract_shared(&mut plugin_instance) };
for i in 0..5 {
let model_name = models[i % models.len()];
let mut path = model_dir.clone();
path.push(model_name);
let params = test_util::make_default_params(Some(path));
let state_bytes = serde_json::to_vec(¶ms).unwrap();
let mut handle = plugin_instance.plugin_handle();
state_ext
.load(&mut handle, &mut state_bytes.as_slice())
.expect("Failed to load state");
let mut input_channels = [bufs.in_l.as_mut_slice(), bufs.in_r.as_mut_slice()];
let input_audio = bufs.input_ports.with_input_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_input_only(
input_channels.iter_mut().map(InputChannel::constant),
),
}]);
let output_channels = [bufs.out_l.as_mut_slice(), bufs.out_r.as_mut_slice()];
let mut output_audio = bufs.output_ports.with_output_buffers([AudioPortBuffer {
latency: 0,
channels: AudioPortBufferType::f32_output_only(output_channels.into_iter()),
}]);
let input_events = InputEvents::empty();
let mut output_events = OutputEvents::from_buffer(&mut bufs.output_events_buffer);
started_processor
.process(
&input_audio,
&mut output_audio,
&input_events,
&mut output_events,
None,
None,
)
.unwrap();
}
let stopped = started_processor.stop_processing();
plugin_instance.deactivate(stopped);
drop(plugin_instance);
}
}