Golang time.Now() returns zero value during tests with Ginkgo - go

Why is this returning zero value during tests? Go version 1.20.1
package main
import (
"errors"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type Test struct {
ExpiresAt time.Time
}
func checkIfExpired(t *Test) error {
if time.Now().After(t.ExpiresAt) {
return errors.New("dang")
}
return nil
}
var _ = Describe("test", func() {
createTest := func() *Test {
ts := time.Now()
return &Test{
ExpiresAt: ts,
}
}
Describe("create", func() {
When("created", func() {
BeforeEach(func() {
err = checkIfExpired(createTest())
})
It("should work", func() {
Expect(err).ShouldNot(HaveOccurred())
})
})
})
})
This sets ExpiresAt field as 0001-01-01 00:00:00 +0000 UTC instead of an actual date-time.
I tried my best at writing pseudo-code that would at least resemble an actual code.

Related

Why my counter doesn't work in "uber-go/tally"?

Can you please help me to find out why my event counters don't get reported. I even put the debugger on the reporter and it is not triggering. Time recorder triggers fine though.
Here is the implementation.
I have a Kafka consumer which I have defined the followings: (consumer.go)
reporter := metrics.NewPrintStatsReporter()
// Report every 100*time.Millisecond
rootScope, closer := tally.NewRootScope(tally.ScopeOptions{
Reporter: reporter,
}, time.Second)
defer func(closer io.Closer) {
err := closer.Close()
if err != nil {
...
}
}(closer)
subScope := rootScope.SubScope("Events")
then I do:
supportedEventCount := subScope.Counter(metrics.SupportedEventTypes)
unSupportedEventCount := subScope.Counter(metrics.UnsupportedEventTypes)
totalEventTypes := subScope.Counter(metrics.TotalEventTypes)
kafkaReadLatency := rootScope.Timer(metrics.KafkaReadLatency)
in the same file
go func() {
defer close(messages)
for { ...
st := time.Now()
m, err := c.r.ReadMessage(ctx)
kafkaConsumerReadLatency := time.Since(st)
if kafkaConsumerReadLatency < 5*time.Minute {
kafkaReadLatency.Record(kafkaConsumerReadLatency)
}
// Reporting any kind of event
totalEventTypes.Inc(1)
if helpers.IsSupportedEvent(logger, &kafkaEvent) {
supportedEventCount.Inc(1)
messages <- kafkaEvent
} else {
unSupportedEventCount.Inc(1)
}
}()
...
kafkaReadLatency.Record(kafkaConsumerReadLatency) works fine I get timer kafka_read_latency 34.479058541s
However non of the counters are working... which is very strange.
Here is my tally interface reporter: (very standard, 100% matches the supplied example)
package metrics
import (
"fmt"
tally "github.com/uber-go/tally/v4"
"time"
)
const (
SupportedEventTypes = "supported_event_types"
UnsupportedEventTypes = "unsupported_event_types"
TotalEventTypes = "total_event_types"
KafkaReadLatency = "kafka_read_latency"
)
type printStatsReporter struct{}
func NewPrintStatsReporter() tally.StatsReporter {
return &printStatsReporter{}
}
func (r *printStatsReporter) ReportCounter(name string, _ map[string]string, value int64) {
fmt.Printf("count %s %d\n", name, value)
}
func (r *printStatsReporter) ReportGauge(name string, _ map[string]string, value float64) {
fmt.Printf("gauge %s %f\n", name, value)
}
func (r *printStatsReporter) ReportTimer(name string, _ map[string]string, interval time.Duration) {
fmt.Printf("timer %s %s\n", name, interval.String())
}
...
func (r *printStatsReporter) Capabilities() tally.Capabilities {
return r
}
func (r *printStatsReporter) Reporting() bool {
return true
}
func (r *printStatsReporter) Tagging() bool {
return true
}
func (r *printStatsReporter) Flush() {
fmt.Printf("flush\n")
}
Here is the example:
https://github.com/uber-go/tally/blob/master/example/main.go

viper dynamically loading config file has data race

I would like to dynamically load config file and not restart my Go app. I wrote the below files, which runs but has data race.
config.go
package main
import (
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"log"
"sync"
"time"
)
var (
reloadConfig = make(chan string)
reloadConfig2 = make(chan string)
viperLock1 sync.Mutex
viperLock2 sync.Mutex
)
func setUpConfig(file string, merge bool, v *viper.Viper) {
v.AddConfigPath("./")
v.SetConfigName(file)
v.SetConfigType("yml")
if merge {
err1 := v.MergeInConfig()
checkForFatalError("fatal error occurred while reading config file!", err1)
} else {
err := v.ReadInConfig()
checkForFatalError("fatal error occurred while reading config file!", err)
}
log.Println("Initial config value: ", v.GetString("env"))
}
func loadConfigDynamically(configChannel chan string, viperLock *sync.Mutex, vipe *viper.Viper) {
viperLock.Lock()
vipe.OnConfigChange(func(e fsnotify.Event) {
viperLock.Lock()
log.Println("config file changed", e.Name)
environment := vipe.GetString("env")
configChannel <- environment
viperLock.Unlock()
})
viperLock.Unlock()
vipe.WatchConfig()
}
func loadMultipleConfigsDynamically() {
go func() {
time.Sleep(time.Millisecond * 50)
vipe2 := viper.New()
setUpConfig("config_base", false, vipe2)
loadConfigDynamically(reloadConfig2, &viperLock2, vipe2)
time.Sleep(time.Millisecond * 50)
vipe1 := viper.New()
setUpConfig("config", false, vipe1)
loadConfigDynamically(reloadConfig, &viperLock1, vipe1)
}()
}
main.go
package main
import (
log "github.com/sirupsen/logrus"
"os"
"os/signal"
"syscall"
)
var reloadConfigNow = make(chan bool)
var reloadConfigAgain = make(chan bool)
var newConfigValue string
func main() {
loadMultipleConfigsDynamically()
go printUpdatedValueOnly()
go justAnotherGoroutine()
go yetAnotherGoroutine()
shutdownAppGracefully()
}
func printUpdatedValueOnly() {
for {
select {
case updatedValue := <-reloadConfig:
newConfigValue = updatedValue
log.Println("dynamically loaded config value: ", updatedValue)
reloadConfigNow <-true
reloadConfigAgain <-true
case updatedValue1 := <-reloadConfig2:
newConfigValue = updatedValue1
log.Println("dynamically loaded config value: ", updatedValue1)
reloadConfigNow <-true
reloadConfigAgain <-true
default:
}
}
}
func justAnotherGoroutine(){
existingConfigValue := ""
for {
select {
case <-reloadConfigNow:
existingConfigValue = newConfigValue
log.Println("justAnotherGoroutine: ", existingConfigValue)
default:
}
}
}
func yetAnotherGoroutine() {
existingConfigValue := ""
for {
select {
case <-reloadConfigAgain:
existingConfigValue = newConfigValue
log.Println("yetAnotherGoroutine: ", existingConfigValue)
default:
}
}
}
func checkForFatalError(errorMsg string, err error) {
if err != nil {
log.Fatal(errorMsg, err)
}
}
func shutdownAppGracefully() {
killSignal := make(chan os.Signal, 1)
signal.Notify(killSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
k := <-killSignal
log.Info("OS Interrupt Signal received, application is shutting down!")
logSystemInterruptType(k)
}
func logSystemInterruptType(osInterrupt os.Signal) {
switch osInterrupt {
case syscall.SIGHUP:
log.Info("SIGHUP")
case syscall.SIGINT:
log.Info("SIGINT")
case syscall.SIGTERM:
log.Info("SIGTERM")
case syscall.SIGQUIT:
log.Info("SIGQUIT")
default:
log.Info("Unknown OS Interrupt")
}
}
config.yml
env : "LOCAL"
config_base.yml
env : "dev15"
go.mod
module reload_config
go 1.16
require (
github.com/fsnotify/fsnotify v1.4.9
github.com/spf13/viper v1.8.1
)
I learned recently that viper is not thread safe and hence I need to wrap it with mutex. I tried to do the same. In config.go file, func loadConfigDynamically, where I set OnConfigChange is the data race for read. And in the same function at the same line is previous write data race. I run the above package with
go run -race reload_config
And change the value of env in the config.yml to test if the config file is loading dynamically.This data race only occurs for the very first time config reloading dynamically. For subsequent times, it works just fine.
You lock viperLock called vipe.WatchConfig() and set vipe.OnConfigChange with a function it is also locking viperLock.
Because you already called vipe.WatchConfig() and then it started to call vipe.OnConfigChange in separate go routine. it is also try to acquire the same lock. That's why there is a race condition.
Call vipe.WatchConfig() after setting the vipe.OnConfigChange and after release the lock.
It should be corrected as below.
func loadConfigDynamically() {
go func() {
time.Sleep(time.Second)
viperLock.Lock()
vipe.OnConfigChange(func(e fsnotify.Event) {
viperLock.Lock()
log.Println("config file changed", e.Name)
environment := vipe.GetString("env")
reloadConfig <- environment
viperLock.Unlock()
})
viperLock.Unlock()
vipe.WatchConfig() //this starting call vipe.OnConfigChange
}()
}
It could be that go thinks that a variable is being modified and accessed by two goroutines at the same time and that there is no lock on the modified and accessed places.
Something like the following example:
package main
import (
"time"
)
type Foo struct {
f func(string)
}
func (f *Foo) Watch() {
go func() {
for {
time.Sleep(time.Second * 2)
if f.f != nil {
f.f("hello world")
}
}
}()
}
func (f *Foo) SetF(fun func(string)) {
f.f = fun
}
func main() {
f := Foo{}
f.Watch()
f.SetF(func(s string) {
})
time.Sleep(time.Second * 5)
}
It has a data race. If I put the same lock on both the modified and read places there will be no data race:
package main
import (
"sync"
"time"
)
var lock sync.Mutex
type Foo struct {
f func(string)
}
func (f *Foo) Watch() {
go func() {
for {
time.Sleep(time.Second * 2)
lock.Lock() // read places
if f.f != nil {
f.f("hello world")
}
lock.Unlock()
}
}()
}
func (f *Foo) SetF(fun func(string)) {
f.f = fun
}
func main() {
f := Foo{}
f.Watch()
lock.Lock() // write places
f.SetF(func(s string) {
})
lock.Unlock()
time.Sleep(time.Second * 5)
}
Or to eliminate the possibility of two goroutines reading and writing at the same time would work fine:
func main() {
f := Foo{}
f.SetF(func(s string) {
})
f.Watch()
time.Sleep(time.Second * 5)
}

Finding all functions of certain type

Is there a way to list out all functions that uses/returns a specific type?
For example: I'm interested to use the following function.
func ListenAndServe(addr string, handler Handler) error
How can I find out all functions (across all Go packages) that can return a Handler?
I'd write an analysis tool using the x/tools/go/analysis framework. Here's a rough sketch that you can run on any module (it uses go/packages underneath so it fully supports modules):
import (
"bytes"
"fmt"
"go/ast"
"go/format"
"go/token"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/singlechecker"
)
var RtAnalysis = &analysis.Analyzer{
Name: "rtanalysis",
Doc: "finds functions by return type",
Run: run,
}
func main() {
singlechecker.Main(RtAnalysis)
}
func run(pass *analysis.Pass) (interface{}, error) {
for _, file := range pass.Files {
ast.Inspect(file, func(n ast.Node) bool {
if funcTy, ok := n.(*ast.FuncType); ok {
if funcTy.Results != nil {
for _, fl := range funcTy.Results.List {
if tv, ok := pass.TypesInfo.Types[fl.Type]; ok {
if tv.Type.String() == "net/http.Handler" {
ns := nodeString(funcTy, pass.Fset)
fmt.Printf("%s has return of type net/http.Handler\n", ns)
}
}
}
}
}
return true
})
}
return nil, nil
}
// nodeString formats a syntax tree in the style of gofmt.
func nodeString(n ast.Node, fset *token.FileSet) string {
var buf bytes.Buffer
format.Node(&buf, fset, n)
return buf.String()
}

Mock inner function in golang

I want to mock function using interface and I was able to mock the first function callsomething
With great help from icza , Now it’s a bit more tricky . I want to test function vl1
With mock for function function1 , how it can be done.
https://play.golang.org/p/w367IOjADFV
package main
import (
"fmt"
"time"
"testing"
)
type vInterface interface {
function1() bool
}
type mStruct struct {
info string
time time.Time
}
func (s *mStruct) function1() bool {
return true
}
func callSomething(si vInterface) bool {
return si.function1()
}
func (s *mStruct) vl1() bool {
return callSomething(s)
}
var currentVt1 mStruct
func main() {
vl1 := currentVt1.vl1()
fmt.Println(vl1)
}
//——————————————————TESTS——————————————————
// This test is working as expected (as suggested by icza) for the function "callSomething"
// here we use mock interface and mock function which helps to mock function1
type mockedVInterface struct {
value bool
}
func (m mockedVInterface) fn1() bool {
return m.value
}
func Test_callSomething(t *testing.T) {
type args struct {
si vInterface
}
tests := []struct {
name string
args args
want bool
}{
{
name: "first value",
args: args{
si: mockedVInterface{value: false},
},
want: false,
},
{
name: "second value",
args: args{
si: mockedVInterface{value: true},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := callSomething(tt.args.si); got != tt.want {
t.Errorf("callSomething() = %v, want %v", got, tt.want)
}
})
}
}
//----Here is the test which a bit tricky
//I want to call to "s.vl1()" and test it with mock for "function1"
func Test_mStruct_vl1(t *testing.T) {
type fields struct {
info string
time time.Time
}
tests := []struct {
name string
fields fields
want bool
}{
{
name: "test 2",
fields: struct {
info string
time time.Time
}{info: "myinfo", time: time.Now() },
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &mStruct{
info: tt.fields.info,
time: tt.fields.time,
}
//here the test is starting
if got := s.vl1(); got != tt.want {
t.Errorf("mStruct.vl1() = %v, want %v", got, tt.want)
}
})
}
}
If you want to mock a function in Go you'll have to declare a variable that will hold the function value, have all callers of the function use the variable instead and then during tests you can switch up the function value for a mock implementation.
func callSomethingFunc(si vInterface) bool {
return si.function1()
}
var callSomething = callSomethingFunc
func (s *mStruct) vl1() bool {
return callSomething(s)
}
// ...
func Test_mStruct_vl1(t *testing.T) {
callSomething = func(si vInterface) bool { // set mock
// do mock stuff
}
defer func(){
callSomething = callSomethingFunc // set back original func at end of test
}()
// ...

do repetitive tasks at intervals in golang using time.AfterFunc, just a sample

I just want to do repetitive background tasks in Go, using time.AfterFunc,But seems something wrong with the logic.
The out put just:
interval call
interval call
But at least 5 times to call the function if all things went normal.
package main
import (
"fmt"
"time"
"os"
"os/signal"
)
type Timer struct {
Queue chan *TimeCall
}
func NewTimer(l int) *Timer {
timer := new(Timer)
timer.Queue = make(chan *TimeCall,l)
return timer
}
type TimeCall struct {
timer *time.Timer
callback func()
}
func (this *TimeCall) CallBack() {
defer func() { recover() }()
if this.callback != nil {
this.callback()
}
}
func (this *Timer) AfterFunc(d time.Duration, callback func()) *TimeCall {
call := new(TimeCall)
call.callback = callback
call.timer = time.AfterFunc(d, func() {
this.Queue <- call
})
return call
}
type PipeService struct {
TimeCall *Timer
}
func (this *PipeService) AfterFunc(delay time.Duration, callback func()) *TimeCall {
return this.TimeCall.AfterFunc(delay, callback)
}
func (this *PipeService) IntervalCall(interval time.Duration, callback func()) {
this.TimeCall.AfterFunc(interval,func(){
if callback != nil {
callback()
}
this.AfterFunc(interval,callback)
})
}
func (this *PipeService) Run(closeSig chan bool) {
for {
select {
case <-closeSig:
return
case call := <-this.TimeCall.Queue:
call.CallBack()
}
}
}
func main() {
var closeChan chan bool
InsPipeService := &PipeService{TimeCall: NewTimer(10)}
InsPipeService.IntervalCall(2*time.Second,func(){
fmt.Println("interval call")
})
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill)
go func(){
InsPipeService.Run(closeChan)
}()
time.Sleep(10*time.Second)
}
Run Code
time.AfterFunc() returns a *time.Timer, quoting form its doc:
The Timer type represents a single event. When the Timer expires, the current time will be sent on C, unless the Timer was created by AfterFunc.
The time.Timer returned by time.AfterFunc() does not repeat, so what you see is perfectly normal: in your PipeService.IntervalCall() you execute the callback immediately, and it gets executed after the timeout.
Also note that you pass 2 as interval for the PipeService.IntervalCall() method. This interval parameter is of type time.Duraion. So when you pass 2, that won't be 2 seconds (but actually 2 nanoseconds). You should pass a value constructed from constants from the time package like:
InsPipeService.IntervalCall(2 * time.Second, func(){
fmt.Println("interval call")
})
If you want repetition, use time.Ticker. For example the following code prints a message in every 2 seconds:
t := time.NewTicker(2 * time.Second)
for now := range t.C {
fmt.Println("tick", now)
}
Or simply if you don't need the Ticker and you don't want to shut it down:
c := time.Tick(2 * time.Second)
for now := range c {
fmt.Println("tick", now)
}
set time interval then call Start it will run user Job on each time intervals. set Enabled to false to stop it.
My Sample:
package main
import (
"fmt"
"sync"
"time"
)
type IntervalTimer struct {
Interval time.Duration
Enabled bool
Job func()
Wg sync.WaitGroup
}
func (it *IntervalTimer) isr() {
if it.Enabled {
it.Job()
time.AfterFunc(it.Interval, it.isr)
} else {
it.Wg.Done()
}
}
//trigger
func (it *IntervalTimer) Start() {
if it.Enabled {
it.Wg.Add(1)
time.AfterFunc(it.Interval, it.isr)
}
}
// user code:
var n int = 5
var it *IntervalTimer
func uerTask() {
fmt.Println(n, time.Now()) // do user job ...
n--
if n == 0 {
it.Enabled = false
}
}
func main() {
it = &IntervalTimer{Interval: 500 * time.Millisecond, Enabled: true, Job: uerTask}
it.Start()
//do some job ...
it.Wg.Wait()
fmt.Println("Bye")
}

Resources