1package toml 2 3import ( 4 "fmt" 5 "io/ioutil" 6 "sort" 7 "strings" 8 "testing" 9 "time" 10) 11 12type queryTestNode struct { 13 value interface{} 14 position Position 15} 16 17func valueString(root interface{}) string { 18 result := "" //fmt.Sprintf("%T:", root) 19 switch node := root.(type) { 20 case *tomlValue: 21 return valueString(node.value) 22 case *QueryResult: 23 items := []string{} 24 for i, v := range node.Values() { 25 items = append(items, fmt.Sprintf("%s:%s", 26 node.Positions()[i].String(), valueString(v))) 27 } 28 sort.Strings(items) 29 result = "[" + strings.Join(items, ", ") + "]" 30 case queryTestNode: 31 result = fmt.Sprintf("%s:%s", 32 node.position.String(), valueString(node.value)) 33 case []interface{}: 34 items := []string{} 35 for _, v := range node { 36 items = append(items, valueString(v)) 37 } 38 sort.Strings(items) 39 result = "[" + strings.Join(items, ", ") + "]" 40 case *TomlTree: 41 // workaround for unreliable map key ordering 42 items := []string{} 43 for _, k := range node.Keys() { 44 v := node.GetPath([]string{k}) 45 items = append(items, k+":"+valueString(v)) 46 } 47 sort.Strings(items) 48 result = "{" + strings.Join(items, ", ") + "}" 49 case map[string]interface{}: 50 // workaround for unreliable map key ordering 51 items := []string{} 52 for k, v := range node { 53 items = append(items, k+":"+valueString(v)) 54 } 55 sort.Strings(items) 56 result = "{" + strings.Join(items, ", ") + "}" 57 case int64: 58 result += fmt.Sprintf("%d", node) 59 case string: 60 result += "'" + node + "'" 61 case float64: 62 result += fmt.Sprintf("%f", node) 63 case bool: 64 result += fmt.Sprintf("%t", node) 65 case time.Time: 66 result += fmt.Sprintf("'%v'", node) 67 } 68 return result 69} 70 71func assertValue(t *testing.T, result, ref interface{}) { 72 pathStr := valueString(result) 73 refStr := valueString(ref) 74 if pathStr != refStr { 75 t.Errorf("values do not match") 76 t.Log("test:", pathStr) 77 t.Log("ref: ", refStr) 78 } 79} 80 81func assertQueryPositions(t *testing.T, toml, query string, ref []interface{}) { 82 tree, err := Load(toml) 83 if err != nil { 84 t.Errorf("Non-nil toml parse error: %v", err) 85 return 86 } 87 q, err := CompileQuery(query) 88 if err != nil { 89 t.Error(err) 90 return 91 } 92 results := q.Execute(tree) 93 assertValue(t, results, ref) 94} 95 96func TestQueryRoot(t *testing.T) { 97 assertQueryPositions(t, 98 "a = 42", 99 "$", 100 []interface{}{ 101 queryTestNode{ 102 map[string]interface{}{ 103 "a": int64(42), 104 }, Position{1, 1}, 105 }, 106 }) 107} 108 109func TestQueryKey(t *testing.T) { 110 assertQueryPositions(t, 111 "[foo]\na = 42", 112 "$.foo.a", 113 []interface{}{ 114 queryTestNode{ 115 int64(42), Position{2, 1}, 116 }, 117 }) 118} 119 120func TestQueryKeyString(t *testing.T) { 121 assertQueryPositions(t, 122 "[foo]\na = 42", 123 "$.foo['a']", 124 []interface{}{ 125 queryTestNode{ 126 int64(42), Position{2, 1}, 127 }, 128 }) 129} 130 131func TestQueryIndex(t *testing.T) { 132 assertQueryPositions(t, 133 "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", 134 "$.foo.a[5]", 135 []interface{}{ 136 queryTestNode{ 137 int64(6), Position{2, 1}, 138 }, 139 }) 140} 141 142func TestQuerySliceRange(t *testing.T) { 143 assertQueryPositions(t, 144 "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", 145 "$.foo.a[0:5]", 146 []interface{}{ 147 queryTestNode{ 148 int64(1), Position{2, 1}, 149 }, 150 queryTestNode{ 151 int64(2), Position{2, 1}, 152 }, 153 queryTestNode{ 154 int64(3), Position{2, 1}, 155 }, 156 queryTestNode{ 157 int64(4), Position{2, 1}, 158 }, 159 queryTestNode{ 160 int64(5), Position{2, 1}, 161 }, 162 }) 163} 164 165func TestQuerySliceStep(t *testing.T) { 166 assertQueryPositions(t, 167 "[foo]\na = [1,2,3,4,5,6,7,8,9,0]", 168 "$.foo.a[0:5:2]", 169 []interface{}{ 170 queryTestNode{ 171 int64(1), Position{2, 1}, 172 }, 173 queryTestNode{ 174 int64(3), Position{2, 1}, 175 }, 176 queryTestNode{ 177 int64(5), Position{2, 1}, 178 }, 179 }) 180} 181 182func TestQueryAny(t *testing.T) { 183 assertQueryPositions(t, 184 "[foo.bar]\na=1\nb=2\n[foo.baz]\na=3\nb=4", 185 "$.foo.*", 186 []interface{}{ 187 queryTestNode{ 188 map[string]interface{}{ 189 "a": int64(1), 190 "b": int64(2), 191 }, Position{1, 1}, 192 }, 193 queryTestNode{ 194 map[string]interface{}{ 195 "a": int64(3), 196 "b": int64(4), 197 }, Position{4, 1}, 198 }, 199 }) 200} 201func TestQueryUnionSimple(t *testing.T) { 202 assertQueryPositions(t, 203 "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", 204 "$.*[bar,foo]", 205 []interface{}{ 206 queryTestNode{ 207 map[string]interface{}{ 208 "a": int64(1), 209 "b": int64(2), 210 }, Position{1, 1}, 211 }, 212 queryTestNode{ 213 map[string]interface{}{ 214 "a": int64(3), 215 "b": int64(4), 216 }, Position{4, 1}, 217 }, 218 queryTestNode{ 219 map[string]interface{}{ 220 "a": int64(5), 221 "b": int64(6), 222 }, Position{7, 1}, 223 }, 224 }) 225} 226 227func TestQueryRecursionAll(t *testing.T) { 228 assertQueryPositions(t, 229 "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", 230 "$..*", 231 []interface{}{ 232 queryTestNode{ 233 map[string]interface{}{ 234 "foo": map[string]interface{}{ 235 "bar": map[string]interface{}{ 236 "a": int64(1), 237 "b": int64(2), 238 }, 239 }, 240 "baz": map[string]interface{}{ 241 "foo": map[string]interface{}{ 242 "a": int64(3), 243 "b": int64(4), 244 }, 245 }, 246 "gorf": map[string]interface{}{ 247 "foo": map[string]interface{}{ 248 "a": int64(5), 249 "b": int64(6), 250 }, 251 }, 252 }, Position{1, 1}, 253 }, 254 queryTestNode{ 255 map[string]interface{}{ 256 "bar": map[string]interface{}{ 257 "a": int64(1), 258 "b": int64(2), 259 }, 260 }, Position{1, 1}, 261 }, 262 queryTestNode{ 263 map[string]interface{}{ 264 "a": int64(1), 265 "b": int64(2), 266 }, Position{1, 1}, 267 }, 268 queryTestNode{ 269 int64(1), Position{2, 1}, 270 }, 271 queryTestNode{ 272 int64(2), Position{3, 1}, 273 }, 274 queryTestNode{ 275 map[string]interface{}{ 276 "foo": map[string]interface{}{ 277 "a": int64(3), 278 "b": int64(4), 279 }, 280 }, Position{4, 1}, 281 }, 282 queryTestNode{ 283 map[string]interface{}{ 284 "a": int64(3), 285 "b": int64(4), 286 }, Position{4, 1}, 287 }, 288 queryTestNode{ 289 int64(3), Position{5, 1}, 290 }, 291 queryTestNode{ 292 int64(4), Position{6, 1}, 293 }, 294 queryTestNode{ 295 map[string]interface{}{ 296 "foo": map[string]interface{}{ 297 "a": int64(5), 298 "b": int64(6), 299 }, 300 }, Position{7, 1}, 301 }, 302 queryTestNode{ 303 map[string]interface{}{ 304 "a": int64(5), 305 "b": int64(6), 306 }, Position{7, 1}, 307 }, 308 queryTestNode{ 309 int64(5), Position{8, 1}, 310 }, 311 queryTestNode{ 312 int64(6), Position{9, 1}, 313 }, 314 }) 315} 316 317func TestQueryRecursionUnionSimple(t *testing.T) { 318 assertQueryPositions(t, 319 "[foo.bar]\na=1\nb=2\n[baz.foo]\na=3\nb=4\n[gorf.foo]\na=5\nb=6", 320 "$..['foo','bar']", 321 []interface{}{ 322 queryTestNode{ 323 map[string]interface{}{ 324 "bar": map[string]interface{}{ 325 "a": int64(1), 326 "b": int64(2), 327 }, 328 }, Position{1, 1}, 329 }, 330 queryTestNode{ 331 map[string]interface{}{ 332 "a": int64(3), 333 "b": int64(4), 334 }, Position{4, 1}, 335 }, 336 queryTestNode{ 337 map[string]interface{}{ 338 "a": int64(1), 339 "b": int64(2), 340 }, Position{1, 1}, 341 }, 342 queryTestNode{ 343 map[string]interface{}{ 344 "a": int64(5), 345 "b": int64(6), 346 }, Position{7, 1}, 347 }, 348 }) 349} 350 351func TestQueryFilterFn(t *testing.T) { 352 buff, err := ioutil.ReadFile("example.toml") 353 if err != nil { 354 t.Error(err) 355 return 356 } 357 358 assertQueryPositions(t, string(buff), 359 "$..[?(int)]", 360 []interface{}{ 361 queryTestNode{ 362 int64(8001), Position{13, 1}, 363 }, 364 queryTestNode{ 365 int64(8001), Position{13, 1}, 366 }, 367 queryTestNode{ 368 int64(8002), Position{13, 1}, 369 }, 370 queryTestNode{ 371 int64(5000), Position{14, 1}, 372 }, 373 }) 374 375 assertQueryPositions(t, string(buff), 376 "$..[?(string)]", 377 []interface{}{ 378 queryTestNode{ 379 "TOML Example", Position{3, 1}, 380 }, 381 queryTestNode{ 382 "Tom Preston-Werner", Position{6, 1}, 383 }, 384 queryTestNode{ 385 "GitHub", Position{7, 1}, 386 }, 387 queryTestNode{ 388 "GitHub Cofounder & CEO\nLikes tater tots and beer.", 389 Position{8, 1}, 390 }, 391 queryTestNode{ 392 "192.168.1.1", Position{12, 1}, 393 }, 394 queryTestNode{ 395 "10.0.0.1", Position{21, 3}, 396 }, 397 queryTestNode{ 398 "eqdc10", Position{22, 3}, 399 }, 400 queryTestNode{ 401 "10.0.0.2", Position{25, 3}, 402 }, 403 queryTestNode{ 404 "eqdc10", Position{26, 3}, 405 }, 406 }) 407 408 assertQueryPositions(t, string(buff), 409 "$..[?(float)]", 410 []interface{}{ 411 // no float values in document 412 }) 413 414 tv, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") 415 assertQueryPositions(t, string(buff), 416 "$..[?(tree)]", 417 []interface{}{ 418 queryTestNode{ 419 map[string]interface{}{ 420 "name": "Tom Preston-Werner", 421 "organization": "GitHub", 422 "bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.", 423 "dob": tv, 424 }, Position{5, 1}, 425 }, 426 queryTestNode{ 427 map[string]interface{}{ 428 "server": "192.168.1.1", 429 "ports": []interface{}{int64(8001), int64(8001), int64(8002)}, 430 "connection_max": int64(5000), 431 "enabled": true, 432 }, Position{11, 1}, 433 }, 434 queryTestNode{ 435 map[string]interface{}{ 436 "alpha": map[string]interface{}{ 437 "ip": "10.0.0.1", 438 "dc": "eqdc10", 439 }, 440 "beta": map[string]interface{}{ 441 "ip": "10.0.0.2", 442 "dc": "eqdc10", 443 }, 444 }, Position{17, 1}, 445 }, 446 queryTestNode{ 447 map[string]interface{}{ 448 "ip": "10.0.0.1", 449 "dc": "eqdc10", 450 }, Position{20, 3}, 451 }, 452 queryTestNode{ 453 map[string]interface{}{ 454 "ip": "10.0.0.2", 455 "dc": "eqdc10", 456 }, Position{24, 3}, 457 }, 458 queryTestNode{ 459 map[string]interface{}{ 460 "data": []interface{}{ 461 []interface{}{"gamma", "delta"}, 462 []interface{}{int64(1), int64(2)}, 463 }, 464 }, Position{28, 1}, 465 }, 466 }) 467 468 assertQueryPositions(t, string(buff), 469 "$..[?(time)]", 470 []interface{}{ 471 queryTestNode{ 472 tv, Position{9, 1}, 473 }, 474 }) 475 476 assertQueryPositions(t, string(buff), 477 "$..[?(bool)]", 478 []interface{}{ 479 queryTestNode{ 480 true, Position{15, 1}, 481 }, 482 }) 483} 484