github.com/janelia-flyem/dvid@v1.0.0/datatype/labelmap/labelmap_test.go (about) 1 package labelmap 2 3 import ( 4 "bytes" 5 "compress/gzip" 6 "encoding/binary" 7 "encoding/json" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "log" 12 "os" 13 "path/filepath" 14 "reflect" 15 "runtime" 16 "strings" 17 "sync" 18 "testing" 19 20 pb "google.golang.org/protobuf/proto" 21 22 "github.com/janelia-flyem/dvid/datastore" 23 "github.com/janelia-flyem/dvid/datatype/common/downres" 24 "github.com/janelia-flyem/dvid/datatype/common/labels" 25 "github.com/janelia-flyem/dvid/datatype/common/proto" 26 "github.com/janelia-flyem/dvid/dvid" 27 "github.com/janelia-flyem/dvid/server" 28 lz4 "github.com/janelia-flyem/go/golz4-updated" 29 ) 30 31 var ( 32 labelsT datastore.TypeService 33 testMu sync.Mutex 34 ) 35 36 // Sets package-level testRepo and TestVersionID 37 func initTestRepo() (dvid.UUID, dvid.VersionID) { 38 testMu.Lock() 39 defer testMu.Unlock() 40 if labelsT == nil { 41 var err error 42 labelsT, err = datastore.TypeServiceByName("labelmap") 43 if err != nil { 44 log.Fatalf("Can't get labelmap type: %s\n", err) 45 } 46 } 47 return datastore.NewTestRepo() 48 } 49 50 type tuple [4]int32 51 52 var labelsROI = []tuple{ 53 tuple{3, 3, 2, 4}, tuple{3, 4, 2, 3}, tuple{3, 5, 3, 3}, 54 tuple{4, 3, 2, 5}, tuple{4, 4, 3, 4}, tuple{4, 5, 2, 4}, 55 //tuple{5, 2, 3, 4}, tuple{5, 3, 3, 3}, tuple{5, 4, 2, 3}, tuple{5, 5, 2, 2}, 56 } 57 58 func labelsJSON() string { 59 b, err := json.Marshal(labelsROI) 60 if err != nil { 61 return "" 62 } 63 return string(b) 64 } 65 66 func inroi(x, y, z int32) bool { 67 for _, span := range labelsROI { 68 if span[0] != z { 69 continue 70 } 71 if span[1] != y { 72 continue 73 } 74 if span[2] > x || span[3] < x { 75 continue 76 } 77 return true 78 } 79 return false 80 } 81 82 // A slice of bytes representing 3d label volume always with first voxel at (0,0,0) 83 type testVolume struct { 84 data []byte 85 size dvid.Point3d 86 } 87 88 func newTestVolume(nx, ny, nz int32) *testVolume { 89 return &testVolume{ 90 data: make([]byte, nx*ny*nz*8), 91 size: dvid.Point3d{nx, ny, nz}, 92 } 93 } 94 95 // Add a label to a subvolume. 96 func (v *testVolume) addSubvol(origin, size dvid.Point3d, label uint64) { 97 nx := v.size[0] 98 nxy := nx * v.size[1] 99 spanBytes := size[0] * 8 100 buf := make([]byte, spanBytes) 101 for x := int32(0); x < size[0]; x++ { 102 binary.LittleEndian.PutUint64(buf[x*8:x*8+8], label) 103 } 104 for z := origin[2]; z < origin[2]+size[2]; z++ { 105 for y := origin[1]; y < origin[1]+size[1]; y++ { 106 i := (z*nxy + y*nx + origin[0]) * 8 107 copy(v.data[i:i+spanBytes], buf) 108 } 109 } 110 } 111 112 // add binary blocks, check test volume is sufficient size. 113 func (v *testVolume) addBlocks(t *testing.T, blocks []labels.BinaryBlock, label uint64) { 114 for _, block := range blocks { 115 maxx := block.Offset[0] + block.Size[0] 116 maxy := block.Offset[1] + block.Size[1] 117 maxz := block.Offset[2] + block.Size[2] 118 dvid.Infof("Adding block offset %s, size %s, # voxels %d\n", block.Offset, block.Size, len(block.Voxels)) 119 120 if maxx > v.size[0] { 121 t.Fatalf("block at offset %s, size %s exceeded test volume size %s\n", block.Offset, block.Size, v.size) 122 } 123 if maxy > v.size[1] { 124 t.Fatalf("block at offset %s, size %s exceeded test volume size %s\n", block.Offset, block.Size, v.size) 125 } 126 if maxz > v.size[2] { 127 t.Fatalf("block at offset %s, size %s exceeded test volume size %s\n", block.Offset, block.Size, v.size) 128 } 129 if len(block.Voxels) != int(block.Size.Prod()) { 130 t.Fatalf("binary block at offset %s, size %s has bad # voxels (%d)\n", block.Offset, block.Size, len(block.Voxels)) 131 } 132 bi := 0 133 for z := block.Offset[2]; z < maxz; z++ { 134 for y := block.Offset[1]; y < maxy; y++ { 135 for x := block.Offset[0]; x < maxx; x++ { 136 if block.Voxels[bi] { 137 if x == 16 && y == 40 && z == 8 { 138 dvid.Infof("voxel found and is indeed on\n") 139 } 140 i := (z*v.size[0]*v.size[1] + y*v.size[0] + x) * 8 141 binary.LittleEndian.PutUint64(v.data[i:i+8], label) 142 } 143 bi++ 144 } 145 } 146 } 147 } 148 } 149 150 // downres assumes only binary volume of some label N or label 0. 151 func (v *testVolume) downres(scale uint8) { 152 sz := int32(1 << scale) 153 nx := v.size[0] >> scale 154 ny := v.size[1] >> scale 155 nz := v.size[2] >> scale 156 157 var oldpos, x, y, z int32 158 for z = 0; z < v.size[2]; z++ { 159 newz := z >> scale 160 for y = 0; y < v.size[1]; y++ { 161 newy := y >> scale 162 for x = 0; x < v.size[0]; x++ { 163 label := binary.LittleEndian.Uint64(v.data[oldpos*8 : oldpos*8+8]) 164 oldpos++ 165 if label != 0 || (x%sz == 0 && y%sz == 0 && z%sz == 0) { 166 newx := x >> scale 167 newpos := newz*nx*ny + newy*nx + newx 168 binary.LittleEndian.PutUint64(v.data[newpos*8:newpos*8+8], label) 169 } 170 } 171 } 172 } 173 v.size[0] = nx 174 v.size[1] = ny 175 v.size[2] = nz 176 v.data = v.data[:nx*ny*nz*8] 177 } 178 179 // Put label data into given data instance. 180 func (v *testVolume) put(t *testing.T, uuid dvid.UUID, name string) { 181 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/0_0_0", server.WebAPIPath, 182 uuid, name, v.size[0], v.size[1], v.size[2]) 183 server.TestHTTP(t, "POST", apiStr, bytes.NewBuffer(v.data)) 184 } 185 186 func (v *testVolume) putMutable(t *testing.T, uuid dvid.UUID, name string) { 187 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/0_0_0?mutate=true", server.WebAPIPath, 188 uuid, name, v.size[0], v.size[1], v.size[2]) 189 server.TestHTTP(t, "POST", apiStr, bytes.NewBuffer(v.data)) 190 } 191 192 func (v *testVolume) get(t *testing.T, uuid dvid.UUID, name string, supervoxels bool) { 193 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/0_0_0", server.WebAPIPath, 194 uuid, name, v.size[0], v.size[1], v.size[2]) 195 if supervoxels { 196 apiStr += "?supervoxels=true" 197 } 198 v.data = server.TestHTTP(t, "GET", apiStr, nil) 199 } 200 201 func (v *testVolume) getScale(t *testing.T, uuid dvid.UUID, name string, scale uint8, supervoxels bool) { 202 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/0_0_0?scale=%d", server.WebAPIPath, 203 uuid, name, v.size[0], v.size[1], v.size[2], scale) 204 if supervoxels { 205 apiStr += "&supervoxels=true" 206 } 207 v.data = server.TestHTTP(t, "GET", apiStr, nil) 208 } 209 210 func (v *testVolume) containsLabel(label uint64) bool { 211 var i uint64 212 var x, y, z int32 213 for z = 0; z < v.size[2]; z++ { 214 for y = 0; y < v.size[1]; y++ { 215 for x = 0; x < v.size[0]; x++ { 216 val := binary.LittleEndian.Uint64(v.data[i : i+8]) 217 if val == label { 218 return true 219 } 220 i += 8 221 } 222 } 223 } 224 return false 225 } 226 227 func (v *testVolume) getVoxel(pt dvid.Point3d) uint64 { 228 nx := v.size[0] 229 nxy := nx * v.size[1] 230 i := (pt[2]*nxy + pt[1]*nx + pt[0]) * 8 231 return binary.LittleEndian.Uint64(v.data[i : i+8]) 232 } 233 234 func (v *testVolume) verifyLabel(t *testing.T, expected uint64, x, y, z int32) { 235 pt := dvid.Point3d{x, y, z} 236 label := v.getVoxel(pt) 237 if label != expected { 238 _, fn, line, _ := runtime.Caller(1) 239 t.Errorf("Expected label %d at %s for first downres but got %d instead [%s:%d]\n", expected, pt, label, fn, line) 240 } 241 } 242 243 func (v *testVolume) equals(v2 *testVolume) error { 244 if !v.size.Equals(v2.size) { 245 return fmt.Errorf("volume sizes are not equal") 246 } 247 if len(v.data) != len(v2.data) { 248 return fmt.Errorf("data lengths are not equal") 249 } 250 var i uint64 251 var x, y, z int32 252 for z = 0; z < v.size[2]; z++ { 253 for y = 0; y < v.size[1]; y++ { 254 for x = 0; x < v.size[0]; x++ { 255 val1 := binary.LittleEndian.Uint64(v.data[i : i+8]) 256 val2 := binary.LittleEndian.Uint64(v2.data[i : i+8]) 257 i += 8 258 if val1 != val2 { 259 return fmt.Errorf("For voxel (%d,%d,%d), found value %d != %d\n", x, y, z, val2, val1) 260 } 261 } 262 } 263 } 264 return nil 265 } 266 267 func (v *testVolume) equalsDownres(v2 *testVolume) error { 268 lores, err := labels.DownresLabels(v2.data, v2.size) 269 if err != nil { 270 return err 271 } 272 var i uint64 273 var x, y, z int32 274 for z = 0; z < v.size[2]; z++ { 275 for y = 0; y < v.size[1]; y++ { 276 for x = 0; x < v.size[0]; x++ { 277 val1 := binary.LittleEndian.Uint64(v.data[i : i+8]) 278 val2 := binary.LittleEndian.Uint64(lores[i : i+8]) 279 i += 8 280 if val1 != val2 { 281 return fmt.Errorf("voxel (%d,%d,%d), expected downres label %d, got %d", x, y, z, val2, val1) 282 } 283 } 284 } 285 } 286 return nil 287 } 288 289 func (v *testVolume) testBlock(t *testing.T, context string, bx, by, bz int32, data []byte) { 290 if len(data) < int(DefaultBlockSize*DefaultBlockSize*DefaultBlockSize*8) { 291 t.Fatalf("[%s] got bad uint64 array of len %d for block (%d, %d, %d)\n", context, len(data), bx, by, bz) 292 } 293 p := 0 294 var x, y, z, numerrors int32 295 for z = 0; z < DefaultBlockSize; z++ { 296 vz := bz*DefaultBlockSize + z 297 for y = 0; y < DefaultBlockSize; y++ { 298 vy := by*DefaultBlockSize + y 299 for x = 0; x < DefaultBlockSize; x++ { 300 vx := bx*DefaultBlockSize + x 301 i := (vz*v.size[0]*v.size[1] + vy*v.size[0] + vx) * 8 302 label := binary.LittleEndian.Uint64(v.data[i : i+8]) 303 got := binary.LittleEndian.Uint64(data[p : p+8]) 304 if label != got && numerrors < 5 { 305 t.Errorf("[%s] error block (%d,%d,%d) at voxel (%d,%d,%d): expected %d, got %d\n", context, bx, by, bz, vx, vy, vz, label, got) 306 numerrors++ 307 } 308 p += 8 309 } 310 } 311 } 312 } 313 314 func (v *testVolume) testGetBlocks(t *testing.T, context string, uuid dvid.UUID, name, compression string, scale uint8) { 315 apiStr := fmt.Sprintf("%snode/%s/%s/blocks/%d_%d_%d/0_0_0", server.WebAPIPath, uuid, name, v.size[0]+64, v.size[1]+64, v.size[2]+64) 316 var qstrs []string 317 if compression != "" { 318 qstrs = append(qstrs, "compression="+compression) 319 } 320 if scale > 0 { 321 qstrs = append(qstrs, fmt.Sprintf("scale=%d", scale)) 322 } 323 if len(qstrs) > 0 { 324 apiStr += "?" + strings.Join(qstrs, "&") 325 } 326 data := server.TestHTTP(t, "GET", apiStr, nil) 327 blockBytes := DefaultBlockSize * DefaultBlockSize * DefaultBlockSize * 8 328 329 var b int 330 for { 331 // Get block coord + block size 332 if b+16 > len(data) { 333 t.Fatalf("Only got %d bytes back from block API call, yet next coord in span would put index @ %d\n", len(data), b+16) 334 } 335 x := int32(binary.LittleEndian.Uint32(data[b : b+4])) 336 b += 4 337 y := int32(binary.LittleEndian.Uint32(data[b : b+4])) 338 b += 4 339 z := int32(binary.LittleEndian.Uint32(data[b : b+4])) 340 b += 4 341 n := int(binary.LittleEndian.Uint32(data[b : b+4])) 342 b += 4 343 344 var uncompressed []byte 345 switch compression { 346 case "uncompressed": 347 uncompressed = data[b : b+n] 348 case "", "lz4": 349 uncompressed = make([]byte, blockBytes) 350 if err := lz4.Uncompress(data[b:b+n], uncompressed); err != nil { 351 t.Fatalf("Unable to uncompress LZ4 data (%s), %d bytes: %v\n", apiStr, n, err) 352 } 353 case "gzip", "blocks": 354 gzipIn := bytes.NewBuffer(data[b : b+n]) 355 zr, err := gzip.NewReader(gzipIn) 356 if err != nil { 357 t.Fatalf("can't uncompress gzip block: %v\n", err) 358 } 359 uncompressed, err = ioutil.ReadAll(zr) 360 if err != nil { 361 t.Fatalf("can't uncompress gzip block: %v\n", err) 362 } 363 zr.Close() 364 365 if compression == "blocks" { 366 var block labels.Block 367 if err = block.UnmarshalBinary(uncompressed); err != nil { 368 t.Errorf("unable to deserialize label block (%d, %d, %d): %v\n", x, y, z, err) 369 } 370 uint64array, size := block.MakeLabelVolume() 371 if !size.Equals(dvid.Point3d{DefaultBlockSize, DefaultBlockSize, DefaultBlockSize}) { 372 t.Fatalf("got bad block size from deserialized block (%d, %d, %d): %s\n", x, y, z, size) 373 } 374 uncompressed = uint64array 375 } 376 } 377 v.testBlock(t, context, x, y, z, uncompressed) 378 b += n 379 if b == len(data) { 380 break 381 } 382 } 383 } 384 385 type mergeJSON string 386 387 func (mjson mergeJSON) send(t *testing.T, uuid dvid.UUID, name string) { 388 apiStr := fmt.Sprintf("%snode/%s/%s/merge?u=tester", server.WebAPIPath, uuid, name) 389 server.TestHTTP(t, "POST", apiStr, bytes.NewBufferString(string(mjson))) 390 391 if err := datastore.BlockOnUpdating(uuid, dvid.InstanceName(name)); err != nil { 392 t.Fatalf("Error blocking on bodies update: %v\n", err) 393 } 394 } 395 396 func (mjson mergeJSON) sendErr(t *testing.T, uuid dvid.UUID, name string) error { 397 apiStr := fmt.Sprintf("%snode/%s/%s/merge", server.WebAPIPath, uuid, name) 398 _, err := server.TestHTTPError(t, "POST", apiStr, bytes.NewBufferString(string(mjson))) 399 return err 400 } 401 402 type renumberJSON string 403 404 func (mjson renumberJSON) send(t *testing.T, uuid dvid.UUID, name string) { 405 apiStr := fmt.Sprintf("%snode/%s/%s/renumber", server.WebAPIPath, uuid, name) 406 server.TestHTTP(t, "POST", apiStr, bytes.NewBufferString(string(mjson))) 407 } 408 409 func checkLabels(t *testing.T, text string, expected, got []byte) { 410 if len(expected) != len(got) { 411 t.Errorf("%s byte mismatch: expected %d bytes, got %d bytes\n", text, len(expected), len(got)) 412 } 413 expectLabels, err := dvid.AliasByteToUint64(expected) 414 if err != nil { 415 t.Fatal(err) 416 } 417 gotLabels, err := dvid.AliasByteToUint64(got) 418 if err != nil { 419 t.Fatal(err) 420 } 421 for i, val := range gotLabels { 422 if expectLabels[i] != val { 423 t.Errorf("%s label mismatch found at index %d, expected %d, got %d\n", text, i, expectLabels[i], val) 424 return 425 } 426 } 427 runtime.KeepAlive(&expected) 428 runtime.KeepAlive(&got) 429 } 430 431 type labelVol struct { 432 size dvid.Point3d 433 blockSize dvid.Point3d 434 offset dvid.Point3d 435 name string 436 data []byte 437 438 nx, ny, nz int32 439 440 startLabel uint64 441 } 442 443 func (vol *labelVol) makeLabelVolume(t *testing.T, uuid dvid.UUID, startLabel uint64) { 444 if vol.startLabel == startLabel && vol.data != nil { 445 return 446 } 447 448 vol.startLabel = startLabel 449 450 vol.nx = vol.size[0] * vol.blockSize[0] 451 vol.ny = vol.size[1] * vol.blockSize[1] 452 vol.nz = vol.size[2] * vol.blockSize[2] 453 454 vol.data = make([]byte, vol.numBytes()) 455 var x, y, z, v int32 456 for z = 0; z < vol.nz; z++ { 457 lz := z / 3 458 for y = 0; y < vol.ny; y++ { 459 ly := y / 3 460 for x = 0; x < vol.nx; x++ { 461 label := startLabel + uint64(x>>2+ly+lz) 462 binary.LittleEndian.PutUint64(vol.data[v:v+8], label) 463 v += 8 464 } 465 } 466 } 467 return 468 } 469 470 func (vol *labelVol) numBytes() int32 { 471 return vol.nx * vol.ny * vol.nz * 8 472 } 473 474 // Create a new label volume and post it to the test datastore. 475 // Each voxel in volume has sequential labels in X, Y, then Z order. 476 func (vol *labelVol) postLabelVolume(t *testing.T, uuid dvid.UUID, compression, roi string, startLabel uint64) { 477 vol.makeLabelVolume(t, uuid, startLabel) 478 479 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/%d_%d_%d", server.WebAPIPath, 480 uuid, vol.name, vol.nx, vol.ny, vol.nz, vol.offset[0], vol.offset[1], vol.offset[2]) 481 query := true 482 483 var data []byte 484 var err error 485 switch compression { 486 case "lz4": 487 apiStr += "?compression=lz4" 488 compressed := make([]byte, lz4.CompressBound(vol.data)) 489 var outSize int 490 if outSize, err = lz4.Compress(vol.data, compressed); err != nil { 491 t.Fatal(err) 492 } 493 data = compressed[:outSize] 494 case "gzip": 495 apiStr += "?compression=gzip" 496 var buf bytes.Buffer 497 gw := gzip.NewWriter(&buf) 498 if _, err = gw.Write(vol.data); err != nil { 499 t.Fatal(err) 500 } 501 data = buf.Bytes() 502 if err = gw.Close(); err != nil { 503 t.Fatal(err) 504 } 505 default: 506 data = vol.data 507 query = false 508 } 509 if roi != "" { 510 if query { 511 apiStr += "&roi=" + roi 512 } else { 513 apiStr += "?roi=" + roi 514 } 515 } 516 server.TestHTTP(t, "POST", apiStr, bytes.NewBuffer(data)) 517 } 518 519 func (vol *labelVol) getLabelVolume(t *testing.T, uuid dvid.UUID, compression, roi string) []byte { 520 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/%d_%d_%d", server.WebAPIPath, 521 uuid, vol.name, vol.nx, vol.ny, vol.nz, vol.offset[0], vol.offset[1], vol.offset[2]) 522 query := true 523 switch compression { 524 case "lz4": 525 apiStr += "?compression=lz4" 526 case "gzip": 527 apiStr += "?compression=gzip" 528 default: 529 query = false 530 } 531 if roi != "" { 532 if query { 533 apiStr += "&roi=" + roi 534 } else { 535 apiStr += "?roi=" + roi 536 } 537 } 538 data := server.TestHTTP(t, "GET", apiStr, nil) 539 switch compression { 540 case "lz4": 541 uncompressed := make([]byte, vol.numBytes()) 542 if err := lz4.Uncompress(data, uncompressed); err != nil { 543 t.Fatalf("Unable to uncompress LZ4 data (%s), %d bytes: %v\n", apiStr, len(data), err) 544 } 545 data = uncompressed 546 case "gzip": 547 buf := bytes.NewBuffer(data) 548 gr, err := gzip.NewReader(buf) 549 if err != nil { 550 t.Fatalf("Error on gzip new reader: %v\n", err) 551 } 552 uncompressed, err := ioutil.ReadAll(gr) 553 if err != nil { 554 t.Fatalf("Error on reading gzip: %v\n", err) 555 } 556 if err = gr.Close(); err != nil { 557 t.Fatalf("Error on closing gzip: %v\n", err) 558 } 559 data = uncompressed 560 default: 561 } 562 if len(data) != int(vol.numBytes()) { 563 t.Errorf("Expected %d uncompressed bytes from 3d labelmap GET. Got %d instead.", vol.numBytes(), len(data)) 564 } 565 return data 566 } 567 568 func (vol *labelVol) testGetLabelVolume(t *testing.T, uuid dvid.UUID, compression, roi string) []byte { 569 data := vol.getLabelVolume(t, uuid, compression, roi) 570 571 // run test to make sure it's same volume as we posted. 572 var x, y, z, v int32 573 for z = 0; z < vol.nz; z++ { 574 lz := z / 3 575 for y = 0; y < vol.ny; y++ { 576 ly := y / 3 577 for x = 0; x < vol.nx; x++ { 578 label := vol.startLabel + uint64(x>>2+ly+lz) 579 got := binary.LittleEndian.Uint64(data[v : v+8]) 580 if label != got { 581 t.Errorf("Error on 3d GET compression (%q): expected %d, got %d\n", compression, label, got) 582 goto foundError 583 } 584 v += 8 585 } 586 } 587 } 588 589 foundError: 590 591 checkLabels(t, "testing label volume", vol.data, data) 592 593 return data 594 } 595 596 func (vol *labelVol) testBlock(t *testing.T, context string, bx, by, bz int32, data []byte) { 597 // Get offset of this block in label volume 598 ox := bx*vol.blockSize[0] - vol.offset[0] 599 oy := by*vol.blockSize[1] - vol.offset[1] 600 oz := bz*vol.blockSize[2] - vol.offset[2] 601 602 if int64(len(data)) < vol.blockSize.Prod()*8 { 603 t.Fatalf("[%s] got bad uint64 array of len %d for block (%d, %d, %d)\n", context, len(data), bx, by, bz) 604 } 605 p := 0 606 var x, y, z, numerrors int32 607 for z = 0; z < 32; z++ { 608 lz := (z + oz) / 3 609 for y = 0; y < 32; y++ { 610 ly := (y + oy) / 3 611 for x = 0; x < 32; x++ { 612 lx := x + ox 613 label := vol.startLabel + uint64(lx>>2+ly+lz) 614 got := binary.LittleEndian.Uint64(data[p : p+8]) 615 if label != got && numerrors < 5 { 616 t.Errorf("[%s] error block (%d,%d,%d) at voxel (%d,%d,%d): expected %d, got %d\n", context, bx, by, bz, x, y, z, label, got) 617 numerrors++ 618 } 619 p += 8 620 } 621 } 622 } 623 } 624 625 func (vol *labelVol) testBlocks(t *testing.T, context string, uuid dvid.UUID, compression string) { 626 span := 5 627 apiStr := fmt.Sprintf("%snode/%s/%s/blocks/%d_%d_%d/%d_%d_%d", server.WebAPIPath, 628 uuid, vol.name, 160, 32, 32, vol.offset[0], vol.offset[1], vol.offset[2]) 629 var qstrs []string 630 if compression != "" { 631 qstrs = append(qstrs, "compression="+compression) 632 } 633 if len(qstrs) > 0 { 634 apiStr += "?" + strings.Join(qstrs, "&") 635 } 636 data := server.TestHTTP(t, "GET", apiStr, nil) 637 638 blockBytes := int(vol.blockSize.Prod() * 8) 639 640 // Check if values are what we expect 641 b := 0 642 for i := 0; i < span; i++ { 643 // Get block coord + block size 644 if b+16 > len(data) { 645 t.Fatalf("Only got %d bytes back from block API call, yet next coord in span would put index @ %d\n", len(data), b+16) 646 } 647 x := int32(binary.LittleEndian.Uint32(data[b : b+4])) 648 b += 4 649 y := int32(binary.LittleEndian.Uint32(data[b : b+4])) 650 b += 4 651 z := int32(binary.LittleEndian.Uint32(data[b : b+4])) 652 b += 4 653 n := int(binary.LittleEndian.Uint32(data[b : b+4])) 654 b += 4 655 656 // Read in the block data as assumed LZ4 and check it. 657 var uncompressed []byte 658 switch compression { 659 case "uncompressed": 660 uncompressed = data[b : b+n] 661 case "", "lz4": 662 uncompressed = make([]byte, blockBytes) 663 if err := lz4.Uncompress(data[b:b+n], uncompressed); err != nil { 664 t.Fatalf("Unable to uncompress LZ4 data (%s), %d bytes: %v\n", apiStr, n, err) 665 } 666 case "gzip", "blocks": 667 gzipIn := bytes.NewBuffer(data[b : b+n]) 668 zr, err := gzip.NewReader(gzipIn) 669 if err != nil { 670 t.Fatalf("can't uncompress gzip block: %v\n", err) 671 } 672 uncompressed, err = ioutil.ReadAll(zr) 673 if err != nil { 674 t.Fatalf("can't uncompress gzip block: %v\n", err) 675 } 676 zr.Close() 677 678 if compression == "blocks" { 679 var block labels.Block 680 if err = block.UnmarshalBinary(uncompressed); err != nil { 681 t.Fatalf("unable to deserialize label block (%d, %d, %d): %v\n", x, y, z, err) 682 } 683 uint64array, size := block.MakeLabelVolume() 684 if !size.Equals(vol.blockSize) { 685 t.Fatalf("got bad block size from deserialized block (%d, %d, %d): %s\n", x, y, z, size) 686 } 687 uncompressed = uint64array 688 } 689 } 690 vol.testBlock(t, context, x, y, z, uncompressed) 691 b += n 692 } 693 } 694 695 // the label in the test volume should just be the start label + voxel index + 1 when iterating in ZYX order. 696 // The passed (x,y,z) should be world coordinates, not relative to the volume offset. 697 func (vol *labelVol) label(x, y, z int32) uint64 { 698 if x < vol.offset[0] || x >= vol.offset[0]+vol.size[0]*vol.blockSize[0] { 699 return 0 700 } 701 if y < vol.offset[1] || y >= vol.offset[1]+vol.size[1]*vol.blockSize[1] { 702 return 0 703 } 704 if z < vol.offset[2] || z >= vol.offset[2]+vol.size[2]*vol.blockSize[2] { 705 return 0 706 } 707 x -= vol.offset[0] 708 y -= vol.offset[1] 709 z -= vol.offset[2] 710 return vol.startLabel + uint64(x>>2+y/3+z/3) 711 } 712 713 type sliceTester struct { 714 orient string 715 width int32 716 height int32 717 offset dvid.Point3d // offset of slice 718 } 719 720 func (s sliceTester) apiStr(uuid dvid.UUID, name string) string { 721 return fmt.Sprintf("%snode/%s/%s/raw/%s/%d_%d/%d_%d_%d", server.WebAPIPath, 722 uuid, name, s.orient, s.width, s.height, s.offset[0], s.offset[1], s.offset[2]) 723 } 724 725 // make sure the given labels match what would be expected from the test volume. 726 func (s sliceTester) testLabel(t *testing.T, vol labelVol, img *dvid.Image) { 727 data := img.Data() 728 var x, y, z int32 729 i := 0 730 switch s.orient { 731 case "xy": 732 for y = 0; y < s.height; y++ { 733 for x = 0; x < s.width; x++ { 734 label := binary.LittleEndian.Uint64(data[i*8 : (i+1)*8]) 735 i++ 736 vx := x + s.offset[0] 737 vy := y + s.offset[1] 738 vz := s.offset[2] 739 expected := vol.label(vx, vy, vz) 740 if label != expected { 741 t.Errorf("Bad label @ (%d,%d,%d): expected %d, got %d\n", vx, vy, vz, expected, label) 742 return 743 } 744 } 745 } 746 return 747 case "xz": 748 for z = 0; z < s.height; z++ { 749 for x = 0; x < s.width; x++ { 750 label := binary.LittleEndian.Uint64(data[i*8 : (i+1)*8]) 751 i++ 752 vx := x + s.offset[0] 753 vy := s.offset[1] 754 vz := z + s.offset[2] 755 expected := vol.label(vx, vy, vz) 756 if label != expected { 757 t.Errorf("Bad label @ (%d,%d,%d): expected %d, got %d\n", vx, vy, vz, expected, label) 758 return 759 } 760 } 761 } 762 return 763 case "yz": 764 for z = 0; z < s.height; z++ { 765 for y = 0; y < s.width; y++ { 766 label := binary.LittleEndian.Uint64(data[i*8 : (i+1)*8]) 767 i++ 768 vx := s.offset[0] 769 vy := y + s.offset[1] 770 vz := z + s.offset[2] 771 expected := vol.label(vx, vy, vz) 772 if label != expected { 773 t.Errorf("Bad label @ (%d,%d,%d): expected %d, got %d\n", vx, vy, vz, expected, label) 774 return 775 } 776 } 777 } 778 return 779 default: 780 t.Fatalf("Unknown slice orientation %q\n", s.orient) 781 } 782 } 783 784 func (vol labelVol) testSlices(t *testing.T, uuid dvid.UUID) { 785 // Verify XY slice. 786 sliceOffset := vol.offset 787 sliceOffset[0] += 51 788 sliceOffset[1] += 11 789 sliceOffset[2] += 23 790 slice := sliceTester{"xy", 67, 83, sliceOffset} 791 apiStr := slice.apiStr(uuid, vol.name) 792 xy := server.TestHTTP(t, "GET", apiStr, nil) 793 img, format, err := dvid.ImageFromBytes(xy, EncodeFormat(), false) 794 if err != nil { 795 t.Fatalf("Error on XY labels GET: %v\n", err) 796 } 797 if format != "png" { 798 t.Errorf("Expected XY labels GET to return %q image, got %q instead.\n", "png", format) 799 } 800 if img.NumBytes() != 67*83*8 { 801 t.Errorf("Expected %d bytes from XY labelmap GET. Got %d instead.", 160*160*8, img.NumBytes()) 802 } 803 slice.testLabel(t, vol, img) 804 805 // Verify XZ slice returns what we expect. 806 sliceOffset = vol.offset 807 sliceOffset[0] += 11 808 sliceOffset[1] += 4 809 sliceOffset[2] += 3 810 slice = sliceTester{"xz", 67, 83, sliceOffset} 811 apiStr = slice.apiStr(uuid, vol.name) 812 xz := server.TestHTTP(t, "GET", apiStr, nil) 813 img, format, err = dvid.ImageFromBytes(xz, EncodeFormat(), false) 814 if err != nil { 815 t.Fatalf("Error on XZ labels GET: %v\n", err) 816 } 817 if format != "png" { 818 t.Errorf("Expected XZ labels GET to return %q image, got %q instead.\n", "png", format) 819 } 820 if img.NumBytes() != 67*83*8 { 821 t.Errorf("Expected %d bytes from XZ labelmap GET. Got %d instead.", 67*83*8, img.NumBytes()) 822 } 823 slice.testLabel(t, vol, img) 824 825 // Verify YZ slice returns what we expect. 826 sliceOffset = vol.offset 827 sliceOffset[0] += 7 828 sliceOffset[1] += 33 829 sliceOffset[2] += 33 830 slice = sliceTester{"yz", 67, 83, sliceOffset} 831 apiStr = slice.apiStr(uuid, vol.name) 832 yz := server.TestHTTP(t, "GET", apiStr, nil) 833 img, format, err = dvid.ImageFromBytes(yz, EncodeFormat(), false) 834 if err != nil { 835 t.Fatalf("Error on YZ labels GET: %v\n", err) 836 } 837 if format != "png" { 838 t.Errorf("Expected YZ labels GET to return %q image, got %q instead.\n", "png", format) 839 } 840 if img.NumBytes() != 67*83*8 { 841 t.Errorf("Expected %d bytes from YZ labelmap GET. Got %d instead.", 67*83*8, img.NumBytes()) 842 } 843 slice.testLabel(t, vol, img) 844 } 845 846 type labelResp struct { 847 Label uint64 848 } 849 850 // Data from which to construct repeatable 3d images where adjacent voxels have different values. 851 var xdata = []uint64{23, 819229, 757, 100303, 9991} 852 var ydata = []uint64{66599, 201, 881067, 5488, 0} 853 var zdata = []uint64{1, 734, 43990122, 42, 319596} 854 855 // Make a 2d slice of bytes with top left corner at (ox,oy,oz) and size (nx,ny) 856 func makeSlice(offset dvid.Point3d, size dvid.Point2d) []byte { 857 numBytes := size[0] * size[1] * 8 858 slice := make([]byte, numBytes, numBytes) 859 i := 0 860 modz := offset[2] % int32(len(zdata)) 861 for y := int32(0); y < size[1]; y++ { 862 sy := y + offset[1] 863 mody := sy % int32(len(ydata)) 864 sx := offset[0] 865 for x := int32(0); x < size[0]; x++ { 866 modx := sx % int32(len(xdata)) 867 binary.BigEndian.PutUint64(slice[i:i+8], xdata[modx]+ydata[mody]+zdata[modz]) 868 i += 8 869 sx++ 870 } 871 } 872 return slice 873 } 874 875 // Make a 3d volume of bytes with top left corner at (ox,oy,oz) and size (nx, ny, nz) 876 func makeVolume(offset, size dvid.Point3d) []byte { 877 sliceBytes := size[0] * size[1] * 8 878 volumeBytes := sliceBytes * size[2] 879 volume := make([]byte, volumeBytes, volumeBytes) 880 var i int32 881 size2d := dvid.Point2d{size[0], size[1]} 882 startZ := offset[2] 883 endZ := startZ + size[2] 884 for z := startZ; z < endZ; z++ { 885 offset[2] = z 886 copy(volume[i:i+sliceBytes], makeSlice(offset, size2d)) 887 i += sliceBytes 888 } 889 return volume 890 } 891 892 // Creates a new data instance for labelmap 893 func newDataInstance(uuid dvid.UUID, t *testing.T, name dvid.InstanceName) *Data { 894 config := dvid.NewConfig() 895 dataservice, err := datastore.NewData(uuid, labelsT, name, config) 896 if err != nil { 897 t.Fatalf("Unable to create labelmap instance %q: %v\n", name, err) 898 } 899 labels, ok := dataservice.(*Data) 900 if !ok { 901 t.Fatalf("Can't cast labels data service into Data\n") 902 } 903 return labels 904 } 905 906 func TestLabelarrayDirectAPI(t *testing.T) { 907 if err := server.OpenTest(); err != nil { 908 t.Fatalf("can't open test server: %v\n", err) 909 } 910 defer server.CloseTest() 911 912 uuid, versionID := initTestRepo() 913 lbls := newDataInstance(uuid, t, "mylabels") 914 labelsCtx := datastore.NewVersionedCtx(lbls, versionID) 915 916 // Create a fake block-aligned label volume 917 offset := dvid.Point3d{DefaultBlockSize, 0, DefaultBlockSize} 918 size := dvid.Point3d{2 * DefaultBlockSize, DefaultBlockSize, 3 * DefaultBlockSize} 919 subvol := dvid.NewSubvolume(offset, size) 920 data := makeVolume(offset, size) 921 922 // Store it into datastore at root 923 v, err := lbls.NewVoxels(subvol, data) 924 if err != nil { 925 t.Fatalf("Unable to make new labels Voxels: %v\n", err) 926 } 927 if err = lbls.IngestVoxels(versionID, 1, v, ""); err != nil { 928 t.Fatalf("Unable to put labels for %s: %v\n", labelsCtx, err) 929 } 930 if v.NumVoxels() != int64(len(data))/8 { 931 t.Errorf("# voxels (%d) after PutVoxels != # original voxels (%d)\n", 932 v.NumVoxels(), int64(len(data))/8) 933 } 934 935 // Read the stored image 936 v2, err := lbls.NewVoxels(subvol, nil) 937 if err != nil { 938 t.Fatalf("Unable to make new labels ExtHandler: %v\n", err) 939 } 940 if err = lbls.GetVoxels(versionID, v2, ""); err != nil { 941 t.Fatalf("Unable to get voxels for %s: %v\n", labelsCtx, err) 942 } 943 944 // Make sure the retrieved image matches the original 945 if v.Stride() != v2.Stride() { 946 t.Errorf("Stride in retrieved subvol incorrect\n") 947 } 948 if v.Interpolable() != v2.Interpolable() { 949 t.Errorf("Interpolable bool in retrieved subvol incorrect\n") 950 } 951 if !reflect.DeepEqual(v.Size(), v2.Size()) { 952 t.Errorf("Size in retrieved subvol incorrect: %s vs expected %s\n", 953 v2.Size(), v.Size()) 954 } 955 if v.NumVoxels() != v2.NumVoxels() { 956 t.Errorf("# voxels in retrieved is different: %d vs expected %d\n", 957 v2.NumVoxels(), v.NumVoxels()) 958 } 959 byteData := v2.Data() 960 961 for i := int64(0); i < v2.NumVoxels()*8; i++ { 962 if byteData[i] != data[i] { 963 t.Logf("Size of data: %d bytes from GET, %d bytes in PUT\n", len(data), len(data)) 964 t.Fatalf("GET subvol (%d) != PUT subvol (%d) @ uint64 #%d", byteData[i], data[i], i) 965 } 966 } 967 } 968 969 func TestLabelarrayRepoPersistence(t *testing.T) { 970 if err := server.OpenTest(); err != nil { 971 t.Fatalf("can't open test server: %v\n", err) 972 } 973 defer server.CloseTest() 974 975 uuid, _ := initTestRepo() 976 977 // Make labels and set various properties 978 config := dvid.NewConfig() 979 config.Set("BlockSize", "12,13,14") 980 config.Set("VoxelSize", "1.1,2.8,11") 981 config.Set("VoxelUnits", "microns,millimeters,nanometers") 982 config.Set("MinPoint", "1,2,3") 983 config.Set("MaxPoint", "7,8,9") 984 config.Set("CountLabels", "false") 985 config.Set("MaxDownresLevel", "5") 986 dataservice, err := datastore.NewData(uuid, labelsT, "mylabels", config) 987 if err == nil { 988 t.Fatalf("expected error with bad block size but didn't get it") 989 } 990 config.Set("BlockSize", "64,32,16") 991 dataservice, err = datastore.NewData(uuid, labelsT, "mylabels", config) 992 if err != nil || dataservice == nil { 993 t.Fatalf("Unable to create labels instance: %v\n", err) 994 } 995 lbls, ok := dataservice.(*Data) 996 if !ok { 997 t.Errorf("Can't cast labels data service into Data\n") 998 } 999 if !lbls.IndexedLabels { 1000 t.Errorf("expected IndexedLabels to be true for default but was false\n") 1001 } 1002 if lbls.MaxDownresLevel != 5 { 1003 t.Errorf("expected MaxDownresLevel to be 5, not %d\n", lbls.MaxDownresLevel) 1004 } 1005 oldData := *lbls 1006 1007 // Check properties setting. 1008 apiStr := fmt.Sprintf("%snode/%s/mylabels/info", server.WebAPIPath, uuid) 1009 result := server.TestHTTP(t, "GET", apiStr, nil) 1010 var parsed = struct { 1011 Base struct { 1012 TypeName, Name string 1013 } 1014 Extended struct { 1015 BlockSize dvid.Point3d 1016 VoxelSize dvid.NdFloat32 1017 VoxelUnits dvid.NdString 1018 MinPoint dvid.Point3d 1019 MaxPoint dvid.Point3d 1020 MaxDownresLevel int 1021 } 1022 }{} 1023 if err := json.Unmarshal(result, &parsed); err != nil { 1024 t.Fatalf("Error parsing JSON response of metadata: %v\n", err) 1025 } 1026 if !parsed.Extended.BlockSize.Equals(dvid.Point3d{64, 32, 16}) { 1027 t.Errorf("Bad BlockSize in new labelmap instance: %s\n", parsed.Extended.BlockSize) 1028 } 1029 if parsed.Extended.MaxDownresLevel != 5 { 1030 t.Errorf("Bad MaxDownresLevel in new labelmap instance: %d\n", parsed.Extended.MaxDownresLevel) 1031 } 1032 if !parsed.Extended.VoxelSize.Equals(dvid.NdFloat32{1.1, 2.8, 11.0}) { 1033 t.Errorf("Bad VoxelSize in new labelmap instance: %s\n", parsed.Extended.VoxelSize) 1034 } 1035 if !parsed.Extended.MinPoint.Equals(dvid.Point3d{1, 2, 3}) { 1036 t.Errorf("Bad MinPoint in new labelmap instance: %s\n", parsed.Extended.MinPoint) 1037 } 1038 if !parsed.Extended.MaxPoint.Equals(dvid.Point3d{7, 8, 9}) { 1039 t.Errorf("Bad MaxPoint in new labelmap instance: %s\n", parsed.Extended.MaxPoint) 1040 } 1041 1042 payload := bytes.NewBufferString(`{"MaxDownresLevel":"8"}`) 1043 result = server.TestHTTP(t, "POST", apiStr, payload) 1044 if lbls.MaxDownresLevel != 8 { 1045 t.Errorf("Bad MaxDownresLevel after POST /info: %d\n", lbls.MaxDownresLevel) 1046 } 1047 1048 // Restart test datastore and see if datasets are still there. 1049 if err = datastore.SaveDataByUUID(uuid, lbls); err != nil { 1050 t.Fatalf("Unable to save repo during labels persistence test: %v\n", err) 1051 } 1052 datastore.CloseReopenTest() 1053 1054 dataservice2, err := datastore.GetDataByUUIDName(uuid, "mylabels") 1055 if err != nil { 1056 t.Fatalf("Can't get labels instance from reloaded test db: %v\n", err) 1057 } 1058 lbls2, ok := dataservice2.(*Data) 1059 if !ok { 1060 t.Errorf("Returned new data instance 2 is not imageblk.Data\n") 1061 } 1062 if !oldData.Equals(lbls2) { 1063 t.Errorf("Expected %v, got %v\n", oldData, *lbls2) 1064 } 1065 if lbls2.MaxDownresLevel != 8 { 1066 t.Errorf("Bad MaxDownresLevel: %d\n", lbls2.MaxDownresLevel) 1067 } 1068 } 1069 1070 func TestBadCalls(t *testing.T) { 1071 if err := server.OpenTest(); err != nil { 1072 t.Fatalf("can't open test server: %v\n", err) 1073 } 1074 defer server.CloseTest() 1075 1076 // Create testbed volume and data instances 1077 uuid, _ := initTestRepo() 1078 var config dvid.Config 1079 config.Set("MaxDownresLevel", "2") 1080 server.CreateTestInstance(t, uuid, "labelmap", "labels", config) 1081 1082 reqStr := fmt.Sprintf("%snode/%s/labels/supervoxels/100", server.WebAPIPath, uuid) 1083 server.TestBadHTTP(t, "GET", reqStr, nil) 1084 1085 reqStr = fmt.Sprintf("%snode/%s/labels/size/100", server.WebAPIPath, uuid) 1086 server.TestBadHTTP(t, "GET", reqStr, nil) 1087 1088 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol-size/100", server.WebAPIPath, uuid) 1089 server.TestBadHTTP(t, "GET", reqStr, nil) 1090 1091 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/100", server.WebAPIPath, uuid) 1092 server.TestBadHTTP(t, "GET", reqStr, nil) 1093 1094 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol-coarse/100", server.WebAPIPath, uuid) 1095 server.TestBadHTTP(t, "GET", reqStr, nil) 1096 1097 reqStr = fmt.Sprintf("%snode/%s/labels/cleave/100", server.WebAPIPath, uuid) 1098 server.TestBadHTTP(t, "POST", reqStr, bytes.NewBufferString("[4]")) 1099 1100 reqStr = fmt.Sprintf("%snode/%s/labels/split-supervoxel/100", server.WebAPIPath, uuid) 1101 server.TestBadHTTP(t, "POST", reqStr, nil) 1102 1103 reqStr = fmt.Sprintf("%snode/%s/labels/split/100", server.WebAPIPath, uuid) 1104 server.TestBadHTTP(t, "POST", reqStr, nil) 1105 1106 reqStr = fmt.Sprintf("%snode/%s/labels/index/100", server.WebAPIPath, uuid) 1107 server.TestBadHTTP(t, "POST", reqStr, nil) 1108 1109 reqStr = fmt.Sprintf("%snode/%s/labels/indices", server.WebAPIPath, uuid) 1110 server.TestBadHTTP(t, "POST", reqStr, nil) 1111 1112 reqStr = fmt.Sprintf("%snode/%s/labels/mappings", server.WebAPIPath, uuid) 1113 server.TestBadHTTP(t, "POST", reqStr, nil) 1114 } 1115 1116 func TestExtents(t *testing.T) { 1117 if err := server.OpenTest(); err != nil { 1118 t.Fatalf("can't open test server: %v\n", err) 1119 } 1120 defer server.CloseTest() 1121 1122 uuid, _ := datastore.NewTestRepo() 1123 if len(uuid) < 5 { 1124 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 1125 } 1126 server.CreateTestInstance(t, uuid, "labelmap", "labels", dvid.Config{}) 1127 1128 extents := `{ 1129 "MinPoint": [68, 127, 210], 1130 "MaxPoint": [1023, 4811, 12187] 1131 }` 1132 apiStr := fmt.Sprintf("%snode/%s/labels/extents", server.WebAPIPath, uuid) 1133 server.TestHTTP(t, "POST", apiStr, bytes.NewBufferString(extents)) 1134 1135 apiStr = fmt.Sprintf("%snode/%s/commit", server.WebAPIPath, uuid) 1136 payload := bytes.NewBufferString(`{"note": "first version"}`) 1137 server.TestHTTP(t, "POST", apiStr, payload) 1138 1139 versionReq := fmt.Sprintf("%snode/%s/newversion", server.WebAPIPath, uuid) 1140 respData := server.TestHTTP(t, "POST", versionReq, nil) 1141 resp := struct { 1142 Child dvid.UUID `json:"child"` 1143 }{} 1144 if err := json.Unmarshal(respData, &resp); err != nil { 1145 t.Errorf("Expected 'child' JSON response. Got %s\n", string(respData)) 1146 } 1147 1148 extents2 := `{ 1149 "MinPoint": [3, 4, 210], 1150 "MaxPoint": [2048, 4811, 12187] 1151 }` 1152 apiStr = fmt.Sprintf("%snode/%s/labels/extents", server.WebAPIPath, resp.Child) 1153 server.TestHTTP(t, "POST", apiStr, bytes.NewBufferString(extents2)) 1154 1155 apiStr = fmt.Sprintf("%snode/%s/labels/info", server.WebAPIPath, uuid) 1156 result := server.TestHTTP(t, "GET", apiStr, nil) 1157 var parsed = struct { 1158 Base struct { 1159 TypeName, Name string 1160 } 1161 Extended struct { 1162 BlockSize dvid.Point3d 1163 VoxelSize dvid.NdFloat32 1164 VoxelUnits dvid.NdString 1165 MinPoint dvid.Point3d 1166 MaxPoint dvid.Point3d 1167 MinIndex dvid.Point3d 1168 MaxIndex dvid.Point3d 1169 } 1170 }{} 1171 if err := json.Unmarshal(result, &parsed); err != nil { 1172 t.Fatalf("Error parsing JSON response of new instance metadata: %v\n", err) 1173 } 1174 fmt.Printf("got: %s\n", string(result)) 1175 if !parsed.Extended.MinPoint.Equals(dvid.Point3d{68, 127, 210}) { 1176 t.Errorf("Bad MinPoint in new labelmap instance: %s\n", parsed.Extended.MinPoint) 1177 } 1178 if !parsed.Extended.MaxPoint.Equals(dvid.Point3d{1023, 4811, 12187}) { 1179 t.Errorf("Bad MaxPoint in new labelmap instance: %s\n", parsed.Extended.MaxPoint) 1180 } 1181 if !parsed.Extended.MinIndex.Equals(dvid.Point3d{1, 1, 3}) { 1182 t.Errorf("Bad MinIndex in new labelmap instance: %s\n", parsed.Extended.MinIndex) 1183 } 1184 if !parsed.Extended.MaxIndex.Equals(dvid.Point3d{15, 75, 190}) { 1185 t.Errorf("Bad MaxIndex in new labelmap instance: %s\n", parsed.Extended.MaxIndex) 1186 } 1187 1188 apiStr = fmt.Sprintf("%snode/%s/labels/info", server.WebAPIPath, resp.Child) 1189 result = server.TestHTTP(t, "GET", apiStr, nil) 1190 if err := json.Unmarshal(result, &parsed); err != nil { 1191 t.Fatalf("Error parsing JSON response of new instance metadata: %v\n", err) 1192 } 1193 fmt.Printf("got: %s\n", string(result)) 1194 if !parsed.Extended.MinPoint.Equals(dvid.Point3d{3, 4, 210}) { 1195 t.Errorf("Bad MinPoint in new labelmap instance: %s\n", parsed.Extended.MinPoint) 1196 } 1197 if !parsed.Extended.MaxPoint.Equals(dvid.Point3d{2048, 4811, 12187}) { 1198 t.Errorf("Bad MaxPoint in new labelmap instance: %s\n", parsed.Extended.MaxPoint) 1199 } 1200 if !parsed.Extended.MinIndex.Equals(dvid.Point3d{0, 0, 3}) { 1201 t.Errorf("Bad MinIndex in new labelmap instance: %s\n", parsed.Extended.MinIndex) 1202 } 1203 if !parsed.Extended.MaxIndex.Equals(dvid.Point3d{32, 75, 190}) { 1204 t.Errorf("Bad MaxIndex in new labelmap instance: %s\n", parsed.Extended.MaxIndex) 1205 } 1206 } 1207 1208 func TestMultiscaleIngest(t *testing.T) { 1209 if err := server.OpenTest(); err != nil { 1210 t.Fatalf("can't open test server: %v\n", err) 1211 } 1212 defer server.CloseTest() 1213 1214 // Create testbed volume and data instances 1215 uuid, _ := initTestRepo() 1216 var config dvid.Config 1217 config.Set("MaxDownresLevel", "2") 1218 server.CreateTestInstance(t, uuid, "labelmap", "labels", config) 1219 1220 // Create an easily interpreted label volume with a couple of labels. 1221 volume := newTestVolume(128, 128, 128) 1222 volume.addSubvol(dvid.Point3d{40, 40, 40}, dvid.Point3d{40, 40, 40}, 1) 1223 volume.addSubvol(dvid.Point3d{40, 40, 80}, dvid.Point3d{40, 40, 40}, 2) 1224 volume.addSubvol(dvid.Point3d{80, 40, 40}, dvid.Point3d{40, 40, 40}, 13) 1225 volume.addSubvol(dvid.Point3d{40, 80, 40}, dvid.Point3d{40, 40, 40}, 209) 1226 volume.addSubvol(dvid.Point3d{80, 80, 40}, dvid.Point3d{40, 40, 40}, 311) 1227 volume.put(t, uuid, "labels") 1228 1229 // Verify initial ingest for hi-res 1230 if err := downres.BlockOnUpdating(uuid, "labels"); err != nil { 1231 t.Fatalf("Error blocking on update for labels: %v\n", err) 1232 } 1233 1234 hires := newTestVolume(128, 128, 128) 1235 hires.get(t, uuid, "labels", false) 1236 hires.verifyLabel(t, 1, 45, 45, 45) 1237 hires.verifyLabel(t, 2, 50, 50, 100) 1238 hires.verifyLabel(t, 13, 100, 60, 60) 1239 hires.verifyLabel(t, 209, 55, 100, 55) 1240 hires.verifyLabel(t, 311, 81, 81, 41) 1241 1242 expectedLabels := []uint64{1, 2, 13, 209, 311} 1243 expectedSize := uint64(40 * 40 * 40) 1244 1245 // Verify our label index voxel counts are correct. 1246 for _, label := range expectedLabels { 1247 reqStr := fmt.Sprintf("%snode/%s/labels/size/%d", server.WebAPIPath, uuid, label) 1248 r := server.TestHTTP(t, "GET", reqStr, nil) 1249 var jsonVal struct { 1250 Voxels uint64 `json:"voxels"` 1251 } 1252 if err := json.Unmarshal(r, &jsonVal); err != nil { 1253 t.Fatalf("unable to get size for label %d: %v", label, err) 1254 } 1255 if jsonVal.Voxels != expectedSize { 1256 t.Errorf("thought label %d would have %d voxels, got %d\n", label, expectedSize, jsonVal.Voxels) 1257 } 1258 } 1259 reqStr := fmt.Sprintf("%snode/%s/labels/size/3", server.WebAPIPath, uuid) 1260 server.TestBadHTTP(t, "GET", reqStr, nil) 1261 1262 reqStr = fmt.Sprintf("%snode/%s/labels/sizes", server.WebAPIPath, uuid) 1263 bodystr := "[1, 2, 3, 13, 209, 311]" 1264 r := server.TestHTTP(t, "GET", reqStr, bytes.NewBufferString(bodystr)) 1265 if string(r) != "[64000,64000,0,64000,64000,64000]" { 1266 t.Errorf("bad batch sizes result. got: %s\n", string(r)) 1267 } 1268 1269 // Verify the label streaming endpoint. 1270 reqStr = fmt.Sprintf("%snode/%s/labels/listlabels", server.WebAPIPath, uuid) 1271 r = server.TestHTTP(t, "GET", reqStr, nil) 1272 if len(r) != len(expectedLabels)*8 { 1273 t.Errorf("expected %d labels from /listlabels but got %d bytes\n", len(expectedLabels), len(r)) 1274 } 1275 for i, label := range expectedLabels { 1276 gotLabel := binary.LittleEndian.Uint64(r[i*8 : i*8+8]) 1277 if label != gotLabel { 1278 t.Errorf("expected label %d but got %d in /listlabels pos %d\n", label, gotLabel, i) 1279 } 1280 } 1281 reqStr = fmt.Sprintf("%snode/%s/labels/listlabels?start=4&number=2", server.WebAPIPath, uuid) 1282 r = server.TestHTTP(t, "GET", reqStr, nil) 1283 if len(r) != 2*8 { 1284 t.Fatalf("expected %d labels from /listlabels but got %d bytes\n", 2, len(r)) 1285 } 1286 for i, label := range expectedLabels[2:4] { 1287 gotLabel := binary.LittleEndian.Uint64(r[i*8 : i*8+8]) 1288 if label != gotLabel { 1289 t.Errorf("expected label %d but got %d in /listlabels pos %d\n", label, gotLabel, i) 1290 } 1291 } 1292 1293 // Verify the label + voxel count streaming endpoint. 1294 reqStr = fmt.Sprintf("%snode/%s/labels/listlabels?sizes=true", server.WebAPIPath, uuid) 1295 r = server.TestHTTP(t, "GET", reqStr, nil) 1296 if len(r) != len(expectedLabels)*16 { 1297 t.Errorf("expected %d labels and sizes from /listlabels but got %d bytes\n", len(expectedLabels), len(r)) 1298 } 1299 for i, label := range expectedLabels { 1300 gotLabel := binary.LittleEndian.Uint64(r[i*16 : i*16+8]) 1301 if label != gotLabel { 1302 t.Errorf("expected label %d but got %d in /listlabels pos %d\n", label, gotLabel, i) 1303 } 1304 gotSize := binary.LittleEndian.Uint64(r[i*16+8 : i*16+16]) 1305 if expectedSize != gotSize { 1306 t.Errorf("expected size %d for label %d but got %d in /listlabels pos %d\n", expectedSize, label, gotSize, i) 1307 } 1308 } 1309 1310 // Check the first downres: 64^3 1311 downres1 := newTestVolume(64, 64, 64) 1312 downres1.getScale(t, uuid, "labels", 1, false) 1313 downres1.verifyLabel(t, 1, 30, 30, 30) 1314 downres1.verifyLabel(t, 2, 21, 21, 45) 1315 downres1.verifyLabel(t, 13, 45, 21, 36) 1316 downres1.verifyLabel(t, 209, 21, 50, 35) 1317 downres1.verifyLabel(t, 311, 45, 55, 35) 1318 expected1 := newTestVolume(64, 64, 64) 1319 expected1.addSubvol(dvid.Point3d{20, 20, 20}, dvid.Point3d{20, 20, 20}, 1) 1320 expected1.addSubvol(dvid.Point3d{20, 20, 40}, dvid.Point3d{20, 20, 20}, 2) 1321 expected1.addSubvol(dvid.Point3d{40, 20, 20}, dvid.Point3d{20, 20, 20}, 13) 1322 expected1.addSubvol(dvid.Point3d{20, 40, 20}, dvid.Point3d{20, 20, 20}, 209) 1323 expected1.addSubvol(dvid.Point3d{40, 40, 20}, dvid.Point3d{20, 20, 20}, 311) 1324 if err := downres1.equals(expected1); err != nil { 1325 t.Errorf("1st downres 'labels' isn't what is expected: %v\n", err) 1326 } 1327 1328 // Check the second downres to voxel: 32^3 1329 expected2 := newTestVolume(32, 32, 32) 1330 expected2.addSubvol(dvid.Point3d{10, 10, 10}, dvid.Point3d{10, 10, 10}, 1) 1331 expected2.addSubvol(dvid.Point3d{10, 10, 20}, dvid.Point3d{10, 10, 10}, 2) 1332 expected2.addSubvol(dvid.Point3d{20, 10, 10}, dvid.Point3d{10, 10, 10}, 13) 1333 expected2.addSubvol(dvid.Point3d{10, 20, 10}, dvid.Point3d{10, 10, 10}, 209) 1334 expected2.addSubvol(dvid.Point3d{20, 20, 10}, dvid.Point3d{10, 10, 10}, 311) 1335 downres2 := newTestVolume(32, 32, 32) 1336 downres2.getScale(t, uuid, "labels", 2, false) 1337 if err := downres2.equals(expected2); err != nil { 1338 t.Errorf("2nd downres 'labels' isn't what is expected: %v\n", err) 1339 } 1340 1341 // // Check blocks endpoint for different scales. 1342 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "", 0) 1343 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "uncompressed", 0) 1344 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "blocks", 0) 1345 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "gzip", 0) 1346 1347 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "", 1) 1348 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "uncompressed", 1) 1349 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "blocks", 1) 1350 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "gzip", 1) 1351 1352 expected2a := newTestVolume(64, 64, 64) // can only get block-aligned subvolumes 1353 expected2a.addSubvol(dvid.Point3d{10, 10, 10}, dvid.Point3d{10, 10, 10}, 1) 1354 expected2a.addSubvol(dvid.Point3d{10, 10, 20}, dvid.Point3d{10, 10, 10}, 2) 1355 expected2a.addSubvol(dvid.Point3d{20, 10, 10}, dvid.Point3d{10, 10, 10}, 13) 1356 expected2a.addSubvol(dvid.Point3d{10, 20, 10}, dvid.Point3d{10, 10, 10}, 209) 1357 expected2a.addSubvol(dvid.Point3d{20, 20, 10}, dvid.Point3d{10, 10, 10}, 311) 1358 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "", 2) 1359 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "uncompressed", 2) 1360 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "blocks", 2) 1361 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "gzip", 2) 1362 } 1363 1364 // TestMultiscaleIngest2 tests ingesting using the /ingest-supervoxels endpoint. 1365 func TestMultiscaleIngest2(t *testing.T) { 1366 if err := server.OpenTest(); err != nil { 1367 t.Fatalf("can't open test server: %v\n", err) 1368 } 1369 defer server.CloseTest() 1370 1371 // Create testbed volume and data instances 1372 uuid, _ := initTestRepo() 1373 var config dvid.Config 1374 config.Set("MaxDownresLevel", "2") 1375 server.CreateTestInstance(t, uuid, "labelmap", "labels", config) 1376 1377 // Create an easily interpreted label volume with a couple of labels. 1378 volume := newTestVolume(128, 128, 128) 1379 volume.addSubvol(dvid.Point3d{40, 40, 40}, dvid.Point3d{40, 40, 40}, 1) 1380 volume.addSubvol(dvid.Point3d{40, 40, 80}, dvid.Point3d{40, 40, 40}, 2) 1381 volume.addSubvol(dvid.Point3d{80, 40, 40}, dvid.Point3d{40, 40, 40}, 13) 1382 volume.addSubvol(dvid.Point3d{40, 80, 40}, dvid.Point3d{40, 40, 40}, 209) 1383 volume.addSubvol(dvid.Point3d{80, 80, 40}, dvid.Point3d{40, 40, 40}, 311) 1384 volume.put(t, uuid, "labels") 1385 1386 // Verify initial ingest for hi-res 1387 if err := downres.BlockOnUpdating(uuid, "labels"); err != nil { 1388 t.Fatalf("Error blocking on update for labels: %v\n", err) 1389 } 1390 1391 hires := newTestVolume(128, 128, 128) 1392 hires.get(t, uuid, "labels", false) 1393 hires.verifyLabel(t, 1, 45, 45, 45) 1394 hires.verifyLabel(t, 2, 50, 50, 100) 1395 hires.verifyLabel(t, 13, 100, 60, 60) 1396 hires.verifyLabel(t, 209, 55, 100, 55) 1397 hires.verifyLabel(t, 311, 81, 81, 41) 1398 1399 expectedLabels := []uint64{1, 2, 13, 209, 311} 1400 expectedSize := uint64(40 * 40 * 40) 1401 1402 // Verify our label index voxel counts are correct. 1403 for _, label := range expectedLabels { 1404 reqStr := fmt.Sprintf("%snode/%s/labels/size/%d", server.WebAPIPath, uuid, label) 1405 r := server.TestHTTP(t, "GET", reqStr, nil) 1406 var jsonVal struct { 1407 Voxels uint64 `json:"voxels"` 1408 } 1409 if err := json.Unmarshal(r, &jsonVal); err != nil { 1410 t.Fatalf("unable to get size for label %d: %v", label, err) 1411 } 1412 if jsonVal.Voxels != expectedSize { 1413 t.Errorf("thought label %d would have %d voxels, got %d\n", label, expectedSize, jsonVal.Voxels) 1414 } 1415 } 1416 reqStr := fmt.Sprintf("%snode/%s/labels/size/3", server.WebAPIPath, uuid) 1417 server.TestBadHTTP(t, "GET", reqStr, nil) 1418 1419 reqStr = fmt.Sprintf("%snode/%s/labels/sizes", server.WebAPIPath, uuid) 1420 bodystr := "[1, 2, 3, 13, 209, 311]" 1421 r := server.TestHTTP(t, "GET", reqStr, bytes.NewBufferString(bodystr)) 1422 if string(r) != "[64000,64000,0,64000,64000,64000]" { 1423 t.Errorf("bad batch sizes result. got: %s\n", string(r)) 1424 } 1425 1426 // Verify the label streaming endpoint. 1427 reqStr = fmt.Sprintf("%snode/%s/labels/listlabels", server.WebAPIPath, uuid) 1428 r = server.TestHTTP(t, "GET", reqStr, nil) 1429 if len(r) != len(expectedLabels)*8 { 1430 t.Errorf("expected %d labels from /listlabels but got %d bytes\n", len(expectedLabels), len(r)) 1431 } 1432 for i, label := range expectedLabels { 1433 gotLabel := binary.LittleEndian.Uint64(r[i*8 : i*8+8]) 1434 if label != gotLabel { 1435 t.Errorf("expected label %d but got %d in /listlabels pos %d\n", label, gotLabel, i) 1436 } 1437 } 1438 reqStr = fmt.Sprintf("%snode/%s/labels/listlabels?start=4&number=2", server.WebAPIPath, uuid) 1439 r = server.TestHTTP(t, "GET", reqStr, nil) 1440 if len(r) != 2*8 { 1441 t.Fatalf("expected %d labels from /listlabels but got %d bytes\n", 2, len(r)) 1442 } 1443 for i, label := range expectedLabels[2:4] { 1444 gotLabel := binary.LittleEndian.Uint64(r[i*8 : i*8+8]) 1445 if label != gotLabel { 1446 t.Errorf("expected label %d but got %d in /listlabels pos %d\n", label, gotLabel, i) 1447 } 1448 } 1449 1450 // Verify the label + voxel count streaming endpoint. 1451 reqStr = fmt.Sprintf("%snode/%s/labels/listlabels?sizes=true", server.WebAPIPath, uuid) 1452 r = server.TestHTTP(t, "GET", reqStr, nil) 1453 if len(r) != len(expectedLabels)*16 { 1454 t.Errorf("expected %d labels and sizes from /listlabels but got %d bytes\n", len(expectedLabels), len(r)) 1455 } 1456 for i, label := range expectedLabels { 1457 gotLabel := binary.LittleEndian.Uint64(r[i*16 : i*16+8]) 1458 if label != gotLabel { 1459 t.Errorf("expected label %d but got %d in /listlabels pos %d\n", label, gotLabel, i) 1460 } 1461 gotSize := binary.LittleEndian.Uint64(r[i*16+8 : i*16+16]) 1462 if expectedSize != gotSize { 1463 t.Errorf("expected size %d for label %d but got %d in /listlabels pos %d\n", expectedSize, label, gotSize, i) 1464 } 1465 } 1466 1467 // Check the first downres: 64^3 1468 downres1 := newTestVolume(64, 64, 64) 1469 downres1.getScale(t, uuid, "labels", 1, false) 1470 downres1.verifyLabel(t, 1, 30, 30, 30) 1471 downres1.verifyLabel(t, 2, 21, 21, 45) 1472 downres1.verifyLabel(t, 13, 45, 21, 36) 1473 downres1.verifyLabel(t, 209, 21, 50, 35) 1474 downres1.verifyLabel(t, 311, 45, 55, 35) 1475 expected1 := newTestVolume(64, 64, 64) 1476 expected1.addSubvol(dvid.Point3d{20, 20, 20}, dvid.Point3d{20, 20, 20}, 1) 1477 expected1.addSubvol(dvid.Point3d{20, 20, 40}, dvid.Point3d{20, 20, 20}, 2) 1478 expected1.addSubvol(dvid.Point3d{40, 20, 20}, dvid.Point3d{20, 20, 20}, 13) 1479 expected1.addSubvol(dvid.Point3d{20, 40, 20}, dvid.Point3d{20, 20, 20}, 209) 1480 expected1.addSubvol(dvid.Point3d{40, 40, 20}, dvid.Point3d{20, 20, 20}, 311) 1481 if err := downres1.equals(expected1); err != nil { 1482 t.Errorf("1st downres 'labels' isn't what is expected: %v\n", err) 1483 } 1484 1485 // Check the second downres to voxel: 32^3 1486 expected2 := newTestVolume(32, 32, 32) 1487 expected2.addSubvol(dvid.Point3d{10, 10, 10}, dvid.Point3d{10, 10, 10}, 1) 1488 expected2.addSubvol(dvid.Point3d{10, 10, 20}, dvid.Point3d{10, 10, 10}, 2) 1489 expected2.addSubvol(dvid.Point3d{20, 10, 10}, dvid.Point3d{10, 10, 10}, 13) 1490 expected2.addSubvol(dvid.Point3d{10, 20, 10}, dvid.Point3d{10, 10, 10}, 209) 1491 expected2.addSubvol(dvid.Point3d{20, 20, 10}, dvid.Point3d{10, 10, 10}, 311) 1492 downres2 := newTestVolume(32, 32, 32) 1493 downres2.getScale(t, uuid, "labels", 2, false) 1494 if err := downres2.equals(expected2); err != nil { 1495 t.Errorf("2nd downres 'labels' isn't what is expected: %v\n", err) 1496 } 1497 1498 // // Check blocks endpoint for different scales. 1499 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "", 0) 1500 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "uncompressed", 0) 1501 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "blocks", 0) 1502 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "gzip", 0) 1503 1504 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "", 1) 1505 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "uncompressed", 1) 1506 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "blocks", 1) 1507 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "gzip", 1) 1508 1509 expected2a := newTestVolume(64, 64, 64) // can only get block-aligned subvolumes 1510 expected2a.addSubvol(dvid.Point3d{10, 10, 10}, dvid.Point3d{10, 10, 10}, 1) 1511 expected2a.addSubvol(dvid.Point3d{10, 10, 20}, dvid.Point3d{10, 10, 10}, 2) 1512 expected2a.addSubvol(dvid.Point3d{20, 10, 10}, dvid.Point3d{10, 10, 10}, 13) 1513 expected2a.addSubvol(dvid.Point3d{10, 20, 10}, dvid.Point3d{10, 10, 10}, 209) 1514 expected2a.addSubvol(dvid.Point3d{20, 20, 10}, dvid.Point3d{10, 10, 10}, 311) 1515 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "", 2) 1516 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "uncompressed", 2) 1517 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "blocks", 2) 1518 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "gzip", 2) 1519 } 1520 1521 // tests vanishing labels when they are downres out of existence 1522 func TestMultiscaleVanish(t *testing.T) { 1523 if err := server.OpenTest(); err != nil { 1524 t.Fatalf("can't open test server: %v\n", err) 1525 } 1526 defer server.CloseTest() 1527 1528 // Create testbed volume and data instances 1529 uuid, _ := initTestRepo() 1530 var config dvid.Config 1531 config.Set("MaxDownresLevel", "8") 1532 server.CreateTestInstance(t, uuid, "labelmap", "labels", config) 1533 1534 hires := newTestVolume(128, 128, 128) 1535 hires.addSubvol(dvid.Point3d{0, 0, 0}, dvid.Point3d{128, 128, 128}, 1) 1536 hires.addSubvol(dvid.Point3d{40, 40, 40}, dvid.Point3d{2, 2, 2}, 2) 1537 hires.addSubvol(dvid.Point3d{16, 16, 16}, dvid.Point3d{4, 4, 4}, 3) 1538 hires.addSubvol(dvid.Point3d{1, 1, 1}, dvid.Point3d{1, 1, 1}, 4) 1539 hires.put(t, uuid, "labels") 1540 if err := downres.BlockOnUpdating(uuid, "labels"); err != nil { 1541 t.Fatalf("Error blocking on update for labels: %v\n", err) 1542 } 1543 1544 // at hires all spares volumes should exist 1545 reqStr := fmt.Sprintf("%snode/%s/labels/sparsevol/1", server.WebAPIPath, uuid) 1546 encoding := server.TestHTTP(t, "GET", reqStr, nil) 1547 if len(encoding) == 0 { 1548 t.Fatalf("expected non-zero sparsevol return for label 1") 1549 } 1550 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/2", server.WebAPIPath, uuid) 1551 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1552 if len(encoding) == 0 { 1553 t.Fatalf("expected non-zero sparsevol return for label 2") 1554 } 1555 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/3", server.WebAPIPath, uuid) 1556 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1557 if len(encoding) == 0 { 1558 t.Fatalf("expected non-zero sparsevol return for label 3") 1559 } 1560 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/4", server.WebAPIPath, uuid) 1561 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1562 if len(encoding) == 0 { 1563 t.Fatalf("expected non-zero sparsevol return for label 4") 1564 } 1565 1566 // -- test bounds 1567 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/3?minx=14&maxx=18", server.WebAPIPath, uuid) 1568 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1569 if len(encoding) == 0 { 1570 t.Fatalf("expected non-zero sparsevol return for label 3 with bounds") 1571 } 1572 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/3?minx=10&maxx=15", server.WebAPIPath, uuid) 1573 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1574 if len(encoding) != 0 { 1575 t.Fatalf("expected zero sparsevol return for label 3 with out-of-scope bounds, got %d bytes", len(encoding)) 1576 } 1577 1578 // downres 1: 4 should drop out 1579 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/1?scale=1", server.WebAPIPath, uuid) 1580 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1581 if len(encoding) == 0 { 1582 t.Fatalf("expected non-zero sparsevol return for label 1") 1583 } 1584 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/2?scale=1", server.WebAPIPath, uuid) 1585 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1586 if len(encoding) == 0 { 1587 t.Fatalf("expected non-zero sparsevol return for label 3") 1588 } 1589 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/3?scale=1", server.WebAPIPath, uuid) 1590 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1591 if len(encoding) == 0 { 1592 t.Fatalf("expected non-zero sparsevol return for label 3") 1593 } 1594 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/4?scale=1", server.WebAPIPath, uuid) 1595 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1596 if len(encoding) != 0 { 1597 t.Fatalf("expected zero sparsevol return for label 4") 1598 } 1599 1600 // -- test bounds 1601 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/3?scale=1&minx=7&maxx=9", server.WebAPIPath, uuid) 1602 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1603 if len(encoding) == 0 { 1604 t.Fatalf("expected non-zero level-1 sparsevol return for label 3 with bounds") 1605 } 1606 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/3?scale=1&minx=5&maxx=7", server.WebAPIPath, uuid) 1607 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1608 if len(encoding) != 0 { 1609 t.Fatalf("expected zero level-1 sparsevol return for label 3 with out-of-scope bounds, got %d bytes", len(encoding)) 1610 } 1611 1612 // downres 2: 2 should drop out 1613 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/1?scale=2", server.WebAPIPath, uuid) 1614 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1615 if len(encoding) == 0 { 1616 t.Fatalf("expected non-zero sparsevol return for label 1") 1617 } 1618 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/2?scale=2", server.WebAPIPath, uuid) 1619 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1620 if len(encoding) != 0 { 1621 t.Fatalf("expected zero sparsevol return for label 2") 1622 } 1623 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/3?scale=2", server.WebAPIPath, uuid) 1624 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1625 if len(encoding) == 0 { 1626 t.Fatalf("expected non-zero sparsevol return for label 3") 1627 } 1628 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/4?scale=2", server.WebAPIPath, uuid) 1629 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1630 if len(encoding) != 0 { 1631 t.Fatalf("expected zero sparsevol return for label 4") 1632 } 1633 1634 // downres 3: 3 should drop out 1635 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/1?scale=3", server.WebAPIPath, uuid) 1636 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1637 if len(encoding) == 0 { 1638 t.Fatalf("expected non-zero sparsevol return for label 1") 1639 } 1640 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/2?scale=3", server.WebAPIPath, uuid) 1641 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1642 if len(encoding) != 0 { 1643 t.Fatalf("expected zero sparsevol return for label 2") 1644 } 1645 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/3?scale=3", server.WebAPIPath, uuid) 1646 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1647 if len(encoding) != 0 { 1648 t.Fatalf("expected zero sparsevol return for label 3") 1649 } 1650 reqStr = fmt.Sprintf("%snode/%s/labels/sparsevol/4?scale=3", server.WebAPIPath, uuid) 1651 encoding = server.TestHTTP(t, "GET", reqStr, nil) 1652 if len(encoding) != 0 { 1653 t.Fatalf("expected zero sparsevol return for label 4") 1654 } 1655 } 1656 1657 func readGzipFile(filename string) ([]byte, error) { 1658 f, err := os.Open(filename) 1659 if err != nil { 1660 return nil, err 1661 } 1662 defer f.Close() 1663 1664 fz, err := gzip.NewReader(f) 1665 if err != nil { 1666 return nil, err 1667 } 1668 defer fz.Close() 1669 1670 data, err := ioutil.ReadAll(fz) 1671 if err != nil { 1672 return nil, err 1673 } 1674 return data, nil 1675 } 1676 1677 type testData struct { 1678 u []uint64 1679 b *labels.Block 1680 filename string 1681 } 1682 1683 func (d testData) String() string { 1684 return filepath.Base(d.filename) 1685 } 1686 1687 func solidTestData(label uint64) (td testData) { 1688 td.b = labels.MakeSolidBlock(label, dvid.Point3d{64, 64, 64}) 1689 numVoxels := 64 * 64 * 64 1690 td.u = make([]uint64, numVoxels) 1691 td.filename = fmt.Sprintf("solid volume of label %d", label) 1692 for i := 0; i < numVoxels; i++ { 1693 td.u[i] = label 1694 } 1695 return 1696 } 1697 1698 var testFiles = []string{ 1699 "../../test_data/fib19-64x64x64-sample1.dat.gz", 1700 "../../test_data/fib19-64x64x64-sample2.dat.gz", 1701 "../../test_data/cx-64x64x64-sample1.dat.gz", 1702 "../../test_data/cx-64x64x64-sample2.dat.gz", 1703 } 1704 1705 func loadTestData(t *testing.T, filename string) (td testData) { 1706 uint64array, err := readGzipFile(filename) 1707 if err != nil { 1708 t.Fatalf("unable to open test data file %q: %v\n", filename, err) 1709 } 1710 td.u, err = dvid.ByteToUint64(uint64array) 1711 if err != nil { 1712 t.Fatalf("unable to create alias []uint64 for data file %q: %v\n", filename, err) 1713 } 1714 td.b, err = labels.MakeBlock(uint64array, dvid.Point3d{64, 64, 64}) 1715 if err != nil { 1716 t.Fatalf("unable to convert data from file %q into block: %v\n", filename, err) 1717 } 1718 td.filename = filename 1719 return 1720 } 1721 1722 func writeTestInt32(t *testing.T, buf *bytes.Buffer, i int32) { 1723 b := make([]byte, 4) 1724 binary.LittleEndian.PutUint32(b, uint32(i)) 1725 n, err := buf.Write(b) 1726 if n != 4 || err != nil { 1727 t.Fatalf("couldn't write value %d (%d bytes) to buffer: %v\n", i, n, err) 1728 } 1729 } 1730 1731 func testGetBlock(t *testing.T, uuid dvid.UUID, name string, bcoord dvid.Point3d, td testData) { 1732 apiStr := fmt.Sprintf("%snode/%s/%s/blocks/%d_%d_%d/%d_%d_%d?compression=blocks", server.WebAPIPath, 1733 uuid, name, 64, 64, 64, bcoord[0]*64, bcoord[1]*64, bcoord[2]*64) 1734 data := server.TestHTTP(t, "GET", apiStr, nil) 1735 1736 b := 0 1737 if b+16 > len(data) { 1738 t.Fatalf("Only got %d bytes back from block API call\n", len(data)) 1739 } 1740 x := int32(binary.LittleEndian.Uint32(data[b : b+4])) 1741 b += 4 1742 y := int32(binary.LittleEndian.Uint32(data[b : b+4])) 1743 b += 4 1744 z := int32(binary.LittleEndian.Uint32(data[b : b+4])) 1745 b += 4 1746 n := int(binary.LittleEndian.Uint32(data[b : b+4])) 1747 b += 4 1748 if x != bcoord[0] || y != bcoord[1] || z != bcoord[2] { 1749 t.Fatalf("Bad block coordinate: expected %s, got (%d,%d,%d)\n", bcoord, x, y, z) 1750 } 1751 1752 gzipIn := bytes.NewBuffer(data[b : b+n]) 1753 zr, err := gzip.NewReader(gzipIn) 1754 if err != nil { 1755 t.Fatalf("can't uncompress gzip block: %v\n", err) 1756 } 1757 uncompressed, err := ioutil.ReadAll(zr) 1758 if err != nil { 1759 t.Fatalf("can't uncompress gzip block: %v\n", err) 1760 } 1761 zr.Close() 1762 1763 var block labels.Block 1764 if err = block.UnmarshalBinary(uncompressed); err != nil { 1765 t.Fatalf("unable to deserialize label block (%d, %d, %d): %v\n", x, y, z, err) 1766 } 1767 izyx := dvid.IndexZYX{bcoord[0], bcoord[1], bcoord[2]} 1768 checkBlock(t, labels.PositionedBlock{block, izyx.ToIZYXString()}, td) 1769 } 1770 1771 func checkBlock(t *testing.T, pb labels.PositionedBlock, td testData) { 1772 bytearray, _ := pb.Block.MakeLabelVolume() 1773 uint64array, err := dvid.AliasByteToUint64(bytearray) 1774 if err != nil { 1775 t.Fatalf("error converting returned block %s to uint64 array: %v\n", pb.BCoord, err) 1776 } 1777 if len(uint64array) != len(td.u) { 1778 t.Fatalf("got block %s with %d labels != expected %d labels\n", pb.BCoord, len(uint64array), len(td.u)) 1779 } 1780 for i, val := range uint64array { 1781 if val != td.u[i] { 1782 t.Fatalf("error in block %s, pos %d: got label %d, expected label %d\n", pb.BCoord, i, val, td.u[i]) 1783 } 1784 } 1785 runtime.KeepAlive(&bytearray) 1786 } 1787 1788 func decodeReturnedBlocks(t *testing.T, data []byte) []labels.PositionedBlock { 1789 var blocks []labels.PositionedBlock 1790 b := 0 1791 for { 1792 if b+16 > len(data) { 1793 t.Fatalf("Only got %d bytes back from block API call\n", len(data)) 1794 } 1795 x := int32(binary.LittleEndian.Uint32(data[b : b+4])) 1796 b += 4 1797 y := int32(binary.LittleEndian.Uint32(data[b : b+4])) 1798 b += 4 1799 z := int32(binary.LittleEndian.Uint32(data[b : b+4])) 1800 b += 4 1801 n := int(binary.LittleEndian.Uint32(data[b : b+4])) 1802 b += 4 1803 bcoord := dvid.ChunkPoint3d{x, y, z}.ToIZYXString() 1804 1805 dvid.Infof("uncompressing block (%d,%d,%d) of %d bytes...\n", x, y, z, n) 1806 gzipIn := bytes.NewBuffer(data[b : b+n]) 1807 zr, err := gzip.NewReader(gzipIn) 1808 if err != nil { 1809 t.Fatalf("can't uncompress gzip block: %v\n", err) 1810 } 1811 uncompressed, err := ioutil.ReadAll(zr) 1812 if err != nil { 1813 t.Fatalf("can't uncompress gzip block: %v\n", err) 1814 } 1815 zr.Close() 1816 1817 var block labels.Block 1818 if err = block.UnmarshalBinary(uncompressed); err != nil { 1819 t.Fatalf("unable to deserialize label block (%d, %d, %d): %v\n", x, y, z, err) 1820 } 1821 blocks = append(blocks, labels.PositionedBlock{BCoord: bcoord, Block: block}) 1822 b += n 1823 if len(data) == b { 1824 break 1825 } 1826 } 1827 return blocks 1828 } 1829 1830 func testGetSpecificBlocks(t *testing.T, uuid dvid.UUID, name string, supervoxels bool, bcoordStr string) []labels.PositionedBlock { 1831 apiStr := fmt.Sprintf("%snode/%s/%s/specificblocks?compression=blocks&supervoxels=%t&blocks=%s", server.WebAPIPath, 1832 uuid, name, supervoxels, bcoordStr) 1833 data := server.TestHTTP(t, "GET", apiStr, nil) 1834 return decodeReturnedBlocks(t, data) 1835 } 1836 1837 func testGetVolumeBlocks(t *testing.T, uuid dvid.UUID, name string, supervoxels bool, size, offset dvid.Point3d) []labels.PositionedBlock { 1838 apiStr := fmt.Sprintf("%snode/%s/%s/blocks/%d_%d_%d/%d_%d_%d?compression=blocks&supervoxels=%t", server.WebAPIPath, 1839 uuid, name, size[0], size[1], size[2], offset[0], offset[1], offset[2], supervoxels) 1840 data := server.TestHTTP(t, "GET", apiStr, nil) 1841 return decodeReturnedBlocks(t, data) 1842 } 1843 1844 func TestPostBlocks(t *testing.T) { 1845 if err := server.OpenTest(); err != nil { 1846 t.Fatalf("can't open test server: %v\n", err) 1847 } 1848 defer server.CloseTest() 1849 1850 uuid, _ := datastore.NewTestRepo() 1851 if len(uuid) < 5 { 1852 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 1853 } 1854 server.CreateTestInstance(t, uuid, "labelmap", "labels", dvid.Config{}) 1855 1856 blockCoords := []dvid.Point3d{ 1857 {1, 2, 3}, 1858 {2, 2, 3}, 1859 {1, 3, 4}, 1860 {2, 3, 4}, 1861 } 1862 var data [4]testData 1863 var buf bytes.Buffer 1864 for i, fname := range testFiles { 1865 writeTestInt32(t, &buf, blockCoords[i][0]) 1866 writeTestInt32(t, &buf, blockCoords[i][1]) 1867 writeTestInt32(t, &buf, blockCoords[i][2]) 1868 data[i] = loadTestData(t, fname) 1869 gzipped, err := data[i].b.CompressGZIP() 1870 if err != nil { 1871 t.Fatalf("unable to gzip compress block: %v\n", err) 1872 } 1873 writeTestInt32(t, &buf, int32(len(gzipped))) 1874 n, err := buf.Write(gzipped) 1875 if err != nil { 1876 t.Fatalf("unable to write gzip block: %v\n", err) 1877 } 1878 if n != len(gzipped) { 1879 t.Fatalf("unable to write %d bytes to buffer, only wrote %d bytes\n", len(gzipped), n) 1880 } 1881 } 1882 1883 apiStr := fmt.Sprintf("%snode/%s/labels/blocks", server.WebAPIPath, uuid) 1884 server.TestHTTP(t, "POST", apiStr, &buf) 1885 1886 if err := datastore.BlockOnUpdating(uuid, "labels"); err != nil { 1887 t.Fatalf("Error blocking on sync of labels: %v\n", err) 1888 } 1889 1890 // test volume GET 1891 start := dvid.Point3d{1 * 64, 2 * 64, 3 * 64} 1892 end := dvid.Point3d{3*64 - 1, 4*64 - 1, 5*64 - 1} 1893 testExtents(t, "labels", uuid, start, end) 1894 1895 vol := labelVol{ 1896 size: dvid.Point3d{2, 2, 2}, // in blocks 1897 nx: 128, 1898 ny: 128, 1899 nz: 128, 1900 blockSize: dvid.Point3d{64, 64, 64}, 1901 offset: dvid.Point3d{64, 128, 192}, 1902 name: "labels", 1903 } 1904 got := vol.getLabelVolume(t, uuid, "", "") 1905 gotLabels, err := dvid.AliasByteToUint64(got) 1906 if err != nil { 1907 t.Fatal(err) 1908 } 1909 var bcoordStr string 1910 for i := 0; i < 4; i++ { 1911 x0 := (blockCoords[i][0] - 1) * 64 1912 y0 := (blockCoords[i][1] - 2) * 64 1913 z0 := (blockCoords[i][2] - 3) * 64 1914 bcoordStr += fmt.Sprintf("%d,%d,%d", blockCoords[i][0], blockCoords[i][1], blockCoords[i][2]) 1915 if i != 3 { 1916 bcoordStr += "," 1917 } 1918 var x, y, z int32 1919 for z = 0; z < 64; z++ { 1920 for y = 0; y < 64; y++ { 1921 for x = 0; x < 64; x++ { 1922 di := z*64*64 + y*64 + x 1923 gi := (z+z0)*128*128 + (y+y0)*128 + x + x0 1924 if data[i].u[di] != gotLabels[gi] { 1925 t.Fatalf("Error in block %s dvid coord (%d,%d,%d): expected %d, got %d\n", blockCoords[i], x+x0, y+y0, z+z0, data[i].u[di], gotLabels[gi]) 1926 } 1927 } 1928 } 1929 } 1930 } 1931 runtime.KeepAlive(&got) 1932 1933 // test GET /blocks 1934 for i, td := range data { 1935 testGetBlock(t, uuid, "labels", blockCoords[i], td) 1936 } 1937 1938 // test GET /specificblocks 1939 pblocks := testGetSpecificBlocks(t, uuid, "labels", true, bcoordStr) 1940 for i, td := range data { 1941 izyx := dvid.IndexZYX{blockCoords[i][0], blockCoords[i][1], blockCoords[i][2]} 1942 izyxStr := izyx.ToIZYXString() 1943 var j int 1944 for _, pb := range pblocks { 1945 if izyxStr == pb.BCoord { 1946 break 1947 } 1948 j++ 1949 } 1950 if j == 4 { 1951 t.Fatalf("Didn't find block %s in retrieved blocks\n", izyxStr) 1952 } 1953 checkBlock(t, pblocks[j], td) 1954 } 1955 bcoordStr2 := "100,23,89," 1956 for i := 0; i < 2; i++ { 1957 bcoordStr2 += fmt.Sprintf("%d,%d,%d,", blockCoords[i][0], blockCoords[i][1], blockCoords[i][2]) 1958 } 1959 bcoordStr2 += "0,50,40" 1960 pblocks = testGetSpecificBlocks(t, uuid, "labels", true, bcoordStr2) 1961 if len(pblocks) != 2 { 1962 t.Fatalf("expected 2 blocks, got %d blocks instead\n", len(pblocks)) 1963 } 1964 var gotBlocks int 1965 for _, pb := range pblocks { 1966 for i, td := range data { 1967 izyx := dvid.IndexZYX{blockCoords[i][0], blockCoords[i][1], blockCoords[i][2]} 1968 izyxStr := izyx.ToIZYXString() 1969 if izyxStr == pb.BCoord { 1970 gotBlocks++ 1971 checkBlock(t, pb, td) 1972 } 1973 } 1974 } 1975 if gotBlocks != 2 { 1976 t.Fatalf("expected to receive 2 blocks, got %d instead\n", gotBlocks) 1977 } 1978 1979 // test GET /indices 1980 apiStr = fmt.Sprintf("%snode/%s/labels/indices", server.WebAPIPath, uuid) 1981 returnData := server.TestHTTP(t, "GET", apiStr, strings.NewReader(`[0, 1, 52011980226]`)) 1982 var indices proto.LabelIndices 1983 if err := pb.Unmarshal(returnData, &indices); err != nil { 1984 t.Fatalf("error unmarshaling label indices: %v\n", err) 1985 } 1986 if len(indices.Indices) != 3 { 1987 t.Fatalf("expected 3 indices returned, got %d\n", len(indices.Indices)) 1988 } 1989 if indices.Indices[0].Label != 0 || len(indices.Indices[0].Blocks) != 0 { 1990 t.Fatalf("got bad label 0 index return: %v\n", indices.Indices[0]) 1991 } 1992 if indices.Indices[1].Label != 1 || len(indices.Indices[1].Blocks) != 0 { 1993 t.Fatalf("got bad label 1 index return: %v\n", indices.Indices[1]) 1994 } 1995 if indices.Indices[2].Label != 52011980226 || len(indices.Indices[2].Blocks) != 1 && indices.Indices[2].Blocks[13194143727618].Counts[52011980226] != 7963 { 1996 t.Fatalf("got bad label 52011980226 index return: %v\n", indices.Indices[2]) 1997 } 1998 server.TestBadHTTP(t, "GET", apiStr, nil) 1999 server.TestBadHTTP(t, "GET", apiStr, strings.NewReader(``)) 2000 server.TestBadHTTP(t, "GET", apiStr, strings.NewReader(`{}`)) 2001 returnData = server.TestHTTP(t, "GET", apiStr, strings.NewReader(`[]`)) 2002 if returnData != nil { 2003 t.Fatalf("got non-nil response from bad request: %v\n", returnData) 2004 } 2005 2006 // test GET /indices-compressed 2007 apiStr = fmt.Sprintf("%snode/%s/labels/indices-compressed", server.WebAPIPath, uuid) 2008 returnData = server.TestHTTP(t, "GET", apiStr, strings.NewReader(`[0, 1, 52011980226]`)) 2009 2010 i := 0 2011 datasize := binary.LittleEndian.Uint64(returnData[i : i+8]) 2012 i += 8 2013 label := binary.LittleEndian.Uint64(returnData[i : i+8]) 2014 if label != 0 || datasize != 0 { 2015 t.Fatalf("got bad label 0 index return: label %d, datasize %d\n", label, datasize) 2016 } 2017 2018 i += 8 2019 datasize = binary.LittleEndian.Uint64(returnData[i : i+8]) 2020 i += 8 2021 label = binary.LittleEndian.Uint64(returnData[i : i+8]) 2022 if label != 1 || datasize != 0 { 2023 t.Fatalf("got bad label 1 index return: label %d, datasize %d\n", label, datasize) 2024 } 2025 2026 i += 8 2027 datasize = binary.LittleEndian.Uint64(returnData[i : i+8]) 2028 i += 8 2029 label = binary.LittleEndian.Uint64(returnData[i : i+8]) 2030 i += 8 2031 if label != 52011980226 || datasize != 38 { 2032 t.Fatalf("got bad label 52011980226 index return: label %d, datasize %d\n", label, datasize) 2033 } 2034 idx := uncompressIndex(t, returnData[i:i+38]) 2035 if idx.Label != 52011980226 || len(idx.Blocks) != 1 && idx.Blocks[13194143727618].Counts[52011980226] != 7963 { 2036 t.Fatalf("got bad label 52011980226 index return: %v\n", *idx) 2037 } 2038 server.TestBadHTTP(t, "GET", apiStr, nil) 2039 server.TestBadHTTP(t, "GET", apiStr, strings.NewReader(``)) 2040 server.TestBadHTTP(t, "GET", apiStr, strings.NewReader(`{}`)) 2041 returnData = server.TestHTTP(t, "GET", apiStr, strings.NewReader(`[]`)) 2042 if returnData != nil { 2043 t.Fatalf("got non-nil response from bad request: %v\n", returnData) 2044 } 2045 } 2046 2047 func uncompressIndex(t *testing.T, data []byte) *labels.Index { 2048 val, _, err := dvid.DeserializeData(data, true) 2049 if err != nil { 2050 t.Fatalf("unable to uncompress label index data, %d bytes", len(data)) 2051 } 2052 2053 idx := new(labels.Index) 2054 if err := pb.Unmarshal(val, idx); err != nil { 2055 t.Fatalf("unable to uncompress label index data, %d bytes", len(data)) 2056 } 2057 return idx 2058 } 2059 2060 func testExtents(t *testing.T, name string, uuid dvid.UUID, min, max dvid.Point3d) { 2061 apiStr := fmt.Sprintf("%snode/%s/%s/metadata", server.WebAPIPath, uuid, name) 2062 r := server.TestHTTP(t, "GET", apiStr, nil) 2063 fmt.Printf("metadata: %s\n", string(r)) 2064 jsonVal := make(map[string]interface{}) 2065 if err := json.Unmarshal(r, &jsonVal); err != nil { 2066 t.Errorf("Unable to get metadata in JSON format. Instead got: %v\n", jsonVal) 2067 } 2068 propData, ok := jsonVal["Properties"] 2069 if !ok { 2070 t.Fatalf("Could not parse Properties out of returned JSON: %v\n", jsonVal) 2071 } 2072 props, ok := propData.(map[string]interface{}) 2073 if !ok { 2074 t.Fatalf("Could not create properties map: %v\n", propData) 2075 } 2076 pt, ok := props["MaxPoint"] 2077 if !ok { 2078 t.Fatalf("Couldn't get MaxPoint from Properties object: %v\n", props) 2079 } 2080 if pt == nil { 2081 t.Fatalf("Couldn't find MaxPoint in Properties object: %v\n", props) 2082 } 2083 maxPoint, ok := pt.([]interface{}) 2084 if !ok { 2085 t.Fatalf("Couldn't parse MaxPoint %s: %v\n", reflect.TypeOf(pt), pt) 2086 } 2087 x, y, z := maxPoint[0].(float64), maxPoint[1].(float64), maxPoint[2].(float64) 2088 if x != float64(max[0]) { 2089 t.Errorf("Bad MaxPoint X: expected %.0f, got %d\n", x, max[0]) 2090 } 2091 if y != float64(max[1]) { 2092 t.Errorf("Bad MaxPoint Y: expected %.0f, got %d\n", y, max[1]) 2093 } 2094 if z != float64(max[2]) { 2095 t.Errorf("Bad MaxPoint Z: expected %.0f, got %d\n", z, max[2]) 2096 } 2097 } 2098 2099 func TestIngestBlocks(t *testing.T) { 2100 if err := server.OpenTest(); err != nil { 2101 t.Fatalf("can't open test server: %v\n", err) 2102 } 2103 defer server.CloseTest() 2104 2105 uuid, _ := datastore.NewTestRepo() 2106 if len(uuid) < 5 { 2107 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 2108 } 2109 server.CreateTestInstance(t, uuid, "labelmap", "labels", dvid.Config{}) 2110 2111 blockCoords := []dvid.Point3d{ 2112 {1, 2, 3}, 2113 {2, 2, 3}, 2114 {1, 3, 4}, 2115 {2, 3, 4}, 2116 } 2117 var data [4]testData 2118 var buf bytes.Buffer 2119 for i, fname := range testFiles { 2120 writeTestInt32(t, &buf, blockCoords[i][0]) 2121 writeTestInt32(t, &buf, blockCoords[i][1]) 2122 writeTestInt32(t, &buf, blockCoords[i][2]) 2123 data[i] = loadTestData(t, fname) 2124 gzipped, err := data[i].b.CompressGZIP() 2125 if err != nil { 2126 t.Fatalf("unable to gzip compress block: %v\n", err) 2127 } 2128 writeTestInt32(t, &buf, int32(len(gzipped))) 2129 n, err := buf.Write(gzipped) 2130 if err != nil { 2131 t.Fatalf("unable to write gzip block: %v\n", err) 2132 } 2133 if n != len(gzipped) { 2134 t.Fatalf("unable to write %d bytes to buffer, only wrote %d bytes\n", len(gzipped), n) 2135 } 2136 } 2137 2138 apiStr := fmt.Sprintf("%snode/%s/labels/ingest-supervoxels", server.WebAPIPath, uuid) 2139 server.TestHTTP(t, "POST", apiStr, &buf) 2140 2141 if err := datastore.BlockOnUpdating(uuid, "labels"); err != nil { 2142 t.Fatalf("Error blocking on update of labels: %v\n", err) 2143 } 2144 2145 // test volume GET 2146 vol := labelVol{ 2147 size: dvid.Point3d{2, 2, 2}, // in blocks 2148 nx: 128, 2149 ny: 128, 2150 nz: 128, 2151 blockSize: dvid.Point3d{64, 64, 64}, 2152 offset: dvid.Point3d{64, 128, 192}, 2153 name: "labels", 2154 } 2155 got := vol.getLabelVolume(t, uuid, "", "") 2156 gotLabels, err := dvid.AliasByteToUint64(got) 2157 if err != nil { 2158 t.Fatal(err) 2159 } 2160 var bcoordStr string 2161 for i := 0; i < 4; i++ { 2162 x0 := (blockCoords[i][0] - 1) * 64 2163 y0 := (blockCoords[i][1] - 2) * 64 2164 z0 := (blockCoords[i][2] - 3) * 64 2165 bcoordStr += fmt.Sprintf("%d,%d,%d", blockCoords[i][0], blockCoords[i][1], blockCoords[i][2]) 2166 if i != 3 { 2167 bcoordStr += "," 2168 } 2169 var x, y, z int32 2170 for z = 0; z < 64; z++ { 2171 for y = 0; y < 64; y++ { 2172 for x = 0; x < 64; x++ { 2173 di := z*64*64 + y*64 + x 2174 gi := (z+z0)*128*128 + (y+y0)*128 + x + x0 2175 if data[i].u[di] != gotLabels[gi] { 2176 t.Fatalf("Error in block %s dvid coord (%d,%d,%d): expected %d, got %d\n", blockCoords[i], x+x0, y+y0, z+z0, data[i].u[di], gotLabels[gi]) 2177 } 2178 } 2179 } 2180 } 2181 } 2182 runtime.KeepAlive(&got) 2183 2184 // test GET /blocks 2185 for i, td := range data { 2186 testGetBlock(t, uuid, "labels", blockCoords[i], td) 2187 } 2188 2189 // test GET /specificblocks 2190 pblocks := testGetSpecificBlocks(t, uuid, "labels", true, bcoordStr) 2191 for i, td := range data { 2192 izyx := dvid.IndexZYX{blockCoords[i][0], blockCoords[i][1], blockCoords[i][2]} 2193 izyxStr := izyx.ToIZYXString() 2194 var j int 2195 for _, pb := range pblocks { 2196 if izyxStr == pb.BCoord { 2197 break 2198 } 2199 j++ 2200 } 2201 if j == 4 { 2202 t.Fatalf("Didn't find block %s in retrieved blocks\n", izyxStr) 2203 } 2204 checkBlock(t, pblocks[j], td) 2205 } 2206 bcoordStr2 := "100,23,89," 2207 for i := 0; i < 2; i++ { 2208 bcoordStr2 += fmt.Sprintf("%d,%d,%d,", blockCoords[i][0], blockCoords[i][1], blockCoords[i][2]) 2209 } 2210 bcoordStr2 += "0,50,40" 2211 pblocks = testGetSpecificBlocks(t, uuid, "labels", true, bcoordStr2) 2212 if len(pblocks) != 2 { 2213 t.Fatalf("expected 2 blocks, got %d blocks instead\n", len(pblocks)) 2214 } 2215 var gotBlocks int 2216 for _, pb := range pblocks { 2217 for i, td := range data { 2218 izyx := dvid.IndexZYX{blockCoords[i][0], blockCoords[i][1], blockCoords[i][2]} 2219 izyxStr := izyx.ToIZYXString() 2220 if izyxStr == pb.BCoord { 2221 gotBlocks++ 2222 checkBlock(t, pb, td) 2223 } 2224 } 2225 } 2226 if gotBlocks != 2 { 2227 t.Fatalf("expected to receive 2 blocks, got %d instead\n", gotBlocks) 2228 } 2229 } 2230 2231 func TestPostBlock(t *testing.T) { 2232 if err := server.OpenTest(); err != nil { 2233 t.Fatalf("can't open test server: %v\n", err) 2234 } 2235 defer server.CloseTest() 2236 2237 uuid, _ := datastore.NewTestRepo() 2238 if len(uuid) < 5 { 2239 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 2240 } 2241 server.CreateTestInstance(t, uuid, "labelmap", "labels", dvid.Config{}) 2242 2243 f, err := os.Open("../../test_data/fib19-64x64x64-sample1-block.dat.gz") 2244 if err != nil { 2245 t.Fatalf("Couldn't open compressed block test data: %v\n", err) 2246 } 2247 data, err := ioutil.ReadAll(f) 2248 if err != nil { 2249 t.Fatalf("Couldn't read compressed block test data: %v\n", err) 2250 } 2251 var buf bytes.Buffer 2252 writeTestInt32(t, &buf, 0) 2253 writeTestInt32(t, &buf, 0) 2254 writeTestInt32(t, &buf, 0) 2255 writeTestInt32(t, &buf, int32(len(data))) 2256 fmt.Printf("Writing %d bytes of compressed block\n", len(data)) 2257 n, err := buf.Write(data) 2258 if err != nil { 2259 t.Fatalf("unable to write gzip block: %v\n", err) 2260 } 2261 if n != len(data) { 2262 t.Fatalf("unable to write %d bytes to buffer, only wrote %d bytes\n", len(data), n) 2263 } 2264 2265 apiStr := fmt.Sprintf("%snode/%s/labels/blocks", server.WebAPIPath, uuid) 2266 server.TestHTTP(t, "POST", apiStr, &buf) 2267 2268 if err := datastore.BlockOnUpdating(uuid, "labels"); err != nil { 2269 t.Fatalf("Error blocking on sync of labels: %v\n", err) 2270 } 2271 } 2272 2273 func TestBigPostBlock(t *testing.T) { 2274 if err := server.OpenTest(); err != nil { 2275 t.Fatalf("can't open test server: %v\n", err) 2276 } 2277 defer server.CloseTest() 2278 2279 uuid, _ := datastore.NewTestRepo() 2280 if len(uuid) < 5 { 2281 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 2282 } 2283 server.CreateTestInstance(t, uuid, "labelmap", "labels", dvid.Config{}) 2284 2285 f, err := os.Open("../../test_data/stream_1block.dat") 2286 if err != nil { 2287 t.Fatalf("Couldn't open compressed block test data: %v\n", err) 2288 } 2289 data, err := ioutil.ReadAll(f) 2290 if err != nil { 2291 t.Fatalf("Couldn't read compressed block test data: %v\n", err) 2292 } 2293 2294 apiStr := fmt.Sprintf("%snode/%s/labels/blocks", server.WebAPIPath, uuid) 2295 server.TestHTTP(t, "POST", apiStr, bytes.NewBuffer(data)) 2296 2297 if err := datastore.BlockOnUpdating(uuid, "labels"); err != nil { 2298 t.Fatalf("Error blocking on sync of labels: %v\n", err) 2299 } 2300 } 2301 2302 type testBlock struct { 2303 bcoord dvid.ChunkPoint3d 2304 labels []uint64 2305 } 2306 2307 // Create two test blocks composed of labels 1 and 2. 2308 // For 1st block, coord (2,3,4), label 2 if x <= 31, label 1 if x >= 32. 2309 // For 2nd block, coord (3,3,4), label 1 if x <= 31, label 2 if x >= 32. 2310 func setupTestBlocks(t *testing.T, uuid dvid.UUID) (testBlockData [2]testBlock) { 2311 server.CreateTestInstance(t, uuid, "labelmap", "labels", dvid.Config{}) 2312 2313 numVoxels := 64 * 64 * 64 2314 blockVol0 := make([]uint64, numVoxels) 2315 i := 0 2316 for z := 0; z < 64; z++ { 2317 for y := 0; y < 64; y++ { 2318 for x := 0; x < 32; x++ { 2319 blockVol0[i] = 2 2320 i++ 2321 } 2322 for x := 32; x < 64; x++ { 2323 blockVol0[i] = 1 2324 i++ 2325 } 2326 } 2327 } 2328 block0, err := labels.MakeBlock(dvid.AliasUint64ToByte(blockVol0), dvid.Point3d{64, 64, 64}) 2329 if err != nil { 2330 t.Fatalf("error making block 0: %v\n", err) 2331 } 2332 runtime.KeepAlive(&blockVol0) 2333 block0data, err := block0.CompressGZIP() 2334 if err != nil { 2335 t.Fatalf("error making block 0: %v\n", err) 2336 } 2337 blockVol1 := make([]uint64, numVoxels) 2338 i = 0 2339 for z := 0; z < 64; z++ { 2340 for y := 0; y < 64; y++ { 2341 for x := 0; x < 32; x++ { 2342 blockVol1[i] = 1 2343 i++ 2344 } 2345 for x := 32; x < 64; x++ { 2346 blockVol1[i] = 2 2347 i++ 2348 } 2349 } 2350 } 2351 block1, err := labels.MakeBlock(dvid.AliasUint64ToByte(blockVol1), dvid.Point3d{64, 64, 64}) 2352 if err != nil { 2353 t.Fatalf("error making block 0: %v\n", err) 2354 } 2355 runtime.KeepAlive(&blockVol1) 2356 block1data, err := block1.CompressGZIP() 2357 if err != nil { 2358 t.Fatalf("error making block 0: %v\n", err) 2359 } 2360 2361 testBlockData = [2]testBlock{ 2362 { 2363 bcoord: dvid.ChunkPoint3d{2, 3, 4}, 2364 labels: blockVol0, 2365 }, 2366 { 2367 bcoord: dvid.ChunkPoint3d{3, 3, 4}, 2368 labels: blockVol1, 2369 }, 2370 } 2371 2372 var buf bytes.Buffer 2373 writeTestInt32(t, &buf, 2) 2374 writeTestInt32(t, &buf, 3) 2375 writeTestInt32(t, &buf, 4) 2376 writeTestInt32(t, &buf, int32(len(block0data))) 2377 n, err := buf.Write(block0data) 2378 if err != nil { 2379 t.Fatalf("unable to write gzip block: %v\n", err) 2380 } 2381 if n != len(block0data) { 2382 t.Fatalf("unable to write %d bytes to buffer, only wrote %d bytes\n", len(block0data), n) 2383 } 2384 writeTestInt32(t, &buf, 3) 2385 writeTestInt32(t, &buf, 3) 2386 writeTestInt32(t, &buf, 4) 2387 writeTestInt32(t, &buf, int32(len(block1data))) 2388 n, err = buf.Write(block1data) 2389 if err != nil { 2390 t.Fatalf("unable to write gzip block: %v\n", err) 2391 } 2392 if n != len(block1data) { 2393 t.Fatalf("unable to write %d bytes to buffer, only wrote %d bytes\n", len(block1data), n) 2394 } 2395 2396 apiStr := fmt.Sprintf("%snode/%s/labels/blocks", server.WebAPIPath, uuid) 2397 server.TestHTTP(t, "POST", apiStr, &buf) 2398 2399 apiStr = fmt.Sprintf("%snode/%s/labels/blocks/128_64_64/128_192_256?compression=blocks", server.WebAPIPath, uuid) 2400 respRec := server.TestHTTPResponse(t, "GET", apiStr, nil) 2401 for bnum := 0; bnum < 2; bnum++ { 2402 gotBlock, _, bx, by, bz, err := readStreamedBlock(respRec.Body, 0) 2403 if err != nil { 2404 t.Fatalf("problem reading block %d from stream: %v\n", bnum, err) 2405 } 2406 for _, label := range gotBlock.Labels { 2407 if label != 1 && label != 2 { 2408 t.Errorf("got bad block with label %d when only 1 or 2 should have been present\n", label) 2409 } 2410 } 2411 bcoord := dvid.ChunkPoint3d{bx, by, bz} 2412 var found bool 2413 for i := 0; i < 2; i++ { 2414 if bcoord == testBlockData[i].bcoord { 2415 found = true 2416 labelBytes, _ := gotBlock.MakeLabelVolume() 2417 labelVol, err := dvid.AliasByteToUint64(labelBytes) 2418 if err != nil { 2419 t.Fatalf("problem inflating block %s labels to []uint64: %v\n", bcoord, err) 2420 } 2421 for v, val := range labelVol { 2422 if testBlockData[i].labels[v] != val { 2423 t.Fatalf("label mismatch found at index %d, expected %d, got %d\n", v, testBlockData[i].labels[v], val) 2424 } 2425 } 2426 runtime.KeepAlive(&labelBytes) 2427 } 2428 } 2429 if !found { 2430 t.Fatalf("got block %s but wasn't an expected block!\n", bcoord) 2431 } 2432 } 2433 return 2434 } 2435 2436 func TestBlocksWithMerge(t *testing.T) { 2437 if err := server.OpenTest(); err != nil { 2438 t.Fatalf("can't open test server: %v\n", err) 2439 } 2440 defer server.CloseTest() 2441 2442 uuid, _ := datastore.NewTestRepo() 2443 if len(uuid) < 5 { 2444 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 2445 } 2446 testBlockData := setupTestBlocks(t, uuid) 2447 2448 testMerge := mergeJSON(`[1, 2]`) 2449 testMerge.send(t, uuid, "labels") 2450 2451 apiStr := fmt.Sprintf("%snode/%s/labels/blocks/128_64_64/128_192_256?compression=blocks", server.WebAPIPath, uuid) 2452 respRec := server.TestHTTPResponse(t, "GET", apiStr, nil) 2453 for bnum := 0; bnum < 2; bnum++ { 2454 gotBlock, _, bx, by, bz, err := readStreamedBlock(respRec.Body, 0) 2455 if err != nil { 2456 t.Fatalf("problem reading block %d from stream: %v\n", bnum, err) 2457 } 2458 for _, label := range gotBlock.Labels { 2459 if label != 1 { 2460 t.Errorf("got bad block with label %d when only 1 should have been present\n", label) 2461 } 2462 } 2463 bcoord := dvid.ChunkPoint3d{bx, by, bz} 2464 var found bool 2465 for i := 0; i < 2; i++ { 2466 if bcoord == testBlockData[i].bcoord { 2467 found = true 2468 labelBytes, _ := gotBlock.MakeLabelVolume() 2469 labelVol, err := dvid.AliasByteToUint64(labelBytes) 2470 if err != nil { 2471 t.Fatalf("problem inflating block %s labels to []uint64: %v\n", bcoord, err) 2472 } 2473 for v, val := range labelVol { 2474 if testBlockData[i].labels[v] != 0 && val != 1 { 2475 t.Fatalf("label mismatch found at index %d, expected 1, got %d\n", v, val) 2476 } 2477 } 2478 runtime.KeepAlive(&labelBytes) 2479 } 2480 } 2481 if !found { 2482 t.Fatalf("got block %s but wasn't an expected block!\n", bcoord) 2483 } 2484 } 2485 2486 apiStr = fmt.Sprintf("%snode/%s/labels/blocks/128_64_64/128_192_256?compression=blocks&supervoxels=true", server.WebAPIPath, uuid) 2487 respRec = server.TestHTTPResponse(t, "GET", apiStr, nil) 2488 gotBlock0, _, _, _, _, err := readStreamedBlock(respRec.Body, 0) 2489 if err != nil { 2490 t.Errorf("error trying to readStreamedBlock: %v\n", err) 2491 } 2492 gotLabels := make(labels.Set) 2493 for _, label := range gotBlock0.Labels { 2494 gotLabels[label] = struct{}{} 2495 if label != 1 && label != 2 { 2496 t.Errorf("got unexpected label in block: %d\n", label) 2497 } 2498 } 2499 if _, found := gotLabels[1]; !found { 2500 t.Errorf("expected label 1 but found none\n") 2501 } 2502 if _, found := gotLabels[2]; !found { 2503 t.Errorf("expected label 2 but found none\n") 2504 } 2505 gotBlock1, _, _, _, _, err := readStreamedBlock(respRec.Body, 0) 2506 if err != nil { 2507 t.Errorf("error trying to readStreamedBlock: %v\n", err) 2508 } 2509 gotLabels = make(labels.Set) 2510 for _, label := range gotBlock1.Labels { 2511 gotLabels[label] = struct{}{} 2512 if label != 1 && label != 2 { 2513 t.Errorf("got unexpected label in block: %d\n", label) 2514 } 2515 } 2516 if _, found := gotLabels[1]; !found { 2517 t.Errorf("expected label 1 but found none\n") 2518 } 2519 if _, found := gotLabels[2]; !found { 2520 t.Errorf("expected label 2 but found none\n") 2521 } 2522 } 2523 2524 func TestBlocksWithRenumber(t *testing.T) { 2525 if err := server.OpenTest(); err != nil { 2526 t.Fatalf("can't open test server: %v\n", err) 2527 } 2528 defer server.CloseTest() 2529 2530 uuid, _ := datastore.NewTestRepo() 2531 if len(uuid) < 5 { 2532 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 2533 } 2534 testBlockData := setupTestBlocks(t, uuid) 2535 2536 testRenumber := renumberJSON(`[3, 1, 4, 2]`) 2537 testRenumber.send(t, uuid, "labels") 2538 2539 apiStr := fmt.Sprintf("%snode/%s/labels/blocks/128_64_64/128_192_256?compression=blocks", server.WebAPIPath, uuid) 2540 respRec := server.TestHTTPResponse(t, "GET", apiStr, nil) 2541 for bnum := 0; bnum < 2; bnum++ { 2542 gotBlock, _, bx, by, bz, err := readStreamedBlock(respRec.Body, 0) 2543 if err != nil { 2544 t.Fatalf("problem reading block %d from stream: %v\n", bnum, err) 2545 } 2546 for _, label := range gotBlock.Labels { 2547 if label != 3 && label != 4 { 2548 t.Errorf("got bad block with label %d when only 3 or 4 should have been present\n", label) 2549 } 2550 } 2551 bcoord := dvid.ChunkPoint3d{bx, by, bz} 2552 var found bool 2553 for i := 0; i < 2; i++ { 2554 if bcoord == testBlockData[i].bcoord { 2555 var first, second uint64 2556 if i == 0 { 2557 first = 4 2558 second = 3 2559 } else { 2560 first = 3 2561 second = 4 2562 } 2563 found = true 2564 labelBytes, _ := gotBlock.MakeLabelVolume() 2565 labelVol, err := dvid.AliasByteToUint64(labelBytes) 2566 if err != nil { 2567 t.Fatalf("problem inflating block %s labels to []uint64: %v\n", bcoord, err) 2568 } 2569 i = 0 2570 for z := 0; z < 64; z++ { 2571 for y := 0; y < 64; y++ { 2572 for x := 0; x < 32; x++ { 2573 if labelVol[i] != first { 2574 t.Fatalf("label mismatch found at index %d, expected %d got %d\n", i, first, labelVol[i]) 2575 } 2576 i++ 2577 } 2578 for x := 32; x < 64; x++ { 2579 if labelVol[i] != second { 2580 t.Fatalf("label mismatch found at index %d, expected %d got %d\n", i, second, labelVol[i]) 2581 } 2582 i++ 2583 } 2584 } 2585 } 2586 runtime.KeepAlive(&labelBytes) 2587 } 2588 } 2589 if !found { 2590 t.Fatalf("got block %s but wasn't an expected block!\n", bcoord) 2591 } 2592 } 2593 2594 apiStr = fmt.Sprintf("%snode/%s/labels/blocks/128_64_64/128_192_256?compression=blocks", server.WebAPIPath, uuid) 2595 respRec = server.TestHTTPResponse(t, "GET", apiStr, nil) 2596 gotBlock0, _, _, _, _, err := readStreamedBlock(respRec.Body, 0) 2597 if err != nil { 2598 t.Errorf("error trying to readStreamedBlock: %v\n", err) 2599 } 2600 gotLabels := make(labels.Set) 2601 for _, label := range gotBlock0.Labels { 2602 gotLabels[label] = struct{}{} 2603 if label != 3 && label != 4 { 2604 t.Errorf("got unexpected label in block: %d\n", label) 2605 } 2606 } 2607 if _, found := gotLabels[3]; !found { 2608 t.Errorf("expected label 3 but found none\n") 2609 } 2610 if _, found := gotLabels[4]; !found { 2611 t.Errorf("expected label 4 but found none\n") 2612 } 2613 gotBlock1, _, _, _, _, err := readStreamedBlock(respRec.Body, 0) 2614 if err != nil { 2615 t.Errorf("error trying to readStreamedBlock: %v\n", err) 2616 } 2617 gotLabels = make(labels.Set) 2618 for _, label := range gotBlock1.Labels { 2619 gotLabels[label] = struct{}{} 2620 if label != 3 && label != 4 { 2621 t.Errorf("got unexpected label in block: %d\n", label) 2622 } 2623 } 2624 if _, found := gotLabels[3]; !found { 2625 t.Errorf("expected label 3 but found none\n") 2626 } 2627 if _, found := gotLabels[3]; !found { 2628 t.Errorf("expected label 4 but found none\n") 2629 } 2630 } 2631 2632 func testLabels(t *testing.T, labelsIndexed bool) { 2633 if err := server.OpenTest(); err != nil { 2634 t.Fatalf("can't open test server: %v\n", err) 2635 } 2636 defer server.CloseTest() 2637 2638 uuid, _ := datastore.NewTestRepo() 2639 if len(uuid) < 5 { 2640 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 2641 } 2642 2643 // Create a labelmap instance 2644 var config dvid.Config 2645 config.Set("BlockSize", "32,32,32") 2646 if !labelsIndexed { 2647 config.Set("IndexedLabels", "false") 2648 } 2649 server.CreateTestInstance(t, uuid, "labelmap", "labels", config) 2650 2651 vol := labelVol{ 2652 startLabel: 2, 2653 size: dvid.Point3d{5, 5, 5}, // in blocks 2654 blockSize: dvid.Point3d{32, 32, 32}, 2655 offset: dvid.Point3d{32, 64, 96}, 2656 name: "labels", 2657 } 2658 vol.postLabelVolume(t, uuid, "", "", 0) 2659 vol.testGetLabelVolume(t, uuid, "", "") 2660 2661 end := dvid.Point3d{32 + 160 - 1, 64 + 160 - 1, 96 + 160 - 1} 2662 testExtents(t, "labels", uuid, vol.offset, end) 2663 2664 // Test the blocks API 2665 vol.testBlocks(t, "GET default blocks (lz4)", uuid, "") 2666 vol.testBlocks(t, "GET uncompressed blocks", uuid, "uncompressed") 2667 vol.testBlocks(t, "GET DVID compressed label blocks", uuid, "blocks") 2668 vol.testBlocks(t, "GET gzip blocks", uuid, "gzip") 2669 2670 apiStr := fmt.Sprintf("%snode/%s/%s/blocks/64_32_32/0_0_0?compression=blocks", server.WebAPIPath, 2671 uuid, vol.name) 2672 data := server.TestHTTP(t, "GET", apiStr, nil) 2673 if len(data) != 0 { 2674 t.Fatalf("expected no data return for unset block, got %d bytes instead\n", len(data)) 2675 } 2676 apiStr = fmt.Sprintf("%snode/%s/%s/blocks/64_32_32/0_64_96?compression=blocks", server.WebAPIPath, 2677 uuid, vol.name) 2678 data = server.TestHTTP(t, "GET", apiStr, nil) 2679 blocks := decodeReturnedBlocks(t, data) 2680 if len(data) == 0 { 2681 t.Fatalf("expected on block of data, got no data in response\n") 2682 } 2683 if len(blocks) != 1 { 2684 t.Fatalf("expected one block of two blocks requested, got %d blocks instead\n", len(blocks)) 2685 } 2686 2687 // Test the "label" endpoint. 2688 apiStr = fmt.Sprintf("%snode/%s/%s/label/100_64_96", server.WebAPIPath, uuid, "labels") 2689 jsonResp := server.TestHTTP(t, "GET", apiStr, nil) 2690 var r labelResp 2691 if err := json.Unmarshal(jsonResp, &r); err != nil { 2692 t.Fatalf("Unable to parse 'label' endpoint response: %s\n", jsonResp) 2693 } 2694 if r.Label != vol.label(100, 64, 96) { 2695 t.Fatalf("Expected label %d @ (100, 64, 96) got label %d\n", vol.label(100, 64, 96), r.Label) 2696 } 2697 2698 apiStr = fmt.Sprintf("%snode/%s/%s/label/10000_64000_9600121", server.WebAPIPath, uuid, "labels") 2699 jsonResp = server.TestHTTP(t, "GET", apiStr, nil) 2700 if err := json.Unmarshal(jsonResp, &r); err != nil { 2701 t.Fatalf("Unable to parse 'label' endpoint response: %s\n", jsonResp) 2702 } 2703 if r.Label != 0 { 2704 t.Fatalf("Expected label 0 at random huge point, got label %d\n", r.Label) 2705 } 2706 2707 // Test the "labels" endpoint. 2708 apiStr = fmt.Sprintf("%snode/%s/%s/labels", server.WebAPIPath, uuid, "labels") 2709 payload := `[[100,64,96],[78,93,156],[104,65,97]]` 2710 jsonResp = server.TestHTTP(t, "GET", apiStr, bytes.NewBufferString(payload)) 2711 var labels [3]uint64 2712 if err := json.Unmarshal(jsonResp, &labels); err != nil { 2713 t.Fatalf("Unable to parse 'labels' endpoint response: %s\n", jsonResp) 2714 } 2715 if labels[0] != vol.label(100, 64, 96) { 2716 t.Fatalf("Expected label %d @ (100, 64, 96) got label %d\n", vol.label(100, 64, 96), labels[0]) 2717 } 2718 if labels[1] != vol.label(78, 93, 156) { 2719 t.Fatalf("Expected label %d @ (78, 93, 156) got label %d\n", vol.label(78, 93, 156), labels[1]) 2720 } 2721 if labels[2] != vol.label(104, 65, 97) { 2722 t.Fatalf("Expected label %d @ (104, 65, 97) got label %d\n", vol.label(104, 65, 97), labels[2]) 2723 } 2724 2725 // Repost the label volume 3 more times with increasing starting values. 2726 vol.postLabelVolume(t, uuid, "", "", 2100) 2727 vol.postLabelVolume(t, uuid, "", "", 8176) 2728 vol.postLabelVolume(t, uuid, "", "", 16623) 2729 2730 vol.testSlices(t, uuid) 2731 2732 // Try to post last volume concurrently 3x and then check result. 2733 wg := new(sync.WaitGroup) 2734 wg.Add(3) 2735 go func() { 2736 vol.postLabelVolume(t, uuid, "", "", 16623) 2737 wg.Done() 2738 }() 2739 go func() { 2740 vol.postLabelVolume(t, uuid, "", "", 16623) 2741 wg.Done() 2742 }() 2743 go func() { 2744 vol.postLabelVolume(t, uuid, "", "", 16623) 2745 wg.Done() 2746 }() 2747 wg.Wait() 2748 vol.testGetLabelVolume(t, uuid, "", "") 2749 2750 // Try concurrent write of disjoint subvolumes. 2751 vol2 := labelVol{ 2752 size: dvid.Point3d{5, 5, 5}, // in blocks 2753 blockSize: dvid.Point3d{32, 32, 32}, 2754 offset: dvid.Point3d{192, 64, 96}, 2755 name: "labels", 2756 } 2757 vol3 := labelVol{ 2758 size: dvid.Point3d{5, 5, 5}, // in blocks 2759 blockSize: dvid.Point3d{32, 32, 32}, 2760 offset: dvid.Point3d{192, 224, 96}, 2761 name: "labels", 2762 } 2763 vol4 := labelVol{ 2764 size: dvid.Point3d{5, 5, 5}, // in blocks 2765 blockSize: dvid.Point3d{32, 32, 32}, 2766 offset: dvid.Point3d{32, 224, 96}, 2767 name: "labels", 2768 } 2769 2770 wg.Add(3) 2771 go func() { 2772 vol2.postLabelVolume(t, uuid, "lz4", "", 4000) 2773 wg.Done() 2774 }() 2775 go func() { 2776 vol3.postLabelVolume(t, uuid, "lz4", "", 8000) 2777 wg.Done() 2778 }() 2779 go func() { 2780 vol4.postLabelVolume(t, uuid, "lz4", "", 1200) 2781 wg.Done() 2782 }() 2783 wg.Wait() 2784 vol.testGetLabelVolume(t, uuid, "", "") 2785 vol2.testGetLabelVolume(t, uuid, "", "") 2786 vol3.testGetLabelVolume(t, uuid, "", "") 2787 vol4.testGetLabelVolume(t, uuid, "", "") 2788 2789 // Verify various GET 3d volume with compressions and no ROI. 2790 vol.testGetLabelVolume(t, uuid, "", "") 2791 vol.testGetLabelVolume(t, uuid, "lz4", "") 2792 vol.testGetLabelVolume(t, uuid, "gzip", "") 2793 2794 // Create a new ROI instance. 2795 roiName := "myroi" 2796 server.CreateTestInstance(t, uuid, "roi", roiName, dvid.Config{}) 2797 2798 // Add ROI data 2799 apiStr = fmt.Sprintf("%snode/%s/%s/roi", server.WebAPIPath, uuid, roiName) 2800 server.TestHTTP(t, "POST", apiStr, bytes.NewBufferString(labelsJSON())) 2801 2802 // Post updated labels without ROI and make sure it returns those values. 2803 var labelNoROI uint64 = 20000 2804 vol.postLabelVolume(t, uuid, "", "", labelNoROI) 2805 returned := vol.testGetLabelVolume(t, uuid, "", "") 2806 startLabel := binary.LittleEndian.Uint64(returned[0:8]) 2807 if startLabel != labelNoROI { 2808 t.Fatalf("Expected first voxel to be label %d and got %d instead\n", labelNoROI, startLabel) 2809 } 2810 2811 // Post again but now with ROI 2812 var labelWithROI uint64 = 40000 2813 vol.postLabelVolume(t, uuid, "", roiName, labelWithROI) 2814 2815 // Verify ROI masking of POST where anything outside ROI is old labels. 2816 returned = vol.getLabelVolume(t, uuid, "", "") 2817 2818 nx := vol.size[0] * vol.blockSize[0] 2819 ny := vol.size[1] * vol.blockSize[1] 2820 nz := vol.size[2] * vol.blockSize[2] 2821 var x, y, z, v int32 2822 for z = 0; z < nz; z++ { 2823 voxz := z + vol.offset[2] 2824 blockz := voxz / vol.blockSize[2] 2825 for y = 0; y < ny; y++ { 2826 voxy := y + vol.offset[1] 2827 blocky := voxy / vol.blockSize[1] 2828 for x = 0; x < nx; x++ { 2829 voxx := x + vol.offset[0] 2830 blockx := voxx / vol.blockSize[0] 2831 incr := uint64(x>>2 + y/3 + z/3) 2832 oldlabel := labelNoROI + incr 2833 newlabel := labelWithROI + incr 2834 got := binary.LittleEndian.Uint64(returned[v : v+8]) 2835 if inroi(blockx, blocky, blockz) { 2836 if got != newlabel { 2837 t.Fatalf("Expected %d in ROI (%d,%d,%d), got %d\n", newlabel, blockx, blocky, blockz, got) 2838 } 2839 } else { 2840 if got != oldlabel { 2841 t.Fatalf("Expected %d outside ROI (%d,%d,%d), got %d\n", oldlabel, blockx, blocky, blockz, got) 2842 } 2843 } 2844 v += 8 2845 } 2846 } 2847 } 2848 2849 // Verify that a ROI-enabled GET has zeros everywhere outside ROI. 2850 returned = vol.getLabelVolume(t, uuid, "", roiName) 2851 2852 x, y, z, v = 0, 0, 0, 0 2853 for z = 0; z < nz; z++ { 2854 voxz := z + vol.offset[2] 2855 blockz := voxz / vol.blockSize[2] 2856 for y = 0; y < ny; y++ { 2857 voxy := y + vol.offset[1] 2858 blocky := voxy / vol.blockSize[1] 2859 for x = 0; x < nx; x++ { 2860 voxx := x + vol.offset[0] 2861 blockx := voxx / vol.blockSize[0] 2862 incr := uint64(x>>2 + y/3 + z/3) 2863 newlabel := labelWithROI + incr 2864 got := binary.LittleEndian.Uint64(returned[v : v+8]) 2865 if inroi(blockx, blocky, blockz) { 2866 if got != newlabel { 2867 t.Fatalf("Expected %d in ROI, got %d\n", newlabel, got) 2868 } 2869 } else { 2870 if got != 0 { 2871 t.Fatalf("Expected zero outside ROI, got %d\n", got) 2872 } 2873 } 2874 v += 8 2875 } 2876 } 2877 } 2878 2879 // Verify non-indexed instances can't access indexed endpoints. 2880 if !labelsIndexed { 2881 methods := []string{ 2882 "GET", 2883 "HEAD", 2884 "GET", 2885 "GET", 2886 "GET", 2887 "GET", 2888 "POST", 2889 "POST", 2890 "POST", 2891 } 2892 reqs := []string{ 2893 "sparsevol/20", 2894 "sparsevol/20", 2895 "sparsevol-by-point/30_89_100", 2896 "sparsevol-coarse/20", 2897 "maxlabel", 2898 "nextlabel", 2899 "nextlabel", 2900 "merge", 2901 "cleave/20", 2902 } 2903 var r io.Reader 2904 for i, req := range reqs { 2905 apiStr = fmt.Sprintf("%snode/%s/labels/%s", server.WebAPIPath, uuid, req) 2906 if methods[i] == "POST" { 2907 r = bytes.NewBufferString("junkdata that should never be used anyway") 2908 } else { 2909 r = nil 2910 } 2911 server.TestBadHTTP(t, methods[i], apiStr, r) 2912 } 2913 } 2914 } 2915 2916 func TestLabels(t *testing.T) { 2917 testLabels(t, true) 2918 } 2919 2920 func TestLabelsUnindexed(t *testing.T) { 2921 testLabels(t, false) 2922 } 2923 2924 func TestNextLabels(t *testing.T) { 2925 if err := server.OpenTest(); err != nil { 2926 t.Fatalf("can't open test server: %v\n", err) 2927 } 2928 defer server.CloseTest() 2929 2930 uuid, versionID := initTestRepo() 2931 lbls := newDataInstance(uuid, t, "mylabels") 2932 2933 if err := lbls.SetNextLabelStart(10); err != nil { 2934 t.Errorf("Error on setting next label start: %v\n", err) 2935 } 2936 expectedLabel := uint64(11) 2937 n, err := lbls.newLabel(versionID) 2938 if err != nil { 2939 t.Errorf("error on newLabel: %v\n", err) 2940 } 2941 if n != expectedLabel { 2942 t.Errorf("Expected next label to be %d, got %d\n", expectedLabel, n) 2943 } 2944 }