This commit is contained in:
teivah 2022-02-07 09:51:28 +01:00
parent ab22488bb0
commit 9e752d102c
No known key found for this signature in database
GPG key ID: 07E4A13FB8F8DF63
18 changed files with 173 additions and 70 deletions

View file

@ -0,0 +1,6 @@
package flight
type Position struct {
Lat float32
Lng float32
}

View file

@ -3,8 +3,25 @@ package main
import (
"context"
"net/http"
"time"
"github.com/teivah/100-go-mistakes/08-concurrency-foundations/60-contexts/flight"
)
type publisher interface {
Publish(ctx context.Context, position flight.Position) error
}
type publishHandler struct {
pub publisher
}
func (h publishHandler) publishPosition(position flight.Position) error {
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
defer cancel()
return h.pub.Publish(ctx, position)
}
type key string
const isValidHostKey key = "isValidHost"

View file

@ -27,8 +27,8 @@ func listing3() {
s := []int{1, 2, 3}
for _, i := range s {
go func(i int) {
fmt.Print(i)
go func(val int) {
fmt.Print(val)
}(i)
}
}

View file

@ -17,7 +17,7 @@ func main() {
}
type Cache struct {
mu sync.Mutex
mu sync.RWMutex
balances map[string]float64
}
@ -28,9 +28,9 @@ func (c *Cache) AddBalance(id string, balance float64) {
}
func (c *Cache) AverageBalance1() float64 {
c.mu.Lock()
c.mu.RLock()
balances := c.balances
c.mu.Unlock()
c.mu.RUnlock()
sum := 0.
for _, balance := range balances {
@ -40,8 +40,8 @@ func (c *Cache) AverageBalance1() float64 {
}
func (c *Cache) AverageBalance2() float64 {
c.mu.Lock()
defer c.mu.Unlock()
c.mu.RLock()
defer c.mu.RUnlock()
sum := 0.
for _, balance := range c.balances {
@ -51,12 +51,12 @@ func (c *Cache) AverageBalance2() float64 {
}
func (c *Cache) AverageBalance3() float64 {
c.mu.Lock()
c.mu.RLock()
m := make(map[string]float64, len(c.balances))
for k, v := range c.balances {
m[k] = v
}
c.mu.Unlock()
c.mu.RUnlock()
sum := 0.
for _, balance := range m {

View file

@ -6,8 +6,8 @@ import (
"log"
)
func (c *conn) get1(ctx context.Context, id string) (string, int, error) {
rows, err := c.db.QueryContext(ctx,
func get1(ctx context.Context, db *sql.DB, id string) (string, int, error) {
rows, err := db.QueryContext(ctx,
"SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
if err != nil {
return "", 0, err
@ -33,8 +33,8 @@ func (c *conn) get1(ctx context.Context, id string) (string, int, error) {
return department, age, nil
}
func (c *conn) get2(ctx context.Context, id string) (string, int, error) {
rows, err := c.db.QueryContext(ctx,
func get2(ctx context.Context, db *sql.DB, id string) (string, int, error) {
rows, err := db.QueryContext(ctx,
"SELECT DEP, AGE FROM EMP WHERE ID = ?", id)
if err != nil {
return "", 0, err
@ -62,7 +62,3 @@ func (c *conn) get2(ctx context.Context, id string) (string, int, error) {
return department, age, nil
}
type conn struct {
db *sql.DB
}

View file

@ -15,7 +15,7 @@ type Event struct {
Data string
}
func (c *Cache) TrimBefore(since time.Duration) {
func (c *Cache) TrimOlderThan(since time.Duration) {
c.mu.RLock()
defer c.mu.RUnlock()

View file

@ -5,7 +5,7 @@ import (
"time"
)
func TestCache_TrimBefore(t *testing.T) {
func TestCache_TrimOlderThan(t *testing.T) {
events := []Event{
{Timestamp: time.Now().Add(-20 * time.Millisecond)},
{Timestamp: time.Now().Add(-10 * time.Millisecond)},
@ -13,7 +13,7 @@ func TestCache_TrimBefore(t *testing.T) {
}
cache := &Cache{}
cache.Add(events)
cache.TrimBefore(15 * time.Millisecond)
cache.TrimOlderThan(15 * time.Millisecond)
got := cache.GetAll()
expected := 2
if len(got) != expected {

View file

@ -25,7 +25,7 @@ type Event struct {
Data string
}
func (c *Cache) TrimBefore(since time.Duration) {
func (c *Cache) TrimOlderThan(since time.Duration) {
c.mu.RLock()
defer c.mu.RUnlock()

View file

@ -5,7 +5,7 @@ import (
"time"
)
func TestCache_TrimBefore(t *testing.T) {
func TestCache_TrimOlderThan(t *testing.T) {
events := []Event{
{Timestamp: parseTime(t, "2020-01-01T12:00:00.04Z")},
{Timestamp: parseTime(t, "2020-01-01T12:00:00.05Z")},
@ -15,7 +15,7 @@ func TestCache_TrimBefore(t *testing.T) {
return parseTime(t, "2020-01-01T12:00:00.06Z")
}}
cache.Add(events)
cache.TrimBefore(15 * time.Millisecond)
cache.TrimOlderThan(15 * time.Millisecond)
// ...
}

View file

@ -15,7 +15,7 @@ type Event struct {
Data string
}
func (c *Cache) TrimBefore(now time.Time, since time.Duration) {
func (c *Cache) TrimOlderThan(now time.Time, since time.Duration) {
c.mu.RLock()
defer c.mu.RUnlock()

View file

@ -5,7 +5,7 @@ import (
"time"
)
func TestCache_TrimBefore(t *testing.T) {
func TestCache_TrimOlderThan(t *testing.T) {
events := []Event{
{Timestamp: parseTime(t, "2020-01-01T12:00:00.04Z")},
{Timestamp: parseTime(t, "2020-01-01T12:00:00.05Z")},
@ -13,7 +13,7 @@ func TestCache_TrimBefore(t *testing.T) {
}
cache := &Cache{}
cache.Add(events)
cache.TrimBefore(parseTime(t, "2020-01-01T12:00:00.06Z"), 15*time.Millisecond)
cache.TrimOlderThan(parseTime(t, "2020-01-01T12:00:00.06Z"), 15*time.Millisecond)
got := cache.GetAll()
expected := 2
if len(got) != expected {

View file

@ -0,0 +1,42 @@
package listing1
import (
"sync"
"time"
)
type Cache struct {
mu sync.RWMutex
events []Event
}
type Event struct {
Timestamp time.Time
Data string
}
func (c *Cache) TrimOlderThan(t time.Time) {
c.mu.RLock()
defer c.mu.RUnlock()
for i := 0; i < len(c.events); i++ {
if c.events[i].Timestamp.After(t) {
c.events = c.events[i:]
return
}
}
}
func (c *Cache) Add(events []Event) {
c.mu.Lock()
defer c.mu.Unlock()
c.events = append(c.events, events...)
}
func (c *Cache) GetAll() []Event {
c.mu.RLock()
defer c.mu.RUnlock()
return c.events
}

View file

@ -0,0 +1,31 @@
package listing1
import (
"testing"
"time"
)
func TestCache_TrimOlderThan(t *testing.T) {
events := []Event{
{Timestamp: parseTime(t, "2020-01-01T12:00:00.04Z")},
{Timestamp: parseTime(t, "2020-01-01T12:00:00.05Z")},
{Timestamp: parseTime(t, "2020-01-01T12:00:00.06Z")},
}
cache := &Cache{}
cache.Add(events)
cache.TrimOlderThan(parseTime(t, "2020-01-01T12:00:00.06Z").
Add(-15 * time.Millisecond))
got := cache.GetAll()
expected := 2
if len(got) != expected {
t.Fatalf("expected %d, got %d", expected, len(got))
}
}
func parseTime(t *testing.T, timestamp string) time.Time {
ts, err := time.Parse(time.RFC3339, timestamp)
if err != nil {
t.FailNow()
}
return ts
}

View file

@ -12,7 +12,7 @@ func (l LowerCaseReader) Read(p []byte) (int, error) {
return 0, nil
}
func foo(r io.Reader) error {
func foo1(r io.Reader) error {
b, err := io.ReadAll(r)
if err != nil {
return err
@ -23,6 +23,17 @@ func foo(r io.Reader) error {
return nil
}
func foo2(r io.Reader) error {
b, err := readAll(r, 3)
if err != nil {
return err
}
// ...
_ = b
return nil
}
func readAll(r io.Reader, retries int) ([]byte, error) {
b := make([]byte, 0, 512)
for {
@ -33,10 +44,10 @@ func readAll(r io.Reader, retries int) ([]byte, error) {
b = b[:len(b)+n]
if err != nil {
if err == io.EOF {
err = nil
return b, nil
}
retries--
if retries <= 0 {
if retries < 0 {
return b, err
}
}

View file

@ -16,8 +16,17 @@ func TestLowerCaseReader(t *testing.T) {
}
}
func TestFoo(t *testing.T) {
err := foo(iotest.TimeoutReader(
func TestFoo1(t *testing.T) {
err := foo1(iotest.TimeoutReader(
strings.NewReader(randomString(1024)),
))
if err != nil {
t.Fatal(err)
}
}
func TestFoo2(t *testing.T) {
err := foo2(iotest.TimeoutReader(
strings.NewReader(randomString(1024)),
))
if err != nil {

View file

@ -7,8 +7,8 @@ import (
)
func TestMySQLIntegration(t *testing.T) {
s := setupMySQL()
defer teardownMySQL(s)
setupMySQL()
defer teardownMySQL()
// ...
}
@ -26,12 +26,12 @@ func createConnection(t *testing.T, dsn string) *sql.DB {
}
func TestMain(m *testing.M) {
s := setupMySQL()
setupMySQL()
code := m.Run()
teardownMySQL(s)
teardownMySQL()
os.Exit(code)
}
func setupMySQL() interface{} { return nil }
func setupMySQL() {}
func teardownMySQL(m interface{}) {}
func teardownMySQL() {}

View file

@ -1,22 +1,5 @@
package main
func NewStore() Store {
return Store{}
}
type Store struct{}
func (s Store) PutCustomer(Customer) error {
return nil
}
type Customer struct {
id string
}
func customerFactory(id string) (Customer, error) {
if id == "" {
return Customer{}, nil
}
return Customer{id: id}, nil
}

View file

@ -1,36 +1,44 @@
package main
import "testing"
import (
"errors"
"testing"
)
func TestPutCustomer1(t *testing.T) {
customer, err := createCustomer1()
func TestCustomer1(t *testing.T) {
customer, err := createCustomer1("foo")
if err != nil {
t.Fatal(err)
}
err = NewStore().PutCustomer(customer)
// Check err
_ = err
// ...
_ = customer
}
func createCustomer1() (Customer, error) {
customer, err := customerFactory("foo")
func createCustomer1(someArg string) (Customer, error) {
customer, err := customerFactory(someArg)
if err != nil {
return Customer{}, nil
return Customer{}, err
}
return customer, nil
}
func TestPutCustomer(t *testing.T) {
customer := createCustomer2(t)
err := NewStore().PutCustomer(customer)
// Check err
_ = err
func TestCustomer2(t *testing.T) {
customer := createCustomer2(t, "foo")
// ...
_ = customer
}
func createCustomer2(t *testing.T) Customer {
customer, err := customerFactory("foo")
func createCustomer2(t *testing.T, someArg string) Customer {
customer, err := customerFactory(someArg)
if err != nil {
t.Fatal(err)
}
return customer
}
func customerFactory(someArg string) (Customer, error) {
if someArg == "" {
return Customer{}, errors.New("empty")
}
return Customer{id: someArg}, nil
}