Skip to content

Commit 79adc9b

Browse files
committed
test(test_suite): add tests for interrupt handling
1 parent 2d2da93 commit 79adc9b

File tree

3 files changed

+241
-0
lines changed

3 files changed

+241
-0
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//! Validates error codes returned by interrupt line manipulation methods. Also,
2+
//! checks miscellaneous properties of interrupt lines.
3+
use constance::{
4+
kernel::{self, InterruptHandler, InterruptLine, Task},
5+
prelude::*,
6+
};
7+
8+
use super::Driver;
9+
10+
#[derive(Debug)]
11+
pub struct App<System> {
12+
int: Option<InterruptLine<System>>,
13+
}
14+
15+
impl<System: Kernel> App<System> {
16+
constance::configure! {
17+
pub const fn new<D: Driver<Self>>(_: &mut CfgBuilder<System>) -> Self {
18+
new! { Task<_>, start = task_body::<System, D>, priority = 0, active = true };
19+
20+
let int = if let [int_line, ..] = *D::INTERRUPT_LINES {
21+
unsafe {
22+
new! { InterruptHandler<_>,
23+
line = int_line, start = isr::<System, D>, unmanaged };
24+
}
25+
26+
Some(new! { InterruptLine<_>, line = int_line })
27+
} else {
28+
None
29+
};
30+
31+
App { int }
32+
}
33+
}
34+
}
35+
36+
fn task_body<System: Kernel, D: Driver<App<System>>>(_: usize) {
37+
let int = if let Some(int) = D::app().int {
38+
int
39+
} else {
40+
log::warn!("No interrupt lines defined, skipping the test");
41+
D::success();
42+
return;
43+
};
44+
45+
let managed_range = System::MANAGED_INTERRUPT_PRIORITY_RANGE;
46+
47+
if managed_range.end > managed_range.start {
48+
for pri in managed_range.clone() {
49+
int.set_priority(pri).unwrap();
50+
}
51+
52+
for pri in managed_range.clone() {
53+
unsafe { int.set_priority_unchecked(pri) }.unwrap();
54+
}
55+
56+
// `set_priority` is disallowed when CPU Lock is active
57+
System::acquire_cpu_lock().unwrap();
58+
assert_eq!(
59+
int.set_priority(managed_range.start),
60+
Err(kernel::SetInterruptLinePriorityError::BadContext),
61+
);
62+
assert_eq!(
63+
unsafe { int.set_priority_unchecked(managed_range.start) },
64+
Err(kernel::SetInterruptLinePriorityError::BadContext),
65+
);
66+
unsafe { System::release_cpu_lock() }.unwrap();
67+
}
68+
69+
// `set_priority` rejects unmanaged priority
70+
if let Some(pri) = managed_range.start.checked_sub(1) {
71+
assert_eq!(
72+
int.set_priority(pri),
73+
Err(kernel::SetInterruptLinePriorityError::BadParam),
74+
);
75+
}
76+
assert_eq!(
77+
int.set_priority(managed_range.end),
78+
Err(kernel::SetInterruptLinePriorityError::BadParam),
79+
);
80+
81+
int.enable().unwrap();
82+
83+
// Before doing the next test, make sure `clear` is supported
84+
if int.clear().is_ok() {
85+
// Pending the interrupt should succeed. We instantly clear the pending
86+
// flag, so the interrupt handler will not actually get called.
87+
System::acquire_cpu_lock().unwrap();
88+
int.pend().unwrap();
89+
match int.is_pending() {
90+
Ok(true) | Err(kernel::QueryInterruptLineError::NotSupported) => {}
91+
value => panic!("{:?}", value),
92+
}
93+
int.clear().unwrap();
94+
unsafe { System::release_cpu_lock() }.unwrap();
95+
96+
// Pending the interrupt should succeed. The interrupt line is disabled,
97+
// so the interrupt handler will not actually get called.
98+
int.disable().unwrap();
99+
int.pend().unwrap();
100+
match int.is_pending() {
101+
Ok(true) | Err(kernel::QueryInterruptLineError::NotSupported) => {}
102+
value => panic!("{:?}", value),
103+
}
104+
int.clear().unwrap();
105+
int.enable().unwrap();
106+
}
107+
108+
match int.is_pending() {
109+
Ok(false) | Err(kernel::QueryInterruptLineError::NotSupported) => {}
110+
value => panic!("{:?}", value),
111+
}
112+
113+
D::success();
114+
}
115+
116+
fn isr<System: Kernel, D: Driver<App<System>>>(_: usize) {
117+
unreachable!();
118+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//! Validates error codes returned by interrupt line manipulation methods. Also,
2+
//! checks miscellaneous properties of interrupt lines.
3+
use constance::{
4+
kernel::{Hunk, InterruptHandler, InterruptLine, Task},
5+
prelude::*,
6+
};
7+
8+
use super::Driver;
9+
use crate::utils::SeqTracker;
10+
11+
pub struct App<System> {
12+
int: [Option<InterruptLine<System>>; 2],
13+
seq: Hunk<System, SeqTracker>,
14+
}
15+
16+
impl<System: Kernel> App<System> {
17+
constance::configure! {
18+
pub const fn new<D: Driver<Self>>(_: &mut CfgBuilder<System>) -> Self {
19+
new! { Task<_>, start = task_body::<System, D>, priority = 0, active = true };
20+
21+
let int = [
22+
if D::INTERRUPT_LINES.len() >= 1 {
23+
let int_line = D::INTERRUPT_LINES[0];
24+
let pri = D::INTERRUPT_PRIORITY_HIGH;
25+
new! { InterruptHandler<_>, line = int_line, start = isr0::<System, D> };
26+
Some(new! { InterruptLine<_>, line = int_line, priority = pri, enabled = true })
27+
} else {
28+
None
29+
},
30+
if D::INTERRUPT_LINES.len() >= 2 {
31+
let int_line = D::INTERRUPT_LINES[1];
32+
let pri = D::INTERRUPT_PRIORITY_LOW;
33+
new! { InterruptHandler<_>, line = int_line, start = isr1::<System, D> };
34+
Some(new! { InterruptLine<_>, line = int_line, priority = pri, enabled = true })
35+
} else {
36+
None
37+
},
38+
];
39+
40+
let seq = new! { Hunk<_, SeqTracker> };
41+
42+
App { int, seq }
43+
}
44+
}
45+
}
46+
47+
fn task_body<System: Kernel, D: Driver<App<System>>>(_: usize) {
48+
D::app().seq.expect_and_replace(0, 1);
49+
50+
// Pend both interrupts at the same time. Regardless the order of reception,
51+
// the higher-priority one should be handled first
52+
System::acquire_cpu_lock().unwrap();
53+
if let Some(int) = D::app().int[1] {
54+
int.pend().unwrap();
55+
}
56+
if let Some(int) = D::app().int[0] {
57+
int.pend().unwrap();
58+
}
59+
unsafe { System::release_cpu_lock() }.unwrap();
60+
61+
if let [None, None] = D::app().int {
62+
log::warn!("No interrupt lines defined, skipping the test");
63+
D::success();
64+
return;
65+
}
66+
}
67+
68+
fn isr1<System: Kernel, D: Driver<App<System>>>(_: usize) {
69+
log::trace!("isr1");
70+
71+
D::app().seq.expect_and_replace(2, 3);
72+
73+
D::success();
74+
}
75+
76+
fn isr0<System: Kernel, D: Driver<App<System>>>(_: usize) {
77+
log::trace!("isr0");
78+
79+
D::app().seq.expect_and_replace(1, 2);
80+
81+
if D::app().int[1].is_none() {
82+
log::warn!("Only one interrupt line is defined, skipping the second part of the test");
83+
D::success();
84+
return;
85+
}
86+
}

src/constance_test_suite/src/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ include!(concat!(env!("OUT_DIR"), "/selective_tests.rs"));
1414

1515
/// Kernel tests
1616
pub mod kernel_tests {
17+
use constance::kernel::{InterruptNum, InterruptPriority};
1718
/// Instantiation parameters of a test case.
1819
///
1920
/// This trait has two purposes: (1) It serves as an interface to a test driver.
@@ -28,6 +29,40 @@ pub mod kernel_tests {
2829

2930
/// Signal to the test runner that a test has failed.
3031
fn fail();
32+
33+
/// The list of interrupt lines that can be used by test programs.
34+
///
35+
/// - The list can have an arbitrary number of elements. Some tests
36+
/// will be silently skipped if it's not enough. There should be at
37+
/// least two for all test cases to run.
38+
///
39+
/// - There must be no duplicates.
40+
///
41+
/// - The port must support [`enable_interrupt_line`],
42+
/// [`disable_interrupt_line`], [`pend_interrupt_line`] for all of
43+
/// the specified interrupt lines.
44+
///
45+
/// [`enable_interrupt_line`]: constance::kernel::Port::enable_interrupt_line
46+
/// [`disable_interrupt_line`]: constance::kernel::Port::disable_interrupt_line
47+
/// [`pend_interrupt_line`]: constance::kernel::Port::pend_interrupt_line
48+
const INTERRUPT_LINES: &'static [InterruptNum] = &[];
49+
50+
/// A low priority value.
51+
///
52+
/// Ignored if `INTERRUPT_LINES` is empty.
53+
///
54+
/// Must be in range [`Port::MANAGED_INTERRUPT_PRIORITY_RANGE`]
55+
///
56+
/// [`Port::MANAGED_INTERRUPT_PRIORITY_RANGE`]: constance::kernel::Port::MANAGED_INTERRUPT_PRIORITY_RANGE
57+
const INTERRUPT_PRIORITY_LOW: InterruptPriority = 0;
58+
/// A high priority value.
59+
///
60+
/// Ignored if `INTERRUPT_LINES` is empty.
61+
///
62+
/// Must be in range [`Port::MANAGED_INTERRUPT_PRIORITY_RANGE`]
63+
///
64+
/// [`Port::MANAGED_INTERRUPT_PRIORITY_RANGE`]: constance::kernel::Port::MANAGED_INTERRUPT_PRIORITY_RANGE
65+
const INTERRUPT_PRIORITY_HIGH: InterruptPriority = 0;
3166
}
3267

3368
macro_rules! define_kernel_tests {
@@ -110,6 +145,8 @@ pub mod kernel_tests {
110145
(mod event_group_order_task_priority {}, "event_group_order_task_priority"),
111146
(mod event_group_set_and_dispatch {}, "event_group_set_and_dispatch"),
112147
(mod event_group_wait_types {}, "event_group_wait_types"),
148+
(mod interrupt_misc {}, "interrupt_misc"),
149+
(mod interrupt_priority {}, "interrupt_priority"),
113150
(mod priority_boost {}, "priority_boost"),
114151
(mod task_activate_and_dispatch {}, "task_activate_and_dispatch"),
115152
(mod task_activate_and_do_not_dispatch {}, "task_activate_and_do_not_dispatch"),

0 commit comments

Comments
 (0)