Skip to main content

shape_vm/feature_tests/
definitions.rs

1//! Feature test case definitions
2//!
3//! This module contains the comprehensive list of all feature tests,
4//! organized by category. Each test covers specific grammar rules
5//! from the Shape pest grammar.
6
7use super::{FeatureCategory, FeatureTest};
8
9// ============================================================================
10// Feature Test Definitions
11// ============================================================================
12
13/// All feature tests - comprehensive coverage of language features
14/// The `covers` arrays use exact rule names from shape.pest
15pub const FEATURE_TESTS: &[FeatureTest] = &[
16    // === Literals ===
17    FeatureTest {
18        name: "number_literal",
19        covers: &["number", "integer", "literal"],
20        code: "function test() { return 42; }",
21        function: "test",
22        category: FeatureCategory::Literal,
23        requires_data: false,
24    },
25    FeatureTest {
26        name: "string_literal",
27        covers: &["string", "literal"],
28        code: r#"function test() { return "hello"; }"#,
29        function: "test",
30        category: FeatureCategory::Literal,
31        requires_data: false,
32    },
33    FeatureTest {
34        name: "boolean_literal",
35        covers: &["boolean", "literal"],
36        code: "function test() { return true; }",
37        function: "test",
38        category: FeatureCategory::Literal,
39        requires_data: false,
40    },
41    FeatureTest {
42        name: "null_literal",
43        covers: &["none_literal", "literal"],
44        code: "function test() { return null; }",
45        function: "test",
46        category: FeatureCategory::Literal,
47        requires_data: false,
48    },
49    FeatureTest {
50        name: "array_literal",
51        covers: &["array_literal", "array_elements", "array_element"],
52        code: "function test() { return [1, 2, 3]; }",
53        function: "test",
54        category: FeatureCategory::Literal,
55        requires_data: false,
56    },
57    FeatureTest {
58        name: "object_literal",
59        covers: &["object_literal", "object_fields", "object_field"],
60        code: "function test() { return { a: 1, b: 2 }; }",
61        function: "test",
62        category: FeatureCategory::Literal,
63        requires_data: false,
64    },
65    FeatureTest {
66        name: "color_literal",
67        covers: &["color", "literal"],
68        code: "function test() { return 'red'; }",
69        function: "test",
70        category: FeatureCategory::Literal,
71        requires_data: false,
72    },
73    FeatureTest {
74        name: "duration_literal",
75        covers: &["duration", "duration_unit", "simple_duration"],
76        code: "function test() { return 5m; }",
77        function: "test",
78        category: FeatureCategory::Literal,
79        requires_data: false,
80    },
81    FeatureTest {
82        name: "datetime_literal",
83        covers: &["datetime_literal", "datetime_expr"],
84        code: r#"function test() { return @"2020-01-01"; }"#,
85        function: "test",
86        category: FeatureCategory::Literal,
87        requires_data: false,
88    },
89    FeatureTest {
90        name: "time_ref",
91        covers: &["time_ref", "named_time"],
92        code: "function test() { return @today; }",
93        function: "test",
94        category: FeatureCategory::Literal,
95        requires_data: false,
96    },
97    // === Operators ===
98    FeatureTest {
99        name: "arithmetic_add_sub",
100        covers: &["additive_expr", "expression"],
101        code: "function test() { return 1 + 2 - 1; }",
102        function: "test",
103        category: FeatureCategory::Operator,
104        requires_data: false,
105    },
106    FeatureTest {
107        name: "arithmetic_mul_div_mod",
108        covers: &["multiplicative_expr"],
109        code: "function test() { return 2 * 3 / 2 % 2; }",
110        function: "test",
111        category: FeatureCategory::Operator,
112        requires_data: false,
113    },
114    FeatureTest {
115        name: "arithmetic_pow",
116        covers: &["exponential_expr"],
117        code: "function test() { return 2 ^ 3; }",
118        function: "test",
119        category: FeatureCategory::Operator,
120        requires_data: false,
121    },
122    FeatureTest {
123        name: "comparison_operators",
124        covers: &["comparison_expr", "comparison_op", "comparison_tail"],
125        code: "function test() { return (5 > 3) && (3 < 5) && (5 >= 5) && (3 <= 5); }",
126        function: "test",
127        category: FeatureCategory::Operator,
128        requires_data: false,
129    },
130    FeatureTest {
131        name: "equality_operators",
132        covers: &["comparison_op"],
133        code: "function test() { return (5 == 5) && (5 != 3); }",
134        function: "test",
135        category: FeatureCategory::Operator,
136        requires_data: false,
137    },
138    // === Fuzzy Comparison Operators ===
139    FeatureTest {
140        name: "fuzzy_equal_basic",
141        covers: &["fuzzy_comparison_tail", "fuzzy_op"],
142        code: "function test() { return 100 ~= 102; }", // Within default 2% tolerance
143        function: "test",
144        category: FeatureCategory::Operator,
145        requires_data: false,
146    },
147    FeatureTest {
148        name: "fuzzy_equal_with_absolute_tolerance",
149        covers: &[
150            "fuzzy_comparison_tail",
151            "fuzzy_op",
152            "within_clause",
153            "tolerance_spec",
154        ],
155        code: "function test() { return 100 ~= 105 within 10; }", // Absolute tolerance: |100-105| <= 10
156        function: "test",
157        category: FeatureCategory::Operator,
158        requires_data: false,
159    },
160    FeatureTest {
161        name: "fuzzy_equal_with_percentage_tolerance",
162        covers: &[
163            "fuzzy_comparison_tail",
164            "fuzzy_op",
165            "within_clause",
166            "tolerance_spec",
167        ],
168        code: "function test() { return 100 ~= 110 within 15%; }", // 15% tolerance
169        function: "test",
170        category: FeatureCategory::Operator,
171        requires_data: false,
172    },
173    FeatureTest {
174        name: "fuzzy_greater",
175        covers: &["fuzzy_comparison_tail", "fuzzy_op"],
176        code: "function test() { return 100 ~> 98; }", // Fuzzy greater with default tolerance
177        function: "test",
178        category: FeatureCategory::Operator,
179        requires_data: false,
180    },
181    FeatureTest {
182        name: "fuzzy_less",
183        covers: &["fuzzy_comparison_tail", "fuzzy_op"],
184        code: "function test() { return 98 ~< 100; }", // Fuzzy less with default tolerance
185        function: "test",
186        category: FeatureCategory::Operator,
187        requires_data: false,
188    },
189    FeatureTest {
190        name: "fuzzy_equal_false",
191        covers: &["fuzzy_comparison_tail", "fuzzy_op", "within_clause"],
192        code: "function test() { return 100 ~= 200 within 5; }", // Should be false: |100-200| > 5
193        function: "test",
194        category: FeatureCategory::Operator,
195        requires_data: false,
196    },
197    FeatureTest {
198        name: "logical_and",
199        covers: &["and_expr"],
200        code: "function test() { return true && false; }",
201        function: "test",
202        category: FeatureCategory::Operator,
203        requires_data: false,
204    },
205    FeatureTest {
206        name: "logical_or",
207        covers: &["or_expr"],
208        code: "function test() { return true || false; }",
209        function: "test",
210        category: FeatureCategory::Operator,
211        requires_data: false,
212    },
213    FeatureTest {
214        name: "logical_not",
215        covers: &["unary_expr"],
216        code: "function test() { return !false; }",
217        function: "test",
218        category: FeatureCategory::Operator,
219        requires_data: false,
220    },
221    FeatureTest {
222        name: "unary_negation",
223        covers: &["unary_expr"],
224        code: "function test() { return -5; }",
225        function: "test",
226        category: FeatureCategory::Operator,
227        requires_data: false,
228    },
229    FeatureTest {
230        name: "ternary_op",
231        covers: &["ternary_expr", "ternary_branch"],
232        code: "function test() { return 1 > 0 ? 1 : 2; }",
233        function: "test",
234        category: FeatureCategory::Operator,
235        requires_data: false,
236    },
237    FeatureTest {
238        name: "null_coalesce",
239        covers: &["null_coalesce_expr"],
240        code: "function test() { let x = null; return x ?? 5; }",
241        function: "test",
242        category: FeatureCategory::Operator,
243        requires_data: false,
244    },
245    FeatureTest {
246        name: "range_expr",
247        covers: &["range_expr"],
248        code: "function test() { return 1:5; }",
249        function: "test",
250        category: FeatureCategory::Operator,
251        requires_data: false,
252    },
253    // === Control Flow ===
254    FeatureTest {
255        name: "if_expr",
256        covers: &["if_expr"],
257        code: "function test() { return if 1 > 0 then 1 else 2; }",
258        function: "test",
259        category: FeatureCategory::ControlFlow,
260        requires_data: false,
261    },
262    FeatureTest {
263        name: "if_stmt",
264        covers: &["if_stmt", "else_clause"],
265        code: "function test() { let x = 0; if (1 > 0) { x = 1; } else { x = 2; } return x; }",
266        function: "test",
267        category: FeatureCategory::ControlFlow,
268        requires_data: false,
269    },
270    FeatureTest {
271        name: "match_expr",
272        covers: &["match_expr", "match_arm", "pattern"],
273        code: "function test() { return match 2 { 1 => 10, 2 => 20, _ => 0 }; }",
274        function: "test",
275        category: FeatureCategory::ControlFlow,
276        requires_data: false,
277    },
278    FeatureTest {
279        name: "match_with_guard",
280        covers: &["match_expr", "match_arm"],
281        code: "function test() { return match 4 { x where x > 3 => x, _ => 0 }; }",
282        function: "test",
283        category: FeatureCategory::ControlFlow,
284        requires_data: false,
285    },
286    FeatureTest {
287        name: "match_array_pattern",
288        covers: &["pattern_array"],
289        code: "function test() { return match [1, 2] { [a, b] => a + b, _ => 0 }; }",
290        function: "test",
291        category: FeatureCategory::ControlFlow,
292        requires_data: false,
293    },
294    FeatureTest {
295        name: "match_object_pattern",
296        covers: &["pattern_object", "pattern_field"],
297        code: "function test() { return match { a: 1, b: 2 } { { a: x, b: y } => x + y, _ => 0 }; }",
298        function: "test",
299        category: FeatureCategory::ControlFlow,
300        requires_data: false,
301    },
302    FeatureTest {
303        name: "match_constructor",
304        covers: &["pattern_constructor"],
305        code: "function test() { return match { value: 3 } { Ok(x) => x, Err(e) => -1 }; }",
306        function: "test",
307        category: FeatureCategory::ControlFlow,
308        requires_data: false,
309    },
310    FeatureTest {
311        name: "for_loop",
312        covers: &["for_loop", "for_clause"],
313        code: "function test() { let sum = 0; for x in [1, 2, 3] { sum = sum + x; } return sum; }",
314        function: "test",
315        category: FeatureCategory::ControlFlow,
316        requires_data: false,
317    },
318    FeatureTest {
319        name: "for_expr",
320        covers: &["for_expr", "for_expr_clause"],
321        code: "function test() { return for x in [1, 2, 3] { x * 2 }; }",
322        function: "test",
323        category: FeatureCategory::ControlFlow,
324        requires_data: false,
325    },
326    FeatureTest {
327        name: "while_loop",
328        covers: &["while_loop"],
329        code: "function test() { let i = 0; while (i < 3) { i = i + 1; } return i; }",
330        function: "test",
331        category: FeatureCategory::ControlFlow,
332        requires_data: false,
333    },
334    FeatureTest {
335        name: "while_expr",
336        covers: &["while_expr"],
337        code: "function test() { let i = 0; return while i < 3 { i = i + 1; i }; }",
338        function: "test",
339        category: FeatureCategory::ControlFlow,
340        requires_data: false,
341    },
342    FeatureTest {
343        name: "loop_expr",
344        covers: &["loop_expr", "block_expr"],
345        code: "function test() { let i = 0; return loop { i = i + 1; if i == 3 then break i; i }; }",
346        function: "test",
347        category: FeatureCategory::ControlFlow,
348        requires_data: false,
349    },
350    FeatureTest {
351        name: "break_continue",
352        covers: &["break_stmt", "break_expr", "continue_stmt", "continue_expr"],
353        code: "function test() { let sum = 0; for x in [1, 2, 3, 4] { if x == 2 { continue; } if x == 4 { break; } sum = sum + x; } return sum; }",
354        function: "test",
355        category: FeatureCategory::ControlFlow,
356        requires_data: false,
357    },
358    FeatureTest {
359        name: "return_stmt",
360        covers: &["return_stmt", "return_expr"],
361        code: "function test() { return 42; }",
362        function: "test",
363        category: FeatureCategory::ControlFlow,
364        requires_data: false,
365    },
366    // === Variables ===
367    FeatureTest {
368        name: "variable_decl",
369        covers: &["variable_decl", "var_keyword"],
370        code: "function test() { let x = 5; return x; }",
371        function: "test",
372        category: FeatureCategory::Variable,
373        requires_data: false,
374    },
375    FeatureTest {
376        name: "const_var_decl",
377        covers: &["variable_decl", "var_keyword"],
378        code: "function test() { const a = 1; var b = 2; return a + b; }",
379        function: "test",
380        category: FeatureCategory::Variable,
381        requires_data: false,
382    },
383    FeatureTest {
384        name: "assignment",
385        covers: &["assignment", "assignment_expr"],
386        code: "function test() { let x = 1; x = 2; return x; }",
387        function: "test",
388        category: FeatureCategory::Variable,
389        requires_data: false,
390    },
391    FeatureTest {
392        name: "destructure_array",
393        covers: &["destructure_pattern", "destructure_array_pattern"],
394        code: "function test() { let [a, b] = [1, 2]; return a + b; }",
395        function: "test",
396        category: FeatureCategory::Variable,
397        requires_data: false,
398    },
399    FeatureTest {
400        name: "destructure_object",
401        covers: &[
402            "destructure_pattern",
403            "destructure_object_pattern",
404            "destructure_object_pattern_field",
405        ],
406        code: "function test() { let { a, b } = { a: 1, b: 2 }; return a + b; }",
407        function: "test",
408        category: FeatureCategory::Variable,
409        requires_data: false,
410    },
411    FeatureTest {
412        name: "destructure_rest",
413        covers: &["destructure_rest_pattern"],
414        code: "function test() { let { a, ...rest } = { a: 1, b: 2 }; return rest.b; }",
415        function: "test",
416        category: FeatureCategory::Variable,
417        requires_data: false,
418    },
419    FeatureTest {
420        name: "let_expr",
421        covers: &["let_expr"],
422        code: "function test() { return let x = 5 in x + 3; }",
423        function: "test",
424        category: FeatureCategory::Variable,
425        requires_data: false,
426    },
427    // === Functions ===
428    FeatureTest {
429        name: "function_def",
430        covers: &[
431            "function_def",
432            "function_params",
433            "function_param",
434            "function_body",
435        ],
436        code: "function add(a, b) { return a + b; } function test() { return add(1, 2); }",
437        function: "test",
438        category: FeatureCategory::Function,
439        requires_data: false,
440    },
441    FeatureTest {
442        name: "arrow_function",
443        covers: &["arrow_function"],
444        code: "function test() { let f = x => x + 1; return f(5); }",
445        function: "test",
446        category: FeatureCategory::Function,
447        requires_data: false,
448    },
449    FeatureTest {
450        name: "function_expr",
451        covers: &["function_expr"],
452        code: "function test() { let f = function(x) { return x + 1; }; return f(5); }",
453        function: "test",
454        category: FeatureCategory::Function,
455        requires_data: false,
456    },
457    FeatureTest {
458        name: "closure_capture",
459        covers: &["function_expr"],
460        code: "function test() { let base = 3; let f = function(x) { return x + base; }; return f(4); }",
461        function: "test",
462        category: FeatureCategory::Function,
463        requires_data: false,
464    },
465    FeatureTest {
466        name: "function_call",
467        covers: &["function_call", "arg_list"],
468        code: "function double(x) { return x * 2; } function test() { return double(5); }",
469        function: "test",
470        category: FeatureCategory::Function,
471        requires_data: false,
472    },
473    FeatureTest {
474        name: "return_type_annotation",
475        covers: &["return_type", "type_annotation"],
476        code: "function add(a, b) -> number { return a + b; } function test() { return add(1, 2); }",
477        function: "test",
478        category: FeatureCategory::Function,
479        requires_data: false,
480    },
481    // === Collections ===
482    FeatureTest {
483        name: "index_access",
484        covers: &["index_access", "index_expr", "postfix_expr"],
485        code: "function test() { return [1, 2, 3][1]; }",
486        function: "test",
487        category: FeatureCategory::Collection,
488        requires_data: false,
489    },
490    FeatureTest {
491        name: "negative_index",
492        covers: &["index_access", "index_expr"],
493        code: "function test() { return [1, 2, 3][-1]; }",
494        function: "test",
495        category: FeatureCategory::Collection,
496        requires_data: false,
497    },
498    FeatureTest {
499        name: "slice_access",
500        covers: &["index_access", "range_expr"],
501        code: "function test() { return [1, 2, 3, 4][1:3]; }",
502        function: "test",
503        category: FeatureCategory::Collection,
504        requires_data: false,
505    },
506    FeatureTest {
507        name: "property_access",
508        covers: &["property_access", "postfix_expr"],
509        code: "function test() { return { a: 1 }.a; }",
510        function: "test",
511        category: FeatureCategory::Collection,
512        requires_data: false,
513    },
514    FeatureTest {
515        name: "optional_property_access",
516        covers: &["optional_property_access"],
517        code: "function test() { let x = null; return x?.a; }",
518        function: "test",
519        category: FeatureCategory::Collection,
520        requires_data: false,
521    },
522    FeatureTest {
523        name: "spread_element",
524        covers: &["spread_element", "array_element"],
525        code: "function test() { let xs = [1, 2]; return [0, ...xs, 3]; }",
526        function: "test",
527        category: FeatureCategory::Collection,
528        requires_data: false,
529    },
530    FeatureTest {
531        name: "object_spread",
532        covers: &["object_spread", "object_field_item"],
533        code: "function test() { let a = { x: 1 }; return { ...a, y: 2 }; }",
534        function: "test",
535        category: FeatureCategory::Collection,
536        requires_data: false,
537    },
538    FeatureTest {
539        name: "list_comprehension",
540        covers: &["list_comprehension", "comprehension_clause"],
541        code: "function test() { return [x * 2 for x in [1, 2, 3]]; }",
542        function: "test",
543        category: FeatureCategory::Collection,
544        requires_data: false,
545    },
546    FeatureTest {
547        name: "list_comprehension_filter",
548        covers: &["list_comprehension", "comprehension_clause"],
549        code: "function test() { return [x for x in [1, 2, 3, 4] if x > 2]; }",
550        function: "test",
551        category: FeatureCategory::Collection,
552        requires_data: false,
553    },
554    // === Domain-Specific ===
555    FeatureTest {
556        name: "data_ref",
557        covers: &["data_ref", "primary_expr"],
558        code: "function test() { return data[0].close; }",
559        function: "test",
560        category: FeatureCategory::Domain,
561        requires_data: true,
562    },
563    FeatureTest {
564        name: "datetime_access",
565        covers: &["datetime_access", "datetime_range"],
566        code: r#"function test() { return data[@"2020-01-01"]; }"#,
567        function: "test",
568        category: FeatureCategory::Domain,
569        requires_data: false,
570    },
571    FeatureTest {
572        name: "timeframe_expr",
573        covers: &["timeframe_expr", "timeframe"],
574        code: "function test() { return on(1h) { 2 + 2 }; }",
575        function: "test",
576        category: FeatureCategory::Domain,
577        requires_data: false,
578    },
579    FeatureTest {
580        name: "temporal_nav",
581        covers: &["temporal_nav", "back_nav", "forward_nav", "nav_amount"],
582        code: "function test() { return back(5); }",
583        function: "test",
584        category: FeatureCategory::Domain,
585        requires_data: false,
586    },
587    FeatureTest {
588        name: "pattern_name",
589        covers: &["pattern_name"],
590        code: "function test() { return pattern::mypat; }",
591        function: "test",
592        category: FeatureCategory::Domain,
593        requires_data: false,
594    },
595    FeatureTest {
596        name: "builtin_function",
597        covers: &["builtin_function"],
598        code: "function test() { return abs(-5) + sqrt(9); }",
599        function: "test",
600        category: FeatureCategory::Domain,
601        requires_data: false,
602    },
603    // Exception handling tests removed: throw() builtin removed, Shape uses Result types
604
605    // === Type System ===
606    FeatureTest {
607        name: "type_method_expr",
608        covers: &["postfix_expr"],
609        code: "function test() { return 123.type().to_string(); }",
610        function: "test",
611        category: FeatureCategory::TypeSystem,
612        requires_data: false,
613    },
614    FeatureTest {
615        name: "type_assertion",
616        covers: &["type_assertion_suffix", "postfix_expr"],
617        code: "function test() { let x = 5 as number; return x; }",
618        function: "test",
619        category: FeatureCategory::TypeSystem,
620        requires_data: false,
621    },
622    FeatureTest {
623        name: "type_annotation",
624        covers: &["type_annotation", "primary_type", "basic_type"],
625        code: "function test(x: number) -> number { return x; } function main() { return test(5); }",
626        function: "main",
627        category: FeatureCategory::TypeSystem,
628        requires_data: false,
629    },
630    FeatureTest {
631        name: "generic_type",
632        covers: &["generic_type", "type_param_list"],
633        code: "function test() { let arr: Vec<number> = [1, 2, 3]; return arr[0]; }",
634        function: "test",
635        category: FeatureCategory::TypeSystem,
636        requires_data: false,
637    },
638    FeatureTest {
639        name: "tuple_type",
640        covers: &["tuple_type"],
641        code: "function test() { let t: [number, string] = [1, \"a\"]; return t[0]; }",
642        function: "test",
643        category: FeatureCategory::TypeSystem,
644        requires_data: false,
645    },
646    FeatureTest {
647        name: "object_type",
648        covers: &["object_type", "object_type_member"],
649        code: "function test() { let obj: { a: number; b: string } = { a: 1, b: \"x\" }; return obj.a; }",
650        function: "test",
651        category: FeatureCategory::TypeSystem,
652        requires_data: false,
653    },
654    // === Module System ===
655    FeatureTest {
656        name: "ident_and_keyword",
657        covers: &["ident", "keyword"],
658        code: "function test() { let myVar = 5; return myVar; }",
659        function: "test",
660        category: FeatureCategory::Module,
661        requires_data: false,
662    },
663    // === Block and Statements ===
664    FeatureTest {
665        name: "block_expr",
666        covers: &["block_expr", "block_items", "block_item"],
667        code: "function test() { return { let x = 1; x + 2 }; }",
668        function: "test",
669        category: FeatureCategory::ControlFlow,
670        requires_data: false,
671    },
672    FeatureTest {
673        name: "expression_stmt",
674        covers: &["expression_stmt", "statement"],
675        code: "function test() { let x = 0; x = x + 1; return x; }",
676        function: "test",
677        category: FeatureCategory::Variable,
678        requires_data: false,
679    },
680];
681
682#[cfg(test)]
683mod tests {
684    use super::*;
685    use std::collections::BTreeSet;
686
687    #[test]
688    fn test_feature_tests_defined() {
689        assert!(!FEATURE_TESTS.is_empty());
690        // Should have at least one test per category
691        let categories: BTreeSet<_> = FEATURE_TESTS.iter().map(|t| t.category).collect();
692        assert!(categories.len() >= 5);
693    }
694}