github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/blobserver/storagetest/storagetest.go (about)

     1  /*
     2  Copyright 2013 The Camlistore Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package storagetest tests blobserver.Storage implementations
    18  package storagetest
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"reflect"
    25  	"sort"
    26  	"strconv"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"camlistore.org/pkg/blob"
    32  	"camlistore.org/pkg/blobserver"
    33  	"camlistore.org/pkg/context"
    34  	"camlistore.org/pkg/syncutil"
    35  	"camlistore.org/pkg/test"
    36  )
    37  
    38  func Test(t *testing.T, fn func(*testing.T) (sto blobserver.Storage, cleanup func())) {
    39  	sto, cleanup := fn(t)
    40  	defer func() {
    41  		if t.Failed() {
    42  			t.Logf("test %T FAILED, skipping cleanup!", sto)
    43  		} else {
    44  			cleanup()
    45  		}
    46  	}()
    47  	t.Logf("Testing blobserver storage %T", sto)
    48  
    49  	t.Logf("Testing Enumerate for empty")
    50  	testEnumerate(t, sto, nil)
    51  
    52  	var blobs []*test.Blob
    53  	var blobRefs []blob.Ref
    54  	var blobSizedRefs []blob.SizedRef
    55  
    56  	contents := []string{"foo", "quux", "asdf", "qwerty", "0123456789"}
    57  	if !testing.Short() {
    58  		for i := 0; i < 95; i++ {
    59  			contents = append(contents, "foo-"+strconv.Itoa(i))
    60  		}
    61  	}
    62  	t.Logf("Testing receive")
    63  	for _, x := range contents {
    64  		b1 := &test.Blob{x}
    65  		b1s, err := sto.ReceiveBlob(b1.BlobRef(), b1.Reader())
    66  		if err != nil {
    67  			t.Fatalf("ReceiveBlob of %s: %v", b1, err)
    68  		}
    69  		if b1s != b1.SizedRef() {
    70  			t.Fatal("Received %v; want %v", b1s, b1.SizedRef())
    71  		}
    72  		blobs = append(blobs, b1)
    73  		blobRefs = append(blobRefs, b1.BlobRef())
    74  		blobSizedRefs = append(blobSizedRefs, b1.SizedRef())
    75  
    76  		switch len(blobSizedRefs) {
    77  		case 1, 5, 100:
    78  			t.Logf("Testing Enumerate for %d blobs", len(blobSizedRefs))
    79  			testEnumerate(t, sto, blobSizedRefs)
    80  		}
    81  	}
    82  	b1 := blobs[0]
    83  
    84  	// finish here if you want to examine the test directory
    85  	//t.Fatalf("FINISH")
    86  
    87  	t.Logf("Testing Fetch")
    88  	for i, b2 := range blobs {
    89  		rc, size, err := sto.Fetch(b2.BlobRef())
    90  		if err != nil {
    91  			t.Fatalf("error fetching %d. %s: %v", i, b2, err)
    92  		}
    93  		defer rc.Close()
    94  		testSizedBlob(t, rc, b2.BlobRef(), int64(size))
    95  	}
    96  
    97  	t.Logf("Testing Stat")
    98  	dest := make(chan blob.SizedRef)
    99  	go func() {
   100  		if err := sto.StatBlobs(dest, blobRefs); err != nil {
   101  			t.Fatalf("error stating blobs %s: %v", blobRefs, err)
   102  		}
   103  	}()
   104  	testStat(t, dest, blobSizedRefs)
   105  
   106  	// Enumerate tests.
   107  	sort.Sort(blob.SizedByRef(blobSizedRefs))
   108  
   109  	t.Logf("Testing Enumerate on all")
   110  	testEnumerate(t, sto, blobSizedRefs)
   111  
   112  	t.Logf("Testing Enumerate 'limit' param")
   113  	testEnumerate(t, sto, blobSizedRefs[:3], 3)
   114  
   115  	// Enumerate 'after'
   116  	{
   117  		after := blobSizedRefs[2].Ref.String()
   118  		t.Logf("Testing Enumerate 'after' param; after %q", after)
   119  		testEnumerate(t, sto, blobSizedRefs[3:], after)
   120  	}
   121  
   122  	// Enumerate 'after' + limit
   123  	{
   124  		after := blobSizedRefs[2].Ref.String()
   125  		t.Logf("Testing Enumerate 'after' + 'limit' param; after %q, limit 1", after)
   126  		testEnumerate(t, sto, blobSizedRefs[3:4], after, 1)
   127  	}
   128  
   129  	// Enumerate 'after' with prefix of a blobref + limit
   130  	{
   131  		after := "a"
   132  		t.Logf("Testing Enumerate 'after' + 'limit' param; after %q, limit 1", after)
   133  		testEnumerate(t, sto, blobSizedRefs[:1], after, 1)
   134  	}
   135  
   136  	t.Logf("Testing Remove")
   137  	if err := sto.RemoveBlobs(blobRefs); err != nil {
   138  		if strings.Contains(err.Error(), "not implemented") {
   139  			t.Logf("RemoveBlob %s: %v", b1, err)
   140  		} else {
   141  			t.Fatalf("RemoveBlob %s: %v", b1, err)
   142  		}
   143  	}
   144  }
   145  
   146  func testSizedBlob(t *testing.T, r io.Reader, b1 blob.Ref, size int64) {
   147  	h := b1.Hash()
   148  	n, err := io.Copy(h, r)
   149  	if err != nil {
   150  		t.Fatalf("error reading from %s: %v", r, err)
   151  	}
   152  	if n != size {
   153  		t.Fatalf("read %d bytes from %s, metadata said %d!", n, size)
   154  	}
   155  	b2 := blob.RefFromHash(h)
   156  	if b2 != b1 {
   157  		t.Fatalf("content mismatch (awaited %s, got %s)", b1, b2)
   158  	}
   159  }
   160  
   161  func CheckEnumerate(sto blobserver.Storage, wantUnsorted []blob.SizedRef, opts ...interface{}) error {
   162  	var after string
   163  	var n = 1000
   164  	for _, opt := range opts {
   165  		switch v := opt.(type) {
   166  		case string:
   167  			after = v
   168  		case int:
   169  			n = v
   170  		default:
   171  			panic("bad option of type " + fmt.Sprint("%T", v))
   172  		}
   173  	}
   174  
   175  	want := append([]blob.SizedRef(nil), wantUnsorted...)
   176  	sort.Sort(blob.SizedByRef(want))
   177  
   178  	sbc := make(chan blob.SizedRef, 10)
   179  
   180  	var got []blob.SizedRef
   181  	var grp syncutil.Group
   182  	sawEnd := make(chan bool, 1)
   183  	grp.Go(func() error {
   184  		if err := sto.EnumerateBlobs(context.New(), sbc, after, n); err != nil {
   185  			return fmt.Errorf("EnumerateBlobs(%q, %d): %v", after, n, err)
   186  		}
   187  		return nil
   188  	})
   189  	grp.Go(func() error {
   190  		for sb := range sbc {
   191  			if !sb.Valid() {
   192  				return fmt.Errorf("invalid blobref %#v received in enumerate", sb)
   193  			}
   194  			got = append(got, sb)
   195  		}
   196  		sawEnd <- true
   197  		return nil
   198  
   199  	})
   200  	grp.Go(func() error {
   201  		select {
   202  		case <-sawEnd:
   203  			return nil
   204  		case <-time.After(10 * time.Second):
   205  			return errors.New("timeout waiting for EnumerateBlobs to close its channel")
   206  		}
   207  
   208  	})
   209  	if err := grp.Err(); err != nil {
   210  		return fmt.Errorf("Enumerate error: %v", err)
   211  	}
   212  	if len(got) == 0 && len(want) == 0 {
   213  		return nil
   214  	}
   215  	if !reflect.DeepEqual(got, want) {
   216  		return fmt.Errorf("Enumerate mismatch. Got %d; want %d.\n Got: %v\nWant: %v\n",
   217  			len(got), len(want), got, want)
   218  	}
   219  	return nil
   220  }
   221  
   222  func testEnumerate(t *testing.T, sto blobserver.Storage, wantUnsorted []blob.SizedRef, opts ...interface{}) {
   223  	if err := CheckEnumerate(sto, wantUnsorted, opts...); err != nil {
   224  		t.Fatalf("%v", err)
   225  	}
   226  }
   227  
   228  func testStat(t *testing.T, enum <-chan blob.SizedRef, want []blob.SizedRef) {
   229  	// blobs may arrive in ANY order
   230  	m := make(map[string]int, len(want))
   231  	for i, sb := range want {
   232  		m[sb.Ref.String()] = i
   233  	}
   234  
   235  	i := 0
   236  	for sb := range enum {
   237  		if !sb.Valid() {
   238  			break
   239  		}
   240  		wanted := want[m[sb.Ref.String()]]
   241  		if wanted.Size != sb.Size {
   242  			t.Fatalf("received blob size is %d, wanted %d for &%d", sb.Size, wanted.Size, i)
   243  		}
   244  		if wanted.Ref != sb.Ref {
   245  			t.Fatalf("received blob ref mismatch &%d: wanted %s, got %s", i, sb.Ref, wanted.Ref)
   246  		}
   247  		i++
   248  		if i >= len(want) {
   249  			break
   250  		}
   251  	}
   252  }