github.com/petermattis/pebble@v0.0.0-20190905164901-ab51a2166067/error_test.go (about)

     1  // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use
     2  // of this source code is governed by a BSD-style license that can be found in
     3  // the LICENSE file.
     4  
     5  package pebble
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"sync/atomic"
    12  	"testing"
    13  
    14  	"github.com/petermattis/pebble/vfs"
    15  )
    16  
    17  type panicLogger struct{}
    18  
    19  func (l panicLogger) Infof(format string, args ...interface{}) {
    20  }
    21  
    22  func (l panicLogger) Fatalf(format string, args ...interface{}) {
    23  	panic(fmt.Errorf("fatal: "+format, args...))
    24  }
    25  
    26  type errorFS struct {
    27  	fs    vfs.FS
    28  	index int32
    29  }
    30  
    31  func newErrorFS(index int32) *errorFS {
    32  	return &errorFS{
    33  		fs:    vfs.NewMem(),
    34  		index: index,
    35  	}
    36  }
    37  
    38  func (fs *errorFS) maybeError() error {
    39  	if atomic.AddInt32(&fs.index, -1) == -1 {
    40  		return fmt.Errorf("injected error")
    41  	}
    42  	return nil
    43  }
    44  
    45  func (fs *errorFS) Create(name string) (vfs.File, error) {
    46  	if err := fs.maybeError(); err != nil {
    47  		return nil, err
    48  	}
    49  	f, err := fs.fs.Create(name)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	return errorFile{f, fs}, nil
    54  }
    55  
    56  func (fs *errorFS) Link(oldname, newname string) error {
    57  	if err := fs.maybeError(); err != nil {
    58  		return err
    59  	}
    60  	return fs.fs.Link(oldname, newname)
    61  }
    62  
    63  func (fs *errorFS) Open(name string, opts ...vfs.OpenOption) (vfs.File, error) {
    64  	if err := fs.maybeError(); err != nil {
    65  		return nil, err
    66  	}
    67  	f, err := fs.fs.Open(name)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	ef := errorFile{f, fs}
    72  	for _, opt := range opts {
    73  		opt.Apply(ef)
    74  	}
    75  	return ef, nil
    76  }
    77  
    78  func (fs *errorFS) OpenDir(name string) (vfs.File, error) {
    79  	if err := fs.maybeError(); err != nil {
    80  		return nil, err
    81  	}
    82  	f, err := fs.fs.OpenDir(name)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	return errorFile{f, fs}, nil
    87  }
    88  
    89  func (fs *errorFS) Remove(name string) error {
    90  	if _, err := fs.fs.Stat(name); os.IsNotExist(err) {
    91  		return nil
    92  	}
    93  
    94  	if err := fs.maybeError(); err != nil {
    95  		return err
    96  	}
    97  	return fs.fs.Remove(name)
    98  }
    99  
   100  func (fs *errorFS) Rename(oldname, newname string) error {
   101  	if err := fs.maybeError(); err != nil {
   102  		return err
   103  	}
   104  	return fs.fs.Rename(oldname, newname)
   105  }
   106  
   107  func (fs *errorFS) MkdirAll(dir string, perm os.FileMode) error {
   108  	if err := fs.maybeError(); err != nil {
   109  		return err
   110  	}
   111  	return fs.fs.MkdirAll(dir, perm)
   112  }
   113  
   114  func (fs *errorFS) Lock(name string) (io.Closer, error) {
   115  	if err := fs.maybeError(); err != nil {
   116  		return nil, err
   117  	}
   118  	return fs.fs.Lock(name)
   119  }
   120  
   121  func (fs *errorFS) List(dir string) ([]string, error) {
   122  	if err := fs.maybeError(); err != nil {
   123  		return nil, err
   124  	}
   125  	return fs.fs.List(dir)
   126  }
   127  
   128  func (fs *errorFS) Stat(name string) (os.FileInfo, error) {
   129  	if err := fs.maybeError(); err != nil {
   130  		return nil, err
   131  	}
   132  	return fs.fs.Stat(name)
   133  }
   134  
   135  type errorFile struct {
   136  	file vfs.File
   137  	fs   *errorFS
   138  }
   139  
   140  func (f errorFile) Close() error {
   141  	// We don't inject errors during close as those calls should never fail in
   142  	// practice.
   143  	return f.file.Close()
   144  }
   145  
   146  func (f errorFile) Read(p []byte) (int, error) {
   147  	if err := f.fs.maybeError(); err != nil {
   148  		return 0, err
   149  	}
   150  	return f.file.Read(p)
   151  }
   152  
   153  func (f errorFile) ReadAt(p []byte, off int64) (int, error) {
   154  	if err := f.fs.maybeError(); err != nil {
   155  		return 0, err
   156  	}
   157  	return f.file.ReadAt(p, off)
   158  }
   159  
   160  func (f errorFile) Write(p []byte) (int, error) {
   161  	if err := f.fs.maybeError(); err != nil {
   162  		return 0, err
   163  	}
   164  	return f.file.Write(p)
   165  }
   166  
   167  func (f errorFile) Stat() (os.FileInfo, error) {
   168  	if err := f.fs.maybeError(); err != nil {
   169  		return nil, err
   170  	}
   171  	return f.file.Stat()
   172  }
   173  
   174  func (f errorFile) Sync() error {
   175  	if err := f.fs.maybeError(); err != nil {
   176  		return err
   177  	}
   178  	return f.file.Sync()
   179  }
   180  
   181  // TestErrors repeatedly runs a short sequence of operations, injecting FS
   182  // errors at different points, until success is achieved.
   183  func TestErrors(t *testing.T) {
   184  	run := func(fs *errorFS) (err error) {
   185  		defer func() {
   186  			if r := recover(); r != nil {
   187  				if e, ok := r.(error); ok {
   188  					err = e
   189  				} else {
   190  					err = fmt.Errorf("%v", r)
   191  				}
   192  			}
   193  		}()
   194  
   195  		d, err := Open("", &Options{
   196  			FS:     fs,
   197  			Logger: panicLogger{},
   198  		})
   199  		if err != nil {
   200  			return err
   201  		}
   202  
   203  		key := []byte("a")
   204  		value := []byte("b")
   205  		if err := d.Set(key, value, nil); err != nil {
   206  			return err
   207  		}
   208  		if err := d.Flush(); err != nil {
   209  			return err
   210  		}
   211  		if err := d.Compact(nil, nil); err != nil {
   212  			return err
   213  		}
   214  
   215  		iter := d.NewIter(nil)
   216  		for valid := iter.First(); valid; valid = iter.Next() {
   217  		}
   218  		if err := iter.Close(); err != nil {
   219  			return err
   220  		}
   221  		return d.Close()
   222  	}
   223  
   224  	errorCounts := make(map[string]int)
   225  	for i := int32(0); ; i++ {
   226  		fs := newErrorFS(i)
   227  		err := run(fs)
   228  		if err == nil {
   229  			t.Logf("success %d\n", i)
   230  			break
   231  		}
   232  		errorCounts[err.Error()]++
   233  	}
   234  
   235  	expectedErrors := []string{
   236  		"fatal: MANIFEST flush failed: injected error",
   237  		"fatal: MANIFEST sync failed: injected error",
   238  		"fatal: MANIFEST set current failed: injected error",
   239  		"fatal: MANIFEST dirsync failed: injected error",
   240  	}
   241  	for _, expected := range expectedErrors {
   242  		if errorCounts[expected] == 0 {
   243  			t.Errorf("expected error %q did not occur", expected)
   244  		}
   245  	}
   246  }