github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/test/fetcher.go (about)

     1  /*
     2  Copyright 2011 Google Inc.
     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 test
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"io/ioutil"
    23  	"os"
    24  	"sort"
    25  	"strings"
    26  	"sync"
    27  
    28  	"camlistore.org/pkg/blob"
    29  	"camlistore.org/pkg/blobserver"
    30  	"camlistore.org/pkg/context"
    31  	"camlistore.org/pkg/types"
    32  )
    33  
    34  // Fetcher is an in-memory implementation of the blobserver Storage
    35  // interface.  It started as just a fetcher and grew. It also includes
    36  // other convenience methods for testing.
    37  type Fetcher struct {
    38  	l      sync.Mutex
    39  	m      map[string]*Blob // keyed by blobref string
    40  	sorted []string         // blobrefs sorted
    41  
    42  	// ReceiveErr optionally returns the error to return on receive.
    43  	ReceiveErr error
    44  
    45  	// FetchErr, if non-nil, specifies the error to return on the next fetch call.
    46  	// If it returns nil, fetches proceed as normal.
    47  	FetchErr func() error
    48  }
    49  
    50  var _ blobserver.Storage = (*Fetcher)(nil)
    51  
    52  func (tf *Fetcher) AddBlob(b *Blob) {
    53  	tf.l.Lock()
    54  	defer tf.l.Unlock()
    55  	if tf.m == nil {
    56  		tf.m = make(map[string]*Blob)
    57  	}
    58  	key := b.BlobRef().String()
    59  	_, had := tf.m[key]
    60  	tf.m[key] = b
    61  	if !had {
    62  		tf.sorted = append(tf.sorted, key)
    63  		sort.Strings(tf.sorted)
    64  	}
    65  }
    66  
    67  func (tf *Fetcher) FetchStreaming(ref blob.Ref) (file io.ReadCloser, size int64, err error) {
    68  	return tf.Fetch(ref)
    69  }
    70  
    71  var dummyCloser = ioutil.NopCloser(nil)
    72  
    73  func (tf *Fetcher) Fetch(ref blob.Ref) (file types.ReadSeekCloser, size int64, err error) {
    74  	if tf.FetchErr != nil {
    75  		if err = tf.FetchErr(); err != nil {
    76  			return
    77  		}
    78  	}
    79  	tf.l.Lock()
    80  	defer tf.l.Unlock()
    81  	if tf.m == nil {
    82  		err = os.ErrNotExist
    83  		return
    84  	}
    85  	tb, ok := tf.m[ref.String()]
    86  	if !ok {
    87  		err = os.ErrNotExist
    88  		return
    89  	}
    90  	size = int64(len(tb.Contents))
    91  	return struct {
    92  		*io.SectionReader
    93  		io.Closer
    94  	}{
    95  		io.NewSectionReader(strings.NewReader(tb.Contents), 0, size),
    96  		dummyCloser,
    97  	}, size, nil
    98  }
    99  
   100  func (tf *Fetcher) BlobContents(br blob.Ref) (contents string, ok bool) {
   101  	tf.l.Lock()
   102  	defer tf.l.Unlock()
   103  	b, ok := tf.m[br.String()]
   104  	if !ok {
   105  		return
   106  	}
   107  	return b.Contents, true
   108  }
   109  
   110  func (tf *Fetcher) ReceiveBlob(br blob.Ref, source io.Reader) (blob.SizedRef, error) {
   111  	sb := blob.SizedRef{}
   112  	h := br.Hash()
   113  	if h == nil {
   114  		return sb, fmt.Errorf("Unsupported blobref hash for %s", br)
   115  	}
   116  	all, err := ioutil.ReadAll(io.TeeReader(source, h))
   117  	if err != nil {
   118  		return sb, err
   119  	}
   120  	if !br.HashMatches(h) {
   121  		// This is a somewhat redundant check, since
   122  		// blobserver.Receive now does it. But for testing code,
   123  		// it's worth the cost.
   124  		return sb, fmt.Errorf("Hash mismatch receiving blob %s", br)
   125  	}
   126  	if err := tf.ReceiveErr; err != nil {
   127  		return sb, err
   128  	}
   129  	b := &Blob{Contents: string(all)}
   130  	tf.AddBlob(b)
   131  	return blob.SizedRef{br, int64(len(all))}, nil
   132  }
   133  
   134  func (tf *Fetcher) StatBlobs(dest chan<- blob.SizedRef, blobs []blob.Ref) error {
   135  	for _, br := range blobs {
   136  		tf.l.Lock()
   137  		b, ok := tf.m[br.String()]
   138  		tf.l.Unlock()
   139  		if ok {
   140  			dest <- blob.SizedRef{br, int64(len(b.Contents))}
   141  		}
   142  	}
   143  	return nil
   144  }
   145  
   146  // BlobrefStrings returns the sorted stringified blobrefs stored in this fetcher.
   147  func (tf *Fetcher) BlobrefStrings() []string {
   148  	tf.l.Lock()
   149  	defer tf.l.Unlock()
   150  	s := make([]string, len(tf.sorted))
   151  	copy(s, tf.sorted)
   152  	return s
   153  }
   154  
   155  func (tf *Fetcher) EnumerateBlobs(ctx *context.Context, dest chan<- blob.SizedRef, after string, limit int) error {
   156  	defer close(dest)
   157  	tf.l.Lock()
   158  	defer tf.l.Unlock()
   159  	n := 0
   160  	for _, k := range tf.sorted {
   161  		if k <= after {
   162  			continue
   163  		}
   164  		b := tf.m[k]
   165  		select {
   166  		case dest <- blob.SizedRef{b.BlobRef(), b.Size()}:
   167  		case <-ctx.Done():
   168  			return context.ErrCanceled
   169  		}
   170  		n++
   171  		if limit > 0 && n == limit {
   172  			break
   173  		}
   174  	}
   175  	return nil
   176  }
   177  
   178  func (tf *Fetcher) RemoveBlobs(blobs []blob.Ref) error {
   179  	tf.l.Lock()
   180  	defer tf.l.Unlock()
   181  	for _, br := range blobs {
   182  		delete(tf.m, br.String())
   183  	}
   184  	tf.sorted = tf.sorted[:0]
   185  	for k := range tf.m {
   186  		tf.sorted = append(tf.sorted, k)
   187  	}
   188  	sort.Strings(tf.sorted)
   189  	return nil
   190  }