Compare commits

...

12 Commits

6 changed files with 216 additions and 0 deletions

View File

@ -0,0 +1,31 @@
package concurrency
type WebsiteChecker func(string) bool
type result struct {
string
bool
}
func CheckWebsites(wc WebsiteChecker, urls []string) map[string]bool {
results := make(map[string]bool)
resultChannel := make(chan result)
for _, url := range urls {
go func(u string) {
resultChannel <- result{u, wc(u)}
}(url)
}
// XXX: it seems that this works on Go 1.23
// for _, u := range urls {
// go func() {
// resultChannel <- result{u, wc(u)}
// }()
// }
for i := 0; i < len(urls); i++ {
r := <-resultChannel
results[r.string] = r.bool
}
return results
}

View File

@ -0,0 +1,47 @@
package concurrency
import (
"reflect"
"testing"
"time"
)
func mockWebsiteChecker(url string) bool {
return url != "waat://furhurterwe.geds"
}
func TestCheckWebsites(t *testing.T) {
websites := []string{
"http://google.com",
"http://blog.abc.com",
"waat://furhurterwe.geds",
}
want := map[string]bool{
"http://google.com": true,
"http://blog.abc.com": true,
"waat://furhurterwe.geds": false,
}
got := CheckWebsites(mockWebsiteChecker, websites)
if !reflect.DeepEqual(got, want) {
t.Fatalf("wanted %v, got %v", want, got)
}
}
func slowStubWebsiteChecker(_ string) bool {
time.Sleep(20 * time.Millisecond)
return true
}
func BenchmarkCheckWebsites(b *testing.B) {
urls := make([]string, 100)
for i := 0; i < len(urls); i++ {
urls[i] = "a url"
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
CheckWebsites(slowStubWebsiteChecker, urls)
}
}

45
racer/racer.go Normal file
View File

@ -0,0 +1,45 @@
package racer
import (
"errors"
"net/http"
"time"
)
var ErrRacerTimeout = errors.New("timeout")
const tenSecondTimeout = 10 * time.Second
func ConfigurableRacer(a, b string, timeout time.Duration) (string, error) {
select {
// wait until channel closed
case <-ping(a):
return a, nil
case <-ping(b):
return b, nil
case <-time.After(timeout):
return "", ErrRacerTimeout
}
}
func Racer(a, b string) (string, error) {
return ConfigurableRacer(a, b, tenSecondTimeout)
}
func ping(url string) chan struct{} {
ch := make(chan struct{})
go func() {
http.Get(url)
// if got, close channel
close(ch)
}()
return ch
}
// func measureResponseTime(url string) time.Duration {
// start := time.Now()
// http.Get(url)
// duration := time.Since(start)
// fmt.Println(duration)
// return duration
// }

55
racer/racer_test.go Normal file
View File

@ -0,0 +1,55 @@
package racer
import (
"net/http"
"net/http/httptest"
"testing"
"time"
)
type SpyRacerTimeout struct{}
func (s *SpyRacerTimeout) Timeout() <-chan time.Time {
return time.After(1 * time.Millisecond)
}
func TestRacer(t *testing.T) {
t.Run("compares speeds of servers, returning the url of the fasted one", func(t *testing.T) {
slowServer := makeDelayedServer(20 * time.Millisecond)
defer slowServer.Close()
fastServer := makeDelayedServer(0 * time.Millisecond)
defer fastServer.Close()
slowURL := slowServer.URL
fastURL := fastServer.URL
want := fastURL
got, _ := Racer(slowURL, fastURL)
if got != want {
t.Errorf("got %q, want %q", got, want)
}
})
t.Run("returns an error if a server doesn't respond within 10s", func(t *testing.T) {
serverA := makeDelayedServer(25 * time.Millisecond)
defer serverA.Close()
serverB := makeDelayedServer(25 * time.Millisecond)
defer serverB.Close()
_, err := ConfigurableRacer(serverA.URL, serverB.URL, 20*time.Millisecond)
if err == nil {
t.Error("expected an error but didn't got one")
}
})
}
func makeDelayedServer(delay time.Duration) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
time.Sleep(delay)
w.WriteHeader(http.StatusOK)
}))
}

11
walk/walk.go Normal file
View File

@ -0,0 +1,11 @@
package walk
import (
"reflect"
)
func Walk(x interface{}, fn func(string)) {
val := reflect.ValueOf(x)
field := val.Field(0)
fn(field.String())
}

27
walk/walk_test.go Normal file
View File

@ -0,0 +1,27 @@
package walk
import (
"testing"
)
func TestWalk(t *testing.T) {
t.Run("walk function test", func(t *testing.T) {
expected := "Chris"
var got []string
x := struct {
Name string
}{expected}
Walk(x, func(input string) {
got = append(got, input)
})
if len(got) != 1 {
t.Errorf("wrong number of function calls, got %d want %d", len(got), 1)
}
if got[0] != expected {
t.Errorf("got %q want %q", got[0], expected)
}
})
}