github.com/XiaoMi/Gaea@v1.2.5/parser/tidb-types/json/binary_test.go (about) 1 // Copyright 2017 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package json 15 16 import ( 17 "testing" 18 19 . "github.com/pingcap/check" 20 ) 21 22 var _ = Suite(&testJSONSuite{}) 23 24 type testJSONSuite struct{} 25 26 func TestT(t *testing.T) { 27 TestingT(t) 28 } 29 30 func (s *testJSONSuite) TestBinaryJSONMarshalUnmarshal(c *C) { 31 strs := []string{ 32 `{"a": [1, "2", {"aa": "bb"}, 4, null], "b": true, "c": null}`, 33 `{"aaaaaaaaaaa": [1, "2", {"aa": "bb"}, 4.1], "bbbbbbbbbb": true, "ccccccccc": "d"}`, 34 `[{"a": 1, "b": true}, 3, 3.5, "hello, world", null, true]`, 35 } 36 for _, str := range strs { 37 parsedBJ := mustParseBinaryFromString(c, str) 38 c.Assert(parsedBJ.String(), Equals, str) 39 } 40 } 41 42 func (s *testJSONSuite) TestBinaryJSONExtract(c *C) { 43 bj1 := mustParseBinaryFromString(c, `{"\"hello\"": "world", "a": [1, "2", {"aa": "bb"}, 4.0, {"aa": "cc"}], "b": true, "c": ["d"]}`) 44 bj2 := mustParseBinaryFromString(c, `[{"a": 1, "b": true}, 3, 3.5, "hello, world", null, true]`) 45 46 var tests = []struct { 47 bj BinaryJSON 48 pathExprStrings []string 49 expected BinaryJSON 50 found bool 51 err error 52 }{ 53 // test extract with only one path expression. 54 {bj1, []string{"$.a"}, mustParseBinaryFromString(c, `[1, "2", {"aa": "bb"}, 4.0, {"aa": "cc"}]`), true, nil}, 55 {bj2, []string{"$.a"}, mustParseBinaryFromString(c, "null"), false, nil}, 56 {bj1, []string{"$[0]"}, bj1, true, nil}, // in Extract, autowraped bj1 as an array. 57 {bj2, []string{"$[0]"}, mustParseBinaryFromString(c, `{"a": 1, "b": true}`), true, nil}, 58 {bj1, []string{"$.a[2].aa"}, mustParseBinaryFromString(c, `"bb"`), true, nil}, 59 {bj1, []string{"$.a[*].aa"}, mustParseBinaryFromString(c, `["bb", "cc"]`), true, nil}, 60 {bj1, []string{"$.*[0]"}, mustParseBinaryFromString(c, `["world", 1, true, "d"]`), true, nil}, 61 {bj1, []string{`$.a[*]."aa"`}, mustParseBinaryFromString(c, `["bb", "cc"]`), true, nil}, 62 {bj1, []string{`$."\"hello\""`}, mustParseBinaryFromString(c, `"world"`), true, nil}, 63 {bj1, []string{`$**[1]`}, mustParseBinaryFromString(c, `"2"`), true, nil}, 64 65 // test extract with multi path expressions. 66 {bj1, []string{"$.a", "$[5]"}, mustParseBinaryFromString(c, `[[1, "2", {"aa": "bb"}, 4.0, {"aa": "cc"}]]`), true, nil}, 67 {bj2, []string{"$.a", "$[0]"}, mustParseBinaryFromString(c, `[{"a": 1, "b": true}]`), true, nil}, 68 } 69 70 for _, tt := range tests { 71 var pathExprList = make([]PathExpression, 0) 72 for _, peStr := range tt.pathExprStrings { 73 pe, err := ParseJSONPathExpr(peStr) 74 c.Assert(err, IsNil) 75 pathExprList = append(pathExprList, pe) 76 } 77 78 result, found := tt.bj.Extract(pathExprList) 79 c.Assert(found, Equals, tt.found) 80 if found { 81 c.Assert(result.String(), Equals, tt.expected.String()) 82 } 83 } 84 } 85 86 func (s *testJSONSuite) TestBinaryJSONType(c *C) { 87 var tests = []struct { 88 In string 89 Out string 90 }{ 91 {`{"a": "b"}`, "OBJECT"}, 92 {`["a", "b"]`, "ARRAY"}, 93 {`3`, "INTEGER"}, 94 {`3.0`, "DOUBLE"}, 95 {`null`, "NULL"}, 96 {`true`, "BOOLEAN"}, 97 } 98 for _, tt := range tests { 99 bj := mustParseBinaryFromString(c, tt.In) 100 c.Assert(bj.Type(), Equals, tt.Out) 101 } 102 // we can't parse '9223372036854775808' to JSON::Uint64 now, 103 // because go builtin JSON parser treats that as DOUBLE. 104 c.Assert(CreateBinary(uint64(1<<63)).Type(), Equals, "UNSIGNED INTEGER") 105 } 106 107 func (s *testJSONSuite) TestBinaryJSONUnquote(c *C) { 108 var tests = []struct { 109 j string 110 unquoted string 111 }{ 112 {j: `3`, unquoted: "3"}, 113 {j: `"3"`, unquoted: "3"}, 114 {j: `"hello, \"escaped quotes\" world"`, unquoted: "hello, \"escaped quotes\" world"}, 115 {j: "\"\\u4f60\"", unquoted: "你"}, 116 {j: `true`, unquoted: "true"}, 117 {j: `null`, unquoted: "null"}, 118 {j: `{"a": [1, 2]}`, unquoted: `{"a": [1, 2]}`}, 119 {j: `"\""`, unquoted: `"`}, 120 {j: `"'"`, unquoted: `'`}, 121 {j: `"''"`, unquoted: `''`}, 122 {j: `""`, unquoted: ``}, 123 } 124 for _, tt := range tests { 125 bj := mustParseBinaryFromString(c, tt.j) 126 unquoted, err := bj.Unquote() 127 c.Assert(err, IsNil) 128 c.Assert(unquoted, Equals, tt.unquoted) 129 } 130 } 131 132 func (s *testJSONSuite) TestBinaryJSONModify(c *C) { 133 var tests = []struct { 134 base string 135 setField string 136 setValue string 137 mt ModifyType 138 expected string 139 success bool 140 }{ 141 {`null`, "$", `{}`, ModifySet, `{}`, true}, 142 {`{}`, "$.a", `3`, ModifySet, `{"a": 3}`, true}, 143 {`{"a": 3}`, "$.a", `[]`, ModifyReplace, `{"a": []}`, true}, 144 {`{"a": 3}`, "$.b", `"3"`, ModifySet, `{"a": 3, "b": "3"}`, true}, 145 {`{"a": []}`, "$.a[0]", `3`, ModifySet, `{"a": [3]}`, true}, 146 {`{"a": [3]}`, "$.a[1]", `4`, ModifyInsert, `{"a": [3, 4]}`, true}, 147 {`{"a": [3]}`, "$[0]", `4`, ModifySet, `4`, true}, 148 {`{"a": [3]}`, "$[1]", `4`, ModifySet, `[{"a": [3]}, 4]`, true}, 149 {`{"b": true}`, "$.b", `false`, ModifySet, `{"b": false}`, true}, 150 151 // nothing changed because the path is empty and we want to insert. 152 {`{}`, "$", `1`, ModifyInsert, `{}`, true}, 153 // nothing changed because the path without last leg doesn't exist. 154 {`{"a": [3, 4]}`, "$.b[1]", `3`, ModifySet, `{"a": [3, 4]}`, true}, 155 // nothing changed because the path without last leg doesn't exist. 156 {`{"a": [3, 4]}`, "$.a[2].b", `3`, ModifySet, `{"a": [3, 4]}`, true}, 157 // nothing changed because we want to insert but the full path exists. 158 {`{"a": [3, 4]}`, "$.a[0]", `30`, ModifyInsert, `{"a": [3, 4]}`, true}, 159 // nothing changed because we want to replace but the full path doesn't exist. 160 {`{"a": [3, 4]}`, "$.a[2]", `30`, ModifyReplace, `{"a": [3, 4]}`, true}, 161 162 // bad path expression. 163 {"null", "$.*", "{}", ModifySet, "null", false}, 164 {"null", "$[*]", "{}", ModifySet, "null", false}, 165 {"null", "$**.a", "{}", ModifySet, "null", false}, 166 {"null", "$**[3]", "{}", ModifySet, "null", false}, 167 } 168 for _, tt := range tests { 169 pathExpr, err := ParseJSONPathExpr(tt.setField) 170 c.Assert(err, IsNil) 171 172 base := mustParseBinaryFromString(c, tt.base) 173 value := mustParseBinaryFromString(c, tt.setValue) 174 expected := mustParseBinaryFromString(c, tt.expected) 175 obtain, err := base.Modify([]PathExpression{pathExpr}, []BinaryJSON{value}, tt.mt) 176 if tt.success { 177 c.Assert(err, IsNil) 178 c.Assert(obtain.String(), Equals, expected.String()) 179 } else { 180 c.Assert(err, NotNil) 181 } 182 } 183 } 184 185 func (s *testJSONSuite) TestBinaryJSONRemove(c *C) { 186 var tests = []struct { 187 base string 188 path string 189 expected string 190 success bool 191 }{ 192 {`null`, "$", `{}`, false}, 193 {`{"a":[3]}`, "$.a[*]", `{"a":[3]}`, false}, 194 {`{}`, "$.a", `{}`, true}, 195 {`{"a":3}`, "$.a", `{}`, true}, 196 {`{"a":1,"b":2,"c":3}`, "$.b", `{"a":1,"c":3}`, true}, 197 {`{"a":1,"b":2,"c":3}`, "$.d", `{"a":1,"b":2,"c":3}`, true}, 198 {`{"a":3}`, "$[0]", `{"a":3}`, true}, 199 {`{"a":[3,4,5]}`, "$.a[0]", `{"a":[4,5]}`, true}, 200 {`{"a":[3,4,5]}`, "$.a[1]", `{"a":[3,5]}`, true}, 201 {`{"a":[3,4,5]}`, "$.a[4]", `{"a":[3,4,5]}`, true}, 202 {`{"a": [1, 2, {"aa": "xx"}]}`, "$.a[2].aa", `{"a": [1, 2, {}]}`, true}, 203 } 204 for _, tt := range tests { 205 pathExpr, err := ParseJSONPathExpr(tt.path) 206 c.Assert(err, IsNil) 207 208 base := mustParseBinaryFromString(c, tt.base) 209 expected := mustParseBinaryFromString(c, tt.expected) 210 obtain, err := base.Remove([]PathExpression{pathExpr}) 211 if tt.success { 212 c.Assert(err, IsNil) 213 c.Assert(obtain.String(), Equals, expected.String()) 214 } else { 215 c.Assert(err, NotNil) 216 } 217 } 218 } 219 220 func (s *testJSONSuite) TestCompareBinary(c *C) { 221 jNull := mustParseBinaryFromString(c, `null`) 222 jBoolTrue := mustParseBinaryFromString(c, `true`) 223 jBoolFalse := mustParseBinaryFromString(c, `false`) 224 jIntegerLarge := CreateBinary(uint64(1 << 63)) 225 jIntegerSmall := mustParseBinaryFromString(c, `3`) 226 jStringLarge := mustParseBinaryFromString(c, `"hello, world"`) 227 jStringSmall := mustParseBinaryFromString(c, `"hello"`) 228 jArrayLarge := mustParseBinaryFromString(c, `["a", "c"]`) 229 jArraySmall := mustParseBinaryFromString(c, `["a", "b"]`) 230 jObject := mustParseBinaryFromString(c, `{"a": "b"}`) 231 232 var tests = []struct { 233 left BinaryJSON 234 right BinaryJSON 235 }{ 236 {jNull, jIntegerSmall}, 237 {jIntegerSmall, jIntegerLarge}, 238 {jIntegerLarge, jStringSmall}, 239 {jStringSmall, jStringLarge}, 240 {jStringLarge, jObject}, 241 {jObject, jArraySmall}, 242 {jArraySmall, jArrayLarge}, 243 {jArrayLarge, jBoolFalse}, 244 {jBoolFalse, jBoolTrue}, 245 } 246 for _, tt := range tests { 247 cmp := CompareBinary(tt.left, tt.right) 248 c.Assert(cmp < 0, IsTrue) 249 } 250 } 251 252 func (s *testJSONSuite) TestBinaryJSONMerge(c *C) { 253 var tests = []struct { 254 suffixes []string 255 expected string 256 }{ 257 {[]string{`{"a": 1}`, `{"b": 2}`}, `{"a": 1, "b": 2}`}, 258 {[]string{`{"a": 1}`, `{"a": 2}`}, `{"a": [1, 2]}`}, 259 {[]string{`[1]`, `[2]`}, `[1, 2]`}, 260 {[]string{`{"a": 1}`, `[1]`}, `[{"a": 1}, 1]`}, 261 {[]string{`[1]`, `{"a": 1}`}, `[1, {"a": 1}]`}, 262 {[]string{`{"a": 1}`, `4`}, `[{"a": 1}, 4]`}, 263 {[]string{`[1]`, `4`}, `[1, 4]`}, 264 {[]string{`4`, `{"a": 1}`}, `[4, {"a": 1}]`}, 265 {[]string{`4`, `1`}, `[4, 1]`}, 266 {[]string{`{}`, `[]`}, `[{}]`}, 267 } 268 269 for _, tt := range tests { 270 suffixes := make([]BinaryJSON, 0, len(tt.suffixes)+1) 271 for _, s := range tt.suffixes { 272 suffixes = append(suffixes, mustParseBinaryFromString(c, s)) 273 } 274 result := MergeBinary(suffixes) 275 cmp := CompareBinary(result, mustParseBinaryFromString(c, tt.expected)) 276 c.Assert(cmp, Equals, 0) 277 } 278 } 279 280 func mustParseBinaryFromString(c *C, s string) BinaryJSON { 281 bj, err := ParseBinaryFromString(s) 282 c.Assert(err, IsNil) 283 return bj 284 } 285 286 const benchStr = `{"a":[1,"2",{"aa":"bb"},4,null],"b":true,"c":null}` 287 288 func BenchmarkBinaryMarshal(b *testing.B) { 289 b.ReportAllocs() 290 b.SetBytes(int64(len(benchStr))) 291 bj, _ := ParseBinaryFromString(benchStr) 292 for i := 0; i < b.N; i++ { 293 bj.MarshalJSON() 294 } 295 } 296 297 func (s *testJSONSuite) TestBinaryJSONContains(c *C) { 298 var tests = []struct { 299 input string 300 target string 301 expected bool 302 }{ 303 {`{}`, `{}`, true}, 304 {`{"a":1}`, `{}`, true}, 305 {`{"a":1}`, `1`, false}, 306 {`{"a":[1]}`, `[1]`, false}, 307 {`{"b":2, "c":3}`, `{"c":3}`, true}, 308 {`1`, `1`, true}, 309 {`[1]`, `1`, true}, 310 {`[1,2]`, `[1]`, true}, 311 {`[1,2]`, `[1,3]`, false}, 312 {`[1,2]`, `["1"]`, false}, 313 {`[1,2,[1,3]]`, `[1,3]`, true}, 314 {`[1,2,[1,[5,[3]]]]`, `[1,3]`, true}, 315 {`[1,2,[1,[5,{"a":[2,3]}]]]`, `[1,{"a":[3]}]`, true}, 316 {`[{"a":1}]`, `{"a":1}`, true}, 317 {`[{"a":1,"b":2}]`, `{"a":1}`, true}, 318 {`[{"a":{"a":1},"b":2}]`, `{"a":1}`, false}, 319 } 320 321 for _, tt := range tests { 322 obj := mustParseBinaryFromString(c, tt.input) 323 target := mustParseBinaryFromString(c, tt.target) 324 c.Assert(ContainsBinary(obj, target), Equals, tt.expected) 325 } 326 } 327 328 func (s *testJSONSuite) TestBinaryJSONDepth(c *C) { 329 var tests = []struct { 330 input string 331 expected int 332 }{ 333 {`{}`, 1}, 334 {`[]`, 1}, 335 {`true`, 1}, 336 {`[10, 20]`, 2}, 337 {`[[], {}]`, 2}, 338 {`[10, {"a": 20}]`, 3}, 339 {`{"Person": {"Name": "Homer", "Age": 39, "Hobbies": ["Eating", "Sleeping"]} }`, 4}, 340 } 341 342 for _, tt := range tests { 343 obj := mustParseBinaryFromString(c, tt.input) 344 c.Assert(obj.GetElemDepth(), Equals, tt.expected) 345 } 346 }