run tests and linter on every PR

This commit is contained in:
Maria Ines Parnisari
2023-11-20 13:44:15 -08:00
parent 7b5dfadcde
commit de3e573a65
10 changed files with 92 additions and 58 deletions

39
.github/workflows/pull_request.yaml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Pull Request
on:
merge_group:
pull_request:
branches:
- master
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version-file: './go.mod'
check-latest: true
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: latest
test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version-file: './go.mod'
check-latest: true
- name: Unit Tests
run: make t

35
.golangci.yaml Normal file
View File

@@ -0,0 +1,35 @@
run:
timeout: 3m
modules-download-mode: readonly
linters:
enable:
- errname
- gofmt
- goimports
- stylecheck
- importas
- errcheck
- gosimple
- govet
- ineffassign
- mirror
- staticcheck
- tagalign
- testifylint
- typecheck
- unused
- unconvert
- unparam
- wastedassign
- whitespace
- exhaustive
- noctx
- promlinter
linters-settings:
govet:
enable-all: true
disable:
- shadow
- fieldalignment

View File

@@ -1,18 +1,20 @@
.PHONY: l
l: ## Lint Go source files
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest && golangci-lint run
.PHONY: t .PHONY: t
t: t: ## Run unit tests
go test -race -count=1 ./... go test -race -count=1 ./...
.PHONY: f .PHONY: f
f: f: ## Format code
go fmt ./... go fmt ./...
.PHONY: c .PHONY: c
c: c: ## Measure code coverage
go test -race -covermode=atomic ./... -coverprofile=cover.out && \ go test -race -covermode=atomic ./... -coverprofile=cover.out && \
# go tool cover -html=cover.out && \
go tool cover -func cover.out \ go tool cover -func cover.out \
| grep -vP '[89]\d\.\d%' | grep -v '100.0%' \ | grep -vP '[89]\d\.\d%' | grep -v '100.0%' \
|| true || true
rm cover.out rm cover.out

View File

@@ -7,33 +7,6 @@ import (
"time" "time"
) )
// The cache has a generic 'control' channel that is used to send
// messages to the worker. These are the messages that can be sent to it
type getDropped struct {
res chan int
}
type getSize struct {
res chan int64
}
type setMaxSize struct {
size int64
done chan struct{}
}
type clear struct {
done chan struct{}
}
type syncWorker struct {
done chan struct{}
}
type gc struct {
done chan struct{}
}
type Cache[T any] struct { type Cache[T any] struct {
*Configuration[T] *Configuration[T]
control control
@@ -203,11 +176,6 @@ func (c *Cache[T]) Delete(key string) bool {
return false return false
} }
func (c *Cache[T]) deleteItem(bucket *bucket[T], item *Item[T]) {
bucket.delete(item.key) //stop other GETs from getting it
c.deletables <- item
}
func (c *Cache[T]) set(key string, value T, duration time.Duration, track bool) *Item[T] { func (c *Cache[T]) set(key string, value T, duration time.Duration, track bool) *Item[T] {
item, existing := c.bucket(key).set(key, value, duration, track) item, existing := c.bucket(key).set(key, value, duration, track)
if existing != nil { if existing != nil {
@@ -389,7 +357,7 @@ func (c *Cache[T]) gc() int {
} }
prev := node.Prev prev := node.Prev
item := node.Value item := node.Value
if c.tracking == false || atomic.LoadInt32(&item.refCount) == 0 { if !c.tracking || atomic.LoadInt32(&item.refCount) == 0 {
c.bucket(item.key).delete(item.key) c.bucket(item.key).delete(item.key)
c.size -= item.size c.size -= item.size
c.list.Remove(node) c.list.Remove(node)

View File

@@ -124,7 +124,6 @@ func Test_CacheDeletesAFunc(t *testing.T) {
return key == "d" return key == "d"
}), 1) }), 1)
assert.Equal(t, cache.ItemCount(), 2) assert.Equal(t, cache.ItemCount(), 2)
} }
func Test_CacheOnDeleteCallbackCalled(t *testing.T) { func Test_CacheOnDeleteCallbackCalled(t *testing.T) {

View File

@@ -37,7 +37,7 @@ func (c *Configuration[T]) MaxSize(max int64) *Configuration[T] {
// requires a write lock on the bucket). Must be a power of 2 (1, 2, 4, 8, 16, ...) // requires a write lock on the bucket). Must be a power of 2 (1, 2, 4, 8, 16, ...)
// [16] // [16]
func (c *Configuration[T]) Buckets(count uint32) *Configuration[T] { func (c *Configuration[T]) Buckets(count uint32) *Configuration[T] {
if count == 0 || ((count&(^count+1)) == count) == false { if count == 0 || !((count & (^count + 1)) == count) {
count = 16 count = 16
} }
c.buckets = int(count) c.buckets = int(count)

View File

@@ -32,7 +32,7 @@ func (b *layeredBucket[T]) getSecondaryBucket(primary string) *bucket[T] {
b.RLock() b.RLock()
bucket, exists := b.buckets[primary] bucket, exists := b.buckets[primary]
b.RUnlock() b.RUnlock()
if exists == false { if !exists {
return nil return nil
} }
return bucket return bucket
@@ -41,7 +41,7 @@ func (b *layeredBucket[T]) getSecondaryBucket(primary string) *bucket[T] {
func (b *layeredBucket[T]) set(primary, secondary string, value T, duration time.Duration, track bool) (*Item[T], *Item[T]) { func (b *layeredBucket[T]) set(primary, secondary string, value T, duration time.Duration, track bool) (*Item[T], *Item[T]) {
b.Lock() b.Lock()
bkt, exists := b.buckets[primary] bkt, exists := b.buckets[primary]
if exists == false { if !exists {
bkt = &bucket[T]{lookup: make(map[string]*Item[T])} bkt = &bucket[T]{lookup: make(map[string]*Item[T])}
b.buckets[primary] = bkt b.buckets[primary] = bkt
} }
@@ -55,7 +55,7 @@ func (b *layeredBucket[T]) delete(primary, secondary string) *Item[T] {
b.RLock() b.RLock()
bucket, exists := b.buckets[primary] bucket, exists := b.buckets[primary]
b.RUnlock() b.RUnlock()
if exists == false { if !exists {
return nil return nil
} }
return bucket.delete(secondary) return bucket.delete(secondary)
@@ -65,7 +65,7 @@ func (b *layeredBucket[T]) deletePrefix(primary, prefix string, deletables chan
b.RLock() b.RLock()
bucket, exists := b.buckets[primary] bucket, exists := b.buckets[primary]
b.RUnlock() b.RUnlock()
if exists == false { if !exists {
return 0 return 0
} }
return bucket.deletePrefix(prefix, deletables) return bucket.deletePrefix(prefix, deletables)
@@ -75,7 +75,7 @@ func (b *layeredBucket[T]) deleteFunc(primary string, matches func(key string, i
b.RLock() b.RLock()
bucket, exists := b.buckets[primary] bucket, exists := b.buckets[primary]
b.RUnlock() b.RUnlock()
if exists == false { if !exists {
return 0 return 0
} }
return bucket.deleteFunc(matches, deletables) return bucket.deleteFunc(matches, deletables)
@@ -85,7 +85,7 @@ func (b *layeredBucket[T]) deleteAll(primary string, deletables chan *Item[T]) b
b.RLock() b.RLock()
bucket, exists := b.buckets[primary] bucket, exists := b.buckets[primary]
b.RUnlock() b.RUnlock()
if exists == false { if !exists {
return false return false
} }

View File

@@ -41,7 +41,7 @@ func Layered[T any](config *Configuration[T]) *LayeredCache[T] {
deletables: make(chan *Item[T], config.deleteBuffer), deletables: make(chan *Item[T], config.deleteBuffer),
promotables: make(chan *Item[T], config.promoteBuffer), promotables: make(chan *Item[T], config.promoteBuffer),
} }
for i := 0; i < int(config.buckets); i++ { for i := 0; i < config.buckets; i++ {
c.buckets[i] = &layeredBucket[T]{ c.buckets[i] = &layeredBucket[T]{
buckets: make(map[string]*bucket[T]), buckets: make(map[string]*bucket[T]),
} }
@@ -334,7 +334,7 @@ func (c *LayeredCache[T]) gc() int {
} }
prev := node.Prev prev := node.Prev
item := node.Value item := node.Value
if c.tracking == false || atomic.LoadInt32(&item.refCount) == 0 { if !c.tracking || atomic.LoadInt32(&item.refCount) == 0 {
c.bucket(item.group).delete(item.group, item.key) c.bucket(item.group).delete(item.group, item.key)
c.size -= item.size c.size -= item.size
c.list.Remove(node) c.list.Remove(node)

View File

@@ -118,7 +118,6 @@ func Test_LayedCache_DeletesAFunc(t *testing.T) {
return key == "d" return key == "d"
}), 1) }), 1)
assert.Equal(t, cache.ItemCount(), 3) assert.Equal(t, cache.ItemCount(), 3)
} }
func Test_LayedCache_OnDeleteCallbackCalled(t *testing.T) { func Test_LayedCache_OnDeleteCallbackCalled(t *testing.T) {

View File

@@ -85,11 +85,3 @@ func assertList(t *testing.T, list *List[int], expected ...int) {
node = node.Prev node = node.Prev
} }
} }
func listFromInts(ints ...int) *List[int] {
l := NewList[int]()
for i := len(ints) - 1; i >= 0; i-- {
l.Insert(ints[i])
}
return l
}