run tests and linter on every PR
This commit is contained in:
39
.github/workflows/pull_request.yaml
vendored
Normal file
39
.github/workflows/pull_request.yaml
vendored
Normal 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
35
.golangci.yaml
Normal 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
|
14
Makefile
14
Makefile
@@ -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
|
||||
t:
|
||||
t: ## Run unit tests
|
||||
go test -race -count=1 ./...
|
||||
|
||||
.PHONY: f
|
||||
f:
|
||||
f: ## Format code
|
||||
go fmt ./...
|
||||
|
||||
|
||||
.PHONY: c
|
||||
c:
|
||||
c: ## Measure code coverage
|
||||
go test -race -covermode=atomic ./... -coverprofile=cover.out && \
|
||||
# go tool cover -html=cover.out && \
|
||||
go tool cover -func cover.out \
|
||||
| grep -vP '[89]\d\.\d%' | grep -v '100.0%' \
|
||||
|| true
|
||||
|
||||
rm cover.out
|
||||
rm cover.out
|
34
cache.go
34
cache.go
@@ -7,33 +7,6 @@ import (
|
||||
"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 {
|
||||
*Configuration[T]
|
||||
control
|
||||
@@ -203,11 +176,6 @@ func (c *Cache[T]) Delete(key string) bool {
|
||||
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] {
|
||||
item, existing := c.bucket(key).set(key, value, duration, track)
|
||||
if existing != nil {
|
||||
@@ -389,7 +357,7 @@ func (c *Cache[T]) gc() int {
|
||||
}
|
||||
prev := node.Prev
|
||||
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.size -= item.size
|
||||
c.list.Remove(node)
|
||||
|
@@ -124,7 +124,6 @@ func Test_CacheDeletesAFunc(t *testing.T) {
|
||||
return key == "d"
|
||||
}), 1)
|
||||
assert.Equal(t, cache.ItemCount(), 2)
|
||||
|
||||
}
|
||||
|
||||
func Test_CacheOnDeleteCallbackCalled(t *testing.T) {
|
||||
|
@@ -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, ...)
|
||||
// [16]
|
||||
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
|
||||
}
|
||||
c.buckets = int(count)
|
||||
|
@@ -32,7 +32,7 @@ func (b *layeredBucket[T]) getSecondaryBucket(primary string) *bucket[T] {
|
||||
b.RLock()
|
||||
bucket, exists := b.buckets[primary]
|
||||
b.RUnlock()
|
||||
if exists == false {
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
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]) {
|
||||
b.Lock()
|
||||
bkt, exists := b.buckets[primary]
|
||||
if exists == false {
|
||||
if !exists {
|
||||
bkt = &bucket[T]{lookup: make(map[string]*Item[T])}
|
||||
b.buckets[primary] = bkt
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func (b *layeredBucket[T]) delete(primary, secondary string) *Item[T] {
|
||||
b.RLock()
|
||||
bucket, exists := b.buckets[primary]
|
||||
b.RUnlock()
|
||||
if exists == false {
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
return bucket.delete(secondary)
|
||||
@@ -65,7 +65,7 @@ func (b *layeredBucket[T]) deletePrefix(primary, prefix string, deletables chan
|
||||
b.RLock()
|
||||
bucket, exists := b.buckets[primary]
|
||||
b.RUnlock()
|
||||
if exists == false {
|
||||
if !exists {
|
||||
return 0
|
||||
}
|
||||
return bucket.deletePrefix(prefix, deletables)
|
||||
@@ -75,7 +75,7 @@ func (b *layeredBucket[T]) deleteFunc(primary string, matches func(key string, i
|
||||
b.RLock()
|
||||
bucket, exists := b.buckets[primary]
|
||||
b.RUnlock()
|
||||
if exists == false {
|
||||
if !exists {
|
||||
return 0
|
||||
}
|
||||
return bucket.deleteFunc(matches, deletables)
|
||||
@@ -85,7 +85,7 @@ func (b *layeredBucket[T]) deleteAll(primary string, deletables chan *Item[T]) b
|
||||
b.RLock()
|
||||
bucket, exists := b.buckets[primary]
|
||||
b.RUnlock()
|
||||
if exists == false {
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@@ -41,7 +41,7 @@ func Layered[T any](config *Configuration[T]) *LayeredCache[T] {
|
||||
deletables: make(chan *Item[T], config.deleteBuffer),
|
||||
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]{
|
||||
buckets: make(map[string]*bucket[T]),
|
||||
}
|
||||
@@ -334,7 +334,7 @@ func (c *LayeredCache[T]) gc() int {
|
||||
}
|
||||
prev := node.Prev
|
||||
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.size -= item.size
|
||||
c.list.Remove(node)
|
||||
|
@@ -118,7 +118,6 @@ func Test_LayedCache_DeletesAFunc(t *testing.T) {
|
||||
return key == "d"
|
||||
}), 1)
|
||||
assert.Equal(t, cache.ItemCount(), 3)
|
||||
|
||||
}
|
||||
|
||||
func Test_LayedCache_OnDeleteCallbackCalled(t *testing.T) {
|
||||
|
@@ -85,11 +85,3 @@ func assertList(t *testing.T, list *List[int], expected ...int) {
|
||||
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
|
||||
}
|
||||
|
Reference in New Issue
Block a user