Put listen before the tests, where clippy can live with it
🚴 Build and Test BikeControl / Workspace tests (push) Successful in 21m48s
Build & Release / Run tests (push) Successful in 14m3s
🚴 Build and Test BikeControl / Android compile check (push) Failing after 10s
Build & Release / Build Linux (deb + AppImage) (push) Successful in 17m22s
Build & Release / Build Arch package (push) Successful in 29m45s
Build & Release / Build Android APK (push) Failing after 22s
Build & Release / Create release (push) Skipped

`probe listen` was appended to the end of commands.rs, which put it
after `mod tests` — `clippy::items_after_test_module`, denied by the
`-D warnings` in the workspace clippy job. Pure code motion: the listen
section moves up as a block, above the test module, and nothing else
changes.

This is the first commit whose clippy run can actually be observed. The
job has been in .gitea/workflows since the images were pinned, but the
image was never in the registry, so it has never run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-20 22:53:40 +02:00
co-authored by Claude Opus 5
parent da8ad4c1b3
commit 3143e7e51c
+76 -76
View File
@@ -1056,82 +1056,6 @@ fn properties(p: CharPropFlags) -> String {
} }
} }
#[cfg(test)]
mod tests {
use super::*;
use bikecontrol_ble::indoor_bike_data::flag;
#[test]
fn flag_names_call_out_the_inverted_bit_zero() {
// Bit 0 clear means speed IS present.
assert!(flag_names(0x0000).contains("InstantaneousSpeed(bit0 clear)"));
// Bit 0 set means it is not.
assert!(flag_names(flag::MORE_DATA).contains("MoreData(bit0 set: no speed)"));
}
#[test]
fn flag_names_list_every_present_field() {
let names = flag_names(flag::INSTANTANEOUS_CADENCE | flag::INSTANTANEOUS_POWER);
assert!(names.contains("Cadence"));
assert!(names.contains("Power"));
assert!(!names.contains("HeartRate"));
}
#[test]
fn flag_names_flag_reserved_bits() {
assert!(flag_names(0x8000).contains("<reserved bits set>"));
assert!(!flag_names(0x0001).contains("<reserved bits set>"));
}
#[test]
fn device_selector_mapping() {
assert_eq!(
Device::Address("AA:BB".into()).selector(),
TrainerSelector::Address("AA:BB".into())
);
assert_eq!(
Device::Name("D100".into()).selector(),
TrainerSelector::NameContains("D100".into())
);
}
#[test]
fn describe_write_names_the_op_code() {
assert_eq!(
describe_write(&ControlTarget::Gradient { percent: 4.0 }, false).0,
"SetTargetInclination (0x03)"
);
assert_eq!(
describe_write(&ControlTarget::Gradient { percent: 4.0 }, true).0,
"SetIndoorBikeSimulationParameters (0x11)"
);
assert_eq!(
describe_write(&ControlTarget::Resistance { level: 10 }, false).0,
"SetTargetResistanceLevel (0x04)"
);
assert_eq!(
describe_write(&ControlTarget::Power { watts: 100 }, false).0,
"SetTargetPower (0x05)"
);
}
#[test]
fn as_text_only_renders_printable_payloads() {
assert_eq!(as_text(b"D100"), "\"D100\"");
assert_eq!(as_text(&[0x00, 0x01, 0xff]), "");
assert_eq!(as_text(&[]), "");
}
#[test]
fn properties_are_listed_in_order() {
assert_eq!(
properties(CharPropFlags::READ | CharPropFlags::INDICATE),
"read, indicate"
);
assert_eq!(properties(CharPropFlags::empty()), "(none)");
}
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// listen // listen
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -1313,3 +1237,79 @@ pub async fn listen(
disconnect(&peripheral).await; disconnect(&peripheral).await;
Ok(()) Ok(())
} }
#[cfg(test)]
mod tests {
use super::*;
use bikecontrol_ble::indoor_bike_data::flag;
#[test]
fn flag_names_call_out_the_inverted_bit_zero() {
// Bit 0 clear means speed IS present.
assert!(flag_names(0x0000).contains("InstantaneousSpeed(bit0 clear)"));
// Bit 0 set means it is not.
assert!(flag_names(flag::MORE_DATA).contains("MoreData(bit0 set: no speed)"));
}
#[test]
fn flag_names_list_every_present_field() {
let names = flag_names(flag::INSTANTANEOUS_CADENCE | flag::INSTANTANEOUS_POWER);
assert!(names.contains("Cadence"));
assert!(names.contains("Power"));
assert!(!names.contains("HeartRate"));
}
#[test]
fn flag_names_flag_reserved_bits() {
assert!(flag_names(0x8000).contains("<reserved bits set>"));
assert!(!flag_names(0x0001).contains("<reserved bits set>"));
}
#[test]
fn device_selector_mapping() {
assert_eq!(
Device::Address("AA:BB".into()).selector(),
TrainerSelector::Address("AA:BB".into())
);
assert_eq!(
Device::Name("D100".into()).selector(),
TrainerSelector::NameContains("D100".into())
);
}
#[test]
fn describe_write_names_the_op_code() {
assert_eq!(
describe_write(&ControlTarget::Gradient { percent: 4.0 }, false).0,
"SetTargetInclination (0x03)"
);
assert_eq!(
describe_write(&ControlTarget::Gradient { percent: 4.0 }, true).0,
"SetIndoorBikeSimulationParameters (0x11)"
);
assert_eq!(
describe_write(&ControlTarget::Resistance { level: 10 }, false).0,
"SetTargetResistanceLevel (0x04)"
);
assert_eq!(
describe_write(&ControlTarget::Power { watts: 100 }, false).0,
"SetTargetPower (0x05)"
);
}
#[test]
fn as_text_only_renders_printable_payloads() {
assert_eq!(as_text(b"D100"), "\"D100\"");
assert_eq!(as_text(&[0x00, 0x01, 0xff]), "");
assert_eq!(as_text(&[]), "");
}
#[test]
fn properties_are_listed_in_order() {
assert_eq!(
properties(CharPropFlags::READ | CharPropFlags::INDICATE),
"read, indicate"
);
assert_eq!(properties(CharPropFlags::empty()), "(none)");
}
}