github.com/janelia-flyem/dvid@v1.0.0/datatype/labelarray/labelarray_test.go (about) 1 package labelarray 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 "github.com/janelia-flyem/dvid/datastore" 21 "github.com/janelia-flyem/dvid/datatype/common/downres" 22 "github.com/janelia-flyem/dvid/datatype/common/labels" 23 "github.com/janelia-flyem/dvid/dvid" 24 "github.com/janelia-flyem/dvid/server" 25 26 lz4 "github.com/janelia-flyem/go/golz4-updated" 27 ) 28 29 var ( 30 labelsT datastore.TypeService 31 testMu sync.Mutex 32 ) 33 34 // Sets package-level testRepo and TestVersionID 35 func initTestRepo() (dvid.UUID, dvid.VersionID) { 36 testMu.Lock() 37 defer testMu.Unlock() 38 if labelsT == nil { 39 var err error 40 labelsT, err = datastore.TypeServiceByName("labelarray") 41 if err != nil { 42 log.Fatalf("Can't get labelarray type: %s\n", err) 43 } 44 } 45 return datastore.NewTestRepo() 46 } 47 48 type tuple [4]int32 49 50 var labelsROI = []tuple{ 51 tuple{3, 3, 2, 4}, tuple{3, 4, 2, 3}, tuple{3, 5, 3, 3}, 52 tuple{4, 3, 2, 5}, tuple{4, 4, 3, 4}, tuple{4, 5, 2, 4}, 53 //tuple{5, 2, 3, 4}, tuple{5, 3, 3, 3}, tuple{5, 4, 2, 3}, tuple{5, 5, 2, 2}, 54 } 55 56 func labelsJSON() string { 57 b, err := json.Marshal(labelsROI) 58 if err != nil { 59 return "" 60 } 61 return string(b) 62 } 63 64 func inroi(x, y, z int32) bool { 65 for _, span := range labelsROI { 66 if span[0] != z { 67 continue 68 } 69 if span[1] != y { 70 continue 71 } 72 if span[2] > x || span[3] < x { 73 continue 74 } 75 return true 76 } 77 return false 78 } 79 80 // A slice of bytes representing 3d label volume always with first voxel at (0,0,0) 81 type testVolume struct { 82 data []byte 83 size dvid.Point3d 84 } 85 86 func newTestVolume(nx, ny, nz int32) *testVolume { 87 return &testVolume{ 88 data: make([]byte, nx*ny*nz*8), 89 size: dvid.Point3d{nx, ny, nz}, 90 } 91 } 92 93 // Add a label to a subvolume. 94 func (v *testVolume) addSubvol(origin, size dvid.Point3d, label uint64) { 95 nx := v.size[0] 96 nxy := nx * v.size[1] 97 spanBytes := size[0] * 8 98 buf := make([]byte, spanBytes) 99 for x := int32(0); x < size[0]; x++ { 100 binary.LittleEndian.PutUint64(buf[x*8:x*8+8], label) 101 } 102 for z := origin[2]; z < origin[2]+size[2]; z++ { 103 for y := origin[1]; y < origin[1]+size[1]; y++ { 104 i := (z*nxy + y*nx + origin[0]) * 8 105 copy(v.data[i:i+spanBytes], buf) 106 } 107 } 108 } 109 110 // add binary blocks, check test volume is sufficient size. 111 func (v *testVolume) addBlocks(t *testing.T, blocks []labels.BinaryBlock, label uint64) { 112 for _, block := range blocks { 113 maxx := block.Offset[0] + block.Size[0] 114 maxy := block.Offset[1] + block.Size[1] 115 maxz := block.Offset[2] + block.Size[2] 116 dvid.Infof("Adding block offset %s, size %s, # voxels %d\n", block.Offset, block.Size, len(block.Voxels)) 117 118 if maxx > v.size[0] { 119 t.Fatalf("block at offset %s, size %s exceeded test volume size %s\n", block.Offset, block.Size, v.size) 120 } 121 if maxy > v.size[1] { 122 t.Fatalf("block at offset %s, size %s exceeded test volume size %s\n", block.Offset, block.Size, v.size) 123 } 124 if maxz > v.size[2] { 125 t.Fatalf("block at offset %s, size %s exceeded test volume size %s\n", block.Offset, block.Size, v.size) 126 } 127 if len(block.Voxels) != int(block.Size.Prod()) { 128 t.Fatalf("binary block at offset %s, size %s has bad # voxels (%d)\n", block.Offset, block.Size, len(block.Voxels)) 129 } 130 bi := 0 131 for z := block.Offset[2]; z < maxz; z++ { 132 for y := block.Offset[1]; y < maxy; y++ { 133 for x := block.Offset[0]; x < maxx; x++ { 134 if block.Voxels[bi] { 135 if x == 16 && y == 40 && z == 8 { 136 dvid.Infof("voxel found and is indeed on\n") 137 } 138 i := (z*v.size[0]*v.size[1] + y*v.size[0] + x) * 8 139 binary.LittleEndian.PutUint64(v.data[i:i+8], label) 140 } 141 bi++ 142 } 143 } 144 } 145 } 146 } 147 148 // downres assumes only binary volume of some label N or label 0. 149 func (v *testVolume) downres(scale uint8) { 150 sz := int32(1 << scale) 151 nx := v.size[0] >> scale 152 ny := v.size[1] >> scale 153 nz := v.size[2] >> scale 154 155 var oldpos, x, y, z int32 156 for z = 0; z < v.size[2]; z++ { 157 newz := z >> scale 158 for y = 0; y < v.size[1]; y++ { 159 newy := y >> scale 160 for x = 0; x < v.size[0]; x++ { 161 label := binary.LittleEndian.Uint64(v.data[oldpos*8 : oldpos*8+8]) 162 oldpos++ 163 if label != 0 || (x%sz == 0 && y%sz == 0 && z%sz == 0) { 164 newx := x >> scale 165 newpos := newz*nx*ny + newy*nx + newx 166 binary.LittleEndian.PutUint64(v.data[newpos*8:newpos*8+8], label) 167 } 168 } 169 } 170 } 171 v.size[0] = nx 172 v.size[1] = ny 173 v.size[2] = nz 174 v.data = v.data[:nx*ny*nz*8] 175 } 176 177 // Put label data into given data instance. 178 func (v *testVolume) put(t *testing.T, uuid dvid.UUID, name string) { 179 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/0_0_0", server.WebAPIPath, 180 uuid, name, v.size[0], v.size[1], v.size[2]) 181 server.TestHTTP(t, "POST", apiStr, bytes.NewBuffer(v.data)) 182 } 183 184 func (v *testVolume) putMutable(t *testing.T, uuid dvid.UUID, name string) { 185 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/0_0_0?mutate=true", server.WebAPIPath, 186 uuid, name, v.size[0], v.size[1], v.size[2]) 187 server.TestHTTP(t, "POST", apiStr, bytes.NewBuffer(v.data)) 188 } 189 190 func (v *testVolume) get(t *testing.T, uuid dvid.UUID, name string) { 191 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/0_0_0", server.WebAPIPath, 192 uuid, name, v.size[0], v.size[1], v.size[2]) 193 v.data = server.TestHTTP(t, "GET", apiStr, nil) 194 } 195 196 func (v *testVolume) getScale(t *testing.T, uuid dvid.UUID, name string, scale uint8) { 197 t.Logf("Got scale %d label data %q\n", scale, name) 198 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/0_0_0?scale=%d", server.WebAPIPath, 199 uuid, name, v.size[0], v.size[1], v.size[2], scale) 200 v.data = server.TestHTTP(t, "GET", apiStr, nil) 201 t.Logf("Returning from getScale scale=%d\n", scale) 202 } 203 204 func (v *testVolume) getVoxel(pt dvid.Point3d) uint64 { 205 nx := v.size[0] 206 nxy := nx * v.size[1] 207 i := (pt[2]*nxy + pt[1]*nx + pt[0]) * 8 208 return binary.LittleEndian.Uint64(v.data[i : i+8]) 209 } 210 211 func (v *testVolume) verifyLabel(t *testing.T, expected uint64, x, y, z int32) { 212 pt := dvid.Point3d{x, y, z} 213 label := v.getVoxel(pt) 214 if label != expected { 215 _, fn, line, _ := runtime.Caller(1) 216 t.Errorf("Expected label %d at %s for first downres but got %d instead [%s:%d]\n", expected, pt, label, fn, line) 217 } 218 } 219 220 func (v *testVolume) equals(v2 *testVolume) error { 221 if !v.size.Equals(v2.size) { 222 return fmt.Errorf("volume sizes are not equal") 223 } 224 if len(v.data) != len(v2.data) { 225 return fmt.Errorf("data lengths are not equal") 226 } 227 var i uint64 228 var x, y, z int32 229 for z = 0; z < v.size[2]; z++ { 230 for y = 0; y < v.size[1]; y++ { 231 for x = 0; x < v.size[0]; x++ { 232 val1 := binary.LittleEndian.Uint64(v.data[i : i+8]) 233 val2 := binary.LittleEndian.Uint64(v2.data[i : i+8]) 234 i += 8 235 if val1 != val2 { 236 return fmt.Errorf("For voxel (%d,%d,%d), found value %d != %d\n", x, y, z, val2, val1) 237 } 238 } 239 } 240 } 241 return nil 242 } 243 244 func (v *testVolume) testBlock(t *testing.T, context string, bx, by, bz int32, data []byte) { 245 if len(data) < int(DefaultBlockSize*DefaultBlockSize*DefaultBlockSize*8) { 246 t.Fatalf("[%s] got bad uint64 array of len %d for block (%d, %d, %d)\n", context, len(data), bx, by, bz) 247 } 248 p := 0 249 var x, y, z, numerrors int32 250 for z = 0; z < DefaultBlockSize; z++ { 251 vz := bz*DefaultBlockSize + z 252 for y = 0; y < DefaultBlockSize; y++ { 253 vy := by*DefaultBlockSize + y 254 for x = 0; x < DefaultBlockSize; x++ { 255 vx := bx*DefaultBlockSize + x 256 i := (vz*v.size[0]*v.size[1] + vy*v.size[0] + vx) * 8 257 label := binary.LittleEndian.Uint64(v.data[i : i+8]) 258 got := binary.LittleEndian.Uint64(data[p : p+8]) 259 if label != got && numerrors < 5 { 260 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) 261 numerrors++ 262 } 263 p += 8 264 } 265 } 266 } 267 } 268 269 func (v *testVolume) testGetBlocks(t *testing.T, context string, uuid dvid.UUID, name, compression string, scale uint8) { 270 apiStr := fmt.Sprintf("%snode/%s/%s/blocks/%d_%d_%d/0_0_0", server.WebAPIPath, uuid, name, v.size[0], v.size[1], v.size[2]) 271 var qstrs []string 272 if compression != "" { 273 qstrs = append(qstrs, "compression="+compression) 274 } 275 if scale > 0 { 276 qstrs = append(qstrs, fmt.Sprintf("scale=%d", scale)) 277 } 278 if len(qstrs) > 0 { 279 apiStr += "?" + strings.Join(qstrs, "&") 280 } 281 data := server.TestHTTP(t, "GET", apiStr, nil) 282 blockBytes := DefaultBlockSize * DefaultBlockSize * DefaultBlockSize * 8 283 284 var b int 285 for { 286 // Get block coord + block size 287 if b+16 > len(data) { 288 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) 289 } 290 x := int32(binary.LittleEndian.Uint32(data[b : b+4])) 291 b += 4 292 y := int32(binary.LittleEndian.Uint32(data[b : b+4])) 293 b += 4 294 z := int32(binary.LittleEndian.Uint32(data[b : b+4])) 295 b += 4 296 n := int(binary.LittleEndian.Uint32(data[b : b+4])) 297 b += 4 298 299 var uncompressed []byte 300 switch compression { 301 case "uncompressed": 302 uncompressed = data[b : b+n] 303 case "", "lz4": 304 uncompressed = make([]byte, blockBytes) 305 if err := lz4.Uncompress(data[b:b+n], uncompressed); err != nil { 306 t.Fatalf("Unable to uncompress LZ4 data (%s), %d bytes: %v\n", apiStr, n, err) 307 } 308 case "gzip", "blocks": 309 gzipIn := bytes.NewBuffer(data[b : b+n]) 310 zr, err := gzip.NewReader(gzipIn) 311 if err != nil { 312 t.Fatalf("can't uncompress gzip block: %v\n", err) 313 } 314 uncompressed, err = ioutil.ReadAll(zr) 315 if err != nil { 316 t.Fatalf("can't uncompress gzip block: %v\n", err) 317 } 318 zr.Close() 319 320 if compression == "blocks" { 321 var block labels.Block 322 if err = block.UnmarshalBinary(uncompressed); err != nil { 323 t.Errorf("unable to deserialize label block (%d, %d, %d): %v\n", x, y, z, err) 324 } 325 uint64array, size := block.MakeLabelVolume() 326 if !size.Equals(dvid.Point3d{DefaultBlockSize, DefaultBlockSize, DefaultBlockSize}) { 327 t.Fatalf("got bad block size from deserialized block (%d, %d, %d): %s\n", x, y, z, size) 328 } 329 uncompressed = uint64array 330 } 331 } 332 v.testBlock(t, context, x, y, z, uncompressed) 333 b += n 334 if b == len(data) { 335 break 336 } 337 } 338 } 339 340 type mergeJSON string 341 342 func (mjson mergeJSON) send(t *testing.T, uuid dvid.UUID, name string) { 343 apiStr := fmt.Sprintf("%snode/%s/%s/merge", server.WebAPIPath, uuid, name) 344 server.TestHTTP(t, "POST", apiStr, bytes.NewBufferString(string(mjson))) 345 } 346 347 func checkLabels(t *testing.T, text string, expected, got []byte) { 348 if len(expected) != len(got) { 349 t.Errorf("%s byte mismatch: expected %d bytes, got %d bytes\n", text, len(expected), len(got)) 350 } 351 expectLabels, err := dvid.AliasByteToUint64(expected) 352 if err != nil { 353 t.Fatal(err) 354 } 355 gotLabels, err := dvid.AliasByteToUint64(got) 356 if err != nil { 357 t.Fatal(err) 358 } 359 for i, val := range gotLabels { 360 if expectLabels[i] != val { 361 t.Errorf("%s label mismatch found at index %d, expected %d, got %d\n", text, i, expectLabels[i], val) 362 return 363 } 364 } 365 runtime.KeepAlive(&expected) 366 runtime.KeepAlive(&got) 367 } 368 369 type labelVol struct { 370 size dvid.Point3d 371 blockSize dvid.Point3d 372 offset dvid.Point3d 373 name string 374 data []byte 375 376 nx, ny, nz int32 377 378 startLabel uint64 379 } 380 381 func (vol *labelVol) makeLabelVolume(t *testing.T, uuid dvid.UUID, startLabel uint64) { 382 if vol.startLabel == startLabel && vol.data != nil { 383 return 384 } 385 386 vol.startLabel = startLabel 387 388 vol.nx = vol.size[0] * vol.blockSize[0] 389 vol.ny = vol.size[1] * vol.blockSize[1] 390 vol.nz = vol.size[2] * vol.blockSize[2] 391 392 vol.data = make([]byte, vol.numBytes()) 393 var x, y, z, v int32 394 for z = 0; z < vol.nz; z++ { 395 lz := z / 3 396 for y = 0; y < vol.ny; y++ { 397 ly := y / 3 398 for x = 0; x < vol.nx; x++ { 399 label := startLabel + uint64(x>>2+ly+lz) 400 binary.LittleEndian.PutUint64(vol.data[v:v+8], label) 401 v += 8 402 } 403 } 404 } 405 return 406 } 407 408 func (vol *labelVol) numBytes() int32 { 409 return vol.nx * vol.ny * vol.nz * 8 410 } 411 412 // Create a new label volume and post it to the test datastore. 413 // Each voxel in volume has sequential labels in X, Y, then Z order. 414 func (vol *labelVol) postLabelVolume(t *testing.T, uuid dvid.UUID, compression, roi string, startLabel uint64) { 415 vol.makeLabelVolume(t, uuid, startLabel) 416 417 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/%d_%d_%d", server.WebAPIPath, 418 uuid, vol.name, vol.nx, vol.ny, vol.nz, vol.offset[0], vol.offset[1], vol.offset[2]) 419 query := true 420 421 var data []byte 422 var err error 423 switch compression { 424 case "lz4": 425 apiStr += "?compression=lz4" 426 compressed := make([]byte, lz4.CompressBound(vol.data)) 427 var outSize int 428 if outSize, err = lz4.Compress(vol.data, compressed); err != nil { 429 t.Fatal(err) 430 } 431 data = compressed[:outSize] 432 case "gzip": 433 apiStr += "?compression=gzip" 434 var buf bytes.Buffer 435 gw := gzip.NewWriter(&buf) 436 if _, err = gw.Write(vol.data); err != nil { 437 t.Fatal(err) 438 } 439 data = buf.Bytes() 440 if err = gw.Close(); err != nil { 441 t.Fatal(err) 442 } 443 default: 444 data = vol.data 445 query = false 446 } 447 if roi != "" { 448 if query { 449 apiStr += "&roi=" + roi 450 } else { 451 apiStr += "?roi=" + roi 452 } 453 } 454 server.TestHTTP(t, "POST", apiStr, bytes.NewBuffer(data)) 455 } 456 457 func (vol *labelVol) getLabelVolume(t *testing.T, uuid dvid.UUID, compression, roi string) []byte { 458 apiStr := fmt.Sprintf("%snode/%s/%s/raw/0_1_2/%d_%d_%d/%d_%d_%d", server.WebAPIPath, 459 uuid, vol.name, vol.nx, vol.ny, vol.nz, vol.offset[0], vol.offset[1], vol.offset[2]) 460 query := true 461 switch compression { 462 case "lz4": 463 apiStr += "?compression=lz4" 464 case "gzip": 465 apiStr += "?compression=gzip" 466 default: 467 query = false 468 } 469 if roi != "" { 470 if query { 471 apiStr += "&roi=" + roi 472 } else { 473 apiStr += "?roi=" + roi 474 } 475 } 476 data := server.TestHTTP(t, "GET", apiStr, nil) 477 switch compression { 478 case "lz4": 479 uncompressed := make([]byte, vol.numBytes()) 480 if err := lz4.Uncompress(data, uncompressed); err != nil { 481 t.Fatalf("Unable to uncompress LZ4 data (%s), %d bytes: %v\n", apiStr, len(data), err) 482 } 483 data = uncompressed 484 case "gzip": 485 buf := bytes.NewBuffer(data) 486 gr, err := gzip.NewReader(buf) 487 if err != nil { 488 t.Fatalf("Error on gzip new reader: %v\n", err) 489 } 490 uncompressed, err := ioutil.ReadAll(gr) 491 if err != nil { 492 t.Fatalf("Error on reading gzip: %v\n", err) 493 } 494 if err = gr.Close(); err != nil { 495 t.Fatalf("Error on closing gzip: %v\n", err) 496 } 497 data = uncompressed 498 default: 499 } 500 if len(data) != int(vol.numBytes()) { 501 t.Errorf("Expected %d uncompressed bytes from 3d labelarray GET. Got %d instead.", vol.numBytes(), len(data)) 502 } 503 return data 504 } 505 506 func (vol *labelVol) testGetLabelVolume(t *testing.T, uuid dvid.UUID, compression, roi string) []byte { 507 data := vol.getLabelVolume(t, uuid, compression, roi) 508 509 // run test to make sure it's same volume as we posted. 510 var x, y, z, v int32 511 for z = 0; z < vol.nz; z++ { 512 lz := z / 3 513 for y = 0; y < vol.ny; y++ { 514 ly := y / 3 515 for x = 0; x < vol.nx; x++ { 516 label := vol.startLabel + uint64(x>>2+ly+lz) 517 got := binary.LittleEndian.Uint64(data[v : v+8]) 518 if label != got { 519 t.Errorf("Error on 3d GET compression (%q): expected %d, got %d\n", compression, label, got) 520 goto foundError 521 } 522 v += 8 523 } 524 } 525 } 526 527 foundError: 528 529 checkLabels(t, "testing label volume", vol.data, data) 530 531 return data 532 } 533 534 func (vol *labelVol) testBlock(t *testing.T, context string, bx, by, bz int32, data []byte) { 535 // Get offset of this block in label volume 536 ox := bx*vol.blockSize[0] - vol.offset[0] 537 oy := by*vol.blockSize[1] - vol.offset[1] 538 oz := bz*vol.blockSize[2] - vol.offset[2] 539 540 if int64(len(data)) < vol.blockSize.Prod()*8 { 541 t.Fatalf("[%s] got bad uint64 array of len %d for block (%d, %d, %d)\n", context, len(data), bx, by, bz) 542 } 543 p := 0 544 var x, y, z, numerrors int32 545 for z = 0; z < 32; z++ { 546 lz := (z + oz) / 3 547 for y = 0; y < 32; y++ { 548 ly := (y + oy) / 3 549 for x = 0; x < 32; x++ { 550 lx := x + ox 551 label := vol.startLabel + uint64(lx>>2+ly+lz) 552 got := binary.LittleEndian.Uint64(data[p : p+8]) 553 if label != got && numerrors < 5 { 554 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) 555 numerrors++ 556 } 557 p += 8 558 } 559 } 560 } 561 } 562 563 func (vol *labelVol) testBlocks(t *testing.T, context string, uuid dvid.UUID, compression string) { 564 span := 5 565 apiStr := fmt.Sprintf("%snode/%s/%s/blocks/%d_%d_%d/%d_%d_%d", server.WebAPIPath, 566 uuid, vol.name, 160, 32, 32, vol.offset[0], vol.offset[1], vol.offset[2]) 567 var qstrs []string 568 if compression != "" { 569 qstrs = append(qstrs, "compression="+compression) 570 } 571 if len(qstrs) > 0 { 572 apiStr += "?" + strings.Join(qstrs, "&") 573 } 574 data := server.TestHTTP(t, "GET", apiStr, nil) 575 576 blockBytes := int(vol.blockSize.Prod() * 8) 577 578 // Check if values are what we expect 579 bx := vol.offset[0] / vol.blockSize[0] 580 by := vol.offset[1] / vol.blockSize[1] 581 bz := vol.offset[2] / vol.blockSize[2] 582 b := 0 583 for i := 0; i < span; i++ { 584 // Get block coord + block size 585 if b+16 > len(data) { 586 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) 587 } 588 x := int32(binary.LittleEndian.Uint32(data[b : b+4])) 589 b += 4 590 y := int32(binary.LittleEndian.Uint32(data[b : b+4])) 591 b += 4 592 z := int32(binary.LittleEndian.Uint32(data[b : b+4])) 593 b += 4 594 n := int(binary.LittleEndian.Uint32(data[b : b+4])) 595 b += 4 596 if x != bx || y != by || z != bz { 597 t.Fatalf("Bad block coordinate: expected (%d,%d,%d), got (%d,%d,%d)\n", bx, by, bz, x, y, z) 598 } 599 600 // Read in the block data as assumed LZ4 and check it. 601 var uncompressed []byte 602 switch compression { 603 case "uncompressed": 604 uncompressed = data[b : b+n] 605 case "", "lz4": 606 uncompressed = make([]byte, blockBytes) 607 if err := lz4.Uncompress(data[b:b+n], uncompressed); err != nil { 608 t.Fatalf("Unable to uncompress LZ4 data (%s), %d bytes: %v\n", apiStr, n, err) 609 } 610 case "gzip", "blocks": 611 gzipIn := bytes.NewBuffer(data[b : b+n]) 612 zr, err := gzip.NewReader(gzipIn) 613 if err != nil { 614 t.Fatalf("can't uncompress gzip block: %v\n", err) 615 } 616 uncompressed, err = ioutil.ReadAll(zr) 617 if err != nil { 618 t.Fatalf("can't uncompress gzip block: %v\n", err) 619 } 620 zr.Close() 621 622 if compression == "blocks" { 623 var block labels.Block 624 if err = block.UnmarshalBinary(uncompressed); err != nil { 625 t.Errorf("unable to deserialize label block (%d, %d, %d): %v\n", x, y, z, err) 626 } 627 uint64array, size := block.MakeLabelVolume() 628 if !size.Equals(vol.blockSize) { 629 t.Fatalf("got bad block size from deserialized block (%d, %d, %d): %s\n", x, y, z, size) 630 } 631 uncompressed = uint64array 632 } 633 } 634 vol.testBlock(t, context, x, y, z, uncompressed) 635 b += n 636 bx++ 637 } 638 } 639 640 // the label in the test volume should just be the start label + voxel index + 1 when iterating in ZYX order. 641 // The passed (x,y,z) should be world coordinates, not relative to the volume offset. 642 func (vol *labelVol) label(x, y, z int32) uint64 { 643 if x < vol.offset[0] || x >= vol.offset[0]+vol.size[0]*vol.blockSize[0] { 644 return 0 645 } 646 if y < vol.offset[1] || y >= vol.offset[1]+vol.size[1]*vol.blockSize[1] { 647 return 0 648 } 649 if z < vol.offset[2] || z >= vol.offset[2]+vol.size[2]*vol.blockSize[2] { 650 return 0 651 } 652 x -= vol.offset[0] 653 y -= vol.offset[1] 654 z -= vol.offset[2] 655 return vol.startLabel + uint64(x>>2+y/3+z/3) 656 } 657 658 type sliceTester struct { 659 orient string 660 width int32 661 height int32 662 offset dvid.Point3d // offset of slice 663 } 664 665 func (s sliceTester) apiStr(uuid dvid.UUID, name string) string { 666 return fmt.Sprintf("%snode/%s/%s/raw/%s/%d_%d/%d_%d_%d", server.WebAPIPath, 667 uuid, name, s.orient, s.width, s.height, s.offset[0], s.offset[1], s.offset[2]) 668 } 669 670 // make sure the given labels match what would be expected from the test volume. 671 func (s sliceTester) testLabel(t *testing.T, vol labelVol, img *dvid.Image) { 672 data := img.Data() 673 var x, y, z int32 674 i := 0 675 switch s.orient { 676 case "xy": 677 for y = 0; y < s.height; y++ { 678 for x = 0; x < s.width; x++ { 679 label := binary.LittleEndian.Uint64(data[i*8 : (i+1)*8]) 680 i++ 681 vx := x + s.offset[0] 682 vy := y + s.offset[1] 683 vz := s.offset[2] 684 expected := vol.label(vx, vy, vz) 685 if label != expected { 686 t.Errorf("Bad label @ (%d,%d,%d): expected %d, got %d\n", vx, vy, vz, expected, label) 687 return 688 } 689 } 690 } 691 return 692 case "xz": 693 for z = 0; z < s.height; z++ { 694 for x = 0; x < s.width; x++ { 695 label := binary.LittleEndian.Uint64(data[i*8 : (i+1)*8]) 696 i++ 697 vx := x + s.offset[0] 698 vy := s.offset[1] 699 vz := z + s.offset[2] 700 expected := vol.label(vx, vy, vz) 701 if label != expected { 702 t.Errorf("Bad label @ (%d,%d,%d): expected %d, got %d\n", vx, vy, vz, expected, label) 703 return 704 } 705 } 706 } 707 return 708 case "yz": 709 for z = 0; z < s.height; z++ { 710 for y = 0; y < s.width; y++ { 711 label := binary.LittleEndian.Uint64(data[i*8 : (i+1)*8]) 712 i++ 713 vx := s.offset[0] 714 vy := y + s.offset[1] 715 vz := z + s.offset[2] 716 expected := vol.label(vx, vy, vz) 717 if label != expected { 718 t.Errorf("Bad label @ (%d,%d,%d): expected %d, got %d\n", vx, vy, vz, expected, label) 719 return 720 } 721 } 722 } 723 return 724 default: 725 t.Fatalf("Unknown slice orientation %q\n", s.orient) 726 } 727 } 728 729 func (vol labelVol) testSlices(t *testing.T, uuid dvid.UUID) { 730 // Verify XY slice. 731 sliceOffset := vol.offset 732 sliceOffset[0] += 51 733 sliceOffset[1] += 11 734 sliceOffset[2] += 23 735 slice := sliceTester{"xy", 67, 83, sliceOffset} 736 apiStr := slice.apiStr(uuid, vol.name) 737 xy := server.TestHTTP(t, "GET", apiStr, nil) 738 img, format, err := dvid.ImageFromBytes(xy, EncodeFormat(), false) 739 if err != nil { 740 t.Fatalf("Error on XY labels GET: %v\n", err) 741 } 742 if format != "png" { 743 t.Errorf("Expected XY labels GET to return %q image, got %q instead.\n", "png", format) 744 } 745 if img.NumBytes() != 67*83*8 { 746 t.Errorf("Expected %d bytes from XY labelarray GET. Got %d instead.", 160*160*8, img.NumBytes()) 747 } 748 slice.testLabel(t, vol, img) 749 750 // Verify XZ slice returns what we expect. 751 sliceOffset = vol.offset 752 sliceOffset[0] += 11 753 sliceOffset[1] += 4 754 sliceOffset[2] += 3 755 slice = sliceTester{"xz", 67, 83, sliceOffset} 756 apiStr = slice.apiStr(uuid, vol.name) 757 xz := server.TestHTTP(t, "GET", apiStr, nil) 758 img, format, err = dvid.ImageFromBytes(xz, EncodeFormat(), false) 759 if err != nil { 760 t.Fatalf("Error on XZ labels GET: %v\n", err) 761 } 762 if format != "png" { 763 t.Errorf("Expected XZ labels GET to return %q image, got %q instead.\n", "png", format) 764 } 765 if img.NumBytes() != 67*83*8 { 766 t.Errorf("Expected %d bytes from XZ labelarray GET. Got %d instead.", 67*83*8, img.NumBytes()) 767 } 768 slice.testLabel(t, vol, img) 769 770 // Verify YZ slice returns what we expect. 771 sliceOffset = vol.offset 772 sliceOffset[0] += 7 773 sliceOffset[1] += 33 774 sliceOffset[2] += 33 775 slice = sliceTester{"yz", 67, 83, sliceOffset} 776 apiStr = slice.apiStr(uuid, vol.name) 777 yz := server.TestHTTP(t, "GET", apiStr, nil) 778 img, format, err = dvid.ImageFromBytes(yz, EncodeFormat(), false) 779 if err != nil { 780 t.Fatalf("Error on YZ labels GET: %v\n", err) 781 } 782 if format != "png" { 783 t.Errorf("Expected YZ labels GET to return %q image, got %q instead.\n", "png", format) 784 } 785 if img.NumBytes() != 67*83*8 { 786 t.Errorf("Expected %d bytes from YZ labelarray GET. Got %d instead.", 67*83*8, img.NumBytes()) 787 } 788 slice.testLabel(t, vol, img) 789 } 790 791 type labelResp struct { 792 Label uint64 793 } 794 795 // Data from which to construct repeatable 3d images where adjacent voxels have different values. 796 var xdata = []uint64{23, 819229, 757, 100303, 9991} 797 var ydata = []uint64{66599, 201, 881067, 5488, 0} 798 var zdata = []uint64{1, 734, 43990122, 42, 319596} 799 800 // Make a 2d slice of bytes with top left corner at (ox,oy,oz) and size (nx,ny) 801 func makeSlice(offset dvid.Point3d, size dvid.Point2d) []byte { 802 numBytes := size[0] * size[1] * 8 803 slice := make([]byte, numBytes, numBytes) 804 i := 0 805 modz := offset[2] % int32(len(zdata)) 806 for y := int32(0); y < size[1]; y++ { 807 sy := y + offset[1] 808 mody := sy % int32(len(ydata)) 809 sx := offset[0] 810 for x := int32(0); x < size[0]; x++ { 811 modx := sx % int32(len(xdata)) 812 binary.BigEndian.PutUint64(slice[i:i+8], xdata[modx]+ydata[mody]+zdata[modz]) 813 i += 8 814 sx++ 815 } 816 } 817 return slice 818 } 819 820 // Make a 3d volume of bytes with top left corner at (ox,oy,oz) and size (nx, ny, nz) 821 func makeVolume(offset, size dvid.Point3d) []byte { 822 sliceBytes := size[0] * size[1] * 8 823 volumeBytes := sliceBytes * size[2] 824 volume := make([]byte, volumeBytes, volumeBytes) 825 var i int32 826 size2d := dvid.Point2d{size[0], size[1]} 827 startZ := offset[2] 828 endZ := startZ + size[2] 829 for z := startZ; z < endZ; z++ { 830 offset[2] = z 831 copy(volume[i:i+sliceBytes], makeSlice(offset, size2d)) 832 i += sliceBytes 833 } 834 return volume 835 } 836 837 // Creates a new data instance for labelarray 838 func newDataInstance(uuid dvid.UUID, t *testing.T, name dvid.InstanceName) *Data { 839 config := dvid.NewConfig() 840 dataservice, err := datastore.NewData(uuid, labelsT, name, config) 841 if err != nil { 842 t.Fatalf("Unable to create labelarray instance %q: %v\n", name, err) 843 } 844 labels, ok := dataservice.(*Data) 845 if !ok { 846 t.Fatalf("Can't cast labels data service into Data\n") 847 } 848 return labels 849 } 850 851 func TestBigNums(t *testing.T) { 852 if err := server.OpenTest(); err != nil { 853 t.Fatalf("can't open test server: %v\n", err) 854 } 855 defer server.CloseTest() 856 857 uuid, v := initTestRepo() 858 lbls := newDataInstance(uuid, t, "mylabels") 859 bigPt := dvid.Point3d{-2147483648, -2147483648, -2147483648} 860 _, err := lbls.GetLabelBytesAtScaledPoint(v, bigPt, 0) 861 if err != nil { 862 t.Errorf("problem getting bytes at %s: %v\n", bigPt, err) 863 } 864 } 865 866 func TestLabelarrayDirectAPI(t *testing.T) { 867 if err := server.OpenTest(); err != nil { 868 t.Fatalf("can't open test server: %v\n", err) 869 } 870 defer server.CloseTest() 871 872 uuid, versionID := initTestRepo() 873 lbls := newDataInstance(uuid, t, "mylabels") 874 labelsCtx := datastore.NewVersionedCtx(lbls, versionID) 875 876 // Create a fake block-aligned label volume 877 offset := dvid.Point3d{DefaultBlockSize, 0, DefaultBlockSize} 878 size := dvid.Point3d{2 * DefaultBlockSize, DefaultBlockSize, 3 * DefaultBlockSize} 879 subvol := dvid.NewSubvolume(offset, size) 880 data := makeVolume(offset, size) 881 882 // Store it into datastore at root 883 v, err := lbls.NewVoxels(subvol, data) 884 if err != nil { 885 t.Fatalf("Unable to make new labels Voxels: %v\n", err) 886 } 887 if err = lbls.IngestVoxels(versionID, 1, v, ""); err != nil { 888 t.Fatalf("Unable to put labels for %s: %v\n", labelsCtx, err) 889 } 890 if v.NumVoxels() != int64(len(data))/8 { 891 t.Errorf("# voxels (%d) after PutVoxels != # original voxels (%d)\n", 892 v.NumVoxels(), int64(len(data))/8) 893 } 894 895 // Read the stored image 896 v2, err := lbls.NewVoxels(subvol, nil) 897 if err != nil { 898 t.Fatalf("Unable to make new labels ExtHandler: %v\n", err) 899 } 900 if err = lbls.GetVoxels(versionID, v2, ""); err != nil { 901 t.Fatalf("Unable to get voxels for %s: %v\n", labelsCtx, err) 902 } 903 904 // Make sure the retrieved image matches the original 905 if v.Stride() != v2.Stride() { 906 t.Errorf("Stride in retrieved subvol incorrect\n") 907 } 908 if v.Interpolable() != v2.Interpolable() { 909 t.Errorf("Interpolable bool in retrieved subvol incorrect\n") 910 } 911 if !reflect.DeepEqual(v.Size(), v2.Size()) { 912 t.Errorf("Size in retrieved subvol incorrect: %s vs expected %s\n", 913 v2.Size(), v.Size()) 914 } 915 if v.NumVoxels() != v2.NumVoxels() { 916 t.Errorf("# voxels in retrieved is different: %d vs expected %d\n", 917 v2.NumVoxels(), v.NumVoxels()) 918 } 919 byteData := v2.Data() 920 921 for i := int64(0); i < v2.NumVoxels()*8; i++ { 922 if byteData[i] != data[i] { 923 t.Logf("Size of data: %d bytes from GET, %d bytes in PUT\n", len(data), len(data)) 924 t.Fatalf("GET subvol (%d) != PUT subvol (%d) @ uint64 #%d", byteData[i], data[i], i) 925 } 926 } 927 } 928 929 func TestLabelarrayRepoPersistence(t *testing.T) { 930 if err := server.OpenTest(); err != nil { 931 t.Fatalf("can't open test server: %v\n", err) 932 } 933 defer server.CloseTest() 934 935 uuid, _ := initTestRepo() 936 937 // Make labels and set various properties 938 config := dvid.NewConfig() 939 config.Set("BlockSize", "12,13,14") 940 config.Set("VoxelSize", "1.1,2.8,11") 941 config.Set("VoxelUnits", "microns,millimeters,nanometers") 942 config.Set("CountLabels", "false") 943 config.Set("MaxDownresLevel", "5") 944 dataservice, err := datastore.NewData(uuid, labelsT, "mylabels", config) 945 if err != nil { 946 t.Errorf("Unable to create labels instance: %v\n", err) 947 } 948 lbls, ok := dataservice.(*Data) 949 if !ok { 950 t.Errorf("Can't cast labels data service into Data\n") 951 } 952 if lbls.CountLabels { 953 t.Errorf("expected CountLabels to be set false, not default true\n") 954 } 955 if !lbls.IndexedLabels { 956 t.Errorf("expected IndexedLabels to be true for default but was false\n") 957 } 958 if lbls.MaxDownresLevel != 5 { 959 t.Errorf("expected MaxDownresLevel to be 5, not %d\n", lbls.MaxDownresLevel) 960 } 961 oldData := *lbls 962 963 // Restart test datastore and see if datasets are still there. 964 if err = datastore.SaveDataByUUID(uuid, lbls); err != nil { 965 t.Fatalf("Unable to save repo during labels persistence test: %v\n", err) 966 } 967 datastore.CloseReopenTest() 968 969 dataservice2, err := datastore.GetDataByUUIDName(uuid, "mylabels") 970 if err != nil { 971 t.Fatalf("Can't get labels instance from reloaded test db: %v\n", err) 972 } 973 lbls2, ok := dataservice2.(*Data) 974 if !ok { 975 t.Errorf("Returned new data instance 2 is not imageblk.Data\n") 976 } 977 if !oldData.Equals(lbls2) { 978 t.Errorf("Expected %v, got %v\n", oldData, *lbls2) 979 } 980 if lbls2.MaxDownresLevel != 5 { 981 t.Errorf("Bad MaxDownresLevel: %d\n", lbls2.MaxDownresLevel) 982 } 983 if lbls2.CountLabels != false { 984 t.Errorf("Bad CountLabels: %v\n", lbls2.CountLabels) 985 } 986 } 987 988 func TestMultiscaleIngest(t *testing.T) { 989 if err := server.OpenTest(); err != nil { 990 t.Fatalf("can't open test server: %v\n", err) 991 } 992 defer server.CloseTest() 993 994 // Create testbed volume and data instances 995 uuid, _ := initTestRepo() 996 var config dvid.Config 997 config.Set("MaxDownresLevel", "2") 998 server.CreateTestInstance(t, uuid, "labelarray", "labels", config) 999 1000 // Create an easily interpreted label volume with a couple of labels. 1001 volume := newTestVolume(128, 128, 128) 1002 volume.addSubvol(dvid.Point3d{40, 40, 40}, dvid.Point3d{40, 40, 40}, 1) 1003 volume.addSubvol(dvid.Point3d{40, 40, 80}, dvid.Point3d{40, 40, 40}, 2) 1004 volume.addSubvol(dvid.Point3d{80, 40, 40}, dvid.Point3d{40, 40, 40}, 13) 1005 volume.addSubvol(dvid.Point3d{40, 80, 40}, dvid.Point3d{40, 40, 40}, 209) 1006 volume.addSubvol(dvid.Point3d{80, 80, 40}, dvid.Point3d{40, 40, 40}, 311) 1007 volume.put(t, uuid, "labels") 1008 1009 // Verify initial ingest for hi-res 1010 if err := downres.BlockOnUpdating(uuid, "labels"); err != nil { 1011 t.Fatalf("Error blocking on update for labels: %v\n", err) 1012 } 1013 hires := newTestVolume(128, 128, 128) 1014 hires.get(t, uuid, "labels") 1015 hires.verifyLabel(t, 1, 45, 45, 45) 1016 hires.verifyLabel(t, 2, 50, 50, 100) 1017 hires.verifyLabel(t, 13, 100, 60, 60) 1018 hires.verifyLabel(t, 209, 55, 100, 55) 1019 hires.verifyLabel(t, 311, 81, 81, 41) 1020 1021 // Check the first downres: 64^3 1022 downres1 := newTestVolume(64, 64, 64) 1023 downres1.getScale(t, uuid, "labels", 1) 1024 downres1.verifyLabel(t, 1, 30, 30, 30) 1025 downres1.verifyLabel(t, 2, 21, 21, 45) 1026 downres1.verifyLabel(t, 13, 45, 21, 36) 1027 downres1.verifyLabel(t, 209, 21, 50, 35) 1028 downres1.verifyLabel(t, 311, 45, 55, 35) 1029 expected1 := newTestVolume(64, 64, 64) 1030 expected1.addSubvol(dvid.Point3d{20, 20, 20}, dvid.Point3d{20, 20, 20}, 1) 1031 expected1.addSubvol(dvid.Point3d{20, 20, 40}, dvid.Point3d{20, 20, 20}, 2) 1032 expected1.addSubvol(dvid.Point3d{40, 20, 20}, dvid.Point3d{20, 20, 20}, 13) 1033 expected1.addSubvol(dvid.Point3d{20, 40, 20}, dvid.Point3d{20, 20, 20}, 209) 1034 expected1.addSubvol(dvid.Point3d{40, 40, 20}, dvid.Point3d{20, 20, 20}, 311) 1035 if err := downres1.equals(expected1); err != nil { 1036 t.Errorf("1st downres 'labels' isn't what is expected: %v\n", err) 1037 } 1038 1039 // Check the second downres to voxel: 32^3 1040 expected2 := newTestVolume(32, 32, 32) 1041 expected2.addSubvol(dvid.Point3d{10, 10, 10}, dvid.Point3d{10, 10, 10}, 1) 1042 expected2.addSubvol(dvid.Point3d{10, 10, 20}, dvid.Point3d{10, 10, 10}, 2) 1043 expected2.addSubvol(dvid.Point3d{20, 10, 10}, dvid.Point3d{10, 10, 10}, 13) 1044 expected2.addSubvol(dvid.Point3d{10, 20, 10}, dvid.Point3d{10, 10, 10}, 209) 1045 expected2.addSubvol(dvid.Point3d{20, 20, 10}, dvid.Point3d{10, 10, 10}, 311) 1046 downres2 := newTestVolume(32, 32, 32) 1047 downres2.getScale(t, uuid, "labels", 2) 1048 if err := downres2.equals(expected2); err != nil { 1049 t.Errorf("2nd downres 'labels' isn't what is expected: %v\n", err) 1050 } 1051 1052 // // Check blocks endpoint for different scales. 1053 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "", 0) 1054 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "uncompressed", 0) 1055 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "blocks", 0) 1056 volume.testGetBlocks(t, "hi-res block check", uuid, "labels", "gzip", 0) 1057 1058 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "", 1) 1059 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "uncompressed", 1) 1060 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "blocks", 1) 1061 expected1.testGetBlocks(t, "downres #1 block check", uuid, "labels", "gzip", 1) 1062 1063 expected2a := newTestVolume(64, 64, 64) // can only get block-aligned subvolumes 1064 expected2a.addSubvol(dvid.Point3d{10, 10, 10}, dvid.Point3d{10, 10, 10}, 1) 1065 expected2a.addSubvol(dvid.Point3d{10, 10, 20}, dvid.Point3d{10, 10, 10}, 2) 1066 expected2a.addSubvol(dvid.Point3d{20, 10, 10}, dvid.Point3d{10, 10, 10}, 13) 1067 expected2a.addSubvol(dvid.Point3d{10, 20, 10}, dvid.Point3d{10, 10, 10}, 209) 1068 expected2a.addSubvol(dvid.Point3d{20, 20, 10}, dvid.Point3d{10, 10, 10}, 311) 1069 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "", 2) 1070 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "uncompressed", 2) 1071 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "blocks", 2) 1072 expected2a.testGetBlocks(t, "downres #2 block check", uuid, "labels", "gzip", 2) 1073 } 1074 1075 func readGzipFile(filename string) ([]byte, error) { 1076 f, err := os.Open(filename) 1077 if err != nil { 1078 return nil, err 1079 } 1080 defer f.Close() 1081 1082 fz, err := gzip.NewReader(f) 1083 if err != nil { 1084 return nil, err 1085 } 1086 defer fz.Close() 1087 1088 data, err := ioutil.ReadAll(fz) 1089 if err != nil { 1090 return nil, err 1091 } 1092 return data, nil 1093 } 1094 1095 type testData struct { 1096 u []uint64 1097 b *labels.Block 1098 filename string 1099 } 1100 1101 func (d testData) String() string { 1102 return filepath.Base(d.filename) 1103 } 1104 1105 func solidTestData(label uint64) (td testData) { 1106 td.b = labels.MakeSolidBlock(label, dvid.Point3d{64, 64, 64}) 1107 numVoxels := 64 * 64 * 64 1108 td.u = make([]uint64, numVoxels) 1109 td.filename = fmt.Sprintf("solid volume of label %d", label) 1110 for i := 0; i < numVoxels; i++ { 1111 td.u[i] = label 1112 } 1113 return 1114 } 1115 1116 var testFiles = []string{ 1117 "../../test_data/fib19-64x64x64-sample1.dat.gz", 1118 "../../test_data/fib19-64x64x64-sample2.dat.gz", 1119 "../../test_data/cx-64x64x64-sample1.dat.gz", 1120 "../../test_data/cx-64x64x64-sample2.dat.gz", 1121 } 1122 1123 func loadTestData(t *testing.T, filename string) (td testData) { 1124 uint64array, err := readGzipFile(filename) 1125 if err != nil { 1126 t.Fatalf("unable to open test data file %q: %v\n", filename, err) 1127 } 1128 td.u, err = dvid.ByteToUint64(uint64array) 1129 if err != nil { 1130 t.Fatalf("unable to create alias []uint64 for data file %q: %v\n", filename, err) 1131 } 1132 td.b, err = labels.MakeBlock(uint64array, dvid.Point3d{64, 64, 64}) 1133 if err != nil { 1134 t.Fatalf("unable to convert data from file %q into block: %v\n", filename, err) 1135 } 1136 td.filename = filename 1137 return 1138 } 1139 1140 func writeInt32(t *testing.T, buf *bytes.Buffer, i int32) { 1141 b := make([]byte, 4) 1142 binary.LittleEndian.PutUint32(b, uint32(i)) 1143 n, err := buf.Write(b) 1144 if n != 4 || err != nil { 1145 t.Fatalf("couldn't write value %d (%d bytes) to buffer: %v\n", i, n, err) 1146 } 1147 } 1148 1149 func TestPostBlocks(t *testing.T) { 1150 if err := server.OpenTest(); err != nil { 1151 t.Fatalf("can't open test server: %v\n", err) 1152 } 1153 defer server.CloseTest() 1154 1155 uuid, _ := datastore.NewTestRepo() 1156 if len(uuid) < 5 { 1157 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 1158 } 1159 server.CreateTestInstance(t, uuid, "labelarray", "labels", dvid.Config{}) 1160 1161 blockCoords := []dvid.Point3d{ 1162 {1, 2, 3}, 1163 {2, 2, 3}, 1164 {1, 3, 4}, 1165 {2, 3, 4}, 1166 } 1167 var data [4]testData 1168 var buf bytes.Buffer 1169 for i, fname := range testFiles { 1170 writeInt32(t, &buf, blockCoords[i][0]) 1171 writeInt32(t, &buf, blockCoords[i][1]) 1172 writeInt32(t, &buf, blockCoords[i][2]) 1173 data[i] = loadTestData(t, fname) 1174 gzipped, err := data[i].b.CompressGZIP() 1175 if err != nil { 1176 t.Fatalf("unable to gzip compress block: %v\n", err) 1177 } 1178 writeInt32(t, &buf, int32(len(gzipped))) 1179 n, err := buf.Write(gzipped) 1180 if err != nil { 1181 t.Fatalf("unable to write gzip block: %v\n", err) 1182 } 1183 if n != len(gzipped) { 1184 t.Fatalf("unable to write %d bytes to buffer, only wrote %d bytes\n", len(gzipped), n) 1185 } 1186 } 1187 1188 apiStr := fmt.Sprintf("%snode/%s/labels/blocks", server.WebAPIPath, uuid) 1189 server.TestHTTP(t, "POST", apiStr, &buf) 1190 1191 if err := datastore.BlockOnUpdating(uuid, "labels"); err != nil { 1192 t.Fatalf("Error blocking on sync of labels: %v\n", err) 1193 } 1194 1195 start := dvid.Point3d{1 * 64, 2 * 64, 3 * 64} 1196 end := dvid.Point3d{3*64 - 1, 4*64 - 1, 5*64 - 1} 1197 testExtents(t, "labels", uuid, start, end) 1198 1199 vol := labelVol{ 1200 size: dvid.Point3d{2, 2, 2}, // in blocks 1201 nx: 128, 1202 ny: 128, 1203 nz: 128, 1204 blockSize: dvid.Point3d{64, 64, 64}, 1205 offset: dvid.Point3d{64, 128, 192}, 1206 name: "labels", 1207 } 1208 got := vol.getLabelVolume(t, uuid, "", "") 1209 gotLabels, err := dvid.AliasByteToUint64(got) 1210 if err != nil { 1211 t.Fatal(err) 1212 } 1213 for i := 0; i < 4; i++ { 1214 x0 := (blockCoords[i][0] - 1) * 64 1215 y0 := (blockCoords[i][1] - 2) * 64 1216 z0 := (blockCoords[i][2] - 3) * 64 1217 1218 var x, y, z int32 1219 for z = 0; z < 64; z++ { 1220 for y = 0; y < 64; y++ { 1221 for x = 0; x < 64; x++ { 1222 di := z*64*64 + y*64 + x 1223 gi := (z+z0)*128*128 + (y+y0)*128 + x + x0 1224 if data[i].u[di] != gotLabels[gi] { 1225 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]) 1226 } 1227 } 1228 } 1229 } 1230 } 1231 runtime.KeepAlive(&got) 1232 } 1233 1234 func testExtents(t *testing.T, name string, uuid dvid.UUID, min, max dvid.Point3d) { 1235 apiStr := fmt.Sprintf("%snode/%s/%s/metadata", server.WebAPIPath, uuid, name) 1236 r := server.TestHTTP(t, "GET", apiStr, nil) 1237 jsonVal := make(map[string]interface{}) 1238 if err := json.Unmarshal(r, &jsonVal); err != nil { 1239 t.Errorf("Unable to get metadata in JSON format. Instead got: %v\n", jsonVal) 1240 } 1241 propData, ok := jsonVal["Properties"] 1242 if !ok { 1243 t.Fatalf("Could not parse Properties out of returned JSON: %v\n", jsonVal) 1244 } 1245 props, ok := propData.(map[string]interface{}) 1246 if !ok { 1247 t.Fatalf("Could not create properties map: %v\n", propData) 1248 } 1249 pt, ok := props["MaxPoint"] 1250 if !ok { 1251 t.Fatalf("Couldn't get MaxPoint from Properties object: %v\n", props) 1252 } 1253 if pt == nil { 1254 t.Fatalf("Couldn't find MaxPoint in Properties object: %v\n", props) 1255 } 1256 maxPoint, ok := pt.([]interface{}) 1257 if !ok { 1258 t.Fatalf("Couldn't parse MaxPoint %s: %v\n", reflect.TypeOf(pt), pt) 1259 } 1260 x, y, z := maxPoint[0].(float64), maxPoint[1].(float64), maxPoint[2].(float64) 1261 if x != float64(max[0]) { 1262 t.Errorf("Bad MaxPoint X: expected %.0f, got %d\n", x, max[0]) 1263 } 1264 if y != float64(max[1]) { 1265 t.Errorf("Bad MaxPoint Y: expected %.0f, got %d\n", y, max[1]) 1266 } 1267 if z != float64(max[2]) { 1268 t.Errorf("Bad MaxPoint Z: expected %.0f, got %d\n", z, max[2]) 1269 } 1270 } 1271 1272 func TestBigPostBlock(t *testing.T) { 1273 if err := server.OpenTest(); err != nil { 1274 t.Fatalf("can't open test server: %v\n", err) 1275 } 1276 defer server.CloseTest() 1277 1278 uuid, _ := datastore.NewTestRepo() 1279 if len(uuid) < 5 { 1280 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 1281 } 1282 server.CreateTestInstance(t, uuid, "labelarray", "labels", dvid.Config{}) 1283 1284 f, err := os.Open("../../test_data/fib19-64x64x64-sample1-block.dat.gz") 1285 if err != nil { 1286 t.Fatalf("Couldn't open compressed block test data: %v\n", err) 1287 } 1288 data, err := ioutil.ReadAll(f) 1289 if err != nil { 1290 t.Fatalf("Couldn't read compressed block test data: %v\n", err) 1291 } 1292 var buf bytes.Buffer 1293 writeInt32(t, &buf, 0) 1294 writeInt32(t, &buf, 0) 1295 writeInt32(t, &buf, 0) 1296 writeInt32(t, &buf, int32(len(data))) 1297 // fmt.Printf("Writing %d bytes of compressed block\n", len(data)) 1298 n, err := buf.Write(data) 1299 if err != nil { 1300 t.Fatalf("unable to write gzip block: %v\n", err) 1301 } 1302 if n != len(data) { 1303 t.Fatalf("unable to write %d bytes to buffer, only wrote %d bytes\n", len(data), n) 1304 } 1305 1306 apiStr := fmt.Sprintf("%snode/%s/labels/blocks", server.WebAPIPath, uuid) 1307 server.TestHTTP(t, "POST", apiStr, &buf) 1308 1309 if err := datastore.BlockOnUpdating(uuid, "labels"); err != nil { 1310 t.Fatalf("Error blocking on sync of labels: %v\n", err) 1311 } 1312 } 1313 1314 func TestBigPostBlock2(t *testing.T) { 1315 if err := server.OpenTest(); err != nil { 1316 t.Fatalf("can't open test server: %v\n", err) 1317 } 1318 defer server.CloseTest() 1319 1320 uuid, _ := datastore.NewTestRepo() 1321 if len(uuid) < 5 { 1322 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 1323 } 1324 server.CreateTestInstance(t, uuid, "labelarray", "labels", dvid.Config{}) 1325 1326 f, err := os.Open("../../test_data/stream_1block.dat") 1327 if err != nil { 1328 t.Fatalf("Couldn't open compressed block test data: %v\n", err) 1329 } 1330 data, err := ioutil.ReadAll(f) 1331 if err != nil { 1332 t.Fatalf("Couldn't read compressed block test data: %v\n", err) 1333 } 1334 1335 apiStr := fmt.Sprintf("%snode/%s/labels/blocks", server.WebAPIPath, uuid) 1336 server.TestHTTP(t, "POST", apiStr, bytes.NewBuffer(data)) 1337 1338 if err := datastore.BlockOnUpdating(uuid, "labels"); err != nil { 1339 t.Fatalf("Error blocking on sync of labels: %v\n", err) 1340 } 1341 } 1342 1343 func testLabels(t *testing.T, labelsIndexed bool) { 1344 if err := server.OpenTest(); err != nil { 1345 t.Fatalf("can't open test server: %v\n", err) 1346 } 1347 defer server.CloseTest() 1348 1349 uuid, _ := datastore.NewTestRepo() 1350 if len(uuid) < 5 { 1351 t.Fatalf("Bad root UUID for new repo: %s\n", uuid) 1352 } 1353 1354 // Create a labelarray instance 1355 var config dvid.Config 1356 config.Set("BlockSize", "32,32,32") 1357 if !labelsIndexed { 1358 config.Set("IndexedLabels", "false") 1359 } 1360 server.CreateTestInstance(t, uuid, "labelarray", "labels", config) 1361 1362 vol := labelVol{ 1363 startLabel: 2, 1364 size: dvid.Point3d{5, 5, 5}, // in blocks 1365 blockSize: dvid.Point3d{32, 32, 32}, 1366 offset: dvid.Point3d{32, 64, 96}, 1367 name: "labels", 1368 } 1369 vol.postLabelVolume(t, uuid, "", "", 0) 1370 vol.testGetLabelVolume(t, uuid, "", "") 1371 1372 end := dvid.Point3d{32 + 160 - 1, 64 + 160 - 1, 96 + 160 - 1} 1373 testExtents(t, "labels", uuid, vol.offset, end) 1374 1375 // Test the blocks API 1376 vol.testBlocks(t, "GET default blocks (lz4)", uuid, "") 1377 vol.testBlocks(t, "GET uncompressed blocks", uuid, "uncompressed") 1378 vol.testBlocks(t, "GET DVID compressed label blocks", uuid, "blocks") 1379 vol.testBlocks(t, "GET gzip blocks", uuid, "gzip") 1380 1381 // Test the "label" endpoint. 1382 apiStr := fmt.Sprintf("%snode/%s/%s/label/100_64_96", server.WebAPIPath, uuid, "labels") 1383 jsonResp := server.TestHTTP(t, "GET", apiStr, nil) 1384 var r labelResp 1385 if err := json.Unmarshal(jsonResp, &r); err != nil { 1386 t.Fatalf("Unable to parse 'label' endpoint response: %s\n", jsonResp) 1387 } 1388 if r.Label != vol.label(100, 64, 96) { 1389 t.Fatalf("Expected label %d @ (100, 64, 96) got label %d\n", vol.label(100, 64, 96), r.Label) 1390 } 1391 1392 apiStr = fmt.Sprintf("%snode/%s/%s/label/10000_64000_9600121", server.WebAPIPath, uuid, "labels") 1393 jsonResp = server.TestHTTP(t, "GET", apiStr, nil) 1394 if err := json.Unmarshal(jsonResp, &r); err != nil { 1395 t.Fatalf("Unable to parse 'label' endpoint response: %s\n", jsonResp) 1396 } 1397 if r.Label != 0 { 1398 t.Fatalf("Expected label 0 at random huge point, got label %d\n", r.Label) 1399 } 1400 1401 // Test the "labels" endpoint. 1402 apiStr = fmt.Sprintf("%snode/%s/%s/labels", server.WebAPIPath, uuid, "labels") 1403 payload := `[[100,64,96],[78,93,156],[104,65,97]]` 1404 jsonResp = server.TestHTTP(t, "GET", apiStr, bytes.NewBufferString(payload)) 1405 var labels [3]uint64 1406 if err := json.Unmarshal(jsonResp, &labels); err != nil { 1407 t.Fatalf("Unable to parse 'labels' endpoint response: %s\n", jsonResp) 1408 } 1409 if labels[0] != vol.label(100, 64, 96) { 1410 t.Fatalf("Expected label %d @ (100, 64, 96) got label %d\n", vol.label(100, 64, 96), labels[0]) 1411 } 1412 if labels[1] != vol.label(78, 93, 156) { 1413 t.Fatalf("Expected label %d @ (78, 93, 156) got label %d\n", vol.label(78, 93, 156), labels[1]) 1414 } 1415 if labels[2] != vol.label(104, 65, 97) { 1416 t.Fatalf("Expected label %d @ (104, 65, 97) got label %d\n", vol.label(104, 65, 97), labels[2]) 1417 } 1418 1419 // Repost the label volume 3 more times with increasing starting values. 1420 vol.postLabelVolume(t, uuid, "", "", 2100) 1421 vol.postLabelVolume(t, uuid, "", "", 8176) 1422 vol.postLabelVolume(t, uuid, "", "", 16623) 1423 1424 vol.testSlices(t, uuid) 1425 1426 // Try to post last volume concurrently 3x and then check result. 1427 wg := new(sync.WaitGroup) 1428 wg.Add(3) 1429 go func() { 1430 vol.postLabelVolume(t, uuid, "", "", 16623) 1431 wg.Done() 1432 }() 1433 go func() { 1434 vol.postLabelVolume(t, uuid, "", "", 16623) 1435 wg.Done() 1436 }() 1437 go func() { 1438 vol.postLabelVolume(t, uuid, "", "", 16623) 1439 wg.Done() 1440 }() 1441 wg.Wait() 1442 vol.testGetLabelVolume(t, uuid, "", "") 1443 1444 // Try concurrent write of disjoint subvolumes. 1445 vol2 := labelVol{ 1446 size: dvid.Point3d{5, 5, 5}, // in blocks 1447 blockSize: dvid.Point3d{32, 32, 32}, 1448 offset: dvid.Point3d{192, 64, 96}, 1449 name: "labels", 1450 } 1451 vol3 := labelVol{ 1452 size: dvid.Point3d{5, 5, 5}, // in blocks 1453 blockSize: dvid.Point3d{32, 32, 32}, 1454 offset: dvid.Point3d{192, 224, 96}, 1455 name: "labels", 1456 } 1457 vol4 := labelVol{ 1458 size: dvid.Point3d{5, 5, 5}, // in blocks 1459 blockSize: dvid.Point3d{32, 32, 32}, 1460 offset: dvid.Point3d{32, 224, 96}, 1461 name: "labels", 1462 } 1463 1464 wg.Add(3) 1465 go func() { 1466 vol2.postLabelVolume(t, uuid, "lz4", "", 4000) 1467 wg.Done() 1468 }() 1469 go func() { 1470 vol3.postLabelVolume(t, uuid, "lz4", "", 8000) 1471 wg.Done() 1472 }() 1473 go func() { 1474 vol4.postLabelVolume(t, uuid, "lz4", "", 1200) 1475 wg.Done() 1476 }() 1477 wg.Wait() 1478 vol.testGetLabelVolume(t, uuid, "", "") 1479 vol2.testGetLabelVolume(t, uuid, "", "") 1480 vol3.testGetLabelVolume(t, uuid, "", "") 1481 vol4.testGetLabelVolume(t, uuid, "", "") 1482 1483 // Verify various GET 3d volume with compressions and no ROI. 1484 vol.testGetLabelVolume(t, uuid, "", "") 1485 vol.testGetLabelVolume(t, uuid, "lz4", "") 1486 vol.testGetLabelVolume(t, uuid, "gzip", "") 1487 1488 // Create a new ROI instance. 1489 roiName := "myroi" 1490 server.CreateTestInstance(t, uuid, "roi", roiName, dvid.Config{}) 1491 1492 // Add ROI data 1493 apiStr = fmt.Sprintf("%snode/%s/%s/roi", server.WebAPIPath, uuid, roiName) 1494 server.TestHTTP(t, "POST", apiStr, bytes.NewBufferString(labelsJSON())) 1495 1496 // Post updated labels without ROI and make sure it returns those values. 1497 var labelNoROI uint64 = 20000 1498 vol.postLabelVolume(t, uuid, "", "", labelNoROI) 1499 returned := vol.testGetLabelVolume(t, uuid, "", "") 1500 startLabel := binary.LittleEndian.Uint64(returned[0:8]) 1501 if startLabel != labelNoROI { 1502 t.Fatalf("Expected first voxel to be label %d and got %d instead\n", labelNoROI, startLabel) 1503 } 1504 1505 // Post again but now with ROI 1506 var labelWithROI uint64 = 40000 1507 vol.postLabelVolume(t, uuid, "", roiName, labelWithROI) 1508 1509 // Verify ROI masking of POST where anything outside ROI is old labels. 1510 returned = vol.getLabelVolume(t, uuid, "", "") 1511 1512 nx := vol.size[0] * vol.blockSize[0] 1513 ny := vol.size[1] * vol.blockSize[1] 1514 nz := vol.size[2] * vol.blockSize[2] 1515 var x, y, z, v int32 1516 for z = 0; z < nz; z++ { 1517 voxz := z + vol.offset[2] 1518 blockz := voxz / vol.blockSize[2] 1519 for y = 0; y < ny; y++ { 1520 voxy := y + vol.offset[1] 1521 blocky := voxy / vol.blockSize[1] 1522 for x = 0; x < nx; x++ { 1523 voxx := x + vol.offset[0] 1524 blockx := voxx / vol.blockSize[0] 1525 incr := uint64(x>>2 + y/3 + z/3) 1526 oldlabel := labelNoROI + incr 1527 newlabel := labelWithROI + incr 1528 got := binary.LittleEndian.Uint64(returned[v : v+8]) 1529 if inroi(blockx, blocky, blockz) { 1530 if got != newlabel { 1531 t.Fatalf("Expected %d in ROI (%d,%d,%d), got %d\n", newlabel, blockx, blocky, blockz, got) 1532 } 1533 } else { 1534 if got != oldlabel { 1535 t.Fatalf("Expected %d outside ROI (%d,%d,%d), got %d\n", oldlabel, blockx, blocky, blockz, got) 1536 } 1537 } 1538 v += 8 1539 } 1540 } 1541 } 1542 1543 // Verify that a ROI-enabled GET has zeros everywhere outside ROI. 1544 returned = vol.getLabelVolume(t, uuid, "", roiName) 1545 1546 x, y, z, v = 0, 0, 0, 0 1547 for z = 0; z < nz; z++ { 1548 voxz := z + vol.offset[2] 1549 blockz := voxz / vol.blockSize[2] 1550 for y = 0; y < ny; y++ { 1551 voxy := y + vol.offset[1] 1552 blocky := voxy / vol.blockSize[1] 1553 for x = 0; x < nx; x++ { 1554 voxx := x + vol.offset[0] 1555 blockx := voxx / vol.blockSize[0] 1556 incr := uint64(x>>2 + y/3 + z/3) 1557 newlabel := labelWithROI + incr 1558 got := binary.LittleEndian.Uint64(returned[v : v+8]) 1559 if inroi(blockx, blocky, blockz) { 1560 if got != newlabel { 1561 t.Fatalf("Expected %d in ROI, got %d\n", newlabel, got) 1562 } 1563 } else { 1564 if got != 0 { 1565 t.Fatalf("Expected zero outside ROI, got %d\n", got) 1566 } 1567 } 1568 v += 8 1569 } 1570 } 1571 } 1572 1573 // TODO - Use the ROI to retrieve a 2d xy image. 1574 1575 // TODO - Make sure we aren't getting labels back in non-ROI points. 1576 1577 // Verify non-indexed instances can't access indexed endpoints. 1578 if !labelsIndexed { 1579 methods := []string{ 1580 "GET", 1581 "HEAD", 1582 "GET", 1583 "GET", 1584 "GET", 1585 "GET", 1586 "POST", 1587 "POST", 1588 "POST", 1589 "POST", 1590 } 1591 reqs := []string{ 1592 "sparsevol/20", 1593 "sparsevol/20", 1594 "sparsevol-by-point/30_89_100", 1595 "sparsevol-coarse/20", 1596 "maxlabel", 1597 "nextlabel", 1598 "nextlabel", 1599 "merge", 1600 "split/20", 1601 "split-coarse/20", 1602 } 1603 var r io.Reader 1604 for i, req := range reqs { 1605 apiStr = fmt.Sprintf("%snode/%s/labels/%s", server.WebAPIPath, uuid, req) 1606 if methods[i] == "POST" { 1607 r = bytes.NewBufferString("junkdata that should never be used anyway") 1608 } else { 1609 r = nil 1610 } 1611 server.TestBadHTTP(t, methods[i], apiStr, r) 1612 } 1613 } 1614 } 1615 1616 func TestLabels(t *testing.T) { 1617 testLabels(t, true) 1618 } 1619 1620 func TestLabelsUnindexed(t *testing.T) { 1621 testLabels(t, false) 1622 }