github.com/janelia-flyem/dvid@v1.0.0/datatype/roi/roi_test.go (about)

     1  package roi
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"log"
     9  	"net/http"
    10  	"net/http/httptest"
    11  	"reflect"
    12  	"sync"
    13  	"testing"
    14  
    15  	"github.com/janelia-flyem/dvid/datastore"
    16  	"github.com/janelia-flyem/dvid/dvid"
    17  	"github.com/janelia-flyem/dvid/server"
    18  )
    19  
    20  var (
    21  	roitype datastore.TypeService
    22  	testMu  sync.Mutex
    23  )
    24  
    25  var testSpans = []dvid.Span{
    26  	dvid.Span{100, 101, 200, 210}, dvid.Span{100, 102, 200, 210}, dvid.Span{100, 103, 201, 212},
    27  	dvid.Span{101, 101, 201, 213}, dvid.Span{101, 102, 202, 215}, dvid.Span{101, 103, 202, 216},
    28  	dvid.Span{102, 101, 200, 210}, dvid.Span{102, 103, 201, 216}, dvid.Span{102, 104, 203, 217},
    29  	dvid.Span{103, 101, 200, 210}, dvid.Span{103, 103, 200, 210}, dvid.Span{103, 105, 201, 212},
    30  }
    31  
    32  func TestVoxelBoundsInside(t *testing.T) {
    33  	size := dvid.Point3d{10, 10, 10}
    34  	ext := dvid.Extents3d{dvid.Point3d{2030, 1010, 990}, dvid.Point3d{2040, 1040, 990}}
    35  	if inside, err := VoxelBoundsInside(ext, size, testSpans); err != nil || inside {
    36  		t.Errorf("Bad intersection of ext %v and test spans: err %v\n", ext, err)
    37  	}
    38  	ext = dvid.Extents3d{dvid.Point3d{2030, 1010, 1000}, dvid.Point3d{2040, 1040, 1000}}
    39  	if inside, err := VoxelBoundsInside(ext, size, testSpans); err != nil || !inside {
    40  		t.Errorf("Bad intersection of ext %v and test spans: err %v\n", ext, err)
    41  	}
    42  	ext = dvid.Extents3d{dvid.Point3d{2130, 1010, 1000}, dvid.Point3d{2900, 1040, 1000}}
    43  	if inside, err := VoxelBoundsInside(ext, size, testSpans); err != nil || inside {
    44  		t.Errorf("Bad intersection of ext %v and test spans: err %v\n", ext, err)
    45  	}
    46  	ext = dvid.Extents3d{dvid.Point3d{2129, 1030, 1000}, dvid.Point3d{2900, 1040, 1000}}
    47  	if inside, err := VoxelBoundsInside(ext, size, testSpans); err != nil || !inside {
    48  		t.Errorf("Bad intersection of ext %v and test spans: err %v\n", ext, err)
    49  	}
    50  	ext = dvid.Extents3d{dvid.Point3d{2130, 1010, 200}, dvid.Point3d{2130, 1040, 900}}
    51  	if inside, err := VoxelBoundsInside(ext, size, testSpans); err != nil || inside {
    52  		t.Errorf("Bad intersection of ext %v and test spans: err %v\n", ext, err)
    53  	}
    54  	ext = dvid.Extents3d{dvid.Point3d{2130, 1040, 1000}, dvid.Point3d{2900, 1040, 2000}}
    55  	if inside, err := VoxelBoundsInside(ext, size, testSpans); err != nil || !inside {
    56  		t.Errorf("Bad intersection of ext %v and test spans: err %v\n", ext, err)
    57  	}
    58  }
    59  
    60  func getSpansJSON(spans []dvid.Span) io.Reader {
    61  	jsonBytes, err := json.Marshal(spans)
    62  	if err != nil {
    63  		log.Fatalf("Can't encode spans into JSON: %v\n", err)
    64  	}
    65  	return bytes.NewReader(jsonBytes)
    66  }
    67  
    68  func putSpansJSON(data []byte) ([]dvid.Span, error) {
    69  	var spans []dvid.Span
    70  	if err := json.Unmarshal(data, &spans); err != nil {
    71  		return nil, err
    72  	}
    73  	return spans, nil
    74  }
    75  
    76  var testPoints = []dvid.Point3d{
    77  	dvid.Point3d{6400, 3232, 3167}, // false
    78  	dvid.Point3d{6400, 3232, 3200}, // true
    79  	dvid.Point3d{6719, 3232, 3200}, // true
    80  	dvid.Point3d{6720, 3232, 3200}, // true
    81  	dvid.Point3d{6720, 4100, 3263}, // false
    82  	dvid.Point3d{6720, 3233, 3201}, // true
    83  	dvid.Point3d{6720, 3234, 3200}, // true
    84  }
    85  
    86  var expectedInclusions = []bool{
    87  	false,
    88  	true,
    89  	true,
    90  	true,
    91  	false,
    92  	true,
    93  	true,
    94  }
    95  
    96  func getPointsJSON(pts []dvid.Point3d) io.Reader {
    97  	jsonBytes, err := json.Marshal(pts)
    98  	if err != nil {
    99  		log.Fatalf("Can't encode points into JSON: %v\n", err)
   100  	}
   101  	return bytes.NewReader(jsonBytes)
   102  }
   103  
   104  func putInclusionJSON(data []byte) ([]bool, error) {
   105  	var inclusions []bool
   106  	if err := json.Unmarshal(data, &inclusions); err != nil {
   107  		return nil, err
   108  	}
   109  	return inclusions, nil
   110  }
   111  
   112  // Sets package-level testRepo and TestVersionID
   113  func initTestRepo() (dvid.UUID, dvid.VersionID) {
   114  	testMu.Lock()
   115  	defer testMu.Unlock()
   116  	if roitype == nil {
   117  		var err error
   118  		roitype, err = datastore.TypeServiceByName(TypeName)
   119  		if err != nil {
   120  			log.Fatalf("Can't get ROI type: %v\n", err)
   121  		}
   122  	}
   123  	return datastore.NewTestRepo()
   124  }
   125  
   126  func TestTuples(t *testing.T) {
   127  	tup := dvid.Span{10, 11, 20, 30}
   128  	if tup.LessChunkPoint3d(dvid.ChunkPoint3d{20, 11, 10}) {
   129  		t.Errorf("Bad tuple.Less()\n")
   130  	}
   131  	if tup.LessChunkPoint3d(dvid.ChunkPoint3d{30, 11, 10}) {
   132  		t.Errorf("Bad tuple.Less()\n")
   133  	}
   134  	if !tup.LessChunkPoint3d(dvid.ChunkPoint3d{31, 11, 10}) {
   135  		t.Errorf("Bad tuple.Less()\n")
   136  	}
   137  	if !tup.LessChunkPoint3d(dvid.ChunkPoint3d{20, 11, 11}) {
   138  		t.Errorf("Bad tuple.Less()\n")
   139  	}
   140  	if tup.LessChunkPoint3d(dvid.ChunkPoint3d{20, 11, 9}) {
   141  		t.Errorf("Bad tuple.Less()\n")
   142  	}
   143  	if !tup.LessChunkPoint3d(dvid.ChunkPoint3d{20, 11, 11}) {
   144  		t.Errorf("Bad tuple.Less()\n")
   145  	}
   146  
   147  	if tup.Includes(dvid.ChunkPoint3d{19, 11, 10}) {
   148  		t.Errorf("Bad tuple.Includes()\n")
   149  	}
   150  	if !tup.Includes(dvid.ChunkPoint3d{20, 11, 10}) {
   151  		t.Errorf("Bad tuple.Includes()\n")
   152  	}
   153  	if !tup.Includes(dvid.ChunkPoint3d{30, 11, 10}) {
   154  		t.Errorf("Bad tuple.Includes()\n")
   155  	}
   156  	if tup.Includes(dvid.ChunkPoint3d{31, 11, 10}) {
   157  		t.Errorf("Bad tuple.Includes()\n")
   158  	}
   159  	if tup.Includes(dvid.ChunkPoint3d{25, 11, 11}) {
   160  		t.Errorf("Bad tuple.Includes()\n")
   161  	}
   162  	if tup.Includes(dvid.ChunkPoint3d{25, 11, 9}) {
   163  		t.Errorf("Bad tuple.Includes()\n")
   164  	}
   165  	if tup.Includes(dvid.ChunkPoint3d{25, 10, 10}) {
   166  		t.Errorf("Bad tuple.Includes()\n")
   167  	}
   168  	if tup.Includes(dvid.ChunkPoint3d{25, 12, 10}) {
   169  		t.Errorf("Bad tuple.Includes()\n")
   170  	}
   171  }
   172  
   173  func TestROIRequests(t *testing.T) {
   174  	if err := server.OpenTest(); err != nil {
   175  		t.Fatalf("can't open test server: %v\n", err)
   176  	}
   177  	defer server.CloseTest()
   178  
   179  	// Create the ROI dataservice.
   180  	uuid, _ := initTestRepo()
   181  
   182  	config := dvid.NewConfig()
   183  	dataservice, err := datastore.NewData(uuid, roitype, "roi", config)
   184  	if err != nil {
   185  		t.Errorf("Error creating new roi instance: %v\n", err)
   186  	}
   187  	data, ok := dataservice.(*Data)
   188  	if !ok {
   189  		t.Errorf("Returned new data instance is not roi.Data\n")
   190  	}
   191  
   192  	// PUT an ROI
   193  	roiRequest := fmt.Sprintf("%snode/%s/%s/roi", server.WebAPIPath, uuid, data.DataName())
   194  	server.TestHTTP(t, "POST", roiRequest, getSpansJSON(testSpans))
   195  
   196  	// Get back the ROI
   197  	returnedData := server.TestHTTP(t, "GET", roiRequest, nil)
   198  	spans, err := putSpansJSON(returnedData)
   199  	if err != nil {
   200  		t.Errorf("Error on getting back JSON from roi GET: %v\n", err)
   201  	}
   202  
   203  	// Make sure the two are the same.
   204  	if !reflect.DeepEqual(spans, testSpans) {
   205  		t.Errorf("Bad PUT/GET ROI roundtrip\nOriginal:\n%s\nReturned:\n%s\n", testSpans, spans)
   206  	}
   207  
   208  	// Test the ptquery
   209  	ptqueryRequest := fmt.Sprintf("%snode/%s/%s/ptquery", server.WebAPIPath, uuid, data.DataName())
   210  	returnedData = server.TestHTTP(t, "POST", ptqueryRequest, getPointsJSON(testPoints))
   211  	inclusions, err := putInclusionJSON(returnedData)
   212  	if err != nil {
   213  		t.Fatalf("Error on getting back JSON from ptquery: %v\n", err)
   214  	}
   215  
   216  	// Make sure the two are the same.
   217  	if !reflect.DeepEqual(inclusions, expectedInclusions) {
   218  		t.Errorf("Bad ptquery results\nOriginal:\n%v\nReturned:\n%v\n", expectedInclusions, inclusions)
   219  	}
   220  
   221  	// Test ROI mask out of range -- should be all 0.
   222  	maskRequest := fmt.Sprintf("%snode/%s/%s/mask/0_1_2/100_100_100/10_40_70", server.WebAPIPath, uuid, data.DataName())
   223  	returnedData = server.TestHTTP(t, "GET", maskRequest, nil)
   224  	if len(returnedData) != 100*100*100 {
   225  		t.Errorf("Expected mask volume of %d bytes, got %d bytes instead\n", 100*100*100, len(returnedData))
   226  	}
   227  	for i, value := range returnedData {
   228  		if value != 0 {
   229  			t.Errorf("Expected all-zero mask, got %d at index %d\n", value, i)
   230  			break
   231  		}
   232  	}
   233  
   234  	// Test ROI mask within range.
   235  	maskRequest = fmt.Sprintf("%snode/%s/%s/mask/0_1_2/100_100_100/6350_3232_3200", server.WebAPIPath, uuid, data.DataName())
   236  	returnedData = server.TestHTTP(t, "GET", maskRequest, nil)
   237  	if len(returnedData) != 100*100*100 {
   238  		t.Errorf("Expected mask volume of %d bytes, got %d bytes instead\n", 100*100*100, len(returnedData))
   239  	}
   240  	// Check first block plane
   241  	for y := 0; y < 100; y++ {
   242  		for x := 0; x < 100; x++ {
   243  			value := returnedData[y*100+x]
   244  			if x < 50 && value != 0 {
   245  				t.Errorf("Expected mask to be zero at (%d, %d) before ROI, got %d instead\n", x, y, value)
   246  				break
   247  			}
   248  			if x >= 50 && y < 64 && value != 1 {
   249  				t.Errorf("Expected mask to be 1 at (%d, %d) within ROI, got %d instead\n", x, y, value)
   250  				break
   251  			}
   252  			// tuple{100, 103, 201, 212}
   253  			if x <= 81 && y >= 64 && y < 96 && value != 0 {
   254  				t.Errorf("Expected mask to be zero at (%d, %d) before ROI, got %d instead\n", x, y, value)
   255  				break
   256  			}
   257  			if x > 81 && y >= 64 && y < 96 && value != 1 {
   258  				t.Errorf("Expected mask to be 1 at (%d, %d) within ROI, got %d instead\n", x, y, value)
   259  				break
   260  			}
   261  		}
   262  	}
   263  	// Check second block plane
   264  	offset := 32 * 100 * 100 // moves to next block in Z
   265  	for y := 0; y < 100; y++ {
   266  		for x := 0; x < 100; x++ {
   267  			value := returnedData[offset+y*100+x]
   268  			if x < 50 && value != 0 {
   269  				t.Errorf("Expected mask to be zero at (%d, %d) before ROI, got %d instead\n", x, y, value)
   270  				break
   271  			}
   272  			if x <= 81 && y < 32 && value != 0 {
   273  				t.Errorf("Expected mask to be zero at (%d, %d) before ROI, got %d instead\n", x, y, value)
   274  				break
   275  			}
   276  			if x > 81 && y < 32 && value != 1 {
   277  				t.Errorf("Expected mask to be 1 at (%d, %d) within ROI, got %d instead\n", x, y, value)
   278  				break
   279  			}
   280  			if y >= 32 && value != 0 {
   281  				t.Errorf("Expected mask to be zero at (%d, %d) before ROI, got %d instead\n", x, y, value)
   282  				break
   283  			}
   284  		}
   285  	}
   286  	// Check last block plane
   287  	offset = 96 * 100 * 100 // moves to last ROI layer in Z
   288  	for y := 0; y < 100; y++ {
   289  		for x := 0; x < 100; x++ {
   290  			value := returnedData[offset+y*100+x]
   291  			if x < 50 && value != 0 {
   292  				t.Errorf("Expected mask to be zero at (%d, %d) before ROI, got %d instead\n", x, y, value)
   293  				break
   294  			}
   295  			if x >= 50 && y < 32 && value != 1 {
   296  				t.Errorf("Expected mask to be 1 at (%d, %d) within ROI, got %d instead\n", x, y, value)
   297  				break
   298  			}
   299  			if y >= 32 && y < 64 && value != 0 {
   300  				t.Errorf("Expected mask to be zero at (%d, %d) before ROI, got %d instead\n", x, y, value)
   301  				break
   302  			}
   303  			if x >= 50 && y >= 64 && y < 96 && value != 1 {
   304  				t.Errorf("Expected mask to be 1 at (%d, %d) within ROI, got %d instead\n", x, y, value)
   305  				break
   306  			}
   307  			if y >= 96 && value != 0 {
   308  				t.Errorf("Expected mask to be zero at (%d, %d) before ROI, got %d instead\n", x, y, value)
   309  				break
   310  			}
   311  		}
   312  	}
   313  }
   314  
   315  func TestROIPostAndDelete(t *testing.T) {
   316  	if err := server.OpenTest(); err != nil {
   317  		t.Fatalf("can't open test server: %v\n", err)
   318  	}
   319  	defer server.CloseTest()
   320  
   321  	// Create the ROI dataservice.
   322  	uuid, _ := initTestRepo()
   323  
   324  	config := dvid.NewConfig()
   325  	dataservice, err := datastore.NewData(uuid, roitype, "roi", config)
   326  	if err != nil {
   327  		t.Errorf("Error creating new roi instance: %v\n", err)
   328  	}
   329  	data, ok := dataservice.(*Data)
   330  	if !ok {
   331  		t.Errorf("Returned new data instance is not roi.Data\n")
   332  	}
   333  
   334  	// PUT an ROI
   335  	roiRequest := fmt.Sprintf("%snode/%s/%s/roi", server.WebAPIPath, uuid, data.DataName())
   336  	server.TestHTTP(t, "POST", roiRequest, getSpansJSON(testSpans))
   337  
   338  	// Get back the ROI
   339  	returnedData := server.TestHTTP(t, "GET", roiRequest, nil)
   340  	spans, err := putSpansJSON(returnedData)
   341  	if err != nil {
   342  		t.Errorf("Error on getting back JSON from roi GET: %v\n", err)
   343  	}
   344  	if !reflect.DeepEqual(spans, testSpans) {
   345  		t.Errorf("Bad PUT/GET ROI roundtrip\nOriginal:\n%s\nReturned:\n%s\n", testSpans, spans)
   346  	}
   347  
   348  	// Make a new version
   349  	reqStr := fmt.Sprintf("%snode/%s/commit", server.WebAPIPath, uuid)
   350  	server.TestHTTP(t, "POST", reqStr, nil)
   351  	versionReq := fmt.Sprintf("%snode/%s/newversion", server.WebAPIPath, uuid)
   352  	respData := server.TestHTTP(t, "POST", versionReq, nil)
   353  	resp := struct {
   354  		Child string `json:"child"`
   355  	}{}
   356  	if err := json.Unmarshal(respData, &resp); err != nil {
   357  		t.Errorf("Expected 'child' JSON response.  Got %s\n", string(respData))
   358  	}
   359  	child := dvid.UUID(resp.Child)
   360  
   361  	// Delete the ROI
   362  	roiRequest2 := fmt.Sprintf("%snode/%s/%s/roi", server.WebAPIPath, child, data.DataName())
   363  	_ = server.TestHTTP(t, "DELETE", roiRequest2, nil)
   364  
   365  	// ROI in past version should still be there
   366  	returnedData = server.TestHTTP(t, "GET", roiRequest, nil)
   367  	spans, err = putSpansJSON(returnedData)
   368  	if err != nil {
   369  		t.Errorf("Error on getting back JSON from roi GET: %v\n", err)
   370  	}
   371  	if !reflect.DeepEqual(spans, testSpans) {
   372  		t.Errorf("Bad PUT/GET ROI roundtrip\nOriginal:\n%s\nReturned:\n%s\n", testSpans, spans)
   373  	}
   374  
   375  	// ROI should now be empty for leaf
   376  	returnedData = server.TestHTTP(t, "GET", roiRequest2, nil)
   377  	if string(returnedData) != "[]" {
   378  		t.Errorf("Bad ROI after ROI delete.  Should be [ ] got: %s\n", string(returnedData))
   379  	}
   380  	leafRequest := fmt.Sprintf("%snode/%s:master/%s/roi", server.WebAPIPath, uuid[:8], data.DataName())
   381  	returnedData = server.TestHTTP(t, "GET", leafRequest, nil)
   382  	if string(returnedData) != "[]" {
   383  		t.Errorf("Bad ROI after ROI delete.  Should be [ ] got: %s\n", string(returnedData))
   384  	}
   385  }
   386  
   387  func TestROICreateAndSerialize(t *testing.T) {
   388  	if err := server.OpenTest(); err != nil {
   389  		t.Fatalf("can't open test server: %v\n", err)
   390  	}
   391  	defer server.CloseTest()
   392  
   393  	uuid, _ := initTestRepo()
   394  
   395  	// Add data
   396  	config := dvid.NewConfig()
   397  	dataservice1, err := datastore.NewData(uuid, roitype, "myroi", config)
   398  	if err != nil {
   399  		t.Errorf("Error creating new roi instance: %v\n", err)
   400  	}
   401  	roi1, ok := dataservice1.(*Data)
   402  	if !ok {
   403  		t.Errorf("Returned new data instance 1 is not roi.Data\n")
   404  	}
   405  	if roi1.DataName() != "myroi" {
   406  		t.Errorf("New roi data instance name set incorrectly: %q != %q\n",
   407  			roi1.DataName(), "myroi")
   408  	}
   409  
   410  	config.Set("BlockSize", "15,16,17")
   411  	dataservice2, err := datastore.NewData(uuid, roitype, "myroi2", config)
   412  	if err != nil {
   413  		t.Errorf("Error creating new roi instance: %v\n", err)
   414  	}
   415  	roi2, ok := dataservice2.(*Data)
   416  	if !ok {
   417  		t.Errorf("Returned new data instance 2 is not roi.Data\n")
   418  	}
   419  
   420  	if roi1.InstanceID() == roi2.InstanceID() {
   421  		t.Errorf("Instance IDs should be different: %d == %d\n",
   422  			roi1.InstanceID(), roi2.InstanceID())
   423  	}
   424  
   425  	// Test persistence of storage.
   426  	roi2.MinZ = 13
   427  	roi2.MaxZ = 3098
   428  	gobBytes, err := roi2.GobEncode()
   429  	if err != nil {
   430  		t.Fatalf("Could not Gob encode roi: %v\n", err)
   431  	}
   432  
   433  	var received Data
   434  	if err = received.GobDecode(gobBytes); err != nil {
   435  		t.Fatalf("Could not decode Gob-encoded roi: %v\n", err)
   436  	}
   437  
   438  	if !roi2.Data.Equals(received.Data) {
   439  		t.Errorf("ROI base Data has bad roundtrip:\nOriginal:\n%v\nReceived:\n%v\n",
   440  			*(roi2.Data), *(received.Data))
   441  	}
   442  
   443  	if !reflect.DeepEqual(roi2.Properties, received.Properties) {
   444  		t.Errorf("ROI extended properties has bad roundtrip:\nOriginal:\n%v\nReceived:\n%v\n",
   445  			roi2.Properties, received.Properties)
   446  	}
   447  }
   448  
   449  func TestROIPartition(t *testing.T) {
   450  	if err := server.OpenTest(); err != nil {
   451  		t.Fatalf("can't open test server: %v\n", err)
   452  	}
   453  	defer server.CloseTest()
   454  
   455  	// Create the ROI dataservice.
   456  	uuid, versionID := initTestRepo()
   457  
   458  	config := dvid.NewConfig()
   459  	dataservice, err := datastore.NewData(uuid, roitype, "roi", config)
   460  	if err != nil {
   461  		t.Errorf("Error creating new roi instance: %v\n", err)
   462  	}
   463  	data, ok := dataservice.(*Data)
   464  	if !ok {
   465  		t.Errorf("Returned new data instance is not roi.Data\n")
   466  	}
   467  
   468  	// PUT an ROI
   469  	roiRequest := fmt.Sprintf("%snode/%s/%s/roi", server.WebAPIPath, uuid, data.DataName())
   470  	req, err := http.NewRequest("POST", roiRequest, getSpansJSON(testSpans))
   471  	if err != nil {
   472  		t.Errorf("Unsuccessful POST request (%s): %v\n", roiRequest, err)
   473  	}
   474  	ctx := datastore.NewVersionedCtx(data, versionID)
   475  	w := httptest.NewRecorder()
   476  	data.ServeHTTP(uuid, ctx, w, req)
   477  	if w.Code != http.StatusOK {
   478  		t.Errorf("Bad server response roi POST, status %d, for roi %q\n", w.Code, data.DataName())
   479  	}
   480  
   481  	// Request the standard subvolume partitioning
   482  	partitionReq := fmt.Sprintf("%snode/%s/%s/partition?batchsize=5&optimized=true", server.WebAPIPath, uuid,
   483  		data.DataName())
   484  	req, err = http.NewRequest("GET", partitionReq, nil)
   485  	if err != nil {
   486  		t.Errorf("Unsuccessful GET request (%s): %v\n", partitionReq, err)
   487  	}
   488  	w = httptest.NewRecorder()
   489  	data.ServeHTTP(uuid, ctx, w, req)
   490  	if w.Code != http.StatusOK {
   491  		t.Errorf("Bad server response roi GET, status %d, for roi %q\n", w.Code, data.DataName())
   492  	}
   493  	var subvolJSON, expectedJSON interface{}
   494  	response := w.Body.Bytes()
   495  	if err := json.Unmarshal(response, &subvolJSON); err != nil {
   496  		t.Errorf("Can't unmarshal JSON: %s\n", w.Body.Bytes())
   497  	}
   498  	json.Unmarshal([]byte(expectedPartition), &expectedJSON)
   499  	if !reflect.DeepEqual(subvolJSON, expectedJSON) {
   500  		t.Errorf("Error doing optimized subvolume partitioning.  Got bad result:\n%s\n",
   501  			string(response))
   502  	}
   503  }
   504  
   505  func TestROISimplePartition(t *testing.T) {
   506  	if err := server.OpenTest(); err != nil {
   507  		t.Fatalf("can't open test server: %v\n", err)
   508  	}
   509  	defer server.CloseTest()
   510  
   511  	// Create the ROI dataservice.
   512  	uuid, versionID := initTestRepo()
   513  
   514  	config := dvid.NewConfig()
   515  	dataservice, err := datastore.NewData(uuid, roitype, "roi", config)
   516  	if err != nil {
   517  		t.Errorf("Error creating new roi instance: %v\n", err)
   518  	}
   519  	data, ok := dataservice.(*Data)
   520  	if !ok {
   521  		t.Errorf("Returned new data instance is not roi.Data\n")
   522  	}
   523  
   524  	// PUT an ROI
   525  	roiRequest := fmt.Sprintf("%snode/%s/%s/roi", server.WebAPIPath, uuid, data.DataName())
   526  	req, err := http.NewRequest("POST", roiRequest, getSpansJSON(testSpans))
   527  	if err != nil {
   528  		t.Errorf("Unsuccessful POST request (%s): %v\n", roiRequest, err)
   529  	}
   530  	ctx := datastore.NewVersionedCtx(data, versionID)
   531  	w := httptest.NewRecorder()
   532  	data.ServeHTTP(uuid, ctx, w, req)
   533  	if w.Code != http.StatusOK {
   534  		t.Errorf("Bad server response roi POST, status %d, for roi %q\n", w.Code, data.DataName())
   535  	}
   536  
   537  	// Request the standard subvolume partitioning
   538  	partitionReq := fmt.Sprintf("%snode/%s/%s/partition?batchsize=5", server.WebAPIPath, uuid,
   539  		data.DataName())
   540  	req, err = http.NewRequest("GET", partitionReq, nil)
   541  	if err != nil {
   542  		t.Errorf("Unsuccessful GET request (%s): %v\n", partitionReq, err)
   543  	}
   544  	w = httptest.NewRecorder()
   545  	data.ServeHTTP(uuid, ctx, w, req)
   546  	if w.Code != http.StatusOK {
   547  		t.Errorf("Bad server response roi GET, status %d, for roi %q\n", w.Code, data.DataName())
   548  	}
   549  	var subvolJSON, expectedJSON interface{}
   550  	response := w.Body.Bytes()
   551  	if err := json.Unmarshal(response, &subvolJSON); err != nil {
   552  		t.Errorf("Can't unmarshal JSON: %s\n", w.Body.Bytes())
   553  	}
   554  	json.Unmarshal([]byte(expectedSimplePartition), &expectedJSON)
   555  	if !reflect.DeepEqual(subvolJSON, expectedJSON) {
   556  		t.Errorf("Error doing simple subvolume partitioning.  Got bad result:\n%s\n", string(response))
   557  	}
   558  }
   559  
   560  const expectedPartition = `
   561  		{
   562  		    "NumTotalBlocks": 450,
   563  		    "NumActiveBlocks": 152,
   564  		    "NumSubvolumes": 3,
   565  		    "ROI": {
   566  		        "MinChunk": [
   567  		            0,
   568  		            0,
   569  		            100
   570  		        ],
   571  		        "MaxChunk": [
   572  		            217,
   573  		            105,
   574  		            103
   575  		        ]
   576  		    },
   577  		    "Subvolumes": [
   578  		        {
   579  		            "MinPoint": [
   580  		                6400,
   581  		                3232,
   582  		                3200
   583  		            ],
   584  		            "MaxPoint": [
   585  		                6591,
   586  		                3391,
   587  		                3359
   588  		            ],
   589  		            "MinChunk": [
   590  		                200,
   591  		                101,
   592  		                100
   593  		            ],
   594  		            "MaxChunk": [
   595  		                205,
   596  		                105,
   597  		                104
   598  		            ],
   599  		            "TotalBlocks": 150,
   600  		            "ActiveBlocks": 61
   601  		        },
   602  		        {
   603  		            "MinPoint": [
   604  		                6592,
   605  		                3232,
   606  		                3200
   607  		            ],
   608  		            "MaxPoint": [
   609  		                6783,
   610  		                3391,
   611  		                3359
   612  		            ],
   613  		            "MinChunk": [
   614  		                206,
   615  		                101,
   616  		                100
   617  		            ],
   618  		            "MaxChunk": [
   619  		                211,
   620  		                105,
   621  		                104
   622  		            ],
   623  		            "TotalBlocks": 150,
   624  		            "ActiveBlocks": 67
   625  		        },
   626  		        {
   627  		            "MinPoint": [
   628  		                6784,
   629  		                3232,
   630  		                3200
   631  		            ],
   632  		            "MaxPoint": [
   633  		                6975,
   634  		                3391,
   635  		                3359
   636  		            ],
   637  		            "MinChunk": [
   638  		                212,
   639  		                101,
   640  		                100
   641  		            ],
   642  		            "MaxChunk": [
   643  		                217,
   644  		                105,
   645  		                104
   646  		            ],
   647  		            "TotalBlocks": 150,
   648  		            "ActiveBlocks": 24
   649  		        }
   650  		    ]
   651  		}
   652  `
   653  
   654  const expectedSimplePartition = `
   655  		{
   656  		    "NumTotalBlocks": 1375,
   657  		    "NumActiveBlocks": 152,
   658  		    "NumSubvolumes": 10,
   659  		    "ROI": {
   660  		        "MinChunk": [
   661  		            0,
   662  		            0,
   663  		            100
   664  		        ],
   665  		        "MaxChunk": [
   666  		            217,
   667  		            105,
   668  		            103
   669  		        ]
   670  		    },
   671  		    "Subvolumes": [
   672  		        {
   673  		            "MinPoint": [
   674  		                6368,
   675  		                3168,
   676  		                3136
   677  		            ],
   678  		            "MaxPoint": [
   679  		                6527,
   680  		                3327,
   681  		                3295
   682  		            ],
   683  		            "MinChunk": [
   684  		                199,
   685  		                99,
   686  		                98
   687  		            ],
   688  		            "MaxChunk": [
   689  		                203,
   690  		                103,
   691  		                102
   692  		            ],
   693  		            "TotalBlocks": 125,
   694  		            "ActiveBlocks": 25
   695  		        },
   696  		        {
   697  		            "MinPoint": [
   698  		                6528,
   699  		                3168,
   700  		                3136
   701  		            ],
   702  		            "MaxPoint": [
   703  		                6687,
   704  		                3327,
   705  		                3295
   706  		            ],
   707  		            "MinChunk": [
   708  		                204,
   709  		                99,
   710  		                98
   711  		            ],
   712  		            "MaxChunk": [
   713  		                208,
   714  		                103,
   715  		                102
   716  		            ],
   717  		            "TotalBlocks": 125,
   718  		            "ActiveBlocks": 40
   719  		        },
   720  		        {
   721  		            "MinPoint": [
   722  		                6688,
   723  		                3168,
   724  		                3136
   725  		            ],
   726  		            "MaxPoint": [
   727  		                6847,
   728  		                3327,
   729  		                3295
   730  		            ],
   731  		            "MinChunk": [
   732  		                209,
   733  		                99,
   734  		                98
   735  		            ],
   736  		            "MaxChunk": [
   737  		                213,
   738  		                103,
   739  		                102
   740  		            ],
   741  		            "TotalBlocks": 125,
   742  		            "ActiveBlocks": 30
   743  		        },
   744  		        {
   745  		            "MinPoint": [
   746  		                6848,
   747  		                3168,
   748  		                3136
   749  		            ],
   750  		            "MaxPoint": [
   751  		                7007,
   752  		                3327,
   753  		                3295
   754  		            ],
   755  		            "MinChunk": [
   756  		                214,
   757  		                99,
   758  		                98
   759  		            ],
   760  		            "MaxChunk": [
   761  		                218,
   762  		                103,
   763  		                102
   764  		            ],
   765  		            "TotalBlocks": 125,
   766  		            "ActiveBlocks": 8
   767  		        },
   768  		        {
   769  		            "MinPoint": [
   770  		                6496,
   771  		                3328,
   772  		                3136
   773  		            ],
   774  		            "MaxPoint": [
   775  		                6655,
   776  		                3487,
   777  		                3295
   778  		            ],
   779  		            "MinChunk": [
   780  		                203,
   781  		                104,
   782  		                98
   783  		            ],
   784  		            "MaxChunk": [
   785  		                207,
   786  		                108,
   787  		                102
   788  		            ],
   789  		            "TotalBlocks": 125,
   790  		            "ActiveBlocks": 5
   791  		        },
   792  		        {
   793  		            "MinPoint": [
   794  		                6656,
   795  		                3328,
   796  		                3136
   797  		            ],
   798  		            "MaxPoint": [
   799  		                6815,
   800  		                3487,
   801  		                3295
   802  		            ],
   803  		            "MinChunk": [
   804  		                208,
   805  		                104,
   806  		                98
   807  		            ],
   808  		            "MaxChunk": [
   809  		                212,
   810  		                108,
   811  		                102
   812  		            ],
   813  		            "TotalBlocks": 125,
   814  		            "ActiveBlocks": 5
   815  		        },
   816  		        {
   817  		            "MinPoint": [
   818  		                6816,
   819  		                3328,
   820  		                3136
   821  		            ],
   822  		            "MaxPoint": [
   823  		                6975,
   824  		                3487,
   825  		                3295
   826  		            ],
   827  		            "MinChunk": [
   828  		                213,
   829  		                104,
   830  		                98
   831  		            ],
   832  		            "MaxChunk": [
   833  		                217,
   834  		                108,
   835  		                102
   836  		            ],
   837  		            "TotalBlocks": 125,
   838  		            "ActiveBlocks": 5
   839  		        },
   840  		        {
   841  		            "MinPoint": [
   842  		                6368,
   843  		                3232,
   844  		                3296
   845  		            ],
   846  		            "MaxPoint": [
   847  		                6527,
   848  		                3391,
   849  		                3455
   850  		            ],
   851  		            "MinChunk": [
   852  		                199,
   853  		                101,
   854  		                103
   855  		            ],
   856  		            "MaxChunk": [
   857  		                203,
   858  		                105,
   859  		                107
   860  		            ],
   861  		            "TotalBlocks": 125,
   862  		            "ActiveBlocks": 11
   863  		        },
   864  		        {
   865  		            "MinPoint": [
   866  		                6528,
   867  		                3232,
   868  		                3296
   869  		            ],
   870  		            "MaxPoint": [
   871  		                6687,
   872  		                3391,
   873  		                3455
   874  		            ],
   875  		            "MinChunk": [
   876  		                204,
   877  		                101,
   878  		                103
   879  		            ],
   880  		            "MaxChunk": [
   881  		                208,
   882  		                105,
   883  		                107
   884  		            ],
   885  		            "TotalBlocks": 125,
   886  		            "ActiveBlocks": 15
   887  		        },
   888  		        {
   889  		            "MinPoint": [
   890  		                6688,
   891  		                3232,
   892  		                3296
   893  		            ],
   894  		            "MaxPoint": [
   895  		                6847,
   896  		                3391,
   897  		                3455
   898  		            ],
   899  		            "MinChunk": [
   900  		                209,
   901  		                101,
   902  		                103
   903  		            ],
   904  		            "MaxChunk": [
   905  		                213,
   906  		                105,
   907  		                107
   908  		            ],
   909  		            "TotalBlocks": 125,
   910  		            "ActiveBlocks": 8
   911  		        }
   912  		    ]
   913  		}
   914  `
   915  
   916  func TestROIRepoPersistence(t *testing.T) {
   917  	if err := server.OpenTest(); err != nil {
   918  		t.Fatalf("can't open test server: %v\n", err)
   919  	}
   920  	defer server.CloseTest()
   921  
   922  	uuid, _ := initTestRepo()
   923  
   924  	// Add data
   925  	config := dvid.NewConfig()
   926  	dataservice1, err := datastore.NewData(uuid, roitype, "myroi", config)
   927  	if err != nil {
   928  		t.Errorf("Error creating new roi instance: %v\n", err)
   929  	}
   930  	roi1, ok := dataservice1.(*Data)
   931  	if !ok {
   932  		t.Errorf("Returned new data instance 1 is not roi.Data\n")
   933  	}
   934  	if roi1.DataName() != "myroi" {
   935  		t.Errorf("New roi data instance name set incorrectly: %q != %q\n",
   936  			roi1.DataName(), "myroi")
   937  	}
   938  
   939  	config.Set("BlockSize", "15,16,17")
   940  	dataservice2, err := datastore.NewData(uuid, roitype, "myroi2", config)
   941  	if err != nil {
   942  		t.Errorf("Error creating new roi instance: %v\n", err)
   943  	}
   944  	roi2, ok := dataservice2.(*Data)
   945  	if !ok {
   946  		t.Errorf("Returned new data instance 2 is not roi.Data\n")
   947  	}
   948  	roi2.MinZ = 13
   949  	roi2.MaxZ = 3098
   950  	oldData := *roi2
   951  
   952  	// Check instance IDs
   953  	if roi1.InstanceID() == roi2.InstanceID() {
   954  		t.Errorf("Instance IDs should be different: %d == %d\n",
   955  			roi1.InstanceID(), roi2.InstanceID())
   956  	}
   957  
   958  	// Restart test datastore and see if datasets are still there.
   959  	if err = datastore.SaveDataByUUID(uuid, dataservice1); err != nil {
   960  		t.Fatalf("Unable to save data1 during ROI persistence test: %v\n", err)
   961  	}
   962  	if err = datastore.SaveDataByUUID(uuid, dataservice2); err != nil {
   963  		t.Fatalf("Unable to save data2 during ROI persistence test: %v\n", err)
   964  	}
   965  
   966  	datastore.CloseReopenTest()
   967  
   968  	dataservice3, err := datastore.GetDataByUUIDName(uuid, "myroi2")
   969  	if err != nil {
   970  		t.Fatalf("Can't get first ROI instance from reloaded test db: %v\n", err)
   971  	}
   972  	roi2new, ok := dataservice3.(*Data)
   973  	if !ok {
   974  		t.Errorf("Returned new data instance 3 is not roi.Data\n")
   975  	}
   976  	if !oldData.Equals(roi2new) {
   977  		t.Errorf("Expected %v, got %v\n", oldData, *roi2new)
   978  	}
   979  }