Initial Dever 0.0.1
This commit is contained in:
commit
81233d098c
1479 files changed
+163782
No files matched your search
@@ -0,0 +1,277 @@
|
||||
use super::{Directory, fixture};
|
||||
use dever::compiler::{build, project::Project};
|
||||
|
||||
mod async_stream;
|
||||
mod channel;
|
||||
mod group;
|
||||
mod liveness;
|
||||
mod parallel;
|
||||
mod select;
|
||||
|
||||
#[test]
|
||||
fn asynchronous_clauses_keep_nullable_payloads_axes_and_failure_cleanup() {
|
||||
let directory = Directory::new();
|
||||
let project = Project::load(&fixture().with_file_name("task-clauses.dever")).unwrap();
|
||||
let program = project.check().unwrap();
|
||||
let binary = build::build(
|
||||
&program,
|
||||
&project.sources,
|
||||
"verify",
|
||||
&directory.0.join("clauses"),
|
||||
)
|
||||
.unwrap();
|
||||
let result = std::process::Command::new(binary).output().unwrap();
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
assert_eq!(result.stdout, b"true\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_tasks_transfer_values_wait_cancel_and_preserve_error_origins() {
|
||||
let directory = Directory::new();
|
||||
let project = Project::load(&fixture().with_file_name("task.dever")).unwrap();
|
||||
let program = project.check().unwrap();
|
||||
for (entry, expected) in [
|
||||
("verify", None),
|
||||
("sleepFailure", Some("SleepResult.Failed")),
|
||||
("faultEntry", Some("integer arithmetic fault")),
|
||||
] {
|
||||
let binary =
|
||||
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
|
||||
let result = std::process::Command::new(binary).output().unwrap();
|
||||
if let Some(message) = expected {
|
||||
assert!(!result.status.success());
|
||||
assert!(result.stdout.is_empty());
|
||||
let error = String::from_utf8(result.stderr).unwrap();
|
||||
assert!(error.contains(message), "{error}");
|
||||
if entry == "faultEntry" {
|
||||
let source = &project.sources[0];
|
||||
let (line, column) = source.position(source.text.rfind("value + 1").unwrap());
|
||||
assert!(
|
||||
error.contains(&format!("task.dever:{line}:{column}:")),
|
||||
"{error}"
|
||||
);
|
||||
} else {
|
||||
assert!(error.contains("lib/dever/time.dever:"), "{error}");
|
||||
}
|
||||
} else {
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
assert_eq!(result.stdout, b"true\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asynchronous_commands_keep_typed_inputs_outputs_and_synchronous_peers() {
|
||||
let directory = Directory::new();
|
||||
let mut project = Project::load(&fixture().with_file_name("application")).unwrap();
|
||||
let source = project
|
||||
.sources
|
||||
.iter_mut()
|
||||
.find(|source| source.path.ends_with("greeting/app.dever"))
|
||||
.unwrap();
|
||||
let text = source
|
||||
.text
|
||||
.replace(
|
||||
"message = utility.decorate(domain.greet(name))",
|
||||
"dever.task.sleep(0)\n message = utility.decorate(domain.greet(name))",
|
||||
)
|
||||
.replace(
|
||||
"{ first = value\n second = value + 1 }",
|
||||
"{ dever.task.sleep(0)\n first = value\n second = value + 1 }",
|
||||
);
|
||||
*source = dever::syntax::source::Source::new(source.id, source.path.clone(), text);
|
||||
let program = project.check().unwrap();
|
||||
let binary = build::build(
|
||||
&program,
|
||||
&project.sources,
|
||||
"main",
|
||||
&directory.0.join("commands"),
|
||||
)
|
||||
.unwrap();
|
||||
for (command, input, expected) in [
|
||||
("greet", r#"{"name":"雪"}"#, r#""[Hello, 雪]""#),
|
||||
("pair", r#"{"value":41}"#, r#"{"first":41,"second":42}"#),
|
||||
("double", r#"{"value":21}"#, "42"),
|
||||
] {
|
||||
let result = std::process::Command::new(&binary)
|
||||
.args([&format!("demo.greeting.{command}"), input])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
assert_eq!(
|
||||
String::from_utf8(result.stdout).unwrap(),
|
||||
format!("{{\"code\":0,\"message\":\"ok\",\"data\":{expected}}}\n")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leaf_entry_preserves_business_failure_origin() {
|
||||
let directory = Directory::new();
|
||||
let mut project = Project::load(&fixture().with_file_name("task.dever")).unwrap();
|
||||
let source = &project.sources[0];
|
||||
let text = format!(
|
||||
"{}\nasyncFailure() () {{ dever.task.sleep(-1) }}\n",
|
||||
source.text
|
||||
);
|
||||
project.sources[0] = dever::syntax::source::Source::new(source.id, source.path.clone(), text);
|
||||
let program = project.check().unwrap();
|
||||
let binary = build::build(
|
||||
&program,
|
||||
&project.sources,
|
||||
"asyncFailure",
|
||||
&directory.0.join("failure"),
|
||||
)
|
||||
.unwrap();
|
||||
let result = std::process::Command::new(binary).output().unwrap();
|
||||
assert!(!result.status.success());
|
||||
assert!(result.stdout.is_empty());
|
||||
let source = project
|
||||
.sources
|
||||
.iter()
|
||||
.find(|source| source.path.ends_with("lib/dever/task.dever"))
|
||||
.unwrap();
|
||||
let offset = source
|
||||
.text
|
||||
.find("fail(dever.time.SleepResult.Failed(message))")
|
||||
.unwrap()
|
||||
+ "fail(".len();
|
||||
let (line, column) = source.position(offset);
|
||||
let error = String::from_utf8(result.stderr).unwrap();
|
||||
assert!(error.contains("SleepResult.Failed"), "{error}");
|
||||
assert!(
|
||||
error.contains(&format!("{}:{line}:{column}:", source.path.display())),
|
||||
"{error}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn actual_entries_only_create_task_scopes_when_they_schedule() {
|
||||
let directory = Directory::new();
|
||||
let mut project = Project::load(&fixture().with_file_name("task-performance.dever")).unwrap();
|
||||
let cases = [
|
||||
("syncEntry", "answer = total([1, 2])", false, false, "3\n"),
|
||||
(
|
||||
"leafEntry",
|
||||
"answer = asyncTotal([1, 2], false)",
|
||||
true,
|
||||
false,
|
||||
"3\n",
|
||||
),
|
||||
("forwardEntry", "answer = leafEntry()", true, false, "3\n"),
|
||||
(
|
||||
"scheduledEntry",
|
||||
"answer = asyncPair([1, 2])",
|
||||
true,
|
||||
true,
|
||||
"6\n",
|
||||
),
|
||||
(
|
||||
"blockingEntry",
|
||||
"answer = blockingSum([1, 2])",
|
||||
true,
|
||||
true,
|
||||
"3\n",
|
||||
),
|
||||
("cancelEntry", "answer = cancel()", true, true, "1\n"),
|
||||
];
|
||||
let mut text = project.sources[0].text.clone();
|
||||
for (name, body, _, _, _) in cases {
|
||||
text.push_str(&format!("{name}() (answer: Int) {{ {body} }}\n"));
|
||||
}
|
||||
let source = &project.sources[0];
|
||||
project.sources[0] = dever::syntax::source::Source::new(source.id, source.path.clone(), text);
|
||||
let program = project.check().unwrap();
|
||||
for (entry, _, suspend, schedule, expected) in cases {
|
||||
let signature = &program
|
||||
.functions
|
||||
.iter()
|
||||
.find(|function| function.signature.name == entry)
|
||||
.unwrap()
|
||||
.signature;
|
||||
assert_eq!(
|
||||
(signature.effects.suspend, signature.effects.schedule),
|
||||
(suspend, schedule),
|
||||
"{entry}"
|
||||
);
|
||||
let binary =
|
||||
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
|
||||
let output = std::process::Command::new(binary).output().unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"{entry}: {}",
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
assert_eq!(output.stdout, expected.as_bytes(), "{entry}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pending_tasks_keep_cleanup_on_each_failure_exit() {
|
||||
let directory = Directory::new();
|
||||
let mut project = Project::load(&fixture().with_file_name("task.dever")).unwrap();
|
||||
let source = &project.sources[0];
|
||||
let mut text = source.text.clone();
|
||||
for code in [
|
||||
"explicitFailure() () { pending = run(dever.task.sleep(60000))\n fail(dever.time.SleepResult.Failed(\"explicit\")) }",
|
||||
"explicitVerify() (answer: Bool) { answer = failed(result(explicitFailure())) }",
|
||||
"arithmeticSleep(value: Int) () { next = value + 1\n dever.task.sleep(next) }",
|
||||
"capturedFault(value: Int) (answer: dever.time.SleepResult) { pending = run(dever.task.sleep(60000))\n answer = result(arithmeticSleep(value))\n stop(pending) }",
|
||||
"captureFaultEntry() (answer: dever.time.SleepResult) { answer = capturedFault(9223372036854775807) }",
|
||||
"sumWithPending(values: List<Int>) (answer: Int) { pending = run(dever.task.sleep(60000))\n answer = sum(values)\n stop(pending) }",
|
||||
"sumCleanupEntry() (answer: Int) { answer = sumWithPending([9223372036854775807, 1]) }",
|
||||
"mapWithPending(key: Text) (answer: Map<Text, Int>) { pending = run(dever.task.sleep(60000))\n answer = { key = 1\n \"same\" = 2 }\n stop(pending) }",
|
||||
"mapCleanupEntry() (answer: Map<Text, Int>) { answer = mapWithPending(\"same\") }",
|
||||
] {
|
||||
text.push_str(&format!("{code}\n"));
|
||||
}
|
||||
project.sources[0] = dever::syntax::source::Source::new(source.id, source.path.clone(), text);
|
||||
let program = project.check().unwrap();
|
||||
for (entry, failure) in [
|
||||
("explicitVerify", None),
|
||||
(
|
||||
"captureFaultEntry",
|
||||
Some(("value + 1", "integer arithmetic fault")),
|
||||
),
|
||||
("sumCleanupEntry", Some(("sum(values)", "integer overflow"))),
|
||||
(
|
||||
"mapCleanupEntry",
|
||||
Some(("{ key = 1", "duplicate Map literal key")),
|
||||
),
|
||||
] {
|
||||
let binary =
|
||||
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
|
||||
let result = std::process::Command::new(binary).output().unwrap();
|
||||
if let Some((needle, message)) = failure {
|
||||
assert!(!result.status.success());
|
||||
assert!(result.stdout.is_empty());
|
||||
let source = &project.sources[0];
|
||||
let (line, column) = source.position(source.text.rfind(needle).unwrap());
|
||||
let error = String::from_utf8(result.stderr).unwrap();
|
||||
assert!(
|
||||
error.contains(&format!("task.dever:{line}:{column}:")),
|
||||
"{error}"
|
||||
);
|
||||
assert!(error.contains(message), "{error}");
|
||||
} else {
|
||||
assert!(
|
||||
result.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&result.stderr)
|
||||
);
|
||||
assert_eq!(result.stdout, b"true\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user