Skip to content

Commit 10eebfe

Browse files
authored
test(rvm): Move vm execution limit tests to a separate test to avoid flakiness (microsoft#558)
Having a separate integration test allows the execution tests to freely change the global fallback limits without affecting other tests. also ask release-plz to ignore xtask package Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
1 parent e688806 commit 10eebfe

3 files changed

Lines changed: 170 additions & 270 deletions

File tree

release-plz.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ git_tag_enable = true
1111
git_release_enable = true
1212
changelog_update = true
1313
publish = true
14+
15+
[[package]]
16+
name = "xtask"
17+
release = false

src/rvm/tests/vm.rs

Lines changed: 1 addition & 270 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,9 @@
1818

1919
#[cfg(test)]
2020
mod tests {
21-
use crate::rvm::program::Program;
2221
use crate::rvm::tests::instruction_parser::{parse_instruction, parse_loop_mode};
2322
use crate::rvm::tests::test_utils::test_round_trip_serialization;
24-
#[cfg(any(test, not(feature = "std")))]
25-
use crate::utils::limits::set_time_source;
26-
use crate::utils::limits::{
27-
acquire_limits_test_lock, fallback_execution_timer_config,
28-
set_fallback_execution_timer_config, ExecutionTimerConfig, TimeSource,
29-
};
23+
use crate::utils::limits::ExecutionTimerConfig;
3024
use core::num::NonZeroU32;
3125
use core::time::Duration;
3226
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
@@ -57,122 +51,11 @@ mod tests {
5751
use anyhow::Result;
5852
use serde::{Deserialize, Serialize};
5953
use std::fs;
60-
use std::sync::{Mutex, Once};
6154
use test_generator::test_resources;
6255

6356
extern crate alloc;
6457
extern crate std;
6558

66-
struct FallbackGuard(Option<ExecutionTimerConfig>);
67-
impl Drop for FallbackGuard {
68-
fn drop(&mut self) {
69-
set_fallback_execution_timer_config(self.0);
70-
}
71-
}
72-
73-
fn install_fallback_config(config: Option<ExecutionTimerConfig>) -> FallbackGuard {
74-
let previous = fallback_execution_timer_config();
75-
set_fallback_execution_timer_config(config);
76-
FallbackGuard(previous)
77-
}
78-
79-
struct TimeSourceGuard {
80-
previous_default: Duration,
81-
previous_template: Vec<Duration>,
82-
}
83-
84-
impl Drop for TimeSourceGuard {
85-
fn drop(&mut self) {
86-
let mut state = TIME_SOURCE_STATE
87-
.lock()
88-
.unwrap_or_else(|poisoned| poisoned.into_inner());
89-
state.default_increment = self.previous_default;
90-
state.template_increments = self.previous_template.clone();
91-
state.reset_from_template();
92-
}
93-
}
94-
95-
fn configure_time_source(
96-
increments: Vec<Duration>,
97-
default_increment: Duration,
98-
) -> TimeSourceGuard {
99-
ensure_time_source_registered();
100-
101-
let mut state = TIME_SOURCE_STATE
102-
.lock()
103-
.unwrap_or_else(|poisoned| poisoned.into_inner());
104-
105-
let guard = TimeSourceGuard {
106-
previous_default: state.default_increment,
107-
previous_template: state.template_increments.clone(),
108-
};
109-
110-
state.default_increment = default_increment;
111-
state.template_increments = increments;
112-
state.reset_from_template();
113-
114-
guard
115-
}
116-
117-
struct TestTimeSource;
118-
119-
struct TimeSourceState {
120-
current: Duration,
121-
started: bool,
122-
default_increment: Duration,
123-
increments: VecDeque<Duration>,
124-
template_increments: Vec<Duration>,
125-
}
126-
127-
impl TimeSourceState {
128-
const fn new() -> Self {
129-
Self {
130-
current: Duration::ZERO,
131-
started: false,
132-
default_increment: Duration::from_millis(1),
133-
increments: VecDeque::new(),
134-
template_increments: Vec::new(),
135-
}
136-
}
137-
138-
fn reset_from_template(&mut self) {
139-
self.current = Duration::ZERO;
140-
self.started = false;
141-
self.increments = VecDeque::from(self.template_increments.clone());
142-
}
143-
}
144-
145-
impl TimeSource for TestTimeSource {
146-
fn now(&self) -> Option<Duration> {
147-
let mut state = TIME_SOURCE_STATE
148-
.lock()
149-
.unwrap_or_else(|poisoned| poisoned.into_inner());
150-
151-
if !state.started {
152-
state.started = true;
153-
return Some(state.current);
154-
}
155-
156-
let increment = state
157-
.increments
158-
.pop_front()
159-
.unwrap_or(state.default_increment);
160-
state.current = state.current.saturating_add(increment);
161-
Some(state.current)
162-
}
163-
}
164-
165-
static TEST_TIME_SOURCE: TestTimeSource = TestTimeSource;
166-
static TIME_SOURCE_STATE: Mutex<TimeSourceState> = Mutex::new(TimeSourceState::new());
167-
static TIME_SOURCE_ONCE: Once = Once::new();
168-
169-
fn ensure_time_source_registered() {
170-
#[cfg(any(test, not(feature = "std")))]
171-
TIME_SOURCE_ONCE.call_once(|| {
172-
let _ = set_time_source(&TEST_TIME_SOURCE);
173-
});
174-
}
175-
17659
#[derive(Debug, Clone, Deserialize, Serialize)]
17760
struct HostAwaitResponseSpec {
17861
id: crate::Value,
@@ -1129,158 +1012,6 @@ mod tests {
11291012
Ok(())
11301013
}
11311014

1132-
#[test]
1133-
fn vm_execution_time_limit_triggers_error() -> Result<()> {
1134-
use crate::rvm::instructions::Instruction;
1135-
use crate::utils::limits::acquire_limits_test_lock;
1136-
use core::num::NonZeroU32;
1137-
use core::time::Duration;
1138-
1139-
let _lock = acquire_limits_test_lock();
1140-
let config = ExecutionTimerConfig {
1141-
limit: Duration::from_nanos(1),
1142-
check_interval: NonZeroU32::new(1).unwrap(),
1143-
};
1144-
let _guard = install_fallback_config(Some(config));
1145-
1146-
let mut program = Program::new();
1147-
program.dispatch_window_size = 2;
1148-
program.max_rule_window_size = 2;
1149-
program.entry_points.insert("main".to_string(), 0);
1150-
1151-
const INSTRUCTION_COUNT: usize = 60_000;
1152-
program.instructions = (0..INSTRUCTION_COUNT)
1153-
.map(|_| Instruction::LoadNull { dest: 0 })
1154-
.collect();
1155-
program.instructions.push(Instruction::Return { value: 0 });
1156-
program.instruction_spans = alloc::vec![None; program.instructions.len()];
1157-
program.main_entry_point = 0;
1158-
1159-
let program = Arc::new(program);
1160-
1161-
let mut vm = RegoVM::new();
1162-
vm.set_max_instructions(usize::MAX);
1163-
vm.load_program(program);
1164-
1165-
let result = vm.execute();
1166-
assert!(
1167-
matches!(result, Err(VmError::TimeLimitExceeded { .. })),
1168-
"expected time limit error but got {result:?}"
1169-
);
1170-
1171-
Ok(())
1172-
}
1173-
1174-
#[test]
1175-
fn vm_execution_time_limit_override_allows_completion() -> Result<()> {
1176-
use crate::rvm::instructions::Instruction;
1177-
use crate::utils::limits::acquire_limits_test_lock;
1178-
use core::num::NonZeroU32;
1179-
use core::time::Duration;
1180-
1181-
let _lock = acquire_limits_test_lock();
1182-
let strict_config = ExecutionTimerConfig {
1183-
limit: Duration::from_nanos(1),
1184-
check_interval: NonZeroU32::new(1).unwrap(),
1185-
};
1186-
let _guard = install_fallback_config(Some(strict_config));
1187-
1188-
let mut program = Program::new();
1189-
program.dispatch_window_size = 2;
1190-
program.max_rule_window_size = 2;
1191-
program.entry_points.insert("main".to_string(), 0);
1192-
program.instructions = alloc::vec![
1193-
Instruction::LoadNull { dest: 0 },
1194-
Instruction::Return { value: 0 },
1195-
];
1196-
program.instruction_spans = alloc::vec![None; program.instructions.len()];
1197-
program.main_entry_point = 0;
1198-
1199-
let program = Arc::new(program);
1200-
1201-
let mut vm = RegoVM::new();
1202-
vm.load_program(program);
1203-
1204-
let relaxed_config = ExecutionTimerConfig {
1205-
limit: Duration::from_millis(10),
1206-
check_interval: NonZeroU32::new(1).unwrap(),
1207-
};
1208-
vm.set_execution_timer_config(Some(relaxed_config));
1209-
1210-
let result = vm.execute();
1211-
assert!(
1212-
result.is_ok(),
1213-
"expected successful execution, got {result:?}"
1214-
);
1215-
1216-
Ok(())
1217-
}
1218-
1219-
#[test]
1220-
fn vm_suspend_resume_excludes_suspended_time_from_limit() -> Result<()> {
1221-
use crate::rvm::instructions::Instruction;
1222-
1223-
let _lock = acquire_limits_test_lock();
1224-
let _guard = install_fallback_config(Some(ExecutionTimerConfig {
1225-
limit: Duration::from_millis(10),
1226-
check_interval: NonZeroU32::new(1).unwrap(),
1227-
}));
1228-
1229-
let _time_guard = configure_time_source(
1230-
alloc::vec![
1231-
Duration::from_millis(1),
1232-
Duration::from_millis(1),
1233-
Duration::from_millis(1),
1234-
Duration::from_millis(1),
1235-
Duration::from_millis(100),
1236-
Duration::from_millis(1),
1237-
],
1238-
Duration::from_millis(1),
1239-
);
1240-
1241-
let mut program = Program::new();
1242-
program.dispatch_window_size = 3;
1243-
program.max_rule_window_size = 3;
1244-
program.entry_points.insert("main".to_string(), 0);
1245-
program.literals = alloc::vec![Value::from("id"), Value::from(1)];
1246-
program.instructions = alloc::vec![
1247-
Instruction::Load {
1248-
dest: 0,
1249-
literal_idx: 0
1250-
},
1251-
Instruction::Load {
1252-
dest: 1,
1253-
literal_idx: 1
1254-
},
1255-
Instruction::HostAwait {
1256-
dest: 2,
1257-
arg: 1,
1258-
id: 0
1259-
},
1260-
Instruction::Return { value: 2 },
1261-
];
1262-
program.instruction_spans = alloc::vec![None; program.instructions.len()];
1263-
program.main_entry_point = 0;
1264-
1265-
let program = Arc::new(program);
1266-
let mut vm = RegoVM::new();
1267-
vm.set_execution_mode(ExecutionMode::Suspendable);
1268-
vm.load_program(program);
1269-
1270-
let _ = vm.execute()?;
1271-
match vm.execution_state() {
1272-
ExecutionState::Suspended { reason, .. } => {
1273-
assert!(matches!(reason, SuspendReason::HostAwait { .. }));
1274-
}
1275-
other => panic!("expected suspension, got {other:?}"),
1276-
}
1277-
1278-
let resumed = vm.resume(Some(Value::from(42)))?;
1279-
assert_eq!(resumed, Value::from(42));
1280-
1281-
Ok(())
1282-
}
1283-
12841015
#[test_resources("tests/rvm/vm/suites/*.yaml")]
12851016
fn run_vm_test_file(file: &str) {
12861017
run_vm_test_suite(file).unwrap()

0 commit comments

Comments
 (0)