Skip to content

Commit

Permalink
Merge pull request #3 from mat-kie/version-0.3.5
Browse files Browse the repository at this point in the history
Version 0.3.5
  • Loading branch information
mat-kie authored Dec 5, 2024
2 parents fc40ba2 + 3d67281 commit 64044b6
Show file tree
Hide file tree
Showing 5 changed files with 379 additions and 24 deletions.
2 changes: 1 addition & 1 deletion src/controller/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ impl<ACT: DataAcquisitionApi + Send + 'static, BTCT: BluetoothApi + 'static>
}
}
}
gui_ctx.request_repaint();
}
gui_ctx.request_repaint();
}
}
116 changes: 112 additions & 4 deletions src/model/acquisition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
use super::bluetooth::HeartrateMessage;
use crate::model::hrv::{HrvSessionData, HrvStatistics};
use anyhow::Result;
use log::trace;
use log::{trace, warn};
#[cfg(test)]
use mockall::automock;
use serde::{Deserialize, Deserializer, Serialize};
use std::fmt::Debug;
Expand All @@ -16,7 +17,7 @@ use time::{Duration, OffsetDateTime};
///
/// Defines the interface for managing acquisition-related data, including runtime measurements,
/// HRV statistics, and stored acquisitions.
#[automock]
#[cfg_attr(test, automock)]
pub trait AcquisitionModelApi: Debug + Send + Sync {
/// Retrieves the start time of the current acquisition.
///
Expand Down Expand Up @@ -168,8 +169,13 @@ impl AcquisitionModel {
/// # Returns
/// A result indicating success or failure.
fn update(&mut self) -> Result<()> {
self.sessiondata =
HrvSessionData::from_acquisition(&self.measurements, self.window, self.outlier_filter)?;
match HrvSessionData::from_acquisition(&self.measurements, self.window, self.outlier_filter)
{
Ok(data) => self.sessiondata = data,
Err(e) => {
warn!("could not calculate session data: {}", e);
}
}
Ok(())
}
}
Expand Down Expand Up @@ -235,3 +241,105 @@ impl AcquisitionModelApi for AcquisitionModel {
self.update()
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::model::bluetooth::HeartrateMessage;
use time::macros::datetime;

fn create_test_hr_msg(bpm: u8) -> HeartrateMessage {
let data = [0b00010000, bpm, 0, 4, 1, 4];
HeartrateMessage::new(&data)
}
fn create_test_model() -> AcquisitionModel {
AcquisitionModel {
start_time: datetime!(2023-01-01 00:00:00 UTC),
measurements: vec![
(Duration::seconds(1), create_test_hr_msg(60)),
(Duration::seconds(2), create_test_hr_msg(62)),
],
window: Some(Duration::seconds(60)),
outlier_filter: 1.0,
sessiondata: HrvSessionData::default(),
}
}

#[test]
fn test_get_start_time() {
let model = create_test_model();
assert_eq!(model.get_start_time(), datetime!(2023-01-01 00:00:00 UTC));
}

#[test]
fn test_get_last_msg() {
let model = create_test_model();
assert_eq!(model.get_last_msg(), Some(create_test_hr_msg(62)));
}

#[test]
fn test_get_hrv_stats() {
let model = create_test_model();
assert!(model.get_hrv_stats().is_none());
}

#[test]
fn test_get_stats_window() {
let model = create_test_model();
assert_eq!(model.get_stats_window(), &Some(Duration::seconds(60)));
}

#[test]
fn test_get_outlier_filter_value() {
let model = create_test_model();
assert_eq!(model.get_outlier_filter_value(), 1.0);
}

#[test]
fn test_set_outlier_filter_value() {
let mut model = create_test_model();

model.set_outlier_filter_value(2.0).unwrap();
assert_eq!(model.get_outlier_filter_value(), 2.0);
}

#[test]
fn test_get_poincare_points() {
let model = create_test_model();
assert!(model.get_poincare_points().is_empty());
}

#[test]
fn test_add_measurement() {
let mut model = create_test_model();
let new_msg = create_test_hr_msg(64);
model.add_measurement(&new_msg).unwrap();
assert_eq!(model.get_last_msg(), Some(new_msg));
}

#[test]
fn test_set_stats_window() {
let mut model = create_test_model();
let new_window = Duration::seconds(120);
model.set_stats_window(&new_window).unwrap();
assert_eq!(model.get_stats_window(), &Some(new_window));
}

#[test]
fn test_get_session_data() {
let model = create_test_model();
assert!(model.get_session_data().hrv_stats.is_none());
}

#[test]
fn test_get_messages() {
let model = create_test_model();
assert_eq!(model.get_messages().len(), 2);
}

#[test]
fn test_get_elapsed_time() {
let model = create_test_model();
assert_eq!(model.get_elapsed_time(), Duration::seconds(2));
}
}
110 changes: 107 additions & 3 deletions src/model/bluetooth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ mod tests {

#[test]
fn test_hr_service_msg_short_hr_no_exp() {
// Short HR, no energy expenditure, no sensor contact, RR intervals (1024 and 256)
// Short HR, no energy expenditure, no sensor contact, RR intervals (1000 and 250)
let data = [0b00010000, 80, 0, 4, 0, 1];
let msg = HeartrateMessage::new(&data);
assert_eq!(msg.get_hr(), 80.0);
Expand All @@ -389,7 +389,7 @@ mod tests {

#[test]
fn test_hr_service_msg_long_hr_no_exp() {
// Long HR, no energy expenditure, no sensor contact, RR intervals (1024 and 256)
// Long HR, no energy expenditure, no sensor contact, RR intervals (1000 and 250)
let data = [0b00010001, 80, 0, 0, 4, 0, 1];
let msg = HeartrateMessage::new(&data);

Expand All @@ -405,7 +405,7 @@ mod tests {

#[test]
fn test_hr_service_msg_with_energy_exp() {
// Short HR, energy expenditure, no sensor contact, RR intervals (1024 and 256)
// Short HR, energy expenditure, no sensor contact, RR intervals (1000 and 250)
let data = [0b00011001, 80, 0, 1, 2, 0, 4, 0, 1];
let msg = HeartrateMessage::new(&data);

Expand Down Expand Up @@ -435,4 +435,108 @@ mod tests {
let output = format!("{}", msg);
assert!(output.contains("Heart Rate Value: 80.00"));
}

#[test]
fn test_hr_service_msg_no_rr_intervals() {
// Short HR, no energy expenditure, no sensor contact, no RR intervals
let data = [0b00000000, 75];
let msg = HeartrateMessage::new(&data);
assert_eq!(msg.get_hr(), 75.0);
assert!(!msg.has_rr_interval());
assert_eq!(msg.get_rr_intervals(), &[] as &[u16]);
}

#[test]
fn test_hr_service_msg_with_sensor_contact() {
// Short HR, no energy expenditure, sensor contact, no RR intervals
let data = [0b00000110, 72];
let msg = HeartrateMessage::new(&data);
assert_eq!(msg.get_hr(), 72.0);
assert!(msg.sen_has_contact());
assert!(msg.sen_contact_supported());
}

#[test]
fn test_hr_service_msg_with_long_hr_and_energy_exp() {
// Long HR, energy expenditure, no sensor contact, no RR intervals
let data = [0b00001001, 90, 1, 10, 0];
let msg = HeartrateMessage::new(&data);
assert_eq!(msg.get_hr(), 346.0); // 90 + (1 << 8)
assert!(msg.has_energy_exp());
assert_eq!(msg.get_energy_exp(), 10.0);
}

#[test]
fn test_hr_service_msg_with_all_flags() {
// Long HR, energy expenditure, sensor contact, RR intervals
let data = [0b00011111, 100, 0, 5, 0, 0, 4, 0, 1];
let msg = HeartrateMessage::new(&data);
assert_eq!(msg.get_hr(), 100.0);
assert!(msg.has_energy_exp());
assert_eq!(msg.get_energy_exp(), 5.0);
assert!(msg.has_rr_interval());
assert_eq!(msg.get_rr_intervals(), &[1000, 250]);
assert!(msg.sen_has_contact());
assert!(msg.sen_contact_supported());
}
#[test]
fn test_bluetooth_model_adapters() {
let mut model = BluetoothModel::default();
let adapter1 = AdapterDescriptor::new("Adapter 1".to_string());
let adapter2 = AdapterDescriptor::new("Adapter 2".to_string());
let mut adapters = vec![adapter1.clone(), adapter2.clone()];
model.set_adapters(adapters.clone());
adapters.sort_by(|a, b| a.get_uuid().cmp(b.get_uuid()));
assert_eq!(model.get_adapters(), &adapters);
assert!(model.get_selected_adapter().is_none());

let uuid = *adapter1.get_uuid();
model.select_adapter(&uuid).unwrap();
assert_eq!(model.get_selected_adapter(), &Some(adapter1));
}

#[test]
fn test_bluetooth_model_devices() {
let mut model = BluetoothModel::default();
let device1 = DeviceDescriptor {
name: "Device 1".to_string(),
address: BDAddr::from_str_delim("11:22:33:44:55:66").unwrap(),
};
let device2 = DeviceDescriptor {
name: "Device 2".to_string(),
address: BDAddr::from_str_delim("11:22:33:44:55:67").unwrap(),
};
model.set_devices(vec![device1.clone(), device2.clone()]);

assert_eq!(model.get_devices(), &[device1.clone(), device2.clone()]);
assert!(model.get_selected_device().is_none());

model.select_device(device1.clone());
assert_eq!(model.get_selected_device(), &Some(device1));
}

#[test]
fn test_bluetooth_model_scanning() {
let mut model = BluetoothModel::default();
assert!(!model.is_scanning());

model.set_scanning(true);
assert!(model.is_scanning());

model.set_scanning(false);
assert!(!model.is_scanning());
}

#[test]
fn test_bluetooth_model_listening() {
let mut model = BluetoothModel::default();
assert!(model.is_listening_to().is_none());

let address = BDAddr::from_str_delim("11:22:33:44:55:66").unwrap();
model.set_listening(Some(address));
assert_eq!(model.is_listening_to(), &Some(address));

model.set_listening(None);
assert!(model.is_listening_to().is_none());
}
}
Loading

0 comments on commit 64044b6

Please sign in to comment.