mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 02:44:59 +00:00
* Enable linter: stylecheck This enabled the stylecheck linter, but excludes some staticchecks for now. The following are ignored because they will take a bit of time to address, but the intention is to address them and remove them from the exclusion list. ST1000: at least one file in a package should have a package comment ST1003: golang naming standards ST1008: error should be returned as the last argument ST1016: methods on the same type should have the same receiver name ST1020: comment on exported function * Address ST1015 For some reason this failed in CI but not locally. I can't figure out why that check isn't happening locally. This just moves the switch statements around so that the `default` is the first (or last) item. * Adjust error string in test to match case-adjusted error * Remove TestCloseTimeout
158 lines
4.1 KiB
Go
158 lines
4.1 KiB
Go
// Copyright 2022 Molecula Corp. (DBA FeatureBase).
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
package testhook
|
|
|
|
//TODO: Check() and FinalCheck() should return error as the last argument
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sync"
|
|
)
|
|
|
|
// Auditor represents a thing which knows how to audit events. For instance,
|
|
// it can check on when things are accessed, or when they were opened, or
|
|
// whether opened objects are later closed.
|
|
type Auditor interface {
|
|
|
|
// Registry yields a registry for objects of this type.
|
|
// multiple calls with the same object type yield the same registry.
|
|
Registry(interface{}) (Registry, error)
|
|
// Check performs any error-checking that can be done during
|
|
// usage.
|
|
Check() (error, []error)
|
|
// FinalCheck performs any error-checking that makes sense only
|
|
// after all operations are supposed to be complete, such as
|
|
// verifying that opened objects have been closed.
|
|
FinalCheck() (error, []error)
|
|
}
|
|
|
|
// Created(a, o, kv) is shorthand for a.Registry(o).Created(o, kv) plus
|
|
// the error checking inside that.
|
|
func Created(a Auditor, o interface{}, kv KV) error {
|
|
r, err := a.Registry(o)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return r.Created(o, kv)
|
|
}
|
|
|
|
// Opened(a, o, kv) is shorthand for a.Registry(o).Opened(o, kv) plus
|
|
// the error checking inside that.
|
|
func Opened(a Auditor, o interface{}, kv KV) error {
|
|
r, err := a.Registry(o)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return r.Opened(o, kv)
|
|
}
|
|
|
|
// Closed(a, o, kv) is shorthand for a.Registry(o).Closed(o, kv) plus
|
|
// the error checking inside that.
|
|
func Closed(a Auditor, o interface{}, kv KV) error {
|
|
r, err := a.Registry(o)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return r.Closed(o, kv)
|
|
}
|
|
|
|
// Destroyed(a, o, kv) is shorthand for a.Registry(o).Destroyed(o, kv) plus
|
|
// the error checking inside that.
|
|
func Destroyed(a Auditor, o interface{}, kv KV) error {
|
|
r, err := a.Registry(o)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return r.Destroyed(o, kv)
|
|
}
|
|
|
|
// Seen(a, o, kv) is shorthand for a.Registry(o).Seen(o, kv) plus
|
|
// the error checking inside that.
|
|
func Seen(a Auditor, o interface{}, kv KV) error {
|
|
r, err := a.Registry(o)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return r.Seen(o, kv)
|
|
}
|
|
|
|
// NopAuditor doesn't do anything.
|
|
type NopAuditor struct{}
|
|
|
|
func (*NopAuditor) Registry(interface{}) (Registry, error) {
|
|
return NewNopRegistry(), nil
|
|
}
|
|
|
|
func (*NopAuditor) Check() (error, []error) { //nolint:stylecheck
|
|
return nil, nil
|
|
}
|
|
|
|
func (*NopAuditor) FinalCheck() (error, []error) { //nolint:stylecheck
|
|
return nil, nil
|
|
}
|
|
|
|
func NewNopAuditor() *NopAuditor {
|
|
return &NopAuditor{}
|
|
}
|
|
|
|
// VerifyCloseAuditor provides registries which it will check for things
|
|
// being closed.
|
|
type VerifyCloseAuditor struct {
|
|
registries map[reflect.Type]Registry
|
|
hooks RegistryHooks
|
|
regMu sync.Mutex
|
|
}
|
|
|
|
func (v *VerifyCloseAuditor) Registry(o interface{}) (Registry, error) {
|
|
t := reflect.TypeOf(o)
|
|
v.regMu.Lock()
|
|
defer v.regMu.Unlock()
|
|
if exists, ok := v.registries[t]; ok {
|
|
return exists, nil
|
|
}
|
|
reg := NewSimpleRegistry(v.hooks[t])
|
|
v.registries[t] = reg
|
|
return reg, nil
|
|
}
|
|
|
|
func (*VerifyCloseAuditor) Check() (error, []error) { //nolint:stylecheck
|
|
return nil, nil
|
|
}
|
|
|
|
func (v *VerifyCloseAuditor) FinalCheck() (error, []error) { //nolint:stylecheck
|
|
v.regMu.Lock()
|
|
defer v.regMu.Unlock()
|
|
var errs []error
|
|
for t, reg := range v.registries {
|
|
typeName := t.String()
|
|
live, err := reg.Live()
|
|
if err != nil {
|
|
errs = append(errs, fmt.Errorf("registry[%s]: retrieving live list: %v",
|
|
typeName, err))
|
|
continue
|
|
}
|
|
if len(live) > 0 {
|
|
for addr, entry := range live {
|
|
if entry.Error != nil {
|
|
errs = append(errs, fmt.Errorf("%v: item created at %v, stack %s",
|
|
entry.Error, entry.Stamp, entry.Stack))
|
|
} else {
|
|
errs = append(errs, fmt.Errorf("live item found at %p, created at %v, stack %s",
|
|
addr, entry.Stamp, entry.Stack))
|
|
}
|
|
if entry.Data["stack"] != nil {
|
|
errs = append(errs, fmt.Errorf("stashed stack: %s", entry.Data["stack"]))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if len(errs) > 0 {
|
|
return fmt.Errorf("final check: %d error(s)", len(errs)), errs
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func NewVerifyCloseAuditor(hooks RegistryHooks) *VerifyCloseAuditor {
|
|
return &VerifyCloseAuditor{registries: map[reflect.Type]Registry{}, hooks: hooks}
|
|
}
|