File: test_meta.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 (366 lines) | stat: -rw-r--r-- 11,979 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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
open Test_framework
open Printf

(* Helper functions *)

let parse_meta_string content name = Meta.parse (Filepath.fp name) content name

let archive_to_string (preds, archive) =
  let pred_strs = List.map Meta.Predicate.to_string preds in
  sprintf "archive(%s) = %s" (String.concat "," pred_strs) archive

let archives_to_string archives = String.concat "; " (List.map archive_to_string archives)

(* Compat helpers for old OCaml *)
let list_find_opt pred lst =
  try Some (List.find pred lst) with Not_found -> None

(* Easy test cases *)

let test_basic_meta () =
  let content = "\
version = \"1.0.0\"\n\
description = \"A simple package\"\n\
requires = \"unix\"\n\
archive(byte) = \"simple.cma\"\n\
archive(native) = \"simple.cmxa\"\n"
  in
  let pkg = parse_meta_string content "simple" in
  let tests =
    [
      assert_equal ~expected:"1.0.0" ~actual:pkg.Meta.Pkg.version ~name:"version";
      assert_equal ~expected:"A simple package" ~actual:pkg.Meta.Pkg.description ~name:"description";
    ]
  in
  match
    list_find_opt
      (function
        | TestFailure _ -> true
        | Success -> false)
      tests
  with
  | Some (TestFailure msg) -> TestFailure msg
  | _ -> Success

let test_empty_fields () =
  let content = "\
version = \"\"\n\
description = \"\"\n\
requires = \"\"\n" in
  let pkg = parse_meta_string content "empty" in
  assert_equal ~expected:"" ~actual:pkg.Meta.Pkg.version ~name:"empty version"

let test_simple_archive () =
  let content = "\
archive(byte) = \"test.cma\"\n\
archive(native) = \"test.cmxa\"\n" in
  let pkg = parse_meta_string content "test" in
  let has_byte =
    List.exists (fun (preds, _) -> List.mem Meta.Predicate.Byte preds) pkg.Meta.Pkg.archives
  in
  let has_native =
    List.exists (fun (preds, _) -> List.mem Meta.Predicate.Native preds) pkg.Meta.Pkg.archives
  in
  if has_byte && has_native then
    Success
  else
    TestFailure "Missing byte or native archives"

let test_basic_package () =
  let content = "\
version = \"1.0\"\n\
package \"sub\" (\n\
  description = \"Subpackage\"\n\
  archive(byte) = \"sub.cma\"\n\
)\n"
  in
  let pkg = parse_meta_string content "parent" in
  match pkg.Meta.Pkg.subs with
  | [ sub ] ->
      assert_equal ~expected:"Subpackage" ~actual:sub.Meta.Pkg.description
        ~name:"subpackage description"
  | _ -> TestFailure "Expected exactly one subpackage"

(* Difficult test cases *)

let test_multiline_values () =
  let content = "\
requires =\n\
\"unix\n\
 str  \n\
 bigarray\"\n\
description = \"Multi\n\
line\n\
description\"\n" in
  try
    let _pkg = parse_meta_string content "multiline" in
    (* If parsing succeeds, check if requires were parsed *)
    Success
  with
  | Meta.MetaParseError _ ->
      TestFailure "Failed to parse multiline values (expected to fail with current parser)"
  | _ -> TestFailure "Unexpected error parsing multiline values"

let test_negated_predicates () =
  let content = "\
requires(-mt) = \"single_threaded_lib\"\n\
requires(mt) = \"threaded_lib\"\n\
archive(byte,-debug) = \"release.cma\"\n\
archive(byte,debug) = \"debug.cma\"\n"
  in
  try
    let _pkg = parse_meta_string content "negated" in
    Success
  with
  | Meta.MetaParseError (_, msg) -> TestFailure ("Negated predicates failed: " ^ msg)
  | exn -> TestFailure ("Unexpected error: " ^ Printexc.to_string exn)

let test_unknown_fields () =
  let content = "\
version = \"1.0\"\n\
library_kind = \"ppx_rewriter\"\n\
custom_field = \"custom_value\"\n\
xen_linkopts = \"-lxen\"\n\
browse_interfaces = \"Unit name: Test\"\n"
  in
  try
    let pkg = parse_meta_string content "unknown" in
    (* Check that unknown fields were stored in assignments *)
    let has_library_kind =
      List.exists (fun (field, _) -> field = "library_kind") pkg.Meta.Pkg.assignment
    in
    let has_custom_field =
      List.exists (fun (field, _) -> field = "custom_field") pkg.Meta.Pkg.assignment
    in
    if has_library_kind || has_custom_field then
      Success
    else
      TestFailure "Unknown fields not stored in assignments"
  with
  | Meta.MetaParseError (_, msg) -> TestFailure ("Unknown fields failed: " ^ msg)
  | exn -> TestFailure ("Unexpected error: " ^ Printexc.to_string exn)

let test_complex_predicates () =
  let content = "\
archive(byte,mt,mt_posix) = \"threads_posix.cma\"\n\
archive(native,mt,mt_vm) = \"threads_vm.cmxa\"\n\
requires(ppx_driver,byte) = \"ppx_lib\"\n\
warning(-mt,-debug) = \"Missing thread support\"\n"
  in
  try
    let _pkg = parse_meta_string content "complex" in
    Success
  with
  | Meta.MetaParseError (_, msg) -> TestFailure ("Complex predicates failed: " ^ msg)
  | exn -> TestFailure ("Unexpected error: " ^ Printexc.to_string exn)

let test_plugin_syntax () =
  let content = "\
plugin(byte) = \"test.cma\"\n\
plugin(native) = \"test.cmxs\"\n\
archive(byte,plugin) = \"test_plugin.cma\"\n"
  in
  try
    let _pkg = parse_meta_string content "plugin" in
    Success
  with
  | Meta.MetaParseError (_, msg) -> TestFailure ("Plugin syntax failed: " ^ msg)
  | exn -> TestFailure ("Unexpected error: " ^ Printexc.to_string exn)

let test_ppx_syntax () =
  let content = "\
ppx(-ppx_driver,-custom_ppx) = \"./ppx.exe --as-ppx\"\n\
ppx(ppx_driver) = \"ppx_driver.exe\"\n\
ppxopt(-ppx_driver) = \"-package deriving\"\n"
  in
  try
    let _pkg = parse_meta_string content "ppx" in
    Success
  with
  | Meta.MetaParseError (_, msg) -> TestFailure ("PPX syntax failed: " ^ msg)
  | exn -> TestFailure ("Unexpected error: " ^ Printexc.to_string exn)

let test_nested_packages () =
  let content = "\
version = \"1.0\"\n\
package \"level1\" (\n\
  version = \"1.1\"\n\
  package \"level2\" (\n\
    version = \"1.2\"\n\
    archive(byte) = \"deep.cma\"\n\
    package \"level3\" (\n\
      description = \"Deep nesting\"\n\
    )\n\
  )\n\
)\n"
  in
  try
    let pkg = parse_meta_string content "nested" in
    (* Navigate to level3 package *)
    match pkg.Meta.Pkg.subs with
    | [ level1 ] -> (
        match level1.Meta.Pkg.subs with
        | [ level2 ] -> (
            match level2.Meta.Pkg.subs with
            | [ level3 ] ->
                assert_equal ~expected:"Deep nesting" ~actual:level3.Meta.Pkg.description
                  ~name:"nested package"
            | _ -> TestFailure "Expected level3 package")
        | _ -> TestFailure "Expected level2 package")
    | _ -> TestFailure "Expected level1 package"
  with
  | Meta.MetaParseError (_, msg) -> TestFailure ("Nested packages failed: " ^ msg)
  | exn -> TestFailure ("Unexpected error: " ^ Printexc.to_string exn)

(* Edge cases that should break current parser *)

let test_append_operator () =
  let content = "\
archive(byte) = \"base.cma\"\n\
archive(byte) += \"extra.cma\"\n\
requires = \"unix\"\n\
requires += \"str\"\n"
  in
  try
    let pkg = parse_meta_string content "append" in
    (* Check if append archives were created *)
    let has_append = List.length pkg.Meta.Pkg.append_archives > 0 in
    if has_append then
      Success
    else
      TestFailure "Append operator not working correctly"
  with
  | Meta.MetaParseError (_, msg) -> TestFailure ("Append operator failed: " ^ msg)
  | exn -> TestFailure ("Unexpected error: " ^ Printexc.to_string exn)

let test_comments_and_whitespace () =
  let content = "\
# This is a comment\n\
version = \"1.0\"   # End of line comment\n\
\n\
# Empty lines and spacing\n\
description = \"Test\"\n\
\n\
    # Indented comment\n\
archive(byte) = \"test.cma\"\n"
  in
  try
    let pkg = parse_meta_string content "comments" in
    assert_equal ~expected:"1.0" ~actual:pkg.Meta.Pkg.version ~name:"version with comments"
  with
  | Meta.MetaParseError (_, msg) -> TestFailure ("Comments failed: " ^ msg)
  | exn -> TestFailure ("Unexpected error: " ^ Printexc.to_string exn)

let test_real_world_ppxlib () =
  (* Based on actual ppxlib META file *)
  let content = "\
version = \"0.36.0\"\n\
description = \"\"\n\
requires =\n\
\"compiler-libs.common\n\
 ocaml-compiler-libs.shadow\n\
 ppx_derivers\"\n\
archive(byte) = \"ppxlib.cma\"\n\
archive(native) = \"ppxlib.cmxa\"\n\
plugin(byte) = \"ppxlib.cma\"\n\
plugin(native) = \"ppxlib.cmxs\"\n"
  in
  try
    let _pkg = parse_meta_string content "ppxlib" in
    Success
  with
  | Meta.MetaParseError (_, msg) -> TestFailure ("Real ppxlib META failed: " ^ msg)
  | exn -> TestFailure ("Unexpected error: " ^ Printexc.to_string exn)

let test_malformed_syntax () =
  let content = "\
version = \"1.0\"\n\
broken_field_no_value =\n\
archive(byte) =\n\
requires = unix str\n"
  in
  (* This should fail to parse *)
  try
    let _ = parse_meta_string content "malformed" in
    TestFailure "Expected malformed syntax to fail, but it succeeded"
  with
  | Meta.MetaParseError _ -> Success
  | exn -> TestFailure ("Expected MetaParseError, got: " ^ Printexc.to_string exn)

let test_libname_parsing () =
  let libname_str = "ppx_stable_witness.stable_witness" in
  let libname = Libname.of_string libname_str in
  printf "Testing libname: %s\n" libname_str;
  printf "  main_name: %s\n" libname.Libname.main_name;
  printf "  subnames: [%s]\n" (String.concat "; " libname.Libname.subnames);
  printf "  full string: %s\n" (Libname.to_string libname);
  if
    libname.Libname.main_name = "ppx_stable_witness"
    && libname.Libname.subnames = [ "stable_witness" ]
  then
    Success
  else
    TestFailure "Libname parsing incorrect"

let test_ppx_stable_witness_findlib () =
  FindlibConf.load ();
  try
    let path, pkg = Meta.find_lib "ppx_stable_witness" in
    let stable_witness_libname = Libname.of_string "ppx_stable_witness.stable_witness" in
    let resolved_pkg = Meta.Pkg.find stable_witness_libname.Libname.subnames pkg in
    if resolved_pkg.Meta.Pkg.name = "stable_witness" then
      Success
    else
      TestFailure ("Expected stable_witness package, got: " ^ resolved_pkg.Meta.Pkg.name)
  with
  | Meta.LibraryNotFound name -> TestFailure ("LibraryNotFound: " ^ name)
  | exn -> TestFailure ("Unexpected error: " ^ Printexc.to_string exn)

let test_metacache_consistency () =
  FindlibConf.load ();
  try
    (* First populate cache using Metacache.get *)
    let libname = Libname.of_string "ppx_stable_witness.stable_witness" in
    let _, cached_meta = Metacache.get libname.Libname.main_name in

    (* Then try to get subpackage *)
    let cached_pkg = Meta.Pkg.find libname.Libname.subnames cached_meta in

    if cached_pkg.Meta.Pkg.name = "stable_witness" then
      Success
    else
      TestFailure ("Expected stable_witness from cache, got: " ^ cached_pkg.Meta.Pkg.name)
  with
  | Meta.LibraryNotFound name -> TestFailure ("LibraryNotFound in cache test: " ^ name)
  | Dependencies.DependencyMissing name -> TestFailure ("DependencyMissing in cache test: " ^ name)
  | exn -> TestFailure ("Cache test error: " ^ Printexc.to_string exn)

(* Test suite *)

let all_tests =
  [
    (* Easy cases *)
    make_test "basic_meta" test_basic_meta;
    make_test "empty_fields" test_empty_fields;
    make_test "simple_archive" test_simple_archive;
    make_test "basic_package" test_basic_package;
    (* Difficult cases *)
    make_test "multiline_values" test_multiline_values;
    make_test "negated_predicates" test_negated_predicates;
    make_test "unknown_fields" test_unknown_fields;
    make_test "complex_predicates" test_complex_predicates;
    make_test "plugin_syntax" test_plugin_syntax;
    make_test "ppx_syntax" test_ppx_syntax;
    make_test "nested_packages" test_nested_packages;
    (* Edge cases *)
    make_test "append_operator" test_append_operator;
    make_test "comments_and_whitespace" test_comments_and_whitespace;
    make_test "real_world_ppxlib" test_real_world_ppxlib;
    make_test "malformed_syntax" test_malformed_syntax;
    (* Real library resolution *)
    make_test "libname_parsing" test_libname_parsing;
    make_test "ppx_stable_witness_findlib" test_ppx_stable_witness_findlib;
    make_test "metacache_consistency" test_metacache_consistency;
  ]

let () = run_tests all_tests