Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 56 additions & 12 deletions src/v1/compiler_tests_rust.dag
Original file line number Diff line number Diff line change
Expand Up @@ -3143,6 +3143,22 @@ fn ct_shell_service_output_projection_test() -> String {
// no modeled channel must REFUSE, typed and located, rather than being answered
// with stdout. The refusal is minted at the BINDING, before a target is chosen,
// so there is no renderer default arm left for it to fall through.
//
// ASSERT THROUGH THE WALL, NOT ABOUT THE BYTES. This test first asked for the
// refusal TEXT inside an emitted src/probe.rs, and it was RED on main for exactly
// as long as that shape stood, because emit_artifact in v1.compiler.compile pairs
// the refusal EXPRESSION with a BLOCKING diagnostic and returns `files: []` --
// the compile itself stops, so there is no src/probe.rs to read. The weaker claim
// was not merely unsatisfiable here; it was satisfiable by precisely the class the
// wall exists to close, refusal_deferred_to_emitted_runtime: a compile that reports
// success while shipping bytes that carry the refusal to a runtime nobody reads.
// So the assertion is now the compile's own verdict -- a located diagnostic naming
// the key, and the ABSENCE of any emitted body for the refused module.
//
// ITS POSITIVE CONTROL IS THE SIBLING ABOVE. shell_service_output_projection_binds
// _each_declared_channel compiles the same fixture with every key modeled and gets
// src/probe.rs with zero diagnostics, so "no file" here is the refusal firing and
// not an emitter that emits nothing for services.
fn ct_shell_service_unmodeled_output_key_refusal_test() -> String {
concat(
" #[test]\n",
Expand All @@ -3158,23 +3174,51 @@ fn ct_shell_service_unmodeled_output_key_refusal_test() -> String {
" std::rc::Rc::new(im::vector![source]),\n",
" crate::v1_compiler_artifact::RenderTarget::Rust,\n",
" );\n",
" let emitted = r\n",
" .files\n",
" let errors: Vec<_> = r\n",
" .diagnostics\n",
" .iter()\n",
" .find(|f| f.path == \"src/probe.rs\")\n",
" .map(|f| f.content.clone())\n",
" .expect(\"service module must emit src/probe.rs\");\n",
" .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n",
" .collect();\n",
" // The line stops, and it stops with the TYPED cause -- not merely\n",
" // with some error that happens to be present.\n",
" assert!(\n",
" emitted.contains(\"not_a_channel\")\n",
" && emitted.contains(\"has no modeled channel\"),\n",
" \"an unmodeled output key must refuse and name the key. Got:\\n{}\",\n",
" emitted\n",
" errors.iter().any(|d| matches!(\n",
" *d.diagnostic,\n",
" crate::v1_std_core::CompilerDiagnostic::TransportEmissionNotModeled { .. }\n",
" )),\n",
" \"an unmodeled output key must refuse with TransportEmissionNotModeled. \\\n",
" Got: {:?}\",\n",
" r.diagnostics\n",
" );\n",
" // ...and it is LOCATED at the field: the message names the key the\n",
" // author wrote, so an operation with one bad key among several says\n",
" // which one.\n",
" let messages: Vec<String> = errors\n",
" .iter()\n",
" .map(|d| crate::v1_std_core::diagnostic_to_message(d.diagnostic.clone()))\n",
" .collect();\n",
" assert!(\n",
" messages.iter().any(|m| m.contains(\"not_a_channel\")\n",
" && m.contains(\"has no modeled channel\")),\n",
" \"the refusal must name the unmodeled key. Got: {:?}\",\n",
" messages\n",
" );\n",
" // THE DISCRIMINATING HALF. A refusal deferred into emitted bytes\n",
" // would still satisfy both assertions above while leaving a\n",
" // src/probe.rs behind; the wall is that emit returns no files at all.\n",
" let paths: Vec<String> = r.files.iter().map(|f| f.path.clone()).collect();\n",
" assert!(\n",
" !paths.iter().any(|p| p == \"src/probe.rs\"),\n",
" \"a refused operation must not be emitted at all -- the refusal may \\\n",
" not be deferred into a body. Got files: {:?}\",\n",
" paths\n",
" );\n",
" // And nothing anywhere fell through to the stdout channel.\n",
" assert!(\n",
" !emitted.contains(\"stdout.clone()\"),\n",
" !r.files.iter().any(|f| f.content.contains(\"stdout.clone()\")),\n",
" \"a refused operation must not fall through to the stdout channel. \\\n",
" Got:\\n{}\",\n",
" emitted\n",
" Got files: {:?}\",\n",
" paths\n",
" );\n",
" })\n",
" .expect(\"failed to spawn thread\")\n",
Expand Down
52 changes: 40 additions & 12 deletions src/v1/stage0/src/compiler_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1037,23 +1037,51 @@ mod compiler_tests {
std::rc::Rc::new(im::vector![source]),
crate::v1_compiler_artifact::RenderTarget::Rust,
);
let emitted = r
.files
let errors: Vec<_> = r
.diagnostics
.iter()
.find(|f| f.path == "src/probe.rs")
.map(|f| f.content.clone())
.expect("service module must emit src/probe.rs");
.filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))
.collect();
// The line stops, and it stops with the TYPED cause -- not merely
// with some error that happens to be present.
assert!(
emitted.contains("not_a_channel")
&& emitted.contains("has no modeled channel"),
"an unmodeled output key must refuse and name the key. Got:\n{}",
emitted
errors.iter().any(|d| matches!(
*d.diagnostic,
crate::v1_std_core::CompilerDiagnostic::TransportEmissionNotModeled { .. }
)),
"an unmodeled output key must refuse with TransportEmissionNotModeled. \
Got: {:?}",
r.diagnostics
);
// ...and it is LOCATED at the field: the message names the key the
// author wrote, so an operation with one bad key among several says
// which one.
let messages: Vec<String> = errors
.iter()
.map(|d| crate::v1_std_core::diagnostic_to_message(d.diagnostic.clone()))
.collect();
assert!(
messages.iter().any(|m| m.contains("not_a_channel")
&& m.contains("has no modeled channel")),
"the refusal must name the unmodeled key. Got: {:?}",
messages
);
// THE DISCRIMINATING HALF. A refusal deferred into emitted bytes
// would still satisfy both assertions above while leaving a
// src/probe.rs behind; the wall is that emit returns no files at all.
let paths: Vec<String> = r.files.iter().map(|f| f.path.clone()).collect();
assert!(
!emitted.contains("stdout.clone()"),
!paths.iter().any(|p| p == "src/probe.rs"),
"a refused operation must not be emitted at all -- the refusal may \
not be deferred into a body. Got files: {:?}",
paths
);
// And nothing anywhere fell through to the stdout channel.
assert!(
!r.files.iter().any(|f| f.content.contains("stdout.clone()")),
"a refused operation must not fall through to the stdout channel. \
Got:\n{}",
emitted
Got files: {:?}",
paths
);
})
.expect("failed to spawn thread")
Expand Down
2 changes: 1 addition & 1 deletion src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ pub fn ct_shell_service_output_projection_test() -> String {
}

pub fn ct_shell_service_unmodeled_output_key_refusal_test() -> String {
v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " fn shell_service_unmodeled_output_key_refuses() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(32 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " let source = std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: \"probe.dag\".to_string(),\n".to_string()), " content: \"module probe\\nservice Probe {\\n operation Version {\\n input {}\\n output {\\n first: String from \\\"not_a_channel\\\"\\n out: String from \\\"stdout\\\"\\n }\\n transport shell { argv: [\\\"git\\\", \\\"--version\\\"] }\\n }\\n}\\n\".to_string(),\n".to_string()), " });\n".to_string()), " let r = crate::v1_compiler_compile::compile_sources(\n".to_string()), " std::rc::Rc::new(im::vector![source]),\n".to_string()), " crate::v1_compiler_artifact::RenderTarget::Rust,\n".to_string()), " );\n".to_string()), " let emitted = r\n".to_string()), " .files\n".to_string()), " .iter()\n".to_string()), " .find(|f| f.path == \"src/probe.rs\")\n".to_string()), " .map(|f| f.content.clone())\n".to_string()), " .expect(\"service module must emit src/probe.rs\");\n".to_string()), " assert!(\n".to_string()), " emitted.contains(\"not_a_channel\")\n".to_string()), " && emitted.contains(\"has no modeled channel\"),\n".to_string()), " \"an unmodeled output key must refuse and name the key. Got:\\n{}\",\n".to_string()), " emitted\n".to_string()), " );\n".to_string()), " assert!(\n".to_string()), " !emitted.contains(\"stdout.clone()\"),\n".to_string()), " \"a refused operation must not fall through to the stdout channel. \\\n".to_string()), " Got:\\n{}\",\n".to_string()), " emitted\n".to_string()), " );\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"shell_service_unmodeled_output_key_refuses panicked\");\n".to_string()), " }\n".to_string()), "\n".to_string())
v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(v1_rt::concat(" #[test]\n".to_string(), " fn shell_service_unmodeled_output_key_refuses() {\n".to_string()), " let result = std::thread::Builder::new()\n".to_string()), " .stack_size(32 * 1024 * 1024)\n".to_string()), " .spawn(|| {\n".to_string()), " let source = std::rc::Rc::new(crate::v1_compiler_compile::SourceFile {\n".to_string()), " path: \"probe.dag\".to_string(),\n".to_string()), " content: \"module probe\\nservice Probe {\\n operation Version {\\n input {}\\n output {\\n first: String from \\\"not_a_channel\\\"\\n out: String from \\\"stdout\\\"\\n }\\n transport shell { argv: [\\\"git\\\", \\\"--version\\\"] }\\n }\\n}\\n\".to_string(),\n".to_string()), " });\n".to_string()), " let r = crate::v1_compiler_compile::compile_sources(\n".to_string()), " std::rc::Rc::new(im::vector![source]),\n".to_string()), " crate::v1_compiler_artifact::RenderTarget::Rust,\n".to_string()), " );\n".to_string()), " let errors: Vec<_> = r\n".to_string()), " .diagnostics\n".to_string()), " .iter()\n".to_string()), " .filter(|d| crate::v1_std_core::is_error_diagnostic(d.diagnostic.clone()))\n".to_string()), " .collect();\n".to_string()), " // The line stops, and it stops with the TYPED cause -- not merely\n".to_string()), " // with some error that happens to be present.\n".to_string()), " assert!(\n".to_string()), " errors.iter().any(|d| matches!(\n".to_string()), " *d.diagnostic,\n".to_string()), " crate::v1_std_core::CompilerDiagnostic::TransportEmissionNotModeled { .. }\n".to_string()), " )),\n".to_string()), " \"an unmodeled output key must refuse with TransportEmissionNotModeled. \\\n".to_string()), " Got: {:?}\",\n".to_string()), " r.diagnostics\n".to_string()), " );\n".to_string()), " // ...and it is LOCATED at the field: the message names the key the\n".to_string()), " // author wrote, so an operation with one bad key among several says\n".to_string()), " // which one.\n".to_string()), " let messages: Vec<String> = errors\n".to_string()), " .iter()\n".to_string()), " .map(|d| crate::v1_std_core::diagnostic_to_message(d.diagnostic.clone()))\n".to_string()), " .collect();\n".to_string()), " assert!(\n".to_string()), " messages.iter().any(|m| m.contains(\"not_a_channel\")\n".to_string()), " && m.contains(\"has no modeled channel\")),\n".to_string()), " \"the refusal must name the unmodeled key. Got: {:?}\",\n".to_string()), " messages\n".to_string()), " );\n".to_string()), " // THE DISCRIMINATING HALF. A refusal deferred into emitted bytes\n".to_string()), " // would still satisfy both assertions above while leaving a\n".to_string()), " // src/probe.rs behind; the wall is that emit returns no files at all.\n".to_string()), " let paths: Vec<String> = r.files.iter().map(|f| f.path.clone()).collect();\n".to_string()), " assert!(\n".to_string()), " !paths.iter().any(|p| p == \"src/probe.rs\"),\n".to_string()), " \"a refused operation must not be emitted at all -- the refusal may \\\n".to_string()), " not be deferred into a body. Got files: {:?}\",\n".to_string()), " paths\n".to_string()), " );\n".to_string()), " // And nothing anywhere fell through to the stdout channel.\n".to_string()), " assert!(\n".to_string()), " !r.files.iter().any(|f| f.content.contains(\"stdout.clone()\")),\n".to_string()), " \"a refused operation must not fall through to the stdout channel. \\\n".to_string()), " Got files: {:?}\",\n".to_string()), " paths\n".to_string()), " );\n".to_string()), " })\n".to_string()), " .expect(\"failed to spawn thread\")\n".to_string()), " .join();\n".to_string()), " result.expect(\"shell_service_unmodeled_output_key_refuses panicked\");\n".to_string()), " }\n".to_string()), "\n".to_string())
}

pub fn ct_shell_service_exit_error_arm_boxed_test() -> String {
Expand Down
Loading