open Test_harness let row customer total = Value.VRecord ("order", [ ("customer", Value.VString customer); ("total", Value.VInt total) ]) let input entries = List.mapi (fun index entry -> (index + 1, entry)) entries let run fixture_name entries = let program = infer (read_fixture fixture_name) in Interpret.program program (input entries) let run_text text entries = let program = infer text in Interpret.program program (input entries) let cases = [ ( "the example query selects and projects rows", fun () -> let result = run "expensive_order.delta" [ row "Ada" 1500; row "Bo" 900; row "Lin" 2200 ] in check_equal_string "result" "(collection (1 (tuple \"Ada\" 300)) (3 (tuple \"Lin\" 440)))" (Value.to_string result) ); ( "a filter that keeps nothing yields an empty collection", fun () -> let result = run "expensive_order.delta" [ row "Bo" 10 ] in check_equal_string "empty" "(collection )" (Value.to_string result) ); ( "an empty input yields an empty collection and zero aggregates", fun () -> check_equal_string "no rows" "(collection )" (Value.to_string (run "expensive_order.delta" [])); check_equal_int "revenue" 0 (match run "revenue.delta" [] with Value.VInt total -> total | _ -> -1); check_equal_int "count" 0 (match run "count_large.delta" [] with Value.VInt total -> total | _ -> -1) ); ( "revenue sums the taxed totals", fun () -> let result = run "revenue.delta" [ row "Ada" 1000; row "Bo" 250 ] in check_equal_string "revenue" "250" (Value.to_string result) ); ( "count_large counts the retained rows", fun () -> let result = run "count_large.delta" [ row "Ada" 1000; row "Bo" 250; row "Lin" 501 ] in check_equal_string "count" "2" (Value.to_string result) ); ( "negative values are preserved", fun () -> let result = run_text "input rows : collection int\nquery q = rows |> filter (fun r -> r < 0) |> sum\n" [ Value.VInt (-5); Value.VInt 3; Value.VInt (-7) ] in check_equal_string "sum" "-12" (Value.to_string result) ); ( "integer division truncates towards zero", fun () -> let result = run_text "input rows : collection int\nquery q = rows |> map (fun r -> r / 3) |> sum\n" [ Value.VInt 10; Value.VInt (-10) ] in check_equal_string "sum" "0" (Value.to_string result) ); ( "division by zero is a runtime error", fun () -> (try ignore (run_text "input rows : collection int\nquery q = rows |> map (fun r -> 100 / r) |> sum\n" [ Value.VInt 5; Value.VInt 0 ]); fail "division" "expected a runtime error" with Division_by_zero -> check "raised" true) ); ( "the mapping expression runs only where the filter keeps rows", fun () -> let result = run_text "input rows : collection int\nquery q = rows |> filter (fun r -> r > 10) |> map (fun r -> 1000 / r) |> sum\n" [ Value.VInt 5; Value.VInt 20 ] in check_equal_string "sum" "50" (Value.to_string result) ); ( "boolean operators short circuit", fun () -> let result = run_text "input rows : collection int\nquery q = rows |> count\n" [ Value.VInt 1 ] in check_equal_string "count" "1" (Value.to_string result); let result = run_text "input rows : collection int\nquery q = rows |> filter (fun r -> false && 1 / r > 0) |> count\n" [ Value.VInt 0 ] in check_equal_string "short circuit and" "0" (Value.to_string result); let result = run_text "input rows : collection int\nquery q = rows |> filter (fun r -> true || 1 / r > 0) |> count\n" [ Value.VInt 0 ] in check_equal_string "short circuit or" "1" (Value.to_string result) ); ( "map preserves keys and filter keeps the original keys", fun () -> let result = run_text "input rows : collection int\nquery q = rows |> filter (fun r -> r > 1) |> map (fun r -> r * 10)\n" [ Value.VInt 1; Value.VInt 2; Value.VInt 3 ] in check_equal_string "keys" "(collection (2 20) (3 30))" (Value.to_string result) ); ( "helpers are applied at their call sites", fun () -> let result = run_text "input rows : collection int\nlet scale n = n * 3\nlet offset n = scale n + 1\nquery q = rows |> map offset |> sum\n" [ Value.VInt 1; Value.VInt 2 ] in check_equal_string "sum" "11" (Value.to_string result) ); ( "records and tuples are compared structurally", fun () -> let result = run_text "type pair = { first : int; second : int }\ninput rows : collection pair\nquery q = rows |> filter (fun r -> r = { first = 1; second = 2 }) |> count\n" [ Value.VRecord ("pair", [ ("first", Value.VInt 1); ("second", Value.VInt 2) ]); Value.VRecord ("pair", [ ("first", Value.VInt 2); ("second", Value.VInt 1) ]) ] in check_equal_string "count" "1" (Value.to_string result) ); ( "a constant query ignores the input", fun () -> let result = run_text "input rows : collection int\nquery q = 6 * 7\n" [ Value.VInt 1 ] in check_equal_string "constant" "42" (Value.to_string result) ); ( "out of range arithmetic follows machine integers", fun () -> let result = run_text "input rows : collection int\nquery q = rows |> sum\n" [ Value.VInt max_int; Value.VInt 1 ] in check_equal_string "wrapped" (string_of_int min_int) (Value.to_string result) ); ]