1*1c12ee1eSDan Willemsen// Copyright 2019 The Go Authors. All rights reserved. 2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style 3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file. 4*1c12ee1eSDan Willemsen 5*1c12ee1eSDan Willemsenpackage json_test 6*1c12ee1eSDan Willemsen 7*1c12ee1eSDan Willemsenimport ( 8*1c12ee1eSDan Willemsen "math" 9*1c12ee1eSDan Willemsen "strings" 10*1c12ee1eSDan Willemsen "testing" 11*1c12ee1eSDan Willemsen 12*1c12ee1eSDan Willemsen "github.com/google/go-cmp/cmp" 13*1c12ee1eSDan Willemsen "github.com/google/go-cmp/cmp/cmpopts" 14*1c12ee1eSDan Willemsen 15*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/detrand" 16*1c12ee1eSDan Willemsen "google.golang.org/protobuf/internal/encoding/json" 17*1c12ee1eSDan Willemsen) 18*1c12ee1eSDan Willemsen 19*1c12ee1eSDan Willemsen// Disable detrand to enable direct comparisons on outputs. 20*1c12ee1eSDan Willemsenfunc init() { detrand.Disable() } 21*1c12ee1eSDan Willemsen 22*1c12ee1eSDan Willemsen// splitLines is a cmpopts.Option for comparing strings with line breaks. 23*1c12ee1eSDan Willemsenvar splitLines = cmpopts.AcyclicTransformer("SplitLines", func(s string) []string { 24*1c12ee1eSDan Willemsen return strings.Split(s, "\n") 25*1c12ee1eSDan Willemsen}) 26*1c12ee1eSDan Willemsen 27*1c12ee1eSDan Willemsenfunc TestEncoder(t *testing.T) { 28*1c12ee1eSDan Willemsen tests := []struct { 29*1c12ee1eSDan Willemsen desc string 30*1c12ee1eSDan Willemsen write func(*json.Encoder) 31*1c12ee1eSDan Willemsen wantOut string 32*1c12ee1eSDan Willemsen wantOutIndent string 33*1c12ee1eSDan Willemsen }{ 34*1c12ee1eSDan Willemsen { 35*1c12ee1eSDan Willemsen desc: "null", 36*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 37*1c12ee1eSDan Willemsen e.WriteNull() 38*1c12ee1eSDan Willemsen }, 39*1c12ee1eSDan Willemsen wantOut: `null`, 40*1c12ee1eSDan Willemsen }, 41*1c12ee1eSDan Willemsen { 42*1c12ee1eSDan Willemsen desc: "true", 43*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 44*1c12ee1eSDan Willemsen e.WriteBool(true) 45*1c12ee1eSDan Willemsen }, 46*1c12ee1eSDan Willemsen wantOut: `true`, 47*1c12ee1eSDan Willemsen }, 48*1c12ee1eSDan Willemsen { 49*1c12ee1eSDan Willemsen desc: "false", 50*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 51*1c12ee1eSDan Willemsen e.WriteBool(false) 52*1c12ee1eSDan Willemsen }, 53*1c12ee1eSDan Willemsen wantOut: `false`, 54*1c12ee1eSDan Willemsen }, 55*1c12ee1eSDan Willemsen { 56*1c12ee1eSDan Willemsen desc: "string", 57*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 58*1c12ee1eSDan Willemsen e.WriteString("hello world") 59*1c12ee1eSDan Willemsen }, 60*1c12ee1eSDan Willemsen wantOut: `"hello world"`, 61*1c12ee1eSDan Willemsen }, 62*1c12ee1eSDan Willemsen { 63*1c12ee1eSDan Willemsen desc: "string contains escaped characters", 64*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 65*1c12ee1eSDan Willemsen e.WriteString("\u0000\"\\/\b\f\n\r\t") 66*1c12ee1eSDan Willemsen }, 67*1c12ee1eSDan Willemsen wantOut: `"\u0000\"\\/\b\f\n\r\t"`, 68*1c12ee1eSDan Willemsen }, 69*1c12ee1eSDan Willemsen { 70*1c12ee1eSDan Willemsen desc: "float64", 71*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 72*1c12ee1eSDan Willemsen e.WriteFloat(1.0199999809265137, 64) 73*1c12ee1eSDan Willemsen }, 74*1c12ee1eSDan Willemsen wantOut: `1.0199999809265137`, 75*1c12ee1eSDan Willemsen }, 76*1c12ee1eSDan Willemsen { 77*1c12ee1eSDan Willemsen desc: "float64 max value", 78*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 79*1c12ee1eSDan Willemsen e.WriteFloat(math.MaxFloat64, 64) 80*1c12ee1eSDan Willemsen }, 81*1c12ee1eSDan Willemsen wantOut: `1.7976931348623157e+308`, 82*1c12ee1eSDan Willemsen }, 83*1c12ee1eSDan Willemsen { 84*1c12ee1eSDan Willemsen desc: "float64 min value", 85*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 86*1c12ee1eSDan Willemsen e.WriteFloat(-math.MaxFloat64, 64) 87*1c12ee1eSDan Willemsen }, 88*1c12ee1eSDan Willemsen wantOut: `-1.7976931348623157e+308`, 89*1c12ee1eSDan Willemsen }, 90*1c12ee1eSDan Willemsen { 91*1c12ee1eSDan Willemsen desc: "float64 NaN", 92*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 93*1c12ee1eSDan Willemsen e.WriteFloat(math.NaN(), 64) 94*1c12ee1eSDan Willemsen }, 95*1c12ee1eSDan Willemsen wantOut: `"NaN"`, 96*1c12ee1eSDan Willemsen }, 97*1c12ee1eSDan Willemsen { 98*1c12ee1eSDan Willemsen desc: "float64 Infinity", 99*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 100*1c12ee1eSDan Willemsen e.WriteFloat(math.Inf(+1), 64) 101*1c12ee1eSDan Willemsen }, 102*1c12ee1eSDan Willemsen wantOut: `"Infinity"`, 103*1c12ee1eSDan Willemsen }, 104*1c12ee1eSDan Willemsen { 105*1c12ee1eSDan Willemsen desc: "float64 -Infinity", 106*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 107*1c12ee1eSDan Willemsen e.WriteFloat(math.Inf(-1), 64) 108*1c12ee1eSDan Willemsen }, 109*1c12ee1eSDan Willemsen wantOut: `"-Infinity"`, 110*1c12ee1eSDan Willemsen }, 111*1c12ee1eSDan Willemsen { 112*1c12ee1eSDan Willemsen desc: "float64 negative zero", 113*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 114*1c12ee1eSDan Willemsen e.WriteFloat(math.Copysign(0, -1), 64) 115*1c12ee1eSDan Willemsen }, 116*1c12ee1eSDan Willemsen wantOut: `-0`, 117*1c12ee1eSDan Willemsen }, 118*1c12ee1eSDan Willemsen { 119*1c12ee1eSDan Willemsen desc: "float32", 120*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 121*1c12ee1eSDan Willemsen e.WriteFloat(1.02, 32) 122*1c12ee1eSDan Willemsen }, 123*1c12ee1eSDan Willemsen wantOut: `1.02`, 124*1c12ee1eSDan Willemsen }, 125*1c12ee1eSDan Willemsen { 126*1c12ee1eSDan Willemsen desc: "float32 max value", 127*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 128*1c12ee1eSDan Willemsen e.WriteFloat(math.MaxFloat32, 32) 129*1c12ee1eSDan Willemsen }, 130*1c12ee1eSDan Willemsen wantOut: `3.4028235e+38`, 131*1c12ee1eSDan Willemsen }, 132*1c12ee1eSDan Willemsen { 133*1c12ee1eSDan Willemsen desc: "float32 min value", 134*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 135*1c12ee1eSDan Willemsen e.WriteFloat(-math.MaxFloat32, 32) 136*1c12ee1eSDan Willemsen }, 137*1c12ee1eSDan Willemsen wantOut: `-3.4028235e+38`, 138*1c12ee1eSDan Willemsen }, 139*1c12ee1eSDan Willemsen { 140*1c12ee1eSDan Willemsen desc: "float32 negative zero", 141*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 142*1c12ee1eSDan Willemsen e.WriteFloat(math.Copysign(0, -1), 32) 143*1c12ee1eSDan Willemsen }, 144*1c12ee1eSDan Willemsen wantOut: `-0`, 145*1c12ee1eSDan Willemsen }, 146*1c12ee1eSDan Willemsen { 147*1c12ee1eSDan Willemsen desc: "int", 148*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 149*1c12ee1eSDan Willemsen e.WriteInt(-math.MaxInt64) 150*1c12ee1eSDan Willemsen }, 151*1c12ee1eSDan Willemsen wantOut: `-9223372036854775807`, 152*1c12ee1eSDan Willemsen }, 153*1c12ee1eSDan Willemsen { 154*1c12ee1eSDan Willemsen desc: "uint", 155*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 156*1c12ee1eSDan Willemsen e.WriteUint(math.MaxUint64) 157*1c12ee1eSDan Willemsen }, 158*1c12ee1eSDan Willemsen wantOut: `18446744073709551615`, 159*1c12ee1eSDan Willemsen }, 160*1c12ee1eSDan Willemsen { 161*1c12ee1eSDan Willemsen desc: "empty object", 162*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 163*1c12ee1eSDan Willemsen e.StartObject() 164*1c12ee1eSDan Willemsen e.EndObject() 165*1c12ee1eSDan Willemsen }, 166*1c12ee1eSDan Willemsen wantOut: `{}`, 167*1c12ee1eSDan Willemsen }, 168*1c12ee1eSDan Willemsen { 169*1c12ee1eSDan Willemsen desc: "empty array", 170*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 171*1c12ee1eSDan Willemsen e.StartArray() 172*1c12ee1eSDan Willemsen e.EndArray() 173*1c12ee1eSDan Willemsen }, 174*1c12ee1eSDan Willemsen wantOut: `[]`, 175*1c12ee1eSDan Willemsen }, 176*1c12ee1eSDan Willemsen { 177*1c12ee1eSDan Willemsen desc: "object with one member", 178*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 179*1c12ee1eSDan Willemsen e.StartObject() 180*1c12ee1eSDan Willemsen e.WriteName("hello") 181*1c12ee1eSDan Willemsen e.WriteString("world") 182*1c12ee1eSDan Willemsen e.EndObject() 183*1c12ee1eSDan Willemsen }, 184*1c12ee1eSDan Willemsen wantOut: `{"hello":"world"}`, 185*1c12ee1eSDan Willemsen wantOutIndent: `{ 186*1c12ee1eSDan Willemsen "hello": "world" 187*1c12ee1eSDan Willemsen}`, 188*1c12ee1eSDan Willemsen }, 189*1c12ee1eSDan Willemsen { 190*1c12ee1eSDan Willemsen desc: "array with one member", 191*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 192*1c12ee1eSDan Willemsen e.StartArray() 193*1c12ee1eSDan Willemsen e.WriteNull() 194*1c12ee1eSDan Willemsen e.EndArray() 195*1c12ee1eSDan Willemsen }, 196*1c12ee1eSDan Willemsen wantOut: `[null]`, 197*1c12ee1eSDan Willemsen wantOutIndent: `[ 198*1c12ee1eSDan Willemsen null 199*1c12ee1eSDan Willemsen]`, 200*1c12ee1eSDan Willemsen }, 201*1c12ee1eSDan Willemsen { 202*1c12ee1eSDan Willemsen desc: "simple object", 203*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 204*1c12ee1eSDan Willemsen e.StartObject() 205*1c12ee1eSDan Willemsen { 206*1c12ee1eSDan Willemsen e.WriteName("null") 207*1c12ee1eSDan Willemsen e.WriteNull() 208*1c12ee1eSDan Willemsen } 209*1c12ee1eSDan Willemsen { 210*1c12ee1eSDan Willemsen e.WriteName("bool") 211*1c12ee1eSDan Willemsen e.WriteBool(true) 212*1c12ee1eSDan Willemsen } 213*1c12ee1eSDan Willemsen { 214*1c12ee1eSDan Willemsen e.WriteName("string") 215*1c12ee1eSDan Willemsen e.WriteString("hello") 216*1c12ee1eSDan Willemsen } 217*1c12ee1eSDan Willemsen { 218*1c12ee1eSDan Willemsen e.WriteName("float") 219*1c12ee1eSDan Willemsen e.WriteFloat(6.28318, 64) 220*1c12ee1eSDan Willemsen } 221*1c12ee1eSDan Willemsen { 222*1c12ee1eSDan Willemsen e.WriteName("int") 223*1c12ee1eSDan Willemsen e.WriteInt(42) 224*1c12ee1eSDan Willemsen } 225*1c12ee1eSDan Willemsen { 226*1c12ee1eSDan Willemsen e.WriteName("uint") 227*1c12ee1eSDan Willemsen e.WriteUint(47) 228*1c12ee1eSDan Willemsen } 229*1c12ee1eSDan Willemsen e.EndObject() 230*1c12ee1eSDan Willemsen }, 231*1c12ee1eSDan Willemsen wantOut: `{"null":null,"bool":true,"string":"hello","float":6.28318,"int":42,"uint":47}`, 232*1c12ee1eSDan Willemsen wantOutIndent: `{ 233*1c12ee1eSDan Willemsen "null": null, 234*1c12ee1eSDan Willemsen "bool": true, 235*1c12ee1eSDan Willemsen "string": "hello", 236*1c12ee1eSDan Willemsen "float": 6.28318, 237*1c12ee1eSDan Willemsen "int": 42, 238*1c12ee1eSDan Willemsen "uint": 47 239*1c12ee1eSDan Willemsen}`, 240*1c12ee1eSDan Willemsen }, 241*1c12ee1eSDan Willemsen { 242*1c12ee1eSDan Willemsen desc: "simple array", 243*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 244*1c12ee1eSDan Willemsen e.StartArray() 245*1c12ee1eSDan Willemsen { 246*1c12ee1eSDan Willemsen e.WriteString("hello") 247*1c12ee1eSDan Willemsen e.WriteFloat(6.28318, 32) 248*1c12ee1eSDan Willemsen e.WriteInt(42) 249*1c12ee1eSDan Willemsen e.WriteUint(47) 250*1c12ee1eSDan Willemsen e.WriteBool(true) 251*1c12ee1eSDan Willemsen e.WriteNull() 252*1c12ee1eSDan Willemsen } 253*1c12ee1eSDan Willemsen e.EndArray() 254*1c12ee1eSDan Willemsen }, 255*1c12ee1eSDan Willemsen wantOut: `["hello",6.28318,42,47,true,null]`, 256*1c12ee1eSDan Willemsen wantOutIndent: `[ 257*1c12ee1eSDan Willemsen "hello", 258*1c12ee1eSDan Willemsen 6.28318, 259*1c12ee1eSDan Willemsen 42, 260*1c12ee1eSDan Willemsen 47, 261*1c12ee1eSDan Willemsen true, 262*1c12ee1eSDan Willemsen null 263*1c12ee1eSDan Willemsen]`, 264*1c12ee1eSDan Willemsen }, 265*1c12ee1eSDan Willemsen { 266*1c12ee1eSDan Willemsen desc: "fancy object", 267*1c12ee1eSDan Willemsen write: func(e *json.Encoder) { 268*1c12ee1eSDan Willemsen e.StartObject() 269*1c12ee1eSDan Willemsen { 270*1c12ee1eSDan Willemsen e.WriteName("object0") 271*1c12ee1eSDan Willemsen e.StartObject() 272*1c12ee1eSDan Willemsen e.EndObject() 273*1c12ee1eSDan Willemsen } 274*1c12ee1eSDan Willemsen { 275*1c12ee1eSDan Willemsen e.WriteName("array0") 276*1c12ee1eSDan Willemsen e.StartArray() 277*1c12ee1eSDan Willemsen e.EndArray() 278*1c12ee1eSDan Willemsen } 279*1c12ee1eSDan Willemsen { 280*1c12ee1eSDan Willemsen e.WriteName("object1") 281*1c12ee1eSDan Willemsen e.StartObject() 282*1c12ee1eSDan Willemsen { 283*1c12ee1eSDan Willemsen e.WriteName("null") 284*1c12ee1eSDan Willemsen e.WriteNull() 285*1c12ee1eSDan Willemsen } 286*1c12ee1eSDan Willemsen { 287*1c12ee1eSDan Willemsen e.WriteName("object1-1") 288*1c12ee1eSDan Willemsen e.StartObject() 289*1c12ee1eSDan Willemsen { 290*1c12ee1eSDan Willemsen e.WriteName("bool") 291*1c12ee1eSDan Willemsen e.WriteBool(false) 292*1c12ee1eSDan Willemsen } 293*1c12ee1eSDan Willemsen { 294*1c12ee1eSDan Willemsen e.WriteName("float") 295*1c12ee1eSDan Willemsen e.WriteFloat(3.14159, 32) 296*1c12ee1eSDan Willemsen } 297*1c12ee1eSDan Willemsen e.EndObject() 298*1c12ee1eSDan Willemsen } 299*1c12ee1eSDan Willemsen e.EndObject() 300*1c12ee1eSDan Willemsen } 301*1c12ee1eSDan Willemsen { 302*1c12ee1eSDan Willemsen e.WriteName("array1") 303*1c12ee1eSDan Willemsen e.StartArray() 304*1c12ee1eSDan Willemsen { 305*1c12ee1eSDan Willemsen e.WriteNull() 306*1c12ee1eSDan Willemsen e.StartObject() 307*1c12ee1eSDan Willemsen e.EndObject() 308*1c12ee1eSDan Willemsen e.StartObject() 309*1c12ee1eSDan Willemsen { 310*1c12ee1eSDan Willemsen e.WriteName("hello") 311*1c12ee1eSDan Willemsen e.WriteString("world") 312*1c12ee1eSDan Willemsen } 313*1c12ee1eSDan Willemsen { 314*1c12ee1eSDan Willemsen e.WriteName("hola") 315*1c12ee1eSDan Willemsen e.WriteString("mundo") 316*1c12ee1eSDan Willemsen } 317*1c12ee1eSDan Willemsen e.EndObject() 318*1c12ee1eSDan Willemsen e.StartArray() 319*1c12ee1eSDan Willemsen { 320*1c12ee1eSDan Willemsen e.WriteUint(1) 321*1c12ee1eSDan Willemsen e.WriteUint(0) 322*1c12ee1eSDan Willemsen e.WriteUint(1) 323*1c12ee1eSDan Willemsen } 324*1c12ee1eSDan Willemsen e.EndArray() 325*1c12ee1eSDan Willemsen } 326*1c12ee1eSDan Willemsen e.EndArray() 327*1c12ee1eSDan Willemsen } 328*1c12ee1eSDan Willemsen e.EndObject() 329*1c12ee1eSDan Willemsen }, 330*1c12ee1eSDan Willemsen wantOutIndent: `{ 331*1c12ee1eSDan Willemsen "object0": {}, 332*1c12ee1eSDan Willemsen "array0": [], 333*1c12ee1eSDan Willemsen "object1": { 334*1c12ee1eSDan Willemsen "null": null, 335*1c12ee1eSDan Willemsen "object1-1": { 336*1c12ee1eSDan Willemsen "bool": false, 337*1c12ee1eSDan Willemsen "float": 3.14159 338*1c12ee1eSDan Willemsen } 339*1c12ee1eSDan Willemsen }, 340*1c12ee1eSDan Willemsen "array1": [ 341*1c12ee1eSDan Willemsen null, 342*1c12ee1eSDan Willemsen {}, 343*1c12ee1eSDan Willemsen { 344*1c12ee1eSDan Willemsen "hello": "world", 345*1c12ee1eSDan Willemsen "hola": "mundo" 346*1c12ee1eSDan Willemsen }, 347*1c12ee1eSDan Willemsen [ 348*1c12ee1eSDan Willemsen 1, 349*1c12ee1eSDan Willemsen 0, 350*1c12ee1eSDan Willemsen 1 351*1c12ee1eSDan Willemsen ] 352*1c12ee1eSDan Willemsen ] 353*1c12ee1eSDan Willemsen}`, 354*1c12ee1eSDan Willemsen }} 355*1c12ee1eSDan Willemsen 356*1c12ee1eSDan Willemsen for _, tc := range tests { 357*1c12ee1eSDan Willemsen t.Run(tc.desc, func(t *testing.T) { 358*1c12ee1eSDan Willemsen if tc.wantOut != "" { 359*1c12ee1eSDan Willemsen enc, err := json.NewEncoder("") 360*1c12ee1eSDan Willemsen if err != nil { 361*1c12ee1eSDan Willemsen t.Fatalf("NewEncoder() returned error: %v", err) 362*1c12ee1eSDan Willemsen } 363*1c12ee1eSDan Willemsen tc.write(enc) 364*1c12ee1eSDan Willemsen got := string(enc.Bytes()) 365*1c12ee1eSDan Willemsen if got != tc.wantOut { 366*1c12ee1eSDan Willemsen t.Errorf("%s:\n<got>:\n%v\n<want>\n%v\n", tc.desc, got, tc.wantOut) 367*1c12ee1eSDan Willemsen } 368*1c12ee1eSDan Willemsen } 369*1c12ee1eSDan Willemsen if tc.wantOutIndent != "" { 370*1c12ee1eSDan Willemsen enc, err := json.NewEncoder("\t") 371*1c12ee1eSDan Willemsen if err != nil { 372*1c12ee1eSDan Willemsen t.Fatalf("NewEncoder() returned error: %v", err) 373*1c12ee1eSDan Willemsen } 374*1c12ee1eSDan Willemsen tc.write(enc) 375*1c12ee1eSDan Willemsen got, want := string(enc.Bytes()), tc.wantOutIndent 376*1c12ee1eSDan Willemsen if got != want { 377*1c12ee1eSDan Willemsen t.Errorf("%s(indent):\n<got>:\n%v\n<want>\n%v\n<diff -want +got>\n%v\n", 378*1c12ee1eSDan Willemsen tc.desc, got, want, cmp.Diff(want, got, splitLines)) 379*1c12ee1eSDan Willemsen } 380*1c12ee1eSDan Willemsen } 381*1c12ee1eSDan Willemsen }) 382*1c12ee1eSDan Willemsen } 383*1c12ee1eSDan Willemsen} 384*1c12ee1eSDan Willemsen 385*1c12ee1eSDan Willemsenfunc TestWriteStringError(t *testing.T) { 386*1c12ee1eSDan Willemsen tests := []string{"abc\xff"} 387*1c12ee1eSDan Willemsen 388*1c12ee1eSDan Willemsen for _, in := range tests { 389*1c12ee1eSDan Willemsen t.Run(in, func(t *testing.T) { 390*1c12ee1eSDan Willemsen enc, err := json.NewEncoder("") 391*1c12ee1eSDan Willemsen if err != nil { 392*1c12ee1eSDan Willemsen t.Fatalf("NewEncoder() returned error: %v", err) 393*1c12ee1eSDan Willemsen } 394*1c12ee1eSDan Willemsen if err := enc.WriteString(in); err == nil { 395*1c12ee1eSDan Willemsen t.Errorf("WriteString(%v): got nil error, want error", in) 396*1c12ee1eSDan Willemsen } 397*1c12ee1eSDan Willemsen }) 398*1c12ee1eSDan Willemsen } 399*1c12ee1eSDan Willemsen} 400