Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
use super::{Directory, fixture};
use dever::compiler::{build, project::Project};
#[test]
fn async_stream_consumers_keep_values_and_close_all_aliases() {
let directory = Directory::new();
let project = Project::load(&fixture().with_file_name("async-stream.dever")).unwrap();
let program = project.check().unwrap();
for (entry, expected) in [
("verify", "true\n"),
("cleanup", "true\n"),
("creation", "AsyncStream\n"),
] {
let binary =
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
let result = std::process::Command::new(binary).output().unwrap();
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
assert_eq!(result.stdout, expected.as_bytes());
}
}
#[test]
fn invalid_tick_period_keeps_its_official_library_location() {
let directory = Directory::new();
let project = Project::load(&fixture().with_file_name("async-stream.dever")).unwrap();
let program = project.check().unwrap();
let binary = build::build(
&program,
&project.sources,
"invalid",
&directory.0.join("invalid"),
)
.unwrap();
let result = std::process::Command::new(binary).output().unwrap();
assert!(!result.status.success());
let error = String::from_utf8(result.stderr).unwrap();
assert!(error.contains("tick period must be positive"), "{error}");
let source = project
.sources
.iter()
.find(|source| source.path.ends_with("lib/dever/task.dever"))
.unwrap();
let (line, column) =
source.position(source.text.find("dever.system.taskTicks(millis)").unwrap());
assert!(
error.contains(&format!("{}:{line}:{column}:", source.path.display())),
"{error}"
);
}
+52
View File
@@ -0,0 +1,52 @@
use super::{Directory, fixture};
use dever::compiler::{build, project::Project};
#[test]
fn native_channels_transfer_values_and_share_close_state() {
let directory = Directory::new();
let project = Project::load(&fixture().with_file_name("channel.dever")).unwrap();
let program = project.check().unwrap();
for (entry, expected) in [
("verify", "true\n"),
("creation", "Channel\n"),
("nullable", "None\n"),
] {
let binary =
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
let result = std::process::Command::new(binary).output().unwrap();
assert!(
result.status.success(),
"{}",
String::from_utf8_lossy(&result.stderr)
);
assert_eq!(result.stdout, expected.as_bytes());
}
}
#[test]
fn channel_faults_keep_original_source_locations() {
let directory = Directory::new();
let project = Project::load(&fixture().with_file_name("channel.dever")).unwrap();
let program = project.check().unwrap();
for (entry, needle, message) in [
(
"invalid",
"channel(Int, 0)",
"channel capacity must be within 1..65536",
),
("closed", "send(values, 42)", "channel is closed"),
] {
let binary =
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
let result = std::process::Command::new(binary).output().unwrap();
assert!(!result.status.success());
let source = &project.sources[0];
let (line, column) = source.position(source.text.find(needle).unwrap());
let error = String::from_utf8(result.stderr).unwrap();
assert!(error.contains(message), "{error}");
assert!(
error.contains(&format!("channel.dever:{line}:{column}:")),
"{error}"
);
}
}
+51
View File
@@ -0,0 +1,51 @@
use super::{Directory, fixture};
use dever::compiler::{build, project::Project};
#[test]
fn groups_execute_empty_reused_and_mixed_tasks() {
let directory = Directory::new();
let project = Project::load(&fixture().with_file_name("task-group.dever")).unwrap();
let program = project.check().unwrap();
for entry in ["verify", "verifyLocal"] {
let binary =
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
let result = std::process::Command::new(binary).output().unwrap();
assert!(
result.status.success(),
"{entry}: {}",
String::from_utf8_lossy(&result.stderr)
);
assert_eq!(result.stdout, b"true\n");
}
}
#[test]
fn groups_preserve_child_and_argument_fault_origins() {
let directory = Directory::new();
let project = Project::load(&fixture().with_file_name("task-group.dever")).unwrap();
let program = project.check().unwrap();
for (entry, needle, message) in [
("failedGroup", "Problem.Text(\"worker\")", "Problem.Text"),
("failedAdmission", "Problem.Number(42)", "Problem.Number"),
(
"failedArgument",
"Problem.Text(\"argument\")",
"Problem.Text",
),
("invalidGroup", "group(0)", "group limit must be within"),
] {
let binary =
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
let result = std::process::Command::new(binary).output().unwrap();
assert!(!result.status.success());
assert!(result.stdout.is_empty());
let source = &project.sources[0];
let (line, column) = source.position(source.text.find(needle).unwrap());
let error = String::from_utf8(result.stderr).unwrap();
assert!(error.contains(message), "{entry}: {error}");
assert!(
error.contains(&format!("task-group.dever:{line}:{column}:")),
"{entry}: {error}"
);
}
}
+23
View File
@@ -0,0 +1,23 @@
use super::{Directory, fixture};
use dever::compiler::{build, project::Project};
#[test]
fn suspending_arguments_release_nontransferable_record_fields_before_await() {
let directory = Directory::new();
let project = Project::load(&fixture().with_file_name("task-liveness.dever")).unwrap();
let program = project.check().unwrap();
let binary = build::build(
&program,
&project.sources,
"verify",
&directory.0.join("verify"),
)
.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");
}
+277
View File
@@ -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");
}
}
}
+85
View File
@@ -0,0 +1,85 @@
use super::{Directory, fixture};
use dever::compiler::{build, project::Project};
use dever::syntax::source::Source;
fn project(directory: &Directory) -> Project {
let mut project = Project::load(&fixture().with_file_name("parallel.dever")).unwrap();
let source = &project.sources[0];
let text = source.text.replace(
"PARALLEL_FIXTURE_PATH",
directory.0.join("stream.data").to_str().unwrap(),
);
project.sources[0] = Source::new(source.id, source.path.clone(), text);
project
}
#[test]
fn native_parallel_consumers_cover_threads_workers_tasks_and_streams() {
let directory = Directory::new();
let project = project(&directory);
let program = project.check().unwrap();
for (entry, expected) in [
("syncVerify", "sync\nsync\nsync\nsync\nbyte\nbyte\ntrue\n"),
(
"workerVerify",
"worker\nworker\nworker\nworker\n3\n3\ntrue\n",
),
("streamVerify", "chunk\nchunk\nchunk\nchunk\ntrue\n"),
("asyncVerify", "true\n"),
("nestedVerify", "true\n"),
("sourceFailure", "true\n"),
("cancelVerify", "true\n"),
] {
let binary =
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
let result = std::process::Command::new(binary).output().unwrap();
assert!(
result.status.success(),
"{entry}: {}",
String::from_utf8_lossy(&result.stderr)
);
assert_eq!(result.stdout, expected.as_bytes(), "{entry}");
}
}
#[test]
fn parallel_failures_and_limits_keep_original_locations() {
let directory = Directory::new();
let project = project(&directory);
let program = project.check().unwrap();
for (entry, needle, message) in [
(
"syncFailure",
"dever.io.ReadEvent.Failed(\"sync worker\")",
"ReadEvent.Failed",
),
(
"syncInvalid",
"parallelEach(report, [], 0",
"parallel concurrency must be within 1..256",
),
(
"asyncInvalid",
"parallelEach(sendOne, [], 65537",
"group limit must be within 1..65536",
),
(
"workerInvalid",
"parallelEach(report, [], 257",
"parallel concurrency must be within 1..256",
),
] {
let binary =
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
let result = std::process::Command::new(binary).output().unwrap();
assert!(!result.status.success(), "{entry}");
let error = String::from_utf8(result.stderr).unwrap();
let source = &project.sources[0];
let (line, column) = source.position(source.text.find(needle).unwrap());
assert!(error.contains(message), "{entry}: {error}");
assert!(
error.contains(&format!("parallel.dever:{line}:{column}:")),
"{entry}: {error}"
);
}
}
+71
View File
@@ -0,0 +1,71 @@
use super::{Directory, fixture};
use dever::compiler::{build, project::Project};
#[test]
fn selection_executes_nullable_multi_output_unit_and_expired_branches() {
let directory = Directory::new();
let project = Project::load(&fixture().with_file_name("task-select.dever")).unwrap();
let program = project.check().unwrap();
let binary = build::build(
&program,
&project.sources,
"verify",
&directory.0.join("selection"),
)
.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 selection_preserves_fault_sources_and_argument_failures() {
let directory = Directory::new();
let project = Project::load(&fixture().with_file_name("task-select.dever")).unwrap();
let program = project.check().unwrap();
for (entry, needle, message) in [
(
"timeoutFault",
"ValueResult.Failed(\"worker\")",
"ValueResult.Failed",
),
(
"raceFault",
"ValueResult.Failed(\"worker\")",
"ValueResult.Failed",
),
(
"argumentFault",
"ValueResult.Failed(\"argument\")",
"ValueResult.Failed",
),
(
"raceArgumentFault",
"ValueResult.Failed(\"argument\")",
"ValueResult.Failed",
),
(
"invalidTimeout",
"timeout(run(dever.task.sleep(60000)), 0",
"timeout duration must be positive",
),
] {
let binary =
build::build(&program, &project.sources, entry, &directory.0.join(entry)).unwrap();
let result = std::process::Command::new(binary).output().unwrap();
assert!(!result.status.success());
assert!(result.stdout.is_empty());
let source = &project.sources[0];
let (line, column) = source.position(source.text.find(needle).unwrap());
let error = String::from_utf8(result.stderr).unwrap();
assert!(error.contains(message), "{entry}: {error}");
assert!(
error.contains(&format!("task-select.dever:{line}:{column}:")),
"{entry}: {error}"
);
}
}