package catala

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file clerk_runtest.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
(* This file is part of the Catala build system, a specification language for
   tax and social benefits computation rules. Copyright (C) 2022-2023 Inria,
   contributors: Louis Gesbert <louis.gesbert@inria.fr>

   Licensed under the Apache License, Version 2.0 (the "License"); you may not
   use this file except in compliance with the License. You may obtain a copy of
   the License at

   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   License for the specific language governing permissions and limitations under
   the License. *)

open Catala_utils

let run_catala_test test_flags catala_exe catala_opts file program args oc =
  let cmd_in_rd, cmd_in_wr = Unix.pipe () in
  Unix.set_close_on_exec cmd_in_wr;
  let command_oc = Unix.out_channel_of_descr cmd_in_wr in
  let catala_exe =
    (* If the exe name contains directories, make it absolute. Otherwise don't
       modify it so that it can be looked up in PATH. *)
    if String.contains catala_exe Filename.dir_sep.[0] then
      Unix.realpath catala_exe
    else catala_exe
  in
  let cmd =
    match args with
    | cmd0 :: flags ->
      let cmd0, flags =
        match String.lowercase_ascii cmd0, flags, test_flags with
        | "test-scope", scope_name :: flags, test_flags ->
          "interpret", (("--scope=" ^ scope_name) :: flags) @ test_flags
        | "test-scope", [], _ ->
          output_string oc
            "[INVALID TEST] Invalid test command syntax, the 'test-scope' \
             pseudo-command takes a scope name as first argument\n";
          "interpret", test_flags
        | cmd0, flags, [] -> cmd0, flags
        | _, _, _ :: _ ->
          raise Exit (* Skip other tests when test-flags is specified *)
      in
      Array.of_list
        ((catala_exe :: cmd0 :: catala_opts) @ flags @ ["--name=" ^ file; "-"])
    | [] -> Array.of_list ((catala_exe :: catala_opts) @ [file])
  in
  let env =
    Unix.environment ()
    |> Array.to_seq
    |> Seq.filter (fun s ->
           not
             (String.starts_with ~prefix:"OCAMLRUNPARAM=" s
             || String.starts_with ~prefix:"CATALA_" s))
    |> Seq.cons "CATALA_OUT=-"
    (* |> Seq.cons "CATALA_COLOR=never" *)
    |> Seq.cons "CATALA_PLUGINS="
    |> Array.of_seq
  in
  flush oc;
  let ocfd = Unix.descr_of_out_channel oc in
  let pid = Unix.create_process_env catala_exe cmd env cmd_in_rd ocfd ocfd in
  Unix.close cmd_in_rd;
  Seq.iter (output_string command_oc) program;
  close_out command_oc;
  let return_code =
    match Unix.waitpid [] pid with
    | _, Unix.WEXITED n -> n
    | _, (Unix.WSIGNALED n | Unix.WSTOPPED n) -> 128 - n
  in
  if return_code <> 0 then Printf.fprintf oc "#return code %d#\n" return_code

(** Directly runs the test (not using ninja, this will be called by ninja rules
    through the "clerk runtest" command) *)
let run_inline_tests catala_exe catala_opts test_flags filename =
  let module L = Surface.Lexer_common in
  let lang =
    match Clerk_scan.get_lang filename with
    | Some l -> l
    | None ->
      Message.error "Can't infer catala dialect from file extension of %a"
        File.format filename
  in
  let lines = Surface.Parser_driver.lines filename lang in
  let oc = stdout in
  let lines_until_now = Queue.create () in
  let push str =
    output_string oc str;
    Queue.add str lines_until_now
  in
  let rec run_test lines =
    match Seq.uncons lines with
    | None ->
      output_string oc
        "[INVALID TEST] Missing test command, use '$ catala <args>'\n"
    | Some ((str, L.LINE_BLOCK_END), lines) ->
      output_string oc
        "[INVALID TEST] Missing test command, use '$ catala <args>'\n";
      push str;
      process lines
    | Some ((str, _), lines) -> (
      push str;
      match Clerk_scan.test_command_args str with
      | None ->
        output_string oc
          "[INVALID TEST] Invalid test command syntax, must match '$ catala \
           <args>'\n";
        skip_block lines
      | Some args -> (
        let args = String.split_on_char ' ' args in
        let program =
          let rec drop_last seq () =
            match seq () with
            | Seq.Nil -> assert false
            | Seq.Cons (x, next) -> (
              match next () with
              | Seq.Nil -> Seq.Nil
              | Seq.Cons _ as s -> Seq.Cons (x, drop_last (fun () -> s)))
          in
          Queue.to_seq lines_until_now |> drop_last |> drop_last
        in
        match
          run_catala_test test_flags catala_exe catala_opts filename program
            args oc
        with
        | () -> skip_block lines
        | exception Exit -> process lines))
  and skip_block lines =
    match Seq.uncons lines with
    | None -> ()
    | Some ((str, L.LINE_BLOCK_END), lines) ->
      push str;
      process lines
    | Some ((str, _), lines) ->
      Queue.add str lines_until_now;
      skip_block lines
  and process lines =
    match Seq.uncons lines with
    | Some ((str, L.LINE_INLINE_TEST), lines) ->
      push str;
      run_test lines
    | Some ((str, _), lines) ->
      push str;
      process lines
    | None -> ()
  in
  process lines
OCaml

Innovation. Community. Security.