github.com/Schaudge/grailbase@v0.0.0-20240223061707-44c758a471c0/file/internal/readmatcher/readmatcher_test.go (about)

     1  package readmatcher_test
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"io"
     7  	"math/rand"
     8  	"os"
     9  	"path"
    10  	"runtime"
    11  	"testing"
    12  
    13  	"github.com/Schaudge/grailbase/file/fsnode"
    14  	"github.com/Schaudge/grailbase/file/fsnodefuse"
    15  	"github.com/Schaudge/grailbase/file/internal/readmatcher"
    16  	"github.com/Schaudge/grailbase/file/internal/readmatcher/readmatchertest"
    17  	"github.com/Schaudge/grailbase/ioctx"
    18  	"github.com/Schaudge/grailbase/log"
    19  	"github.com/Schaudge/grailbase/must"
    20  	"github.com/grailbio/testutil"
    21  	"github.com/grailbio/testutil/assert"
    22  	"github.com/hanwen/go-fuse/v2/fs"
    23  	"github.com/hanwen/go-fuse/v2/fuse"
    24  	"github.com/stretchr/testify/require"
    25  )
    26  
    27  var (
    28  	dataBytes         = flag.Int("data-bytes", 1<<27, "read corpus size")
    29  	stressParallelism = flag.Int("stress-parallelism", runtime.NumCPU(),
    30  		"number of parallel readers during stress test")
    31  	fuseFlag = flag.Bool("fuse", false, "create a temporary FUSE mount and test through that")
    32  )
    33  
    34  func TestStress(t *testing.T) {
    35  	var data = make([]byte, *dataBytes)
    36  	_, _ = rand.New(rand.NewSource(1)).Read(data)
    37  	offsetReader := func(start int64) ioctx.ReadCloser {
    38  		return ioctx.FromStdReadCloser(io.NopCloser(bytes.NewReader(data[start:])))
    39  	}
    40  	type fuseCase struct {
    41  		name string
    42  		test func(*testing.T, ioctx.ReaderAt)
    43  	}
    44  	fuseCases := []fuseCase{
    45  		{
    46  			"nofuse",
    47  			func(t *testing.T, r ioctx.ReaderAt) {
    48  				readmatchertest.Stress(data, r, *stressParallelism)
    49  			},
    50  		},
    51  	}
    52  	if *fuseFlag {
    53  		fuseCases = append(fuseCases, fuseCase{
    54  			"fuse",
    55  			func(t *testing.T, rAt ioctx.ReaderAt) {
    56  				mountPoint, cleanUpMountPoint := testutil.TempDir(t, "", "readmatcher_test")
    57  				defer cleanUpMountPoint()
    58  				const filename = "data"
    59  				server, err := fs.Mount(
    60  					mountPoint,
    61  					fsnodefuse.NewRoot(fsnode.NewParent(
    62  						fsnode.NewDirInfo("root"),
    63  						fsnode.ConstChildren(
    64  							fsnode.ReaderAtLeaf(
    65  								fsnode.NewRegInfo(filename).WithSize(int64(len(data))),
    66  								rAt,
    67  							),
    68  						),
    69  					)),
    70  					&fs.Options{
    71  						MountOptions: func() fuse.MountOptions {
    72  							opts := fuse.MountOptions{FsName: "test", Debug: log.At(log.Debug)}
    73  							fsnodefuse.ConfigureRequiredMountOptions(&opts)
    74  							fsnodefuse.ConfigureDefaultMountOptions(&opts)
    75  							return opts
    76  						}(),
    77  					},
    78  				)
    79  				require.NoError(t, err, "mounting %q", mountPoint)
    80  				defer func() {
    81  					log.Printf("unmounting %q", mountPoint)
    82  					assert.NoError(t, server.Unmount(),
    83  						"unmount of FUSE mounted at %q failed; may need manual cleanup",
    84  						mountPoint,
    85  					)
    86  					log.Printf("unmounted %q", mountPoint)
    87  				}()
    88  				f, err := os.Open(path.Join(mountPoint, filename))
    89  				require.NoError(t, err)
    90  				defer func() { require.NoError(t, f.Close()) }()
    91  				readmatchertest.Stress(data, ioctx.FromStdReaderAt(f), *stressParallelism)
    92  			},
    93  		})
    94  	}
    95  	for _, c := range fuseCases {
    96  		t.Run(c.name, func(t *testing.T) {
    97  			t.Run("less parallelism", func(t *testing.T) {
    98  				readerParallelism := *stressParallelism / 2
    99  				must.True(readerParallelism > 0)
   100  				m := readmatcher.New(offsetReader, readmatcher.SoftMaxReaders(readerParallelism))
   101  				c.test(t, m)
   102  			})
   103  			t.Run("more parallelism", func(t *testing.T) {
   104  				readerParallelism := 2 * *stressParallelism
   105  				m := readmatcher.New(offsetReader, readmatcher.SoftMaxReaders(readerParallelism))
   106  				c.test(t, m)
   107  			})
   108  		})
   109  	}
   110  }