File: test.ml

package info (click to toggle)
yojson 3.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,204 kB
  • sloc: ml: 3,436; makefile: 28
file content (145 lines) | stat: -rw-r--r-- 6,553 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
module M = Yojson_five.Safe

let yojson = Alcotest.testable M.pp M.equal

(* any error message will match the string. *)
let any_string = Alcotest.testable Fmt.string (fun _ _ -> true)

let parsing_test_case name error_msg expected input =
  Alcotest.test_case name `Quick (fun () ->
      Alcotest.(check (result yojson error_msg))
        name expected (M.from_string input))

let parsing_should_succeed name input expected =
  parsing_test_case name Alcotest.string (Ok expected) input

let parsing_should_fail name input =
  let failure = Error "<anything>" in
  parsing_test_case name any_string failure input

let parsing_should_fail_with_error name input expected =
  parsing_test_case name Alcotest.string (Error expected) input

let parsing_tests =
  [
    parsing_should_fail "Unexpected line break" {|"foo
    bar"|};
    parsing_should_succeed "true" "true" (`Bool true);
    parsing_should_succeed "false" "false" (`Bool false);
    parsing_should_succeed "null" "null" `Null;
    parsing_should_succeed "double quotes string" {|"hello world"|}
      (`String "hello world");
    parsing_should_succeed "single quotes string" {|'hello world'|}
      (`String "hello world");
    parsing_should_succeed "float" "12345.67890" (`Float 12345.67890);
    parsing_should_succeed "hex" "0x1" (`Int 0x1);
    parsing_should_succeed "hex escape sequence" {|"\x61"|} (`String "a");
    parsing_should_succeed "unicode escape sequence" {|"\u03bb"|} (`String "λ");
    parsing_should_succeed "more string escaping"
      "\"Hello \\u03bb \\x77\\x6F\\x72\\x6C\\x64\"" (`String "Hello λ world");
    parsing_should_succeed "escaping quotes" {|"\"\'"|} (`String {|"'|});
    parsing_should_succeed "more escaping quotes" {|'\"\''|} (`String {|"'|});
    parsing_should_succeed "escaping other chars" {|'\A\C\/\D\C'|}
      (`String "AC/DC");
    parsing_should_succeed "built in escapes" {|'\b\n\r\t\f\v'|}
      (`String "\b\n\r\t\012\011");
    parsing_should_succeed "null byte string" {|"\0"|} (`String "\x00");
    parsing_should_succeed "octal string" {|"\077"|} (`String "?");
    parsing_should_succeed "null and octal string" {|"\07"|} (`String "\x007");
    parsing_should_succeed "int" "1" (`Int 1);
    parsing_should_succeed "backslash escape" {|"foo\\bar"|}
      (`String {|foo\bar|});
    parsing_should_succeed "line break" "\"foo\\\nbar\"" (`String "foobar");
    parsing_should_succeed "string and comment" "\"bar\" //foo" (`String "bar");
    (* objects *)
    parsing_should_succeed "empty object" "{}" (`Assoc []);
    parsing_should_succeed "object with double quote string" {|{"foo": "bar"}|}
      (`Assoc [ ("foo", `String "bar") ]);
    parsing_should_succeed "object with single quote string" {|{'foo': 'bar'}|}
      (`Assoc [ ("foo", `String "bar") ]);
    parsing_should_succeed "object with unquoted string" {|{foo: 'bar'}|}
      (`Assoc [ ("foo", `String "bar") ]);
    parsing_should_succeed "trailing comma in object" {|{"one": 1,}|}
      (`Assoc [ ("one", `Int 1) ]);
    parsing_should_succeed "colon in key" {|{"colon:": 1}|}
      (`Assoc [ ("colon:", `Int 1) ]);
    parsing_should_fail "multiple trailing commas in object" {|{"one": 1,,}|};
    parsing_should_fail "just trailing comma in object" "{,}";
    parsing_should_fail "just trailing commas in object" "{,,,}";
    parsing_should_fail "multiple colons in object" {|{one :: 1}|};
    parsing_should_fail "newline in key" {|{new\nline: 1}|};
    (* lists *)
    parsing_should_succeed "empty list" "[]" (`List []);
    parsing_should_succeed "heterogenous list" {|[1, "2", 3.0]|}
      (`List [ `Int 1; `String "2"; `Float 3. ]);
    parsing_should_succeed "trailing comma in list" "[1, 2, 3,]"
      (`List [ `Int 1; `Int 2; `Int 3 ]);
    parsing_should_succeed "trailing comma with space list" "[1, 2, 3, ]"
      (`List [ `Int 1; `Int 2; `Int 3 ]);
    parsing_should_succeed "newlines in list" "[1, 2\n, 3]"
      (`List [ `Int 1; `Int 2; `Int 3 ]);
    parsing_should_fail "multiple trailing commas in list" "[1, 2, 3,,]";
    parsing_should_fail "just trailing comma in list" "[,]";
    parsing_should_fail "multiple trailing commas in list" "[,,,]";
    (* all together *)
    (let expected =
       `Assoc
         [
           ("unquoted", `String "and you can quote me on that");
           ("singleQuotes", `String "I can use \"double quotes\" here");
           ("lineBreaks", `String {|Look, Mom! No \n's!|});
           ("hexadecimal", `Int 0xdecaf);
           ("leadingDecimalPoint", `Float 0.8675309);
           ("andTrailing", `Float 8675309.0);
           ("positiveSign", `Int 1);
           ("trailingComma", `String "in objects");
           ("andIn", `List [ `String "arrays" ]);
           ("backwardsCompatible", `String "with JSON");
         ]
     in
     parsing_should_succeed "More elaborated"
       {|{
  // comments
  unquoted: 'and you can quote me on that',
  singleQuotes: 'I can use "double quotes" here',
 lineBreaks: "Look, Mom! \
No \\n's!",
  hexadecimal: 0xdecaf,
  leadingDecimalPoint: .8675309, andTrailing: 8675309.,
  positiveSign: +1,
  trailingComma: 'in objects', andIn: ['arrays',],
  "backwardsCompatible": "with JSON",
}|}
       expected);
    parsing_should_fail_with_error "unexpected EOF in list" "[1, 2,"
      "Line 1: Unexpected end of input";
    parsing_should_fail_with_error "unexpected EOF on different line" "\n[1, 2,"
      "Line 2: Unexpected end of input";
    parsing_should_fail_with_error "unexpected EOF in assoc" {|{"foo": 1,|}
      "Line 1: Unexpected end of input";
    parsing_should_fail_with_error "missing colon in assoc" {|{"foo"}|}
      "Line 1: Expected ':' but found '}'";
    parsing_should_fail_with_error "bad identifier in assoc" {|{[0]}|}
      "Line 1: Expected string or identifier but found '['";
  ]

let writing_test_case name input expected =
  Alcotest.test_case name `Quick (fun () ->
      Alcotest.(check string) name expected (M.to_string input))

let writing_tests =
  [
    writing_test_case "Empty object" (`Assoc []) "{}";
    writing_test_case "Empty list" (`List []) "[]";
    writing_test_case "true" (`Bool true) "true";
    writing_test_case "false" (`Bool false) "false";
    writing_test_case "null" `Null "null";
    writing_test_case "string" (`String "hello world") "\"hello world\"";
    writing_test_case "float" (`Float 12345.6789) "12345.6789";
    writing_test_case "hex" (`Int 0x1) "1";
    writing_test_case "int" (`Int 1) "1";
  ]

let () =
  Alcotest.run "JSON5"
    [ ("parsing", parsing_tests); ("writing", writing_tests) ]