fix_: enable tests to run with `-count` more than 1 (#5757)

* fix(TestProfilingCPU)_: enable run with -count=2

* fix(TestProfilingMem)_: enable run with -count=2

* fix(zaputil)_: register encoder only once

* fix(timesource)_: global variables override in tests

* fix(TestClosingsqlDB)_: delete database from cache

* fix(postgres/helpers)_: drop connections before dropping database

* fix_: linter

* chore_: remove redundant condition
This commit is contained in:
Igor Sirotin 2024-08-23 20:30:58 +01:00 committed by GitHub
parent edead41fa6
commit e0eb737c51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 124 additions and 86 deletions

View File

@ -3,7 +3,6 @@ package logutils
import (
"fmt"
"math"
"sync"
"time"
"go.uber.org/zap"
@ -123,15 +122,11 @@ func NewZapAdapter(logger log.Logger, enab zapcore.LevelEnabler) zapcore.Core {
}
}
var registerOnce sync.Once
// NewZapLoggerWithAdapter returns a logger forwarding all logs with level info and above.
func NewZapLoggerWithAdapter(logger log.Logger) (*zap.Logger, error) {
registerOnce.Do(func() {
if err := zaputil.RegisterJSONHexEncoder(); err != nil {
panic(err)
}
})
if err := zaputil.RegisterJSONHexEncoder(); err != nil {
panic(err)
}
cfg := zap.Config{
Level: zap.NewAtomicLevelAt(zapcore.DebugLevel),

View File

@ -89,6 +89,11 @@ func TestClosingsqlDB(t *testing.T) {
d, err := MakeNewDB(db)
require.NoError(t, err)
// Cleanup dbInstances to enable running test with -count more than 1.
dbFileName, err := dbsetup.GetDBFilename(db)
require.NoError(t, err)
defer delete(dbInstances, dbFileName)
// Add settings data to the db
err = d.CreateSettings(settings, config)
require.NoError(t, err)

View File

@ -29,6 +29,21 @@ func ResetDefaultTestPostgresDB() error {
if err != nil {
return err
}
defer func() {
_ = db.Close()
}()
// Drop current and prevent any future connections. Used in tests. Details here:
// https://stackoverflow.com/questions/17449420/postgresql-unable-to-drop-database-because-of-some-auto-connections-to-db
_, err = db.Exec("REVOKE CONNECT ON DATABASE postgres FROM public;")
if err != nil {
return err
}
_, err = db.Exec("SELECT pid, pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'postgres' AND pid <> pg_backend_pid();")
if err != nil {
return err
}
_, err = db.Exec("DROP DATABASE IF EXISTS postgres;")
if err != nil {
@ -36,5 +51,14 @@ func ResetDefaultTestPostgresDB() error {
}
_, err = db.Exec("CREATE DATABASE postgres;")
return err
if err != nil {
return err
}
_, err = db.Exec("GRANT CONNECT ON DATABASE postgres TO public;")
if err != nil {
return err
}
return nil
}

View File

@ -1,6 +1,7 @@
package profiling
import (
"errors"
"os"
"path/filepath"
"runtime/pprof"
@ -14,12 +15,14 @@ var cpuFile *os.File
// StartCPUProfile enables CPU profiling for the current process. While profiling,
// the profile will be buffered and written to the file in folder dataDir.
func StartCPUProfile(dataDir string) error {
if cpuFile == nil {
var err error
cpuFile, err = os.Create(filepath.Join(dataDir, CPUFilename))
if err != nil {
return err
}
if cpuFile != nil {
return errors.New("cpu profiling is already started")
}
var err error
cpuFile, err = os.Create(filepath.Join(dataDir, CPUFilename))
if err != nil {
return err
}
return pprof.StartCPUProfile(cpuFile)
@ -31,5 +34,7 @@ func StopCPUProfile() error {
return nil
}
pprof.StopCPUProfile()
return cpuFile.Close()
err := cpuFile.Close()
cpuFile = nil
return err
}

View File

@ -21,8 +21,12 @@ func WriteHeapFile(dataDir string) error {
if err != nil {
return err
}
defer memFile.Close() //nolint: errcheck
defer func() {
memFile.Close() //nolint: errcheck
memFile = nil
}()
}
runtime.GC()
err = pprof.WriteHeapProfile(memFile)

View File

@ -1,26 +1,20 @@
package tt
import (
"sync"
"github.com/status-im/status-go/protocol/zaputil"
"go.uber.org/zap"
)
var registerOnce sync.Once
// MustCreateTestLogger returns a logger based on the passed flags.
func MustCreateTestLogger() *zap.Logger {
return MustCreateTestLoggerWithConfig(loggerConfig())
}
func MustCreateTestLoggerWithConfig(cfg zap.Config) *zap.Logger {
registerOnce.Do(func() {
if err := zaputil.RegisterConsoleHexEncoder(); err != nil {
panic(err)
}
})
if err := zaputil.RegisterConsoleHexEncoder(); err != nil {
panic(err)
}
cfg.Encoding = "console-hex"
l, err := cfg.Build()
if err != nil {

View File

@ -2,6 +2,7 @@ package zaputil
import (
"encoding/hex"
"sync"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
@ -31,13 +32,22 @@ func (enc *jsonHexEncoder) Clone() zapcore.Encoder {
return &jsonHexEncoder{Encoder: encoderClone}
}
var (
registerJSONHexEncoderOnce sync.Once
registerConsoleHexEncodeOnce sync.Once
)
// RegisterJSONHexEncoder registers a jsonHexEncoder under "json-hex" name.
// Later, this name can be used as a value for zap.Config.Encoding to enable
// jsonHexEncoder.
func RegisterJSONHexEncoder() error {
return zap.RegisterEncoder("json-hex", func(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) {
return NewJSONHexEncoder(cfg), nil
var err error
registerJSONHexEncoderOnce.Do(func() {
err = zap.RegisterEncoder("json-hex", func(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) {
return NewJSONHexEncoder(cfg), nil
})
})
return err
}
type consoleHexEncoder struct {
@ -61,7 +71,11 @@ func (enc *consoleHexEncoder) Clone() zapcore.Encoder {
}
func RegisterConsoleHexEncoder() error {
return zap.RegisterEncoder("console-hex", func(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) {
return NewConsoleHexEncoder(cfg), nil
var err error
registerConsoleHexEncodeOnce.Do(func() {
err = zap.RegisterEncoder("console-hex", func(cfg zapcore.EncoderConfig) (zapcore.Encoder, error) {
return NewConsoleHexEncoder(cfg), nil
})
})
return err
}

View File

@ -42,10 +42,6 @@ var defaultServers = []string{
}
var errUpdateOffset = errors.New("failed to compute offset")
var ntpTimeSource *NTPTimeSource
var ntpTimeSourceCreator func() *NTPTimeSource
var now func() time.Time
type ntpQuery func(string, ntp.QueryOptions) (*ntp.Response, error)
type queryResponse struct {
@ -70,23 +66,6 @@ func (e multiRPCError) Error() string {
return b.String()
}
func init() {
ntpTimeSourceCreator = func() *NTPTimeSource {
if ntpTimeSource != nil {
return ntpTimeSource
}
ntpTimeSource = &NTPTimeSource{
servers: defaultServers,
allowedFailures: DefaultMaxAllowedFailures,
fastNTPSyncPeriod: FastNTPSyncPeriod,
slowNTPSyncPeriod: SlowNTPSyncPeriod,
timeQuery: ntp.QueryWithOptions,
}
return ntpTimeSource
}
now = time.Now
}
func computeOffset(timeQuery ntpQuery, servers []string, allowedFailures int) (time.Duration, error) {
if len(servers) == 0 {
return 0, nil
@ -138,9 +117,18 @@ func computeOffset(timeQuery ntpQuery, servers []string, allowedFailures int) (t
return offsets[mid], nil
}
var defaultTimeSource = &NTPTimeSource{
servers: defaultServers,
allowedFailures: DefaultMaxAllowedFailures,
fastNTPSyncPeriod: FastNTPSyncPeriod,
slowNTPSyncPeriod: SlowNTPSyncPeriod,
timeQuery: ntp.QueryWithOptions,
now: time.Now,
}
// Default initializes time source with default config values.
func Default() *NTPTimeSource {
return ntpTimeSourceCreator()
return defaultTimeSource
}
// NTPTimeSource provides source of time that tries to be resistant to time skews.
@ -151,6 +139,7 @@ type NTPTimeSource struct {
fastNTPSyncPeriod time.Duration
slowNTPSyncPeriod time.Duration
timeQuery ntpQuery // for ease of testing
now func() time.Time
quit chan struct{}
started bool
@ -163,7 +152,8 @@ type NTPTimeSource struct {
func (s *NTPTimeSource) Now() time.Time {
s.mu.RLock()
defer s.mu.RUnlock()
return now().Add(s.latestOffset)
n := s.now()
return n.Add(s.latestOffset)
}
func (s *NTPTimeSource) updateOffset() error {
@ -238,13 +228,25 @@ func (s *NTPTimeSource) Stop() error {
return nil
}
func (s *NTPTimeSource) GetCurrentTime() time.Time {
s.Start()
return s.Now()
}
func (s *NTPTimeSource) GetCurrentTimeInMillis() uint64 {
return convertToMillis(s.GetCurrentTime())
}
func GetCurrentTime() time.Time {
ts := Default()
ts.Start()
return ts.Now()
}
func GetCurrentTimeInMillis() uint64 {
return uint64(GetCurrentTime().UnixNano() / int64(time.Millisecond))
return convertToMillis(GetCurrentTime())
}
func convertToMillis(t time.Time) uint64 {
return uint64(t.UnixNano() / int64(time.Millisecond))
}

View File

@ -177,6 +177,7 @@ func TestNTPTimeSource(t *testing.T) {
servers: tc.servers,
allowedFailures: tc.allowedFailures,
timeQuery: tc.query,
now: time.Now,
}
assert.WithinDuration(t, time.Now(), source.Now(), clockCompareDelta)
err := source.updateOffset()
@ -206,6 +207,7 @@ func TestRunningPeriodically(t *testing.T) {
timeQuery: tc.query,
fastNTPSyncPeriod: time.Duration(fastHits*10) * time.Millisecond,
slowNTPSyncPeriod: time.Duration(slowHits*10) * time.Millisecond,
now: time.Now,
}
lastCall := time.Now()
// we're simulating a calls to updateOffset, testing ntp calls happens
@ -259,54 +261,47 @@ func TestGetCurrentTimeInMillis(t *testing.T) {
tc.responses[i] = queryResponse{Offset: responseOffset}
}
ntpTimeSourceCreator = func() *NTPTimeSource {
return &NTPTimeSource{
servers: tc.servers,
allowedFailures: tc.allowedFailures,
timeQuery: tc.query,
slowNTPSyncPeriod: SlowNTPSyncPeriod,
}
}
now = func() time.Time {
return time.Unix(1, 0)
ts := NTPTimeSource{
servers: tc.servers,
allowedFailures: tc.allowedFailures,
timeQuery: tc.query,
slowNTPSyncPeriod: SlowNTPSyncPeriod,
now: func() time.Time {
return time.Unix(1, 0)
},
}
expectedTime := uint64(11000)
n := GetCurrentTimeInMillis()
n := ts.GetCurrentTimeInMillis()
require.Equal(t, expectedTime, n)
// test repeat invoke GetCurrentTimeInMillis
n = GetCurrentTimeInMillis()
n = ts.GetCurrentTimeInMillis()
require.Equal(t, expectedTime, n)
e := Default().Stop()
e := ts.Stop()
require.NoError(t, e)
// test invoke after stop
n = GetCurrentTimeInMillis()
n = ts.GetCurrentTimeInMillis()
require.Equal(t, expectedTime, n)
e = Default().Stop()
e = ts.Stop()
require.NoError(t, e)
}
func TestGetCurrentTimeOffline(t *testing.T) {
// covers https://github.com/status-im/status-desktop/issues/12691
ntpTimeSourceCreator = func() *NTPTimeSource {
if ntpTimeSource != nil {
return ntpTimeSource
}
ntpTimeSource = &NTPTimeSource{
servers: defaultServers,
allowedFailures: DefaultMaxAllowedFailures,
fastNTPSyncPeriod: 1 * time.Millisecond,
slowNTPSyncPeriod: 1 * time.Second,
timeQuery: func(string, ntp.QueryOptions) (*ntp.Response, error) {
return nil, errors.New("offline")
},
}
return ntpTimeSource
ts := &NTPTimeSource{
servers: defaultServers,
allowedFailures: DefaultMaxAllowedFailures,
fastNTPSyncPeriod: 1 * time.Millisecond,
slowNTPSyncPeriod: 1 * time.Second,
timeQuery: func(string, ntp.QueryOptions) (*ntp.Response, error) {
return nil, errors.New("offline")
},
now: time.Now,
}
// ensure there is no "panic: sync: negative WaitGroup counter"
// when GetCurrentTime() is invoked more than once when offline
_ = GetCurrentTime()
_ = GetCurrentTime()
_ = ts.GetCurrentTime()
_ = ts.GetCurrentTime()
}