Skip to content

Instantly share code, notes, and snippets.

@mniak
Last active August 5, 2022 15:13
Show Gist options
  • Save mniak/70bbcaf2b367fcbb5442a75f7b33a044 to your computer and use it in GitHub Desktop.
Save mniak/70bbcaf2b367fcbb5442a75f7b33a044 to your computer and use it in GitHub Desktop.
Golang utils
package crypto
import (
"bytes"
"crypto/md5"
"encoding/binary"
"io"
)
type fakeRandom struct {
counter int
buffer bytes.Buffer
}
func (fr *fakeRandom) Read(p []byte) (n int, err error) {
num := make([]byte, 4)
for fr.buffer.Len() < len(p) {
binary.BigEndian.PutUint32(num, uint32(fr.counter))
md5Result := md5.Sum(num)
_, err := fr.buffer.Write(md5Result[:])
if err != nil {
return 0, err
}
fr.counter++
}
return fr.buffer.Read(p)
}
func FakeRandom() io.Reader {
return &fakeRandom{}
}
package async
import (
"errors"
"time"
)
func Loop(interval time.Duration, fn func()) (stop func()) {
stopChan := make(chan bool)
go func() {
for {
select {
case <-stopChan:
return
default:
fn()
time.Sleep(interval)
}
}
}()
return func() { stopChan <- true }
}
func waitForSuccessOnce(timeout time.Duration, fn func() bool) error {
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
if fn() {
return nil
}
}
return errors.New("deadline exceeded")
}
func WaitForSuccessOnceThenLoop(interval time.Duration, timeout time.Duration, fn func() bool) (stopfn func(), err error) {
err = waitForSuccessOnce(timeout, fn)
if err != nil {
return nil, err
}
return Loop(interval, func() { fn() }), nil
}
package async
import (
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestLoop(t *testing.T) {
const max = 5
array := make([]int, 0)
counter := 0
stopChan := make(chan int)
stop := Loop(100*time.Millisecond, func() {
array = append(array, counter)
counter++
if counter == max {
stopChan <- counter
}
})
<-stopChan
stop()
time.Sleep(1 * time.Second)
assert.Equal(t, 5, counter)
assert.Equal(t, []int{0, 1, 2, 3, 4}, array)
}
func TestLoop_Stop(t *testing.T) {
counter := 0
stop := Loop(10*time.Millisecond, func() {
counter++
})
time.Sleep(100 * time.Millisecond)
stop()
assert.Equal(t, 10, counter)
}
func TestWaitForSuccessOnceThenLoop(t *testing.T) {
t.Run("Succeed on first attempt", func(t *testing.T) {
counter := 0
var mutex sync.Mutex
_, err := WaitForSuccessOnceThenLoop(time.Millisecond, time.Second, func() bool {
mutex.Lock()
counter++
mutex.Unlock()
return true
})
assert.NoError(t, err)
mutex.Lock()
assert.Equal(t, 1, counter)
mutex.Unlock()
})
t.Run("Succeed on third attempt", func(t *testing.T) {
counter := 0
var mutex sync.Mutex
_, err := WaitForSuccessOnceThenLoop(time.Millisecond, time.Second, func() bool {
if counter == 3 {
return true
}
mutex.Lock()
counter++
mutex.Unlock()
return false
})
assert.NoError(t, err)
mutex.Lock()
assert.Equal(t, 3, counter)
mutex.Unlock()
})
t.Run("Timeout", func(t *testing.T) {
counter := 0
_, err := WaitForSuccessOnceThenLoop(time.Millisecond, time.Second, func() bool {
counter++
time.Sleep(100 * time.Millisecond)
return false
})
assert.Equal(t, 10, counter)
assert.EqualError(t, err, "deadline exceeded")
})
t.Run("Succeed on first attempt and then continue", func(t *testing.T) {
const max = 5
array := make([]int, 0)
counter := 0
ch := make(chan int)
stop, err := WaitForSuccessOnceThenLoop(time.Millisecond, time.Second, func() bool {
array = append(array, counter)
counter++
if counter > 1 {
ch <- counter
}
return true
})
for {
if <-ch >= max {
stop()
break
}
}
assert.NoError(t, err)
assert.Equal(t, []int{0, 1, 2, 3, 4}, array)
})
}
func TestWaitForSuccessOnceThenLoop_Stop(t *testing.T) {
counter := 0
stop, err := WaitForSuccessOnceThenLoop(10*time.Millisecond, time.Second, func() bool {
counter++
return true
})
require.NoError(t, err)
time.Sleep(100 * time.Millisecond)
stop()
assert.Equal(t, 11, counter)
}
package async
type Promise[T any] struct {
chresult <-chan T
cherr <-chan error
}
func (p Promise[T]) Then(fn func(result T)) Promise[T] {
fn(<-p.chresult)
return p
}
func (p Promise[T]) Error(fn func(err error)) Promise[T] {
fn(<-p.cherr)
return p
}
func (p Promise[T]) Wait() (T, error) {
select {
case result := <-p.chresult:
return result, nil
case err := <-p.cherr:
var zero T
return zero, err
}
}
func NewPromise[T any](work func(successChan chan<- T, failureChan chan<- error)) Promise[T] {
chresult := make(chan T)
cherr := make(chan error)
go work(chresult, cherr)
return Promise[T]{
chresult: chresult,
cherr: cherr,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment