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  }