1pub struct PureCase {
2 pub id: &'static str,
3 pub source: &'static str,
4 pub entry: &'static str,
5 pub input_json: &'static str,
6 pub expected_json: &'static str,
7}
8
9pub const PURE_CASES: &[PureCase] = &[
10 PureCase {
11 id: "truthiness-empty-bytes",
12 source: r"
13 fn truth(input: bytes) {
14 if input { return [!input, input ? true : false] }
15 return [!input, false]
16 }
17 ",
18 entry: "truth",
19 input_json: r#"{"$bytes":[]}"#,
20 expected_json: "[true,false]",
21 },
22 PureCase {
23 id: "truthiness-nonempty-bytes-with-zero-byte",
24 source: r"
25 fn truth(input: bytes) {
26 if input { return [!input, input ? true : false] }
27 return [!input, false]
28 }
29 ",
30 entry: "truth",
31 input_json: r#"{"$bytes":[0]}"#,
32 expected_json: "[false,true]",
33 },
34 PureCase {
35 id: "truthiness-nan-folding-and-runtime-branches",
36 source: r"
37 fn truth(input: float) {
38 let nan = input / input
39 let branch = false
40 if nan { branch = true }
41 return [!(0.0 / 0.0), !nan, (0.0 / 0.0) ? true : false,
42 nan ? true : false, (0.0 / 0.0) && true, nan && true,
43 (0.0 / 0.0) || false, nan || false, branch, !input]
44 }
45 ",
46 entry: "truth",
47 input_json: "0.0",
48 expected_json: "[false,false,true,true,true,true,true,true,true,true]",
49 },
50 PureCase {
51 id: "truthiness-duration-and-empty-values",
52 source: r"
53 fn truth(input: {zero: int, one: int, text: string, items: list<int>, record: dict<string, int>}) {
54 return [!0ms, !1ms, !input.zero, !input.one, !input.text, !input.items, !input.record,
55 !nil, !false, !true, ![], !({}), !0.0, !1.0]
56 }
57 ",
58 entry: "truth",
59 input_json: r#"{"zero":0,"one":1,"text":"","items":[],"record":{}}"#,
60 expected_json:
61 "[true,false,true,false,true,true,true,true,true,false,true,true,true,false]",
62 },
63 PureCase {
64 id: "runtime-string-repetition",
65 source: r#"
66 fn repeat(input: {text: string, count: int}) {
67 return [input.text * input.count, input.count * input.text, input.text * 0, input.text * -1, "" * input.count]
68 }
69 "#,
70 entry: "repeat",
71 input_json: r#"{"text":"λx","count":3}"#,
72 expected_json: r#"["λxλxλx","λxλxλx","","",""]"#,
73 },
74 PureCase {
75 id: "runtime-empty-string-repetition-with-huge-count",
76 source: r"fn repeat(input: {text: string, count: int}) { return input.text * input.count }",
77 entry: "repeat",
78 input_json: r#"{"text":"","count":9223372036854775807}"#,
79 expected_json: r#""""#,
80 },
81 PureCase {
82 id: "runtime-integer-exponent-promotes-beyond-u32",
83 source: r"fn power(input: int) { return [1 ** input, (-1) ** input, 2 ** -input] }",
84 entry: "power",
85 input_json: "4294967296",
86 expected_json: "[1.0,1.0,0.0]",
87 },
88 PureCase {
89 id: "runtime-float-integer-exponent-matches-folded-result",
90 source: r"
91 fn power(input: {base: float, exponent: int}) {
92 return [input.base ** input.exponent == 1.1 ** 7, input.base ** -input.exponent == 1.1 ** -7]
93 }
94 ",
95 entry: "power",
96 input_json: r#"{"base":1.1,"exponent":7}"#,
97 expected_json: "[true,true]",
98 },
99 PureCase {
100 id: "runtime-numeric-modulo-with-nonzero-divisors",
101 source: r"
102 fn modulo(input: {integer: int, floating: float}) {
103 return [input.integer % 3, input.integer % 2.5, input.floating % 3, input.floating % 2.5]
104 }
105 ",
106 entry: "modulo",
107 input_json: r#"{"integer":7,"floating":7.5}"#,
108 expected_json: "[1,2.0,1.5,0.0]",
109 },
110 PureCase {
111 id: "typed-record-pick-preserves-nil-and-copies-values",
112 source: r#"
113 type Input = {name: string, age: int, absent: nil, nested: {count: int}}
114 fn project(input: Input) -> {name: string, absent: nil, original: int, copied: int} {
115 let selected = pick(input, ["name", "absent", "nested", "name"])
116 selected.nested.count = 99
117 return {name: selected.name, absent: selected.absent, original: input.nested.count, copied: selected.nested.count}
118 }
119 "#,
120 entry: "project",
121 input_json: r#"{"name":"Ada","age":37,"absent":null,"nested":{"count":1}}"#,
122 expected_json: r#"{"name":"Ada","absent":null,"original":1,"copied":99}"#,
123 },
124 PureCase {
125 id: "typed-record-pick-runtime-keys-and-missing-values",
126 source: r"
127 fn project(input: {data: dict<string, int>, keys: list<string>}) -> dict<string, int> {
128 return pick(input.data, input.keys)
129 }
130 ",
131 entry: "project",
132 input_json: r#"{"data":{"a":1,"b":2},"keys":["b","missing","b"]}"#,
133 expected_json: r#"{"b":2}"#,
134 },
135 PureCase {
136 id: "typed-record-pick-empty",
137 source: r"fn project(input: {name: string}) -> {} { return pick(input, []) }",
138 entry: "project",
139 input_json: r#"{"name":"Ada"}"#,
140 expected_json: "{}",
141 },
142 PureCase {
143 id: "named-record-reducer",
144 source: r#"
145 type State = {count: int, history: list<int>}
146 type Event = {kind: string, amount: int}
147 type Input = {state: State, event: Event}
148
149 fn reduce(input: Input) -> State {
150 if input.event.kind == "reset" {
151 return {count: 0, history: input.state.history + [0]}
152 }
153 const next = input.state.count + input.event.amount
154 return {count: next, history: input.state.history + [next]}
155 }
156 "#,
157 entry: "reduce",
158 input_json: r#"{"state":{"count":2,"history":[1,2]},"event":{"kind":"increment","amount":3}}"#,
159 expected_json: r#"{"count":5,"history":[1,2,5]}"#,
160 },
161 PureCase {
162 id: "closure-capture-and-control-flow",
163 source: r"
164 type TransformInput = {value: int, offset: int, enabled: bool}
165 type TransformResult = {value: int, enabled: bool}
166 fn transform(input: TransformInput) -> TransformResult {
167 const offset = input.offset
168 const add_offset = {value -> value + offset}
169 if input.enabled {
170 return {value: add_offset(input.value), enabled: true}
171 }
172 return {value: input.value, enabled: false}
173 }
174 ",
175 entry: "transform",
176 input_json: r#"{"value":7,"offset":4,"enabled":true}"#,
177 expected_json: r#"{"enabled":true,"value":11}"#,
178 },
179 PureCase {
180 id: "recursive-and-sibling-functions",
181 source: r"
182 fn fib(n: int) -> int {
183 if n <= 1 { return n }
184 return fib(n - 1) + fib(n - 2)
185 }
186 fn solve(input: int) -> int { return fib(input) }
187 ",
188 entry: "solve",
189 input_json: "7",
190 expected_json: "13",
191 },
192 PureCase {
193 id: "forward-declared-capturing-function",
194 source: r"
195 fn reduce(input: int) -> int {
196 const offset = 3
197 return add_offset(input)
198 fn add_offset(value: int) -> int { return value + offset }
199 }
200 ",
201 entry: "reduce",
202 input_json: "4",
203 expected_json: "7",
204 },
205 PureCase {
206 id: "typed-rest-and-sibling-call",
207 source: r"
208 fn collect(...values: int) -> list<int> { return values }
209 fn reduce(input: int) -> list<int> { return collect(input, 2) }
210 ",
211 entry: "reduce",
212 input_json: "7",
213 expected_json: "[7,2]",
214 },
215 PureCase {
216 id: "mutating-methods-rewrite-their-place",
217 source: r#"
218 type MutateInput = {items: list<int>, meta: dict<string, int>}
219 fn mutate(input: MutateInput) {
220 let items = input.items
221 const alias = items
222 items.push(4)
223 const last = items.pop()
224 items.insert(0, 0)
225 items.extend([9])
226 items.reverse()
227 let state = {stack: [1, 2], meta: input.meta}
228 const top = state.stack.pop()
229 const removed = state.meta.remove("a")
230 return {
231 items: items,
232 alias: alias,
233 last: last,
234 top: top,
235 removed: removed,
236 state: state,
237 }
238 }
239 "#,
240 entry: "mutate",
241 input_json: r#"{"items":[1,2,3],"meta":{"a":1,"b":2}}"#,
242 expected_json: r#"{"alias":[1,2,3],"items":[9,3,2,1,0],"last":4,"removed":1,"state":{"meta":{"b":2},"stack":[1]},"top":2}"#,
243 },
244 PureCase {
245 id: "list-string-and-record-operations",
246 source: r"
247 type SummaryInput = {
248 left: list<int>,
249 right: list<int>,
250 prefix: string,
251 name: string,
252 needle: int,
253 meta: dict<string, int>,
254 }
255 fn summarize(input: SummaryInput) {
256 return {
257 items: input.left + input.right,
258 title: input.prefix + input.name,
259 found: input.left.contains(input.needle),
260 fields: input.meta.len(),
261 }
262 }
263 ",
264 entry: "summarize",
265 input_json: r#"{"left":[1,2],"right":[3],"prefix":"Harn ","name":"Kernel","needle":2,"meta":{"a":1,"b":2}}"#,
266 expected_json: r#"{"fields":2,"found":true,"items":[1,2,3],"title":"Harn Kernel"}"#,
267 },
268 PureCase {
269 id: "list-ordering-runtime-and-constant-folding",
270 source: r"
271 type ListComparisonInput = {left: list<int>, right: list<int>}
272 fn compare_lists(input: ListComparisonInput) {
273 return {
274 runtime_less: input.left < input.right,
275 runtime_equal: input.left <= input.left,
276 constant_less: [1, 2] < [1, 3],
277 constant_greater: [2] > [1, 9],
278 }
279 }
280 ",
281 entry: "compare_lists",
282 input_json: r#"{"left":[1,2],"right":[1,3]}"#,
283 expected_json: r#"{"constant_greater":true,"constant_less":true,"runtime_equal":true,"runtime_less":true}"#,
284 },
285 PureCase {
286 id: "mixed-type-equality-is-structural-not-ordering",
287 source: r"
288 fn compare(input: {value: string}) {
289 return {
290 string_is_not_nil: input.value != nil,
291 string_is_not_int: input.value != 7,
292 nil_is_nil: nil == nil,
293 numeric_cross_kind: 1 == 1.0,
294 }
295 }
296 ",
297 entry: "compare",
298 input_json: r#"{"value":"ui://portable"}"#,
299 expected_json: r#"{"nil_is_nil":true,"numeric_cross_kind":true,"string_is_not_int":true,"string_is_not_nil":true}"#,
300 },
301 PureCase {
302 id: "structured-throw-catch",
303 source: r#"
304 fn validate(input: {value: int}) {
305 try {
306 if input.value < 0 {
307 throw {code: "negative", value: input.value}
308 }
309 return {ok: true, value: input.value}
310 } catch error {
311 return {ok: false, value: error.value, code: error.code}
312 }
313 }
314 "#,
315 entry: "validate",
316 input_json: r#"{"value":-9}"#,
317 expected_json: r#"{"code":"negative","ok":false,"value":-9}"#,
318 },
319 PureCase {
320 id: "negative-index-and-slice",
321 source: r"
322 type ChoiceInput = {values: list<int>, text: string}
323 fn choose(input: ChoiceInput) {
324 return {
325 last: input.values[-1],
326 middle: input.values[-4:-1],
327 suffix: input.text[-3:],
328 }
329 }
330 ",
331 entry: "choose",
332 input_json: r#"{"values":[1,2,3,4,5],"text":"kernel"}"#,
333 expected_json: r#"{"last":5,"middle":[2,3,4],"suffix":"nel"}"#,
334 },
335 PureCase {
336 id: "module-capture-property-mutation",
337 source: r"
338 let state = {count: 0}
339 fn reduce(input: {count: int}) {
340 state.count = input.count
341 return {count: state.count}
342 }
343 ",
344 entry: "reduce",
345 input_json: r#"{"count":9}"#,
346 expected_json: r#"{"count":9}"#,
347 },
348 PureCase {
349 id: "iteration-and-copy-on-write-mutation",
350 source: r#"
351 type IterationState = {count: int, tags: list<string>}
352 type IterationInput = {state: IterationState, values: list<int>}
353 fn reduce(input: IterationInput) -> IterationState {
354 let state = input.state
355 for value in input.values {
356 state.count = state.count + value
357 }
358 state.tags[0] = "updated"
359 return state
360 }
361 "#,
362 entry: "reduce",
363 input_json: r#"{"state":{"count":1,"tags":["old"]},"values":[2,3,4]}"#,
364 expected_json: r#"{"count":10,"tags":["updated"]}"#,
365 },
366 PureCase {
367 id: "renderer-string-and-option-primitives",
368 source: r#"
369 type RenderInput = {name: string, validation?: dict<string, bool>}
370 fn reduce(input: RenderInput) {
371 const options = {allow_network: false}.merge(input.validation ?? {})
372 const name = trim(input.name)
373 return {
374 encoded: replace(json_stringify(name), "<", "\\u003c"),
375 portable: starts_with(name, "Portable"),
376 options: options,
377 }
378 }
379 "#,
380 entry: "reduce",
381 input_json: r#"{"name":" Portable <Harn> ","validation":{"allow_host_bridge":true}}"#,
382 expected_json: r#"{"encoded":"\"Portable \\u003cHarn>\"","options":{"allow_host_bridge":true,"allow_network":false},"portable":true}"#,
383 },
384 PureCase {
385 id: "artifact-regex-hash-and-secret-safety-primitives",
386 source: r#"
387 fn inspect(input: string) {
388 const captures = regex_captures("(?is)<body\\b([^>]*)>(.*?)</body>", input)
389 return {
390 body: captures[0].groups[1],
391 scripts: regex_match("(?is)<script\\b", input),
392 text: trim(regex_replace("(?is)<[^>]+>", " ", input)),
393 digest: sha256("abc"),
394 clean: len(secret_scan(input)) == 0,
395 }
396 }
397 "#,
398 entry: "inspect",
399 input_json: r#""<body class='app'>Portable <b>Harn</b></body>""#,
400 expected_json: r#"{"body":"Portable <b>Harn</b>","clean":true,"digest":"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad","scripts":null,"text":"Portable Harn"}"#,
401 },
402 PureCase {
403 id: "target-independent-path-joining",
404 source: r#"
405 fn paths(input: {root: string}) {
406 return {
407 host: path_join(input.root, ".harn", "state.json"),
408 reset: path_join("ignored", "/absolute", "file"),
409 }
410 }
411 "#,
412 entry: "paths",
413 input_json: r#"{"root":"C:\\workspace"}"#,
414 expected_json: r#"{"host":"C:/workspace/.harn/state.json","reset":"/absolute/file"}"#,
415 },
416 PureCase {
417 id: "result-enum-match-and-propagation",
418 source: r#"
419 fn divide(value: int, divisor: int) -> Result<int, string> {
420 if divisor == 0 { return Result.Err("division by zero") }
421 return Result.Ok(value / divisor)
422 }
423 fn halve(value: int, divisor: int) -> Result<int, string> {
424 const divided: int = divide(value, divisor)?
425 return Result.Ok(divided / 2)
426 }
427 fn reduce(input: int) {
428 const result = halve(12, input)
429 match result {
430 Result.Ok(value) -> { return [result.variant, result.fields, value] }
431 Result.Err(message) -> { return [result.variant, result.fields, message] }
432 }
433 }
434 "#,
435 entry: "reduce",
436 input_json: "3",
437 expected_json: r#"["Ok",[2],2]"#,
438 },
439 PureCase {
440 id: "float-param-accepts-int",
443 source: r"
444 fn takes_float(x: float) -> float { return x + 0.0 }
445 fn reduce(input: int) -> float { return takes_float(input) }
446 ",
447 entry: "reduce",
448 input_json: "3",
449 expected_json: "3.0",
450 },
451 PureCase {
452 id: "cleanup-runs-once-for-callee-errors",
456 source: r#"
457 fn fail(message: string) -> int { throw message }
458 fn relay(message: string) -> int { return fail(message) + 1 }
459 fn reduce(input: list<string>) -> {finally_runs: int, defer_runs: int, caught: list<string>} {
460 let finally_runs = 0
461 let defer_runs = 0
462 let caught = []
463 for label in input {
464 try {
465 defer { defer_runs = defer_runs + 1 }
466 try {
467 if label == "direct" {
468 fail(label)
469 } else {
470 relay(label)
471 }
472 } finally {
473 finally_runs = finally_runs + 1
474 }
475 } catch (error) {
476 caught = caught + [error]
477 }
478 }
479 return {finally_runs: finally_runs, defer_runs: defer_runs, caught: caught}
480 }
481 "#,
482 entry: "reduce",
483 input_json: r#"["direct","relayed"]"#,
484 expected_json: r#"{"finally_runs":2,"defer_runs":2,"caught":["direct","relayed"]}"#,
485 },
486 PureCase {
487 id: "try-operator-runs-pending-finally",
491 source: r#"
492 fn check(value: int) -> Result<int, string> {
493 if value < 0 { return Result.Err("negative") }
494 return Result.Ok(value)
495 }
496 fn guarded(value: int) -> Result<int, string> {
497 try {
498 const checked = check(value)?
499 return Result.Ok(checked + 1)
500 } finally {
501 if value < 0 { return Result.Err("cleanup") }
502 }
503 return Result.Err("fell through")
504 }
505 fn reduce(input: list<int>) -> list<list<any>> {
506 let rendered = []
507 for value in input {
508 const result = guarded(value)
509 rendered = rendered + [[result.variant, result.fields]]
510 }
511 return rendered
512 }
513 "#,
514 entry: "reduce",
515 input_json: "[-1,4]",
516 expected_json: r#"[["Err",["cleanup"]],["Ok",[5]]]"#,
517 },
518];
519
520pub struct RuntimeFailureCase {
526 pub id: &'static str,
527 pub source: &'static str,
528 pub entry: &'static str,
529 pub input_json: &'static str,
530 pub expected_code: &'static str,
531}
532
533pub const RUNTIME_FAILURE_CASES: &[RuntimeFailureCase] = &[
534 RuntimeFailureCase {
535 id: "string-repetition-rejects-output-over-byte-limit",
536 source: r"fn repeat(input: {text: string, count: int}) { return input.text * input.count }",
537 entry: "repeat",
538 input_json: r#"{"text":"ab","count":524289}"#,
539 expected_code: "value_byte_limit",
540 },
541 RuntimeFailureCase {
542 id: "string-repetition-rejects-overflowing-output-length",
543 source: r"fn repeat(input: {text: string, count: int}) { return input.count * input.text }",
544 entry: "repeat",
545 input_json: r#"{"text":"abc","count":9223372036854775807}"#,
546 expected_code: "value_byte_limit",
547 },
548 RuntimeFailureCase {
549 id: "integer-modulo-by-integer-zero",
550 source: r"fn modulo(input: {left: int, right: int}) { return input.left % input.right }",
551 entry: "modulo",
552 input_json: r#"{"left":7,"right":0}"#,
553 expected_code: "division_by_zero",
554 },
555 RuntimeFailureCase {
556 id: "float-modulo-by-integer-zero",
557 source: r"fn modulo(input: {left: float, right: int}) { return input.left % input.right }",
558 entry: "modulo",
559 input_json: r#"{"left":7.5,"right":0}"#,
560 expected_code: "division_by_zero",
561 },
562 RuntimeFailureCase {
563 id: "integer-modulo-by-float-zero",
564 source: r"fn modulo(input: {left: int, right: float}) { return input.left % input.right }",
565 entry: "modulo",
566 input_json: r#"{"left":7,"right":0.0}"#,
567 expected_code: "division_by_zero",
568 },
569 RuntimeFailureCase {
570 id: "float-modulo-by-negative-float-zero",
571 source: r"fn modulo(input: {left: float, right: float}) { return input.left % input.right }",
572 entry: "modulo",
573 input_json: r#"{"left":7.5,"right":-0.0}"#,
574 expected_code: "division_by_zero",
575 },
576 RuntimeFailureCase {
577 id: "declared-error-survives-transparent-rethrows",
578 source: r"
579 fn fail(input: int) -> any throws int { throw input }
580 fn reduce(input: int) -> any throws int {
581 try { return retry 2 { try* fail(input) } }
582 finally { try { throw 8 } catch (_ignored) {} }
583 }
584 ",
585 entry: "reduce",
586 input_json: "7",
587 expected_code: "harn_declared_throw",
588 },
589 RuntimeFailureCase {
590 id: "legacy-error-stays-runtime-under-declared-wrapper",
591 source: r"
592 fn fail(input: int) -> any { throw input }
593 fn reduce(input: int) -> any throws int {
594 try { return retry 2 { try* fail(input) } }
595 finally { try { throw 8 } catch (_ignored) {} }
596 }
597 ",
598 entry: "reduce",
599 input_json: "7",
600 expected_code: "harn_throw",
601 },
602 RuntimeFailureCase {
603 id: "pick-rejects-non-record-source",
604 source: r#"fn project(input: any) { return pick(input, ["name"]) }"#,
605 entry: "project",
606 input_json: "42",
607 expected_code: "builtin_type",
608 },
609 RuntimeFailureCase {
610 id: "pick-rejects-non-string-key",
611 source: r"fn project(input: {name: string, keys: list<any>}) { return pick(input, input.keys) }",
612 entry: "project",
613 input_json: r#"{"name":"Ada","keys":[1]}"#,
614 expected_code: "builtin_type",
615 },
616 RuntimeFailureCase {
617 id: "pick-rejects-wrong-arity",
618 source: r"fn project(args: list<any>) { return pick(...args) }",
619 entry: "project",
620 input_json: r#"[{"name":"Ada"}]"#,
621 expected_code: "builtin_type",
622 },
623 RuntimeFailureCase {
624 id: "int-param-rejects-float",
625 source: r"
626 fn takes_int(n: int) -> int { return n }
627 // Deliberate unchecked host boundary: the failure belongs to the
628 // portable runtime parameter contract.
629 fn reduce(input: any) { return takes_int(input) }
630 ",
631 entry: "reduce",
632 input_json: "2.5",
633 expected_code: "argument_type",
634 },
635];
636
637pub struct InvalidCase {
638 pub id: &'static str,
639 pub source: &'static str,
640 pub entry: &'static str,
641 pub expected_code: &'static str,
642}
643
644pub const INVALID_CASES: &[InvalidCase] = &[
645 InvalidCase {
646 id: "pick-unknown-field",
647 source: r#"fn project(input: {name: string}) { return pick(input, ["nmae"]) }"#,
648 entry: "project",
649 expected_code: "compile_frontend",
650 },
651 InvalidCase {
652 id: "pick-dynamic-fields-are-not-required",
653 source: r"fn project(input: {name: string}, keys: list<string>) -> {name: string} { return pick(input, keys) }",
654 entry: "project",
655 expected_code: "compile_frontend",
656 },
657 InvalidCase {
658 id: "frontend-syntax-error",
659 source: "fn reduce( {",
660 entry: "reduce",
661 expected_code: "compile_frontend",
662 },
663 InvalidCase {
664 id: "invalid-mutable-local-program",
665 source: r"
666 fn reduce(input) {
667 var value = input
668 value = value + 1
669 return value
670 }
671 ",
672 entry: "reduce",
673 expected_code: "compile_frontend",
674 },
675];
676
677#[derive(Clone, Copy)]
678pub struct PackageCompilationCase {
679 pub id: &'static str,
680 pub source: &'static str,
681 pub entry: &'static str,
682 pub expected_ok: bool,
683}
684
685impl PackageCompilationCase {
686 pub fn package(&self) -> harn_kernel::PortableSourcePackage {
687 harn_kernel::PortableSourcePackage {
688 root_source: self.source.to_string(),
689 root_imports: Vec::new(),
690 modules: Vec::new(),
691 }
692 }
693
694 pub fn compile(&self) -> Result<harn_kernel::ProgramArtifact, Vec<harn_kernel::Diagnostic>> {
695 let result = harn_kernel::compile_source_package(
696 self.package(),
697 self.entry,
698 harn_kernel::EntryKind::Function,
699 );
700 assert_eq!(
701 result.is_ok(),
702 self.expected_ok,
703 "{} compilation decision drifted",
704 self.id
705 );
706 if let Err(diagnostics) = &result {
707 assert!(
708 !diagnostics.is_empty(),
709 "{} has no failure diagnostic",
710 self.id
711 );
712 }
713 result
714 }
715}
716
717pub fn package_compilation_cases() -> impl Iterator<Item = PackageCompilationCase> {
718 INVALID_CASES
719 .iter()
720 .map(|case| PackageCompilationCase {
721 id: case.id,
722 source: case.source,
723 entry: case.entry,
724 expected_ok: false,
725 })
726 .chain(PACKAGE_COMPILATION_CASES.iter().copied())
727}
728
729pub const PACKAGE_COMPILATION_CASES: &[PackageCompilationCase] = &[
730 PackageCompilationCase {
731 id: "removed-provider-wrapper-alias",
732 source: "fn reduce(input: string) { return with_rate_limit(input) }",
733 entry: "reduce",
734 expected_ok: false,
735 },
736 PackageCompilationCase {
737 id: "retained-provider-call-alias",
738 source: "fn reduce(input: string) { return llm_call(input) }",
739 entry: "reduce",
740 expected_ok: true,
741 },
742 PackageCompilationCase {
743 id: "retained-provider-call-method",
744 source: "fn reduce(harness: Harness, input: string) { return harness.llm.call(input) }",
745 entry: "reduce",
746 expected_ok: true,
747 },
748];