Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,228 @@
|
||||
//! 真实命令的字节管道、非零退出、超时和输出预算;所有写入都在当前测试目录。
|
||||
|
||||
use super::{Asset, Bundle, Directory, Limits, Runtime};
|
||||
use dever_runtime::worker::command::{self, Limits as CommandLimits};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{fs, path::PathBuf, process::Command, time::Duration};
|
||||
|
||||
#[test]
|
||||
fn command_runs_owned_native_fixture_without_shell_and_drains_on_failure() {
|
||||
let _execution = super::EXECUTOR.lock().unwrap();
|
||||
let directory = Directory::new();
|
||||
let fixture = directory.0.join("fixture");
|
||||
let source = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../../test/rust/runtime/worker/fixture.rs");
|
||||
let result = Command::new("rustup")
|
||||
.args(["run", "1.95.0", "rustc", "--edition=2024", "-O"])
|
||||
.arg(source)
|
||||
.arg("-o")
|
||||
.arg(&fixture)
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
let runner = directory.0.join("runner");
|
||||
fs::copy(std::env::current_exe().unwrap(), &runner).unwrap();
|
||||
let result = Command::new(runner)
|
||||
.args(["--exact", "command::owned_command_case", "--nocapture"])
|
||||
.env_clear()
|
||||
.env("DEVER_COMMAND_FIXTURE", fixture)
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}\n{}",
|
||||
String::from_utf8_lossy(&result.stdout),
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn owned_command_case() {
|
||||
let Some(fixture) = std::env::var_os("DEVER_COMMAND_FIXTURE") else {
|
||||
return;
|
||||
};
|
||||
let bytes = fs::read(&fixture).unwrap();
|
||||
fs::remove_file(fixture).unwrap();
|
||||
let entry = "module/demo/tool/command";
|
||||
let launch = serde_json::to_vec(&serde_json::json!({"format":"dever-command-launch-v1","ecosystem":"command","entry":entry,"executable":entry,"arguments":[],"working_directory":"module/demo/tool"})).unwrap();
|
||||
let files = [
|
||||
(entry, bytes, true),
|
||||
("module/demo/tool/command.dever-command.json", launch, false),
|
||||
]
|
||||
.map(|(path, bytes, executable)| Asset {
|
||||
path,
|
||||
sha256: Box::leak(format!("{:x}", Sha256::digest(&bytes)).into_boxed_str()),
|
||||
bytes: Box::leak(bytes.into_boxed_slice()),
|
||||
executable,
|
||||
});
|
||||
let bundle: &'static Bundle = Box::leak(Box::new(Bundle {
|
||||
files: Box::leak(Box::new(files)),
|
||||
}));
|
||||
let root = std::env::current_exe()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
let runtime = Runtime::new(Limits {
|
||||
threads: 2,
|
||||
workers: 1,
|
||||
tasks: 32,
|
||||
})
|
||||
.unwrap();
|
||||
runtime
|
||||
.run(async |scope| {
|
||||
let arguments = ["--echo", "a b", "$(touch never)", "\"quoted\""].map(str::to_owned);
|
||||
let output = command::run(
|
||||
scope,
|
||||
bundle,
|
||||
entry,
|
||||
&arguments,
|
||||
Some(&[0, 255, 1]),
|
||||
CommandLimits::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(output.code, 7);
|
||||
assert_eq!(output.stdout, [0, 255, 1]);
|
||||
assert_eq!(output.stderr, b"a b\n$(touch never)\n\"quoted\"");
|
||||
let empty = command::run(
|
||||
scope,
|
||||
bundle,
|
||||
entry,
|
||||
&["--echo".into()],
|
||||
None,
|
||||
CommandLimits::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(empty.stdout.is_empty());
|
||||
let error = command::run(
|
||||
scope,
|
||||
bundle,
|
||||
entry,
|
||||
&["--flood".into()],
|
||||
None,
|
||||
CommandLimits {
|
||||
timeout_ms: 1000,
|
||||
output_limit: 100,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(error.contains("limit"));
|
||||
let marker = root.join("timed-pid");
|
||||
let error = command::run(
|
||||
scope,
|
||||
bundle,
|
||||
entry,
|
||||
&["--hang".into(), marker.to_str().unwrap().into()],
|
||||
None,
|
||||
CommandLimits {
|
||||
timeout_ms: 100,
|
||||
output_limit: 100,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.err()
|
||||
.unwrap();
|
||||
assert!(error.contains("timed out"));
|
||||
let pid = fs::read_to_string(marker).unwrap();
|
||||
assert!(!PathBuf::from(format!("/proc/{pid}")).exists());
|
||||
let marker = root.join("cancelled-pid");
|
||||
let arguments = ["--hang".into(), marker.to_str().unwrap().into()];
|
||||
let running = command::run(
|
||||
scope,
|
||||
bundle,
|
||||
entry,
|
||||
&arguments,
|
||||
None,
|
||||
CommandLimits::default(),
|
||||
);
|
||||
tokio::pin!(running);
|
||||
tokio::select! {
|
||||
_ = &mut running => panic!("hanging command completed before cancellation"),
|
||||
ready = tokio::time::timeout(Duration::from_secs(3), async {
|
||||
while !marker.is_file() { tokio::time::sleep(Duration::from_millis(5)).await; }
|
||||
}) => ready.unwrap(),
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let pid = fs::read_to_string(root.join("cancelled-pid")).unwrap();
|
||||
assert!(!PathBuf::from(format!("/proc/{pid}")).exists());
|
||||
|
||||
runtime
|
||||
.run(async |scope| {
|
||||
let (started, ready) = tokio::sync::oneshot::channel();
|
||||
let (release, receive) = std::sync::mpsc::channel();
|
||||
let blocker = scope
|
||||
.spawn_sync(move || {
|
||||
started.send(()).unwrap();
|
||||
receive.recv().unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
ready.await.unwrap();
|
||||
let queued = scope
|
||||
.spawn(move |child| async move {
|
||||
command::run(
|
||||
&child,
|
||||
bundle,
|
||||
entry,
|
||||
&["--echo".into()],
|
||||
None,
|
||||
CommandLimits::default(),
|
||||
)
|
||||
.await
|
||||
})
|
||||
.unwrap();
|
||||
let stopped =
|
||||
tokio::time::timeout(Duration::from_millis(300), queued.timeout(30)).await;
|
||||
release.send(()).unwrap();
|
||||
blocker.wait().await.unwrap();
|
||||
assert!(matches!(
|
||||
stopped,
|
||||
Ok(Ok(dever_runtime::task::Timed::Expired(None)))
|
||||
));
|
||||
worker_finish(scope).await;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
runtime
|
||||
.run(async |scope| {
|
||||
let marker = root.join("child-cancelled-pid");
|
||||
let arguments = vec!["--hang".into(), marker.to_str().unwrap().into()];
|
||||
let child = scope
|
||||
.spawn(move |child| async move {
|
||||
command::run(
|
||||
&child,
|
||||
bundle,
|
||||
entry,
|
||||
&arguments,
|
||||
None,
|
||||
CommandLimits::default(),
|
||||
)
|
||||
.await
|
||||
})
|
||||
.unwrap();
|
||||
tokio::time::timeout(Duration::from_secs(3), async {
|
||||
while !marker.is_file() {
|
||||
tokio::time::sleep(Duration::from_millis(5)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
child.stop().await.unwrap();
|
||||
let pid = fs::read_to_string(marker).unwrap();
|
||||
assert!(!PathBuf::from(format!("/proc/{pid}")).exists());
|
||||
worker_finish(scope).await;
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn worker_finish(scope: &dever_runtime::task::Scope<'_>) {
|
||||
dever_runtime::worker::finish(scope, Ok(())).await.unwrap();
|
||||
}
|
||||
Reference in new issue
Block a user