Skip to content

Commit

Permalink
implement feature add unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
peczenyj committed Nov 4, 2023
1 parent 2ea66c9 commit b9090f8
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 19 deletions.
54 changes: 43 additions & 11 deletions fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@ type FS struct {
// "Cannot open requested path"
PathNotFound RequestHandler

// SkipCache if true, will cache no file handler.
//
// By default is false.
SkipCache bool

// Expiration duration for inactive file handlers.
//
// FSHandlerCacheDuration is used by default.
Expand Down Expand Up @@ -448,7 +453,7 @@ func (fs *FS) initRequestHandler() {
}

{
h.cacheManager = newInMemoryCacheManager(fs)
h.cacheManager = newCacheManager(fs)
}

fs.h = h.handleRequest
Expand Down Expand Up @@ -719,15 +724,10 @@ type cacheManager interface {
SetFileToCache(cacheKind CacheKind, path string, ff *fsFile) *fsFile
}

var _ cacheManager = (*inMemoryCacheManager)(nil)

type inMemoryCacheManager struct {
cacheDuration time.Duration
cache map[string]*fsFile
cacheBrotli map[string]*fsFile
cacheGzip map[string]*fsFile
cacheLock sync.Mutex
}
var (
_ cacheManager = (*inMemoryCacheManager)(nil)
_ cacheManager = (*noopCacheManager)(nil)
)

type CacheKind uint8

Expand All @@ -737,7 +737,11 @@ const (
gzipCacheKind
)

func newInMemoryCacheManager(fs *FS) *inMemoryCacheManager {
func newCacheManager(fs *FS) cacheManager {
if fs.SkipCache {
return &noopCacheManager{}
}

cacheDuration := fs.CacheDuration
if cacheDuration <= 0 {
cacheDuration = FSHandlerCacheDuration
Expand All @@ -755,6 +759,34 @@ func newInMemoryCacheManager(fs *FS) *inMemoryCacheManager {
return instance
}

type noopCacheManager struct {
cacheLock sync.Mutex
}

func (n *noopCacheManager) WithLock(work func()) {
n.cacheLock.Lock()

work()

n.cacheLock.Unlock()
}

func (*noopCacheManager) GetFileFromCache(cacheKind CacheKind, path string) (*fsFile, bool) {
return nil, false
}

func (*noopCacheManager) SetFileToCache(cacheKind CacheKind, path string, ff *fsFile) *fsFile {
return ff
}

type inMemoryCacheManager struct {
cacheDuration time.Duration
cache map[string]*fsFile
cacheBrotli map[string]*fsFile
cacheGzip map[string]*fsFile
cacheLock sync.Mutex
}

func (cm *inMemoryCacheManager) WithLock(work func()) {
cm.cacheLock.Lock()

Expand Down
113 changes: 105 additions & 8 deletions fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ func TestNewVHostPathRewriterMaliciousHost(t *testing.T) {
}

func testPathNotFound(t *testing.T, pathNotFoundFunc RequestHandler) {
t.Helper()

var ctx RequestCtx
var req Request
req.SetRequestURI("http//some.url/file")
Expand Down Expand Up @@ -302,11 +304,30 @@ func TestFSByteRangeConcurrent(t *testing.T) {
stop := make(chan struct{})
defer close(stop)

fs := &FS{
runFSByteRangeConcurrent(t, &FS{
Root: ".",
AcceptByteRange: true,
CleanStop: stop,
}
})
}

func TestFSByteRangeConcurrentSkipCache(t *testing.T) {
// This test can't run parallel as files in / might be changed by other tests.

stop := make(chan struct{})
defer close(stop)

runFSByteRangeConcurrent(t, &FS{
Root: ".",
SkipCache: true,
AcceptByteRange: true,
CleanStop: stop,
})
}

func runFSByteRangeConcurrent(t *testing.T, fs *FS) {
t.Helper()

h := fs.NewRequestHandler()

concurrency := 10
Expand Down Expand Up @@ -336,18 +357,39 @@ func TestFSByteRangeSingleThread(t *testing.T) {
stop := make(chan struct{})
defer close(stop)

fs := &FS{
runFSByteRangeSingleThread(t, &FS{
Root: ".",
AcceptByteRange: true,
CleanStop: stop,
}
})
}

func TestFSByteRangeSingleThreadSkipCache(t *testing.T) {
// This test can't run parallel as files in / might be changed by other tests.

stop := make(chan struct{})
defer close(stop)

runFSByteRangeSingleThread(t, &FS{
Root: ".",
AcceptByteRange: true,
SkipCache: true,
CleanStop: stop,
})
}

func runFSByteRangeSingleThread(t *testing.T, fs *FS) {
t.Helper()

h := fs.NewRequestHandler()

testFSByteRange(t, h, "/fs.go")
testFSByteRange(t, h, "/README.md")
}

func testFSByteRange(t *testing.T, h RequestHandler, filePath string) {
t.Helper()

var ctx RequestCtx
ctx.Init(&Request{}, nil, nil)

Expand Down Expand Up @@ -427,6 +469,8 @@ func TestParseByteRangeSuccess(t *testing.T) {
}

func testParseByteRangeSuccess(t *testing.T, v string, contentLength, startPos, endPos int) {
t.Helper()

startPos1, endPos1, err := ParseByteRange([]byte(v), contentLength)
if err != nil {
t.Fatalf("unexpected error: %v. v=%q, contentLength=%d", err, v, contentLength)
Expand Down Expand Up @@ -467,6 +511,8 @@ func TestParseByteRangeError(t *testing.T) {
}

func testParseByteRangeError(t *testing.T, v string, contentLength int) {
t.Helper()

_, _, err := ParseByteRange([]byte(v), contentLength)
if err == nil {
t.Fatalf("expecting error when parsing byte range %q", v)
Expand All @@ -480,17 +526,41 @@ func TestFSCompressConcurrent(t *testing.T) {
}

// This test can't run parallel as files in / might be changed by other tests.

stop := make(chan struct{})
defer close(stop)

fs := &FS{
runFSCompressConcurrent(t, &FS{
Root: ".",
GenerateIndexPages: true,
Compress: true,
CompressBrotli: true,
CleanStop: stop,
})
}

func TestFSCompressConcurrentSkipCache(t *testing.T) {
// Don't run this test on Windows, the Windows GitHub actions are too slow and timeout too often.
if runtime.GOOS == "windows" {
t.SkipNow()
}

// This test can't run parallel as files in / might be changed by other tests.
stop := make(chan struct{})
defer close(stop)

runFSCompressConcurrent(t, &FS{
Root: ".",
GenerateIndexPages: true,
SkipCache: true,
Compress: true,
CompressBrotli: true,
CleanStop: stop,
})
}

func runFSCompressConcurrent(t *testing.T, fs *FS) {
t.Helper()

h := fs.NewRequestHandler()

concurrency := 4
Expand Down Expand Up @@ -521,13 +591,34 @@ func TestFSCompressSingleThread(t *testing.T) {
stop := make(chan struct{})
defer close(stop)

fs := &FS{
runFSCompressSingleThread(t, &FS{
Root: ".",
GenerateIndexPages: true,
Compress: true,
CompressBrotli: true,
CleanStop: stop,
}
})
}

func TestFSCompressSingleThreadSkipCache(t *testing.T) {
// This test can't run parallel as files in / might be changed by other tests.

stop := make(chan struct{})
defer close(stop)

runFSCompressSingleThread(t, &FS{
Root: ".",
GenerateIndexPages: true,
SkipCache: true,
Compress: true,
CompressBrotli: true,
CleanStop: stop,
})
}

func runFSCompressSingleThread(t *testing.T, fs *FS) {
t.Helper()

h := fs.NewRequestHandler()

testFSCompress(t, h, "/fs.go")
Expand All @@ -536,6 +627,8 @@ func TestFSCompressSingleThread(t *testing.T) {
}

func testFSCompress(t *testing.T, h RequestHandler, filePath string) {
t.Helper()

// File locking is flaky on Windows.
if runtime.GOOS == "windows" {
t.SkipNow()
Expand Down Expand Up @@ -755,6 +848,8 @@ func TestStripPathSlashes(t *testing.T) {
}

func testStripPathSlashes(t *testing.T, path string, stripSlashes int, expectedPath string) {
t.Helper()

s := stripLeadingSlashes([]byte(path), stripSlashes)
s = stripTrailingSlashes(s)
if string(s) != expectedPath {
Expand All @@ -779,6 +874,8 @@ func TestFileExtension(t *testing.T) {
}

func testFileExtension(t *testing.T, path string, compressed bool, compressedFileSuffix, expectedExt string) {
t.Helper()

ext := fileExtension(path, compressed, compressedFileSuffix)
if ext != expectedExt {
t.Fatalf("unexpected file extension for file %q: %q. Expecting %q", path, ext, expectedExt)
Expand Down

0 comments on commit b9090f8

Please sign in to comment.