#[cfg(feature = "async_tokio")]
macro_rules! common_tests {
($abiv:expr, $($name:ident),*) => {
$(
#[tokio::test]
async fn $name() {
super::$name($abiv).await
}
)*
}
}
#[cfg(feature = "async_tokio")]
mod chip {
use gpiocdev::{Chip, Request};
use std::path::Path;
use tokio::time::Duration;
const PROPAGATION_DELAY: Duration = Duration::from_millis(10);
#[cfg(feature = "uapi_v1")]
mod uapi_v1 {
common_tests! {
gpiocdev::AbiVersion::V1,
from_chip,
read_line_info_change_event,
info_change_events
}
}
#[cfg(feature = "uapi_v2")]
mod uapi_v2 {
common_tests! {
gpiocdev::AbiVersion::V2,
from_chip,
read_line_info_change_event,
info_change_events
}
}
async fn from_chip(abiv: gpiocdev::AbiVersion) {
let s = gpiosim::Simpleton::new(4);
let c = new_chip(s.dev_path(), abiv);
let ac = gpiocdev::tokio::AsyncChip::from(c);
assert_eq!(ac.as_ref().path(), s.dev_path());
let c = Chip::from(ac);
assert_eq!(c.path(), s.dev_path());
}
async fn info_change_events(abiv: gpiocdev::AbiVersion) {
use gpiocdev::line::InfoChangeKind;
use gpiocdev::tokio::AsyncChip;
use tokio_stream::StreamExt;
let s = gpiosim::Simpleton::new(4);
let c = new_chip(s.dev_path(), abiv);
let offset = 3;
assert!(c.watch_line_info(offset).is_ok());
let ac = AsyncChip::new(c);
let mut events = ac.info_change_events();
let req = Request::builder()
.on_chip(s.dev_path())
.with_line(offset)
.as_input()
.request()
.unwrap();
let evt = events.next().await.unwrap().unwrap();
assert_eq!(evt.kind, InfoChangeKind::Requested);
assert_eq!(evt.info.offset, offset);
let mut cfg = req.config();
cfg.with_bias(gpiocdev::line::Bias::PullUp);
req.reconfigure(&cfg).unwrap();
let evt = events.next().await.unwrap().unwrap();
assert_eq!(evt.kind, InfoChangeKind::Reconfigured);
assert_eq!(evt.info.offset, offset);
drop(req);
let evt = events.next().await.unwrap().unwrap();
assert_eq!(evt.kind, InfoChangeKind::Released);
assert_eq!(evt.info.offset, offset);
}
async fn read_line_info_change_event(abiv: gpiocdev::AbiVersion) {
use gpiocdev::tokio::AsyncChip;
use std::time::Duration;
let s = gpiosim::Simpleton::new(4);
let c = new_chip(s.dev_path(), abiv);
let ac = AsyncChip::new(c);
for offset in 0..s.config().num_lines {
assert_eq!(ac.as_ref().has_line_info_change_event(), Ok(false));
assert!(ac.as_ref().watch_line_info(offset).is_ok());
assert_eq!(ac.as_ref().has_line_info_change_event(), Ok(false));
let req = Request::builder()
.on_chip(s.dev_path())
.with_line(offset)
.as_input()
.request()
.unwrap();
assert_eq!(
ac.as_ref().wait_line_info_change_event(PROPAGATION_DELAY),
Ok(true)
);
assert_eq!(ac.as_ref().has_line_info_change_event(), Ok(true));
let evt = ac.read_line_info_change_event().await.unwrap();
assert_eq!(evt.kind, gpiocdev::line::InfoChangeKind::Requested);
assert_eq!(evt.info.offset, offset);
assert_eq!(evt.info.direction, gpiocdev::line::Direction::Input);
assert_eq!(evt.info.edge_detection, None);
assert_eq!(evt.info.debounce_period, None);
let mut cfg = req.config();
cfg.with_bias(gpiocdev::line::Bias::PullUp);
if abiv == gpiocdev::AbiVersion::V2 {
cfg.with_edge_detection(gpiocdev::line::EdgeDetection::RisingEdge)
.with_debounce_period(Duration::from_millis(10));
}
assert_eq!(ac.as_ref().has_line_info_change_event(), Ok(false));
req.reconfigure(&cfg).unwrap();
assert_eq!(
ac.as_ref().wait_line_info_change_event(PROPAGATION_DELAY),
Ok(true)
);
assert_eq!(ac.as_ref().has_line_info_change_event(), Ok(true));
let evt = ac.read_line_info_change_event().await.unwrap();
assert_eq!(evt.kind, gpiocdev::line::InfoChangeKind::Reconfigured);
assert_eq!(evt.info.offset, offset);
assert_eq!(evt.info.direction, gpiocdev::line::Direction::Input);
assert_eq!(evt.info.bias, Some(gpiocdev::line::Bias::PullUp));
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(
evt.info.edge_detection,
Some(gpiocdev::line::EdgeDetection::RisingEdge)
);
assert_eq!(evt.info.debounce_period, Some(Duration::from_millis(10)));
} else {
assert_eq!(evt.info.edge_detection, None);
assert_eq!(evt.info.debounce_period, None);
}
drop(req);
let evt = ac.read_line_info_change_event().await.unwrap();
assert_eq!(evt.kind, gpiocdev::line::InfoChangeKind::Released);
assert_eq!(evt.info.offset, offset);
assert_eq!(evt.info.edge_detection, None);
assert_eq!(evt.info.debounce_period, None);
}
}
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
fn new_chip(path: &Path, abiv: gpiocdev::AbiVersion) -> gpiocdev::Chip {
let mut c = Chip::from_path(path).unwrap();
c.using_abi_version(abiv);
c
}
#[cfg(not(all(feature = "uapi_v1", feature = "uapi_v2")))]
fn new_chip(path: &Path, _abiv: gpiocdev::AbiVersion) -> gpiocdev::Chip {
Chip::from_path(path).unwrap()
}
}
#[cfg(feature = "async_tokio")]
mod request {
use gpiocdev::line::{EdgeKind, Offset};
use gpiocdev::tokio::AsyncRequest;
use gpiocdev::Request;
use std::path::Path;
use tokio::time::{self, Duration};
use tokio_stream::StreamExt;
#[cfg(feature = "uapi_v1")]
mod uapi_v1 {
common_tests! {
gpiocdev::AbiVersion::V1,
from_request,
read_edge_event,
read_edge_events_into_slice,
new_edge_event_stream,
edge_events
}
}
#[cfg(feature = "uapi_v2")]
mod uapi_v2 {
common_tests! {
gpiocdev::AbiVersion::V2,
from_request,
read_edge_event,
read_edge_events_into_slice,
new_edge_event_stream,
edge_events
}
}
async fn from_request(abiv: gpiocdev::AbiVersion) {
use std::os::fd::AsRawFd as _;
let s = gpiosim::Simpleton::new(4);
let offset = 2;
let req = new_request(s.dev_path(), offset, abiv);
let fd = req.as_raw_fd();
let req = AsyncRequest::from(req);
assert_eq!(req.as_ref().as_raw_fd(), fd);
let req = Request::from(req);
assert_eq!(req.as_ref().as_raw_fd(), fd);
}
async fn read_edge_event(abiv: gpiocdev::AbiVersion) {
let s = gpiosim::Simpleton::new(4);
let offset = 2;
let req = AsyncRequest::new(new_request(s.dev_path(), offset, abiv));
let res = time::timeout(Duration::from_millis(10), req.read_edge_event()).await;
assert!(res.is_err());
s.pullup(offset).unwrap();
let evt = req.read_edge_event().await.unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, EdgeKind::Rising);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 1);
} else {
assert_eq!(evt.line_seqno, 0);
}
let res = time::timeout(Duration::from_millis(10), req.read_edge_event()).await;
assert!(res.is_err());
s.pulldown(offset).unwrap();
let evt = req.read_edge_event().await.unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, EdgeKind::Falling);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 2);
} else {
assert_eq!(evt.line_seqno, 0);
}
let res = time::timeout(Duration::from_millis(10), req.read_edge_event()).await;
assert!(res.is_err());
}
async fn read_edge_events_into_slice(abiv: gpiocdev::AbiVersion) {
let s = gpiosim::Simpleton::new(3);
let offset = 1;
let req = AsyncRequest::new(new_request(s.dev_path(), offset, abiv));
let evt_u64_size = req.as_ref().edge_event_u64_size();
let mut buf = vec![0_u64; evt_u64_size * 3];
s.toggle(offset).unwrap();
propagation_delay().await;
s.toggle(offset).unwrap();
propagation_delay().await;
s.toggle(offset).unwrap();
propagation_delay().await;
s.toggle(offset).unwrap();
propagation_delay().await;
let wlen = req
.read_edge_events_into_slice(buf.as_mut_slice())
.await
.unwrap();
assert_eq!(wlen, buf.capacity());
let evt = req.as_ref().edge_event_from_slice(buf.as_slice()).unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Rising);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 1);
assert_eq!(evt.seqno, 1);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
let evt = req
.as_ref()
.edge_event_from_slice(&buf.as_slice()[evt_u64_size..])
.unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Falling);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 2);
assert_eq!(evt.seqno, 2);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
let evt = req
.as_ref()
.edge_event_from_slice(&buf.as_slice()[evt_u64_size * 2..])
.unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Rising);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 3);
assert_eq!(evt.seqno, 3);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
let wlen = req
.read_edge_events_into_slice(buf.as_mut_slice())
.await
.unwrap();
assert_eq!(wlen, req.as_ref().edge_event_u64_size());
let evt = req.as_ref().edge_event_from_slice(buf.as_slice()).unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Falling);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 4);
assert_eq!(evt.seqno, 4);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
}
async fn new_edge_event_stream(abiv: gpiocdev::AbiVersion) {
let s = gpiosim::Simpleton::new(4);
let offset = 2;
let req = AsyncRequest::new(new_request(s.dev_path(), offset, abiv));
s.toggle(offset).unwrap();
propagation_delay().await;
s.toggle(offset).unwrap();
propagation_delay().await;
s.toggle(offset).unwrap();
propagation_delay().await;
s.toggle(offset).unwrap();
propagation_delay().await;
let mut iter = req.new_edge_event_stream(2);
let evt = iter.next().await.unwrap().unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Rising);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 1);
assert_eq!(evt.seqno, 1);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
let evt = iter.next().await.unwrap().unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Falling);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 2);
assert_eq!(evt.seqno, 2);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
let evt = iter.next().await.unwrap().unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Rising);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 3);
assert_eq!(evt.seqno, 3);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
let evt = iter.next().await.unwrap().unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Falling);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 4);
assert_eq!(evt.seqno, 4);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
}
async fn edge_events(abiv: gpiocdev::AbiVersion) {
let s = gpiosim::Simpleton::new(4);
let offset = 0;
let req = AsyncRequest::new(new_request(s.dev_path(), offset, abiv));
s.toggle(offset).unwrap();
propagation_delay().await;
s.toggle(offset).unwrap();
propagation_delay().await;
s.toggle(offset).unwrap();
propagation_delay().await;
s.toggle(offset).unwrap();
propagation_delay().await;
let mut iter = req.edge_events();
let evt = iter.next().await.unwrap().unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Rising);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 1);
assert_eq!(evt.seqno, 1);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
let evt = iter.next().await.unwrap().unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Falling);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 2);
assert_eq!(evt.seqno, 2);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
let evt = iter.next().await.unwrap().unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Rising);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 3);
assert_eq!(evt.seqno, 3);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
let evt = iter.next().await.unwrap().unwrap();
assert_eq!(evt.offset, offset);
assert_eq!(evt.kind, gpiocdev::line::EdgeKind::Falling);
if abiv == gpiocdev::AbiVersion::V2 {
assert_eq!(evt.line_seqno, 4);
assert_eq!(evt.seqno, 4);
} else {
assert_eq!(evt.line_seqno, 0);
assert_eq!(evt.seqno, 0);
}
}
#[cfg(all(feature = "uapi_v1", feature = "uapi_v2"))]
fn new_request(path: &Path, offset: Offset, abiv: gpiocdev::AbiVersion) -> gpiocdev::Request {
let mut builder = Request::builder();
builder
.on_chip(path)
.with_line(offset)
.as_input()
.with_edge_detection(gpiocdev::line::EdgeDetection::BothEdges);
builder.using_abi_version(abiv);
builder.request().unwrap()
}
#[cfg(not(all(feature = "uapi_v1", feature = "uapi_v2")))]
fn new_request(path: &Path, offset: Offset, _abiv: gpiocdev::AbiVersion) -> gpiocdev::Request {
let mut builder = Request::builder();
builder
.on_chip(path)
.with_line(offset)
.as_input()
.with_edge_detection(gpiocdev::line::EdgeDetection::BothEdges)
.request()
.unwrap()
}
async fn propagation_delay() {
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
}