File: test_project_errors.ml

package info (click to toggle)
ocaml-obuild 0.2.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,456 kB
  • sloc: ml: 14,491; sh: 211; ansic: 34; makefile: 11
file content (228 lines) | stat: -rw-r--r-- 9,631 bytes parent folder | download
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
open Test_framework
open Test_helpers

(** Comprehensive Project parser error tests

    These tests systematically verify error handling for:
    - Required field validation
    - Field syntax errors
    - Block syntax errors
    - Value format errors
    - Semantic validation errors *)

(** {1 Required Field Tests} *)

let test_missing_name () =
  assert_project_parse_error ~content:"version: 1.0.0\nobuild-ver: 1\n"
    ~expected_msg:"Missing required field: name" ~name:"missing name field"

let test_missing_version () =
  assert_project_parse_error ~content:"name: test\nobuild-ver: 1\n"
    ~expected_msg:"Missing required field: version" ~name:"missing version field"

let test_missing_obuild_ver () =
  assert_project_parse_error ~content:"name: test\nversion: 1.0.0\n"
    ~expected_msg:"Missing required field: obuild-ver" ~name:"missing obuild-ver field"

let test_empty_name () =
  assert_project_parse_error ~content:"name:\nversion: 1.0.0\nobuild-ver: 1\n"
    ~expected_msg:"Missing required field: name" ~name:"empty name value"

(** {1 Field Value Tests} *)

let test_invalid_obuild_ver () =
  assert_project_parse_error ~content:"name: test\nversion: 1.0.0\nobuild-ver: not_a_number\n"
    ~expected_msg:"int_of_string" ~name:"invalid obuild-ver value"

let test_future_obuild_ver () =
  assert_project_parse_error ~content:"name: test\nversion: 1.0.0\nobuild-ver: 999\n"
    ~expected_msg:"Unsupported obuild version" ~name:"unsupported future obuild-ver"

let test_valid_minimal () =
  assert_project_parses ~content:minimal_project ~name:"valid minimal project"

(** {1 Block Section Tests} *)

(* Note: "library: value" is lexed as KEY_VALUE, not BLOCK (because of the colon).
   Since "library" isn't a recognized top-level field, it's silently ignored.
   No library is created, so parsing succeeds with just the project metadata. *)
let test_block_as_value () =
  assert_project_parses ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\nlibrary: some_value\n"
    ~name:"library: value is ignored (KEY_VALUE not BLOCK)"

let test_executable_block_as_value () =
  (* Same as above - "executable: value" is KEY_VALUE, silently ignored *)
  assert_project_parses
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\nexecutable: some_value\n"
    ~name:"executable: value is ignored (KEY_VALUE not BLOCK)"

(** {1 Library Block Tests} *)

let test_library_without_modules () =
  (* Library with no modules fails at validation *)
  assert_project_parse_error
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\n\nlibrary mylib\n  src-dir: src\n"
    ~expected_msg:"has no modules" ~name:"library without modules"

let test_valid_library () =
  (* Parser validates that modules exist on disk - this will fail without actual files *)
  assert_project_parse_error
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\n\nlibrary mylib\n  modules: Foo, Bar\n  src-dir: src\n"
    ~expected_msg:"ModuleNotFound" ~name:"library with non-existent modules"

(** {1 Executable Block Tests} *)

let test_executable_without_name () =
  (* New parser allows empty name but validation catches missing main file *)
  assert_project_parse_error
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\n\nexecutable\n  main-is: main.ml\n"
    ~expected_msg:"FileNotFoundInPaths" ~name:"executable without name"

let test_valid_executable () =
  (* Parser validates that main-is file exists on disk *)
  assert_project_parse_error
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\n\nexecutable myexe\n  main-is: main.ml\n  src-dir: src\n"
    ~expected_msg:"FileNotFoundInPaths" ~name:"executable with non-existent main file"

(** {1 Test Block Tests} *)

let test_test_without_name () =
  (* New parser allows empty test name; Project.check() doesn't validate tests *)
  assert_project_parses
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\n\ntest\n  main-is: test.ml\n"
    ~name:"test without name (allowed)"

let test_valid_test () =
  assert_project_parses
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\n\ntest mytest\n  main-is: test.ml\n  src-dir: tests\n"
    ~name:"valid test block"

(** {1 Field Format Tests} *)

let test_multiline_description () =
  assert_project_parses
    ~content:
      "name: test\n\
       version: 1.0.0\n\
       obuild-ver: 1\n\
       description: This is a long\n\
       description that spans\n\
       multiple lines\n"
    ~name:"multiline description"

let test_csv_authors () =
  assert_project_parses
    ~content:
      "name: test\n\
       version: 1.0.0\n\
       obuild-ver: 1\n\
       authors: Alice <alice@example.com>, Bob <bob@example.com>\n"
    ~name:"CSV authors field"

let test_single_author () =
  assert_project_parses
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\nauthor: Alice <alice@example.com>\n"
    ~name:"single author field"

(** {1 Unknown Field Handling} *)

let test_unknown_field_strict () =
  (* In strict mode, unknown fields should cause errors *)
  (* This test documents current behavior *)
  assert_project_parses ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\nunknown-field: value\n"
    ~name:"unknown field in non-strict mode"

(** {1 Indentation and Whitespace} *)

let test_empty_file () =
  assert_project_parse_error ~content:"" ~expected_msg:"Missing required field: name"
    ~name:"empty file"

let test_whitespace_only () =
  assert_project_parse_error ~content:"   \n  \n  " ~expected_msg:"Missing required field: name"
    ~name:"whitespace only"

let test_valid_with_comments () =
  assert_project_parses ~content:"# This is a comment\nname: test\nversion: 1.0.0\nobuild-ver: 1\n"
    ~name:"file with comments"

(** {1 Complex Nested Structures} *)

let test_multiple_libraries () =
  (* Parser validates module existence *)
  assert_project_parse_error
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\n\nlibrary lib1\n  modules: Foo\n  src-dir: src1\n\nlibrary lib2\n  modules: Bar\n  src-dir: src2\n"
    ~expected_msg:"ModuleNotFound" ~name:"multiple libraries with non-existent modules"

let test_mixed_targets () =
  (* Parser validates file/module existence *)
  assert_project_parse_error
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\n\nlibrary mylib\n  modules: Lib\n  src-dir: lib\n\nexecutable myexe\n  main-is: main.ml\n  src-dir: src\n  build-deps: mylib\n\ntest mytest\n  main-is: test.ml\n  src-dir: tests\n  build-deps: mylib\n"
    ~expected_msg:"ModuleNotFound" ~name:"mixed targets with non-existent files"

(** {1 Edge Cases} *)

let test_library_too_many_names () =
  (* New parser takes first name, ignores rest; fails on module validation *)
  assert_project_parse_error
    ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\n\nlibrary lib1 lib2\n  modules: Foo\n"
    ~expected_msg:"ModuleNotFound" ~name:"library with extra names (first used)"

let test_colons_vs_equals () =
  (* Test both : and = syntax *)
  assert_project_parses ~content:"name: test\nversion: 1.0.0\nobuild-ver: 1\n" ~name:"colon syntax"

(** {1 Real-World Examples} *)

let test_real_world_obuild () =
  (* Based on actual obuild.obuild structure - but modules don't exist *)
  assert_project_parse_error
    ~content:"name: example\nversion: 0.1.0\nsynopsis: Example project\nobuild-ver: 1\nlicense: BSD\nauthors: Test Author <test@example.com>\n\nlibrary example_lib\n  modules: Foo, Bar\n  src-dir: lib\n  build-deps: unix\n\nexecutable example_exe\n  main-is: main.ml\n  src-dir: src\n  build-deps: example_lib\n\ntest example_test\n  main-is: test.ml\n  src-dir: tests\n  build-deps: example_lib\n"
    ~expected_msg:"ModuleNotFound" ~name:"real-world obuild file with non-existent files"

(** {1 Test Suite} *)

let all_tests =
  [
    (* Required fields *)
    make_test "missing_name" test_missing_name;
    make_test "missing_version" test_missing_version;
    make_test "missing_obuild_ver" test_missing_obuild_ver;
    make_test "empty_name" test_empty_name;
    (* Field values *)
    make_test "invalid_obuild_ver" test_invalid_obuild_ver;
    make_test "future_obuild_ver" test_future_obuild_ver;
    make_test "valid_minimal" test_valid_minimal;
    (* Block errors *)
    make_test "block_as_value" test_block_as_value;
    make_test "executable_block_as_value" test_executable_block_as_value;
    (* Library blocks *)
    make_test "library_without_modules" test_library_without_modules;
    make_test "library_non_existent_modules" test_valid_library;
    (* Executable blocks *)
    make_test "executable_without_name" test_executable_without_name;
    make_test "executable_non_existent_main" test_valid_executable;
    (* Test blocks *)
    make_test "test_without_name" test_test_without_name;
    make_test "valid_test" test_valid_test;
    (* Field formats *)
    make_test "multiline_description" test_multiline_description;
    make_test "csv_authors" test_csv_authors;
    make_test "single_author" test_single_author;
    (* Unknown fields *)
    make_test "unknown_field_strict" test_unknown_field_strict;
    (* Whitespace *)
    make_test "empty_file" test_empty_file;
    make_test "whitespace_only" test_whitespace_only;
    make_test "valid_with_comments" test_valid_with_comments;
    (* Complex structures *)
    make_test "multiple_libraries_non_existent" test_multiple_libraries;
    make_test "mixed_targets_non_existent" test_mixed_targets;
    (* Edge cases *)
    make_test "library_too_many_names" test_library_too_many_names;
    make_test "colons_vs_equals" test_colons_vs_equals;
    (* Real-world *)
    make_test "real_world_non_existent_files" test_real_world_obuild;
  ]

let () = run_tests all_tests