()
| 123 | |
| 124 | #[test] |
| 125 | fn packages_cases() { |
| 126 | let cases: Vec<Case> = serde_json::from_str( |
| 127 | include_str!("cases/packages.json") |
| 128 | ).expect("invalid JSON"); |
| 129 | |
| 130 | for case in cases { |
| 131 | let resolver = Box::new(build_resolver(&case.modules, &case.aliases, &case.manifests)); |
| 132 | let (tokens, _lex_errs) = lex(&case.src); |
| 133 | let parser = Parser::with_resolver(&case.src, tokens.into_iter(), resolver); |
| 134 | let (chunk, parse_errs) = parser.parse(); |
| 135 | |
| 136 | // Parse-time errors: match `error` substring plus optional span anchor. |
| 137 | if !parse_errs.is_empty() { |
| 138 | let combined = parse_errs.iter().map(|d| d.msg.as_str()).collect::<Vec<_>>().join(" | "); |
| 139 | let expected = case.error.as_deref().unwrap_or_else(|| panic!("unexpected parse error on {:?}: {}", case.src, combined)); |
| 140 | assert!(combined.contains(expected), "parse error mismatch on {:?}: got '{}', expected substring '{}'", case.src, combined, expected); |
| 141 | if let Some(needle) = &case.error_span_covers { |
| 142 | let pos = case.src.find(needle).unwrap_or_else(|| panic!("error_span_covers '{}' not found in src", needle)); |
| 143 | let d = &parse_errs[0]; |
| 144 | assert_eq!(d.start, pos, "diag start mismatch on {:?}", case.src); |
| 145 | assert_eq!(d.end, pos + needle.len(), "diag end mismatch on {:?}", case.src); |
| 146 | } |
| 147 | continue; |
| 148 | } |
| 149 | |
| 150 | // Compile-time chunk-shape assertions (no parse errors). |
| 151 | if let Some(n) = case.expect_externs { assert_eq!(chunk.extern_table.len(), n, "extern_table size mismatch on {:?}", case.src); } |
| 152 | if let Some(n) = case.expect_functions { assert_eq!(chunk.functions.len(), n, "functions count mismatch on {:?}", case.src); } |
| 153 | |
| 154 | let mut vm = VM::new(&chunk); |
| 155 | vm.input_buffer = case.input.clone(); |
| 156 | // Drive loop: resume on PendingHostCall by injecting the next host_results entry as a string Val. |
| 157 | let mut hr_idx = 0usize; |
| 158 | let result = loop { |
| 159 | match vm.run() { |
| 160 | Ok(v) => break Ok(v), |
| 161 | Err(VmErr::HostYield(SchedulerStatus::PendingHostCall)) if hr_idx < case.host_deliveries.len() => { |
| 162 | let d = &case.host_deliveries[hr_idx]; |
| 163 | match &d.error { |
| 164 | Some(msg) => { vm.push_host_error_by_id(d.id.into(), msg); } |
| 165 | None => { vm.push_host_result_by_id(d.id.into(), d.value.as_deref().unwrap_or("")).expect("push_host_result_by_id"); } |
| 166 | } |
| 167 | hr_idx += 1; |
| 168 | } |
| 169 | Err(VmErr::HostYield(SchedulerStatus::PendingHostCall)) if hr_idx < case.host_results.len() => { |
| 170 | vm.push_host_result(&case.host_results[hr_idx]).expect("push_host_result"); |
| 171 | hr_idx += 1; |
| 172 | } |
| 173 | Err(e) => break Err(e), |
| 174 | } |
| 175 | }; |
| 176 | match result { |
| 177 | Ok(_) => { |
| 178 | if case.error.is_some() { panic!("expected error on {:?}, got success with output {:?}", case.src, vm.output); } |
| 179 | assert_eq!(vm.output, case.output, "output mismatch on: {:?}", case.src); |
| 180 | } |
| 181 | Err(e) => match &case.error { |
| 182 | Some(expected) => assert!(e.to_string().contains(expected.as_str()), "runtime error mismatch on {:?}: got '{}', expected substring '{}'", case.src, e, expected), |
nothing calls this directly
no test coverage detected