Skip to content

Commit 4dbeb92

Browse files
committed
Refactor tests to take rusb r/t device handle
Need to support a test that hard resets the device and ensures that it re-enumerates. The current model supplies a device handle to the test case, but this case will require a handle to the rusb context so that it can determine when the device has enumerated.
1 parent 09cbee9 commit 4dbeb92

File tree

3 files changed

+232
-81
lines changed

3 files changed

+232
-81
lines changed

tests/test_class_host/device.rs

Lines changed: 126 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use rusb::{ConfigDescriptor, Context, DeviceDescriptor, DeviceHandle, Language, UsbContext as _};
2+
use std::thread;
23
use std::time::Duration;
4+
use usb_device::device::CONFIGURATION_VALUE;
35
use usb_device::test_class;
46

7+
const TEST_INTERFACE: u8 = 0;
8+
59
pub const TIMEOUT: Duration = Duration::from_secs(1);
610
pub const EN_US: u16 = 0x0409;
711

@@ -18,6 +22,7 @@ impl DeviceHandles {
1822
pub fn is_high_speed(&self) -> bool {
1923
self.handle.device().speed() == rusb::Speed::High
2024
}
25+
2126
/// Returns the max packet size for the `TestClass` bulk endpoint(s).
2227
pub fn bulk_max_packet_size(&self) -> u16 {
2328
self.config_descriptor
@@ -34,6 +39,40 @@ impl DeviceHandles {
3439
.next()
3540
.expect("TestClass has at least one bulk endpoint")
3641
}
42+
43+
/// Puts the device in a consistent state for running a test
44+
pub fn pre_test(&mut self) -> rusb::Result<()> {
45+
let res = self.reset();
46+
if let Err(err) = res {
47+
println!("Failed to reset the device: {}", err);
48+
return res;
49+
}
50+
51+
let res = self.set_active_configuration(CONFIGURATION_VALUE);
52+
if let Err(err) = res {
53+
println!("Failed to set active configuration: {}", err);
54+
return res;
55+
}
56+
57+
let res = self.claim_interface(TEST_INTERFACE);
58+
if let Err(err) = res {
59+
println!("Failed to claim interface: {}", err);
60+
return res;
61+
}
62+
63+
Ok(())
64+
}
65+
66+
/// Cleanup the device following a test
67+
pub fn post_test(&mut self) -> rusb::Result<()> {
68+
let res = self.release_interface(TEST_INTERFACE);
69+
if let Err(err) = res {
70+
println!("Failed to release interface: {}", err);
71+
return res;
72+
}
73+
74+
Ok(())
75+
}
3776
}
3877

3978
impl ::std::ops::Deref for DeviceHandles {
@@ -50,38 +89,102 @@ impl ::std::ops::DerefMut for DeviceHandles {
5089
}
5190
}
5291

53-
pub fn open_device(ctx: &Context) -> rusb::Result<DeviceHandles> {
54-
for device in ctx.devices()?.iter() {
55-
let device_descriptor = device.device_descriptor()?;
92+
pub struct UsbContext {
93+
/// rusb Context handle
94+
inner: Context,
95+
device: Option<DeviceHandles>,
96+
}
5697

57-
if !(device_descriptor.vendor_id() == test_class::VID
58-
&& device_descriptor.product_id() == test_class::PID)
59-
{
60-
continue;
61-
}
98+
impl UsbContext {
99+
pub fn new() -> rusb::Result<Self> {
100+
let inner = rusb::Context::new()?;
62101

63-
let mut handle = device.open()?;
102+
Ok(Self {
103+
inner,
104+
device: None,
105+
})
106+
}
107+
108+
/// Attempt to open the test device once
109+
fn try_open_device(&self) -> rusb::Result<DeviceHandles> {
110+
for device in self.inner.devices()?.iter() {
111+
let device_descriptor = device.device_descriptor()?;
112+
113+
if !(device_descriptor.vendor_id() == test_class::VID
114+
&& device_descriptor.product_id() == test_class::PID)
115+
{
116+
continue;
117+
}
118+
119+
let mut handle = device.open()?;
120+
121+
let langs = handle.read_languages(TIMEOUT)?;
122+
if langs.is_empty() || langs[0].lang_id() != EN_US {
123+
continue;
124+
}
125+
126+
let prod = handle.read_product_string(langs[0], &device_descriptor, TIMEOUT)?;
127+
128+
if prod == test_class::PRODUCT {
129+
handle.reset()?;
130+
131+
let config_descriptor = device.config_descriptor(0)?;
64132

65-
let langs = handle.read_languages(TIMEOUT)?;
66-
if langs.is_empty() || langs[0].lang_id() != EN_US {
67-
continue;
133+
return Ok(DeviceHandles {
134+
device_descriptor,
135+
config_descriptor,
136+
handle,
137+
en_us: langs[0],
138+
});
139+
}
68140
}
69141

70-
let prod = handle.read_product_string(langs[0], &device_descriptor, TIMEOUT)?;
142+
Err(rusb::Error::NoDevice)
143+
}
71144

72-
if prod == test_class::PRODUCT {
73-
handle.reset()?;
145+
/// Look for the device for about 5 seconds in case it hasn't finished enumerating yet
146+
pub fn open_device(&mut self) -> rusb::Result<&mut DeviceHandles> {
147+
if self.device.is_none() {
148+
for _ in 0..50 {
149+
if let Ok(dev) = self.try_open_device() {
150+
self.device = Some(dev);
151+
break;
152+
}
153+
thread::sleep(Duration::from_millis(100));
154+
}
155+
}
74156

75-
let config_descriptor = device.config_descriptor(0)?;
157+
match self.device.as_mut() {
158+
Some(device) => Ok(device),
159+
None => Err(rusb::Error::NoDevice),
160+
}
161+
}
76162

77-
return Ok(DeviceHandles {
78-
device_descriptor,
79-
config_descriptor,
80-
handle,
81-
en_us: langs[0],
82-
});
163+
/// Attempts to open (if necessary) and (re-)initialize a device for a test
164+
pub fn device_for_test(&mut self) -> rusb::Result<&mut DeviceHandles> {
165+
let dev = match self.open_device() {
166+
Ok(dev) => dev,
167+
Err(err) => {
168+
println!("Did not find a TestClass device. Make sure the device is correctly programmed and plugged in. Last error: {}", err);
169+
return Err(err);
170+
}
171+
};
172+
173+
match dev.pre_test() {
174+
Ok(()) => Ok(dev),
175+
Err(err) => {
176+
println!("Failed to prepare for test: {}", err);
177+
Err(err)
178+
}
83179
}
84180
}
85181

86-
Err(rusb::Error::NoDevice)
182+
/// Releases resources that might have been used in a test
183+
pub fn cleanup_after_test(&mut self) -> rusb::Result<()> {
184+
if let Some(dev) = &mut self.device {
185+
dev.post_test()
186+
} else {
187+
Ok(())
188+
}
189+
}
87190
}

tests/test_class_host/main.rs

Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,66 +6,27 @@ mod device;
66
/// cannot be run in parallel.
77
mod tests;
88

9-
use crate::device::open_device;
9+
use crate::device::UsbContext;
1010
use crate::tests::{get_tests, TestFn};
1111
use std::io::prelude::*;
1212
use std::io::stdout;
1313
use std::panic;
14-
use std::thread;
15-
use std::time::Duration;
16-
use usb_device::device::CONFIGURATION_VALUE;
1714

1815
fn main() {
1916
let tests = get_tests();
2017
run_tests(&tests[..]);
2118
}
2219

2320
fn run_tests(tests: &[(&str, TestFn)]) {
24-
const INTERFACE: u8 = 0;
25-
2621
println!("test_class_host starting");
2722
println!("looking for device...");
2823

29-
let ctx = rusb::Context::new().expect("create libusb context");
30-
31-
// Look for the device for about 5 seconds in case it hasn't finished enumerating yet
32-
let mut dev = Err(rusb::Error::NoDevice);
33-
for _ in 0..50 {
34-
dev = open_device(&ctx);
35-
if dev.is_ok() {
36-
break;
37-
}
38-
39-
thread::sleep(Duration::from_millis(100));
40-
}
41-
42-
let mut dev = match dev {
43-
Ok(d) => d,
44-
Err(err) => {
45-
println!("Did not find a TestClass device. Make sure the device is correctly programmed and plugged in. Last error: {}", err);
46-
return;
47-
}
48-
};
24+
let mut ctx = UsbContext::new().expect("create libusb context");
4925

5026
println!("\nrunning {} tests", tests.len());
5127

5228
let mut success = 0;
5329
for (name, test) in tests {
54-
if let Err(err) = dev.reset() {
55-
println!("Failed to reset the device: {}", err);
56-
return;
57-
}
58-
59-
if let Err(err) = dev.set_active_configuration(CONFIGURATION_VALUE) {
60-
println!("Failed to set active configuration: {}", err);
61-
return;
62-
}
63-
64-
if let Err(err) = dev.claim_interface(INTERFACE) {
65-
println!("Failed to claim interface: {}", err);
66-
return;
67-
}
68-
6930
print!("test {} ... ", name);
7031
let _ = stdout().flush();
7132

@@ -75,15 +36,14 @@ fn run_tests(tests: &[(&str, TestFn)]) {
7536
let hook = panic::take_hook();
7637
panic::set_hook(Box::new(|_| {}));
7738
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
78-
test(&mut dev, &mut out);
39+
test(&mut ctx, &mut out);
7940
}));
8041
panic::set_hook(hook);
8142

8243
res
8344
};
8445

85-
dev.release_interface(INTERFACE)
86-
.expect("failed to release interface");
46+
ctx.cleanup_after_test().expect("post test cleanup failed");
8747

8848
if let Err(err) = res {
8949
let err = if let Some(err) = err.downcast_ref::<&'static str>() {

0 commit comments

Comments
 (0)