├── solutions ├── dupes │ ├── test.txtar │ ├── go.mod │ ├── dupes.go │ └── dupes_test.go ├── empty │ ├── test.txtar │ ├── go.mod │ ├── empty.go │ └── empty_test.go ├── group │ ├── test.txtar │ ├── group.go │ ├── go.mod │ └── group_test.go ├── merge │ ├── test.txtar │ ├── go.mod │ ├── merge.go │ └── merge_test.go ├── print │ ├── test.txtar │ ├── go.mod │ ├── print.go │ └── print_test.go ├── stack │ ├── test.txtar │ ├── go.mod │ ├── stack.go │ └── stack_test.go ├── compose │ ├── test.txtar │ ├── go.mod │ ├── compose.go │ └── compose_test.go ├── funcmap │ ├── test.txtar │ ├── go.mod │ ├── funcmap.go │ └── funcmap_test.go ├── greater │ ├── test.txtar │ ├── go.mod │ ├── greater.go │ └── greater_test.go ├── intish │ ├── test.txtar │ ├── go.mod │ ├── intish.go │ └── intish_test.go ├── length │ ├── test.txtar │ ├── go.mod │ ├── length.go │ └── length_test.go ├── product │ ├── test.txtar │ ├── go.mod │ ├── product.go │ └── product_test.go ├── stringy │ ├── test.txtar │ ├── go.mod │ ├── stringy.go │ └── stringy_test.go └── channel │ ├── test.txtar │ ├── go.mod │ ├── channel.go │ └── channel_test.go ├── exercises ├── dupes │ ├── go.mod │ ├── dupes.go │ ├── test.txtar │ └── dupes_test.go ├── empty │ ├── go.mod │ ├── test.txtar │ ├── empty.go │ └── empty_test.go ├── group │ ├── go.mod │ ├── group.go │ ├── test.txtar │ └── group_test.go ├── intish │ ├── go.mod │ ├── test.txtar │ ├── intish.go │ └── intish_test.go ├── length │ ├── go.mod │ ├── test.txtar │ ├── length.go │ └── length_test.go ├── merge │ ├── go.mod │ ├── test.txtar │ ├── merge.go │ └── merge_test.go ├── print │ ├── go.mod │ ├── test.txtar │ ├── print.go │ └── print_test.go ├── stack │ ├── go.mod │ ├── test.txtar │ ├── stack.go │ └── stack_test.go ├── channel │ ├── go.mod │ ├── test.txtar │ ├── channel.go │ └── channel_test.go ├── compose │ ├── go.mod │ ├── test.txtar │ ├── compose.go │ └── compose_test.go ├── funcmap │ ├── go.mod │ ├── test.txtar │ ├── funcmap.go │ └── funcmap_test.go ├── greater │ ├── go.mod │ ├── test.txtar │ ├── greater.go │ └── greater_test.go ├── product │ ├── go.mod │ ├── test.txtar │ ├── product.go │ └── product_test.go └── stringy │ ├── go.mod │ ├── test.txtar │ ├── stringy.go │ └── stringy_test.go ├── cover_small.png ├── run_all_testscripts.sh ├── README.md ├── run_testscript.sh ├── .github └── workflows │ └── test.yml └── LICENSE /solutions/dupes/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/empty/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/group/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/merge/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/print/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/stack/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/compose/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/funcmap/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/greater/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/intish/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/length/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/product/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/stringy/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /solutions/channel/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test -race 2 | -------------------------------------------------------------------------------- /solutions/group/group.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | type Group[E any] []E 4 | -------------------------------------------------------------------------------- /exercises/dupes/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/dupes 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/empty/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/empty 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/group/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/group 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/intish/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/intish 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/length/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/length 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/merge/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/merge 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /exercises/print/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/print 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/stack/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/stack 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/dupes/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/dupes 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/empty/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/empty 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/group/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/group 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/intish/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/intish 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/length/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/length 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/merge/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/merge 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /solutions/print/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/print 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/stack/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/stack 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /cover_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/know-go/HEAD/cover_small.png -------------------------------------------------------------------------------- /exercises/channel/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/channel 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/compose/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/compose 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/funcmap/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/funcmap 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/greater/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/greater 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/group/group.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | // Your Group type goes here! 4 | -------------------------------------------------------------------------------- /exercises/length/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr 'undefined: length.Len' 3 | -------------------------------------------------------------------------------- /exercises/product/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/product 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/stringy/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/stringy 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/channel/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/channel 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/compose/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/compose 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/funcmap/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/funcmap 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/greater/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/greater 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/product/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/product 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /solutions/stringy/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/stringy 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /exercises/dupes/dupes.go: -------------------------------------------------------------------------------- 1 | package dupes 2 | 3 | // Your Dupes function goes here! 4 | -------------------------------------------------------------------------------- /exercises/length/length.go: -------------------------------------------------------------------------------- 1 | package length 2 | 3 | // Your Len function goes here! 4 | -------------------------------------------------------------------------------- /exercises/product/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr 'undefined: product.Product' 3 | -------------------------------------------------------------------------------- /exercises/channel/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr -count=4 'undefined: channel.New' 3 | -------------------------------------------------------------------------------- /exercises/dupes/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr -count=2 'undefined: dupes.Dupes' 3 | -------------------------------------------------------------------------------- /exercises/empty/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr -count=2 'undefined: empty.Sequence' 3 | -------------------------------------------------------------------------------- /exercises/group/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr -count=2 'undefined: group.Group' 3 | -------------------------------------------------------------------------------- /exercises/merge/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr -count=3 'undefined: merge.Merge' 3 | -------------------------------------------------------------------------------- /exercises/print/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr 'undefined: print.PrintAnythingTo' 3 | -------------------------------------------------------------------------------- /exercises/stack/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr -count=3 'undefined: stack.Stack' 3 | -------------------------------------------------------------------------------- /exercises/stringy/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr 'undefined: stringy.StringifyTo' 3 | -------------------------------------------------------------------------------- /exercises/compose/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr -count=4 'undefined: compose.Compose' 3 | -------------------------------------------------------------------------------- /exercises/empty/empty.go: -------------------------------------------------------------------------------- 1 | package empty 2 | 3 | // Your implementation of Sequence goes here! 4 | -------------------------------------------------------------------------------- /exercises/funcmap/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr -count=2 'undefined: funcmap.FuncMap' 3 | -------------------------------------------------------------------------------- /exercises/greater/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr 'syntax error: missing type constraint' 3 | -------------------------------------------------------------------------------- /exercises/merge/merge.go: -------------------------------------------------------------------------------- 1 | package merge 2 | 3 | // Your implementation of Merge goes here! 4 | -------------------------------------------------------------------------------- /exercises/print/print.go: -------------------------------------------------------------------------------- 1 | package print 2 | 3 | // Your PrintAnythingTo function goes here! 4 | -------------------------------------------------------------------------------- /exercises/stack/stack.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | // Your implementation of Stack goes here! 4 | -------------------------------------------------------------------------------- /exercises/stringy/stringy.go: -------------------------------------------------------------------------------- 1 | package stringy 2 | 3 | // Your StringifyTo function goes here! 4 | -------------------------------------------------------------------------------- /exercises/channel/channel.go: -------------------------------------------------------------------------------- 1 | package channel 2 | 3 | // Your implementation of Channel goes here!! 4 | -------------------------------------------------------------------------------- /exercises/compose/compose.go: -------------------------------------------------------------------------------- 1 | package compose 2 | 3 | // Your implementation of Compose goes here! 4 | -------------------------------------------------------------------------------- /exercises/funcmap/funcmap.go: -------------------------------------------------------------------------------- 1 | package funcmap 2 | 3 | // Your implementation of FuncMap goes here! 4 | -------------------------------------------------------------------------------- /exercises/intish/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stderr 'undefined: Intish' 3 | stderr 'cannot convert 0' 4 | -------------------------------------------------------------------------------- /solutions/length/length.go: -------------------------------------------------------------------------------- 1 | package length 2 | 3 | func Len[E any](s []E) int { 4 | return len(s) 5 | } 6 | -------------------------------------------------------------------------------- /exercises/product/product.go: -------------------------------------------------------------------------------- 1 | package product 2 | 3 | // Your constraint definition goes here! 4 | 5 | // Your Product function goes here! 6 | -------------------------------------------------------------------------------- /solutions/compose/compose.go: -------------------------------------------------------------------------------- 1 | package compose 2 | 3 | func Compose[T, U, V any](f func(U) T, g func(V) U, v V) T { 4 | return f(g(v)) 5 | } 6 | -------------------------------------------------------------------------------- /solutions/empty/empty.go: -------------------------------------------------------------------------------- 1 | package empty 2 | 3 | type Sequence[E any] []E 4 | 5 | func (s Sequence[E]) Empty() bool { 6 | return len(s) == 0 7 | } 8 | -------------------------------------------------------------------------------- /solutions/greater/greater.go: -------------------------------------------------------------------------------- 1 | package greater 2 | 3 | func IsGreater[T interface{ Greater(T) bool }](x, y T) bool { 4 | return x.Greater(y) 5 | } 6 | -------------------------------------------------------------------------------- /exercises/greater/greater.go: -------------------------------------------------------------------------------- 1 | package greater 2 | 3 | func IsGreater[T /* Your constraint goes here! */](x, y T) bool { 4 | return x.Greater(y) 5 | } 6 | -------------------------------------------------------------------------------- /exercises/intish/intish.go: -------------------------------------------------------------------------------- 1 | package intish 2 | 3 | // Your Intish interface goes here! 4 | 5 | func IsPositive[T Intish](v T) bool { 6 | return v > 0 7 | } 8 | -------------------------------------------------------------------------------- /solutions/intish/intish.go: -------------------------------------------------------------------------------- 1 | package intish 2 | 3 | type Intish interface { 4 | ~int 5 | } 6 | 7 | func IsPositive[T Intish](v T) bool { 8 | return v > 0 9 | } 10 | -------------------------------------------------------------------------------- /solutions/print/print.go: -------------------------------------------------------------------------------- 1 | package print 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | func PrintAnythingTo[T any](w io.Writer, p T) { 9 | fmt.Fprintln(w, p) 10 | } 11 | -------------------------------------------------------------------------------- /solutions/funcmap/funcmap.go: -------------------------------------------------------------------------------- 1 | package funcmap 2 | 3 | type FuncMap[T, U any] map[string]func(T) U 4 | 5 | func (fm FuncMap[T, U]) Apply(name string, val T) U { 6 | return fm[name](val) 7 | } 8 | -------------------------------------------------------------------------------- /run_all_testscripts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | for LISTING in exercises/* solutions/*; do 5 | # Update deps: 6 | # cd $LISTING && go get -t -u && cd - 7 | ./run_testscript.sh $LISTING 8 | done -------------------------------------------------------------------------------- /solutions/stringy/stringy.go: -------------------------------------------------------------------------------- 1 | package stringy 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | func StringifyTo[T fmt.Stringer](w io.Writer, p T) { 9 | fmt.Fprintln(w, p.String()) 10 | } 11 | -------------------------------------------------------------------------------- /solutions/dupes/dupes.go: -------------------------------------------------------------------------------- 1 | package dupes 2 | 3 | func Dupes[E comparable](s []E) bool { 4 | seen := map[E]bool{} 5 | for _, v := range s { 6 | if seen[v] { 7 | return true 8 | } 9 | seen[v] = true 10 | } 11 | return false 12 | } 13 | -------------------------------------------------------------------------------- /solutions/merge/merge.go: -------------------------------------------------------------------------------- 1 | package merge 2 | 3 | import "maps" 4 | 5 | func Merge[M ~map[K]V, K comparable, V any](ms ...M) M { 6 | result := M{} 7 | for _, m := range ms { 8 | maps.Copy[map[K]V](result, m) 9 | } 10 | return result 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Know Go 2 | 3 | [![](cover_small.png)](https://bitfieldconsulting.com/books/generics) 4 | 5 | This repository contains exercises, solutions, and code samples from the book [Know Go](https://bitfieldconsulting.com/books/generics), by John Arundel. 6 | -------------------------------------------------------------------------------- /run_testscript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ $# -eq 0 ] 3 | then 4 | echo "Usage: $0 example_path" 5 | exit 1 6 | fi 7 | 8 | LISTING=$1 9 | SCRIPT=${LISTING}/tmp-test.txtar 10 | # go install github.com/bitfield/txtar-c@latest 11 | txtar-c -quote -script ${LISTING}/test.txtar $LISTING >>$SCRIPT 12 | echo -n "${LISTING}... " && testscript $SCRIPT && rm $SCRIPT 13 | -------------------------------------------------------------------------------- /exercises/length/length_test.go: -------------------------------------------------------------------------------- 1 | package length_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/length" 7 | ) 8 | 9 | func TestLenOfSliceIs2WhenItContains2Elements(t *testing.T) { 10 | t.Parallel() 11 | s := []int{1, 2} 12 | want := 2 13 | got := length.Len(s) 14 | if want != got { 15 | t.Errorf("Len(%v): want %d, got %d", s, want, got) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /solutions/length/length_test.go: -------------------------------------------------------------------------------- 1 | package length_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/length" 7 | ) 8 | 9 | func TestLenOfSliceIs2WhenItContains2Elements(t *testing.T) { 10 | t.Parallel() 11 | s := []int{1, 2} 12 | want := 2 13 | got := length.Len(s) 14 | if want != got { 15 | t.Errorf("Len(%v): want %d, got %d", s, want, got) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /exercises/print/print_test.go: -------------------------------------------------------------------------------- 1 | package print_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/bitfield/print" 8 | ) 9 | 10 | func TestPrintAnythingTo_PrintsToGivenWriter(t *testing.T) { 11 | t.Parallel() 12 | buf := new(bytes.Buffer) 13 | print.PrintAnythingTo(buf, "Hello, world") 14 | want := "Hello, world\n" 15 | got := buf.String() 16 | if want != got { 17 | t.Errorf("want %q, got %q", want, got) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /solutions/print/print_test.go: -------------------------------------------------------------------------------- 1 | package print_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/bitfield/print" 8 | ) 9 | 10 | func TestPrintAnythingTo_PrintsToGivenWriter(t *testing.T) { 11 | t.Parallel() 12 | buf := new(bytes.Buffer) 13 | print.PrintAnythingTo(buf, "Hello, world") 14 | want := "Hello, world\n" 15 | got := buf.String() 16 | if want != got { 17 | t.Errorf("want %q, got %q", want, got) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /solutions/product/product.go: -------------------------------------------------------------------------------- 1 | package product 2 | 3 | type Integer interface { 4 | ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | 5 | ~uint16 | ~uint32 | ~uint64 6 | } 7 | 8 | type Float interface { 9 | ~float32 | ~float64 10 | } 11 | 12 | type Complex interface { 13 | ~complex64 | ~complex128 14 | } 15 | 16 | type Number interface { 17 | Integer | Float | Complex 18 | } 19 | 20 | func Product[T Number](x, y T) T { 21 | return x * y 22 | } 23 | -------------------------------------------------------------------------------- /solutions/stack/stack.go: -------------------------------------------------------------------------------- 1 | package stack 2 | 3 | type Stack[E any] struct { 4 | data []E 5 | } 6 | 7 | func (s Stack[E]) Len() int { 8 | return len(s.data) 9 | } 10 | 11 | func (s *Stack[E]) Push(vals ...E) { 12 | s.data = append(s.data, vals...) 13 | } 14 | 15 | func (s *Stack[E]) Pop() (v E, ok bool) { 16 | if len(s.data) == 0 { 17 | return v, false 18 | } 19 | v = s.data[len(s.data)-1] 20 | s.data = s.data[:len(s.data)-1] 21 | return v, true 22 | } 23 | -------------------------------------------------------------------------------- /exercises/group/group_test.go: -------------------------------------------------------------------------------- 1 | package group_test 2 | 3 | import ( 4 | "slices" 5 | "testing" 6 | 7 | "github.com/bitfield/group" 8 | ) 9 | 10 | func TestGroupContainsWhatIsAppendedToIt(t *testing.T) { 11 | t.Parallel() 12 | got := group.Group[string]{} 13 | got = append(got, "hello") 14 | got = append(got, "world") 15 | want := group.Group[string]{"hello", "world"} 16 | if !slices.Equal(want, got) { 17 | t.Errorf("want %v, got %v", want, got) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /solutions/group/group_test.go: -------------------------------------------------------------------------------- 1 | package group_test 2 | 3 | import ( 4 | "slices" 5 | "testing" 6 | 7 | "github.com/bitfield/group" 8 | ) 9 | 10 | func TestGroupContainsWhatIsAppendedToIt(t *testing.T) { 11 | t.Parallel() 12 | got := group.Group[string]{} 13 | got = append(got, "hello") 14 | got = append(got, "world") 15 | want := group.Group[string]{"hello", "world"} 16 | if !slices.Equal(want, got) { 17 | t.Errorf("want %v, got %v", want, got) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /exercises/empty/empty_test.go: -------------------------------------------------------------------------------- 1 | package empty_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/empty" 7 | ) 8 | 9 | func TestEmptyIsTrueForEmptySequence(t *testing.T) { 10 | t.Parallel() 11 | s := empty.Sequence[int]{} 12 | if !s.Empty() { 13 | t.Fatalf("Empty(%v): want true, got false", s) 14 | } 15 | } 16 | 17 | func TestEmptyIsFalseForNonEmptySequence(t *testing.T) { 18 | t.Parallel() 19 | s := empty.Sequence[string]{"a", "b", "c"} 20 | if s.Empty() { 21 | t.Fatalf("Empty(%v): want false, got true", s) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /solutions/empty/empty_test.go: -------------------------------------------------------------------------------- 1 | package empty_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/empty" 7 | ) 8 | 9 | func TestEmptyIsTrueForEmptySequence(t *testing.T) { 10 | t.Parallel() 11 | s := empty.Sequence[int]{} 12 | if !s.Empty() { 13 | t.Fatalf("Empty(%v): want true, got false", s) 14 | } 15 | } 16 | 17 | func TestEmptyIsFalseForNonEmptySequence(t *testing.T) { 18 | t.Parallel() 19 | s := empty.Sequence[string]{"a", "b", "c"} 20 | if s.Empty() { 21 | t.Fatalf("Empty(%v): want false, got true", s) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /exercises/dupes/dupes_test.go: -------------------------------------------------------------------------------- 1 | package dupes_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/dupes" 7 | ) 8 | 9 | func TestDupesIsTrueGivenNonConsecutiveDuplicates(t *testing.T) { 10 | t.Parallel() 11 | s := []int{1, 2, 3, 1, 5} 12 | if !dupes.Dupes(s) { 13 | t.Errorf("Dupes(%v): want true, got false", s) 14 | } 15 | } 16 | 17 | func TestDupesIsFalseGivenNoDuplicates(t *testing.T) { 18 | t.Parallel() 19 | s := []string{"a", "b", "c"} 20 | if dupes.Dupes(s) { 21 | t.Errorf("Dupes(%v): want false, got true", s) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /exercises/stringy/stringy_test.go: -------------------------------------------------------------------------------- 1 | package stringy_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/bitfield/stringy" 8 | ) 9 | 10 | type greeting struct{} 11 | 12 | func (greeting) String() string { 13 | return "Howdy!" 14 | } 15 | 16 | func TestStringifyTo_PrintsToSuppliedWriter(t *testing.T) { 17 | t.Parallel() 18 | buf := &bytes.Buffer{} 19 | stringy.StringifyTo[greeting](buf, greeting{}) 20 | want := "Howdy!\n" 21 | got := buf.String() 22 | if want != got { 23 | t.Errorf("want %q, got %q", want, got) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/dupes/dupes_test.go: -------------------------------------------------------------------------------- 1 | package dupes_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/dupes" 7 | ) 8 | 9 | func TestDupesIsTrueGivenNonConsecutiveDuplicates(t *testing.T) { 10 | t.Parallel() 11 | s := []int{1, 2, 3, 1, 5} 12 | if !dupes.Dupes(s) { 13 | t.Errorf("Dupes(%v): want true, got false", s) 14 | } 15 | } 16 | 17 | func TestDupesIsFalseGivenNoDuplicates(t *testing.T) { 18 | t.Parallel() 19 | s := []string{"a", "b", "c"} 20 | if dupes.Dupes(s) { 21 | t.Errorf("Dupes(%v): want false, got true", s) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /solutions/stringy/stringy_test.go: -------------------------------------------------------------------------------- 1 | package stringy_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/bitfield/stringy" 8 | ) 9 | 10 | type greeting struct{} 11 | 12 | func (greeting) String() string { 13 | return "Howdy!" 14 | } 15 | 16 | func TestStringifyTo_PrintsToSuppliedWriter(t *testing.T) { 17 | t.Parallel() 18 | buf := &bytes.Buffer{} 19 | stringy.StringifyTo[greeting](buf, greeting{}) 20 | want := "Howdy!\n" 21 | got := buf.String() 22 | if want != got { 23 | t.Errorf("want %q, got %q", want, got) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /exercises/greater/greater_test.go: -------------------------------------------------------------------------------- 1 | package greater_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/greater" 7 | ) 8 | 9 | type MyInt int 10 | 11 | func (m MyInt) Greater(v MyInt) bool { 12 | return m > v 13 | } 14 | 15 | func TestIsGreater_IsTrueFor2And1(t *testing.T) { 16 | t.Parallel() 17 | if !greater.IsGreater(MyInt(2), MyInt(1)) { 18 | t.Fatalf("IsGreater(2, 1): want true, got false") 19 | } 20 | } 21 | 22 | func TestIsGreater_IsFalseFor1And2(t *testing.T) { 23 | t.Parallel() 24 | if greater.IsGreater(MyInt(1), MyInt(2)) { 25 | t.Fatalf("IsGreater(1, 2): want false, got true") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /solutions/greater/greater_test.go: -------------------------------------------------------------------------------- 1 | package greater_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/greater" 7 | ) 8 | 9 | type MyInt int 10 | 11 | func (m MyInt) Greater(v MyInt) bool { 12 | return m > v 13 | } 14 | 15 | func TestIsGreater_IsTrueFor2And1(t *testing.T) { 16 | t.Parallel() 17 | if !greater.IsGreater(MyInt(2), MyInt(1)) { 18 | t.Fatalf("IsGreater(2, 1): want true, got false") 19 | } 20 | } 21 | 22 | func TestIsGreater_IsFalseFor1And2(t *testing.T) { 23 | t.Parallel() 24 | if greater.IsGreater(MyInt(1), MyInt(2)) { 25 | t.Fatalf("IsGreater(1, 2): want false, got true") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /solutions/channel/channel.go: -------------------------------------------------------------------------------- 1 | package channel 2 | 3 | import "sync/atomic" 4 | 5 | type Channel[T any] struct { 6 | ch chan T 7 | sends, receives atomic.Uint64 8 | } 9 | 10 | func New[T any](length int) *Channel[T] { 11 | return &Channel[T]{ 12 | ch: make(chan T, length), 13 | } 14 | } 15 | 16 | func (c *Channel[T]) Send(v T) { 17 | c.ch <- v 18 | c.sends.Add(1) 19 | } 20 | 21 | func (c *Channel[T]) Receive() T { 22 | v := <-c.ch 23 | c.receives.Add(1) 24 | return v 25 | } 26 | 27 | func (c *Channel[T]) Sends() uint64 { 28 | return c.sends.Load() 29 | } 30 | 31 | func (c *Channel[T]) Receives() uint64 { 32 | return c.receives.Load() 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/mvdan/github-actions-golang 2 | on: [push, pull_request, workflow_dispatch] 3 | name: Tests 4 | jobs: 5 | test: 6 | strategy: 7 | matrix: 8 | go-version: ['stable', 'oldstable'] 9 | os: [ubuntu-latest, macos-latest] 10 | runs-on: ${{ matrix.os }} 11 | steps: 12 | - uses: actions/setup-go@v4 13 | with: 14 | go-version: ${{ matrix.go-version }} 15 | - uses: actions/checkout@v3 16 | - run: | 17 | go install github.com/rogpeppe/go-internal/cmd/testscript@latest 18 | go install github.com/bitfield/txtar-c@latest 19 | ./run_all_testscripts.sh 20 | -------------------------------------------------------------------------------- /exercises/intish/intish_test.go: -------------------------------------------------------------------------------- 1 | package intish_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/intish" 7 | ) 8 | 9 | type MyInt int 10 | 11 | func TestIsPositive_IsTrueFor1(t *testing.T) { 12 | t.Parallel() 13 | input := MyInt(1) 14 | if !intish.IsPositive(input) { 15 | t.Errorf("IsPositive(1): want true, got false") 16 | } 17 | } 18 | 19 | func TestIsPositive_IsFalseForNegative1(t *testing.T) { 20 | t.Parallel() 21 | input := MyInt(-1) 22 | if intish.IsPositive(input) { 23 | t.Errorf("IsPositive(-1): want false, got true") 24 | } 25 | } 26 | 27 | func TestIsPositive_IsFalseForZero(t *testing.T) { 28 | t.Parallel() 29 | input := MyInt(0) 30 | if intish.IsPositive(input) { 31 | t.Errorf("IsPositive(0): want false, got true") 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /solutions/intish/intish_test.go: -------------------------------------------------------------------------------- 1 | package intish_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/intish" 7 | ) 8 | 9 | type MyInt int 10 | 11 | func TestIsPositive_IsTrueFor1(t *testing.T) { 12 | t.Parallel() 13 | input := MyInt(1) 14 | if !intish.IsPositive(input) { 15 | t.Errorf("IsPositive(1): want true, got false") 16 | } 17 | } 18 | 19 | func TestIsPositive_IsFalseForNegative1(t *testing.T) { 20 | t.Parallel() 21 | input := MyInt(-1) 22 | if intish.IsPositive(input) { 23 | t.Errorf("IsPositive(-1): want false, got true") 24 | } 25 | } 26 | 27 | func TestIsPositive_IsFalseForZero(t *testing.T) { 28 | t.Parallel() 29 | input := MyInt(0) 30 | if intish.IsPositive(input) { 31 | t.Errorf("IsPositive(0): want false, got true") 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /exercises/product/product_test.go: -------------------------------------------------------------------------------- 1 | package product_test 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | 7 | "github.com/bitfield/product" 8 | ) 9 | 10 | func TestProductOfInts2And3Is6(t *testing.T) { 11 | t.Parallel() 12 | want := 6 13 | got := product.Product(2, 3) 14 | if want != got { 15 | t.Errorf("want %d, got %d", want, got) 16 | } 17 | } 18 | 19 | func TestProductOfFloats1Point6And2Point3Is3Point68(t *testing.T) { 20 | t.Parallel() 21 | want := 3.68 22 | got := product.Product(1.6, 2.3) 23 | if math.Abs(want-got) > 0.0001 { 24 | t.Errorf("want %f, got %f", want, got) 25 | } 26 | } 27 | 28 | func TestProductOfComplex2Plus3iAnd3Plus2iIs0Plus13i(t *testing.T) { 29 | t.Parallel() 30 | want := 0 + 13i 31 | got := product.Product(2+3i, 3+2i) 32 | if want != got { 33 | t.Errorf("want %v, got %v", want, got) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /solutions/product/product_test.go: -------------------------------------------------------------------------------- 1 | package product_test 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | 7 | "github.com/bitfield/product" 8 | ) 9 | 10 | func TestProductOfInts2And3Is6(t *testing.T) { 11 | t.Parallel() 12 | want := 6 13 | got := product.Product(2, 3) 14 | if want != got { 15 | t.Errorf("want %d, got %d", want, got) 16 | } 17 | } 18 | 19 | func TestProductOfFloats1Point6And2Point3Is3Point68(t *testing.T) { 20 | t.Parallel() 21 | want := 3.68 22 | got := product.Product(1.6, 2.3) 23 | if math.Abs(want-got) > 0.0001 { 24 | t.Errorf("want %f, got %f", want, got) 25 | } 26 | } 27 | 28 | func TestProductOfComplex2Plus3iAnd3Plus2iIs0Plus13i(t *testing.T) { 29 | t.Parallel() 30 | want := 0 + 13i 31 | got := product.Product(2+3i, 3+2i) 32 | if want != got { 33 | t.Errorf("want %v, got %v", want, got) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /exercises/funcmap/funcmap_test.go: -------------------------------------------------------------------------------- 1 | package funcmap_test 2 | 3 | import ( 4 | "testing" 5 | "unicode" 6 | 7 | "github.com/bitfield/funcmap" 8 | ) 9 | 10 | func TestFuncMap_AppliesDoubleTo2AndReturns4(t *testing.T) { 11 | t.Parallel() 12 | fm := funcmap.FuncMap[int, int]{ 13 | "double": func(i int) int { 14 | return i * 2 15 | }, 16 | "addOne": func(i int) int { 17 | return i + 1 18 | }, 19 | } 20 | want := 4 21 | got := fm.Apply("double", 2) 22 | if want != got { 23 | t.Errorf("double(2): want %d, got %d", want, got) 24 | } 25 | } 26 | 27 | func TestFuncMap_AppliesUpperToUppercaseInput(t *testing.T) { 28 | t.Parallel() 29 | fm := funcmap.FuncMap[rune, bool]{ 30 | "upper": unicode.IsUpper, 31 | "lower": unicode.IsLower, 32 | } 33 | got := fm.Apply("upper", 'A') 34 | if !got { 35 | t.Fatal("upper('A'): want true, got false") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /solutions/funcmap/funcmap_test.go: -------------------------------------------------------------------------------- 1 | package funcmap_test 2 | 3 | import ( 4 | "testing" 5 | "unicode" 6 | 7 | "github.com/bitfield/funcmap" 8 | ) 9 | 10 | func TestFuncMap_AppliesDoubleTo2AndReturns4(t *testing.T) { 11 | t.Parallel() 12 | fm := funcmap.FuncMap[int, int]{ 13 | "double": func(i int) int { 14 | return i * 2 15 | }, 16 | "addOne": func(i int) int { 17 | return i + 1 18 | }, 19 | } 20 | want := 4 21 | got := fm.Apply("double", 2) 22 | if want != got { 23 | t.Errorf("double(2): want %d, got %d", want, got) 24 | } 25 | } 26 | 27 | func TestFuncMap_AppliesUpperToUppercaseInput(t *testing.T) { 28 | t.Parallel() 29 | fm := funcmap.FuncMap[rune, bool]{ 30 | "upper": unicode.IsUpper, 31 | "lower": unicode.IsLower, 32 | } 33 | got := fm.Apply("upper", 'A') 34 | if !got { 35 | t.Fatal("upper('A'): want true, got false") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /exercises/stack/stack_test.go: -------------------------------------------------------------------------------- 1 | package stack_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/stack" 7 | ) 8 | 9 | func TestPushToEmptyStackGivesStackWithLength1(t *testing.T) { 10 | t.Parallel() 11 | s := stack.Stack[int]{} 12 | s.Push(0) 13 | if s.Len() != 1 { 14 | t.Fatalf("want stack length 1, got %d", s.Len()) 15 | } 16 | } 17 | 18 | func TestPushPushPopPopLeavesStackEmpty(t *testing.T) { 19 | t.Parallel() 20 | s := stack.Stack[string]{} 21 | s.Push("a", "b") 22 | if s.Len() != 2 { 23 | t.Fatal("Push didn't add all values to stack") 24 | } 25 | s.Pop() 26 | want := "a" 27 | got, ok := s.Pop() 28 | if !ok { 29 | t.Fatal("Pop returned not ok on non-empty stack") 30 | } 31 | if want != got { 32 | t.Errorf("want %q, got %q", want, got) 33 | } 34 | } 35 | 36 | func TestPopReturnsNotOKOnEmptyStack(t *testing.T) { 37 | t.Parallel() 38 | s := stack.Stack[int]{} 39 | _, ok := s.Pop() 40 | if ok { 41 | t.Fatal("Pop returned ok on empty stack") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /solutions/stack/stack_test.go: -------------------------------------------------------------------------------- 1 | package stack_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/stack" 7 | ) 8 | 9 | func TestPushToEmptyStackGivesStackWithLength1(t *testing.T) { 10 | t.Parallel() 11 | s := stack.Stack[int]{} 12 | s.Push(0) 13 | if s.Len() != 1 { 14 | t.Fatalf("want stack length 1, got %d", s.Len()) 15 | } 16 | } 17 | 18 | func TestPushPushPopPopLeavesStackEmpty(t *testing.T) { 19 | t.Parallel() 20 | s := stack.Stack[string]{} 21 | s.Push("a", "b") 22 | if s.Len() != 2 { 23 | t.Fatal("Push didn't add all values to stack") 24 | } 25 | s.Pop() 26 | want := "a" 27 | got, ok := s.Pop() 28 | if !ok { 29 | t.Fatal("Pop returned not ok on non-empty stack") 30 | } 31 | if want != got { 32 | t.Errorf("want %q, got %q", want, got) 33 | } 34 | } 35 | 36 | func TestPopReturnsNotOKOnEmptyStack(t *testing.T) { 37 | t.Parallel() 38 | s := stack.Stack[int]{} 39 | _, ok := s.Pop() 40 | if ok { 41 | t.Fatal("Pop returned ok on empty stack") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 John Arundel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /exercises/merge/merge_test.go: -------------------------------------------------------------------------------- 1 | package merge_test 2 | 3 | import ( 4 | "maps" 5 | "testing" 6 | 7 | "github.com/bitfield/merge" 8 | ) 9 | 10 | func TestMergeCorrectlyMergesTwoMapsOfIntToBool(t *testing.T) { 11 | t.Parallel() 12 | inputs := []map[int]bool{ 13 | { 14 | 1: false, 15 | 2: false, 16 | 3: false, 17 | }, 18 | { 19 | 3: true, 20 | 5: true, 21 | }, 22 | } 23 | want := map[int]bool{ 24 | 1: false, 25 | 2: false, 26 | 3: true, 27 | 5: true, 28 | } 29 | got := merge.Merge(inputs...) 30 | if !maps.Equal(want, got) { 31 | t.Errorf("want %v, got %v", want, got) 32 | } 33 | } 34 | 35 | func TestMergeCorrectlyMergesThreeMapsOfStringToAny(t *testing.T) { 36 | t.Parallel() 37 | inputs := []map[string]any{ 38 | { 39 | "a": nil, 40 | }, 41 | { 42 | "b": "hello, world", 43 | "c": 0, 44 | }, 45 | { 46 | "a": 6 + 2i, 47 | }, 48 | } 49 | want := map[string]any{ 50 | "a": 6 + 2i, 51 | "b": "hello, world", 52 | "c": 0, 53 | } 54 | got := merge.Merge(inputs...) 55 | if !maps.Equal(want, got) { 56 | t.Errorf("want %v, got %v", want, got) 57 | } 58 | } 59 | 60 | func TestMergeHandlesDerivedTypes(t *testing.T) { 61 | t.Parallel() 62 | type menu map[int]string 63 | m1 := menu{1: "eggs"} 64 | m2 := menu{2: "beans"} 65 | want := menu{ 66 | 1: "eggs", 67 | 2: "beans", 68 | } 69 | got := merge.Merge(m1, m2) 70 | if !maps.Equal(want, got) { 71 | t.Errorf("want %v, got %v", want, got) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /solutions/merge/merge_test.go: -------------------------------------------------------------------------------- 1 | package merge_test 2 | 3 | import ( 4 | "maps" 5 | "testing" 6 | 7 | "github.com/bitfield/merge" 8 | ) 9 | 10 | func TestMergeCorrectlyMergesTwoMapsOfIntToBool(t *testing.T) { 11 | t.Parallel() 12 | inputs := []map[int]bool{ 13 | { 14 | 1: false, 15 | 2: false, 16 | 3: false, 17 | }, 18 | { 19 | 3: true, 20 | 5: true, 21 | }, 22 | } 23 | want := map[int]bool{ 24 | 1: false, 25 | 2: false, 26 | 3: true, 27 | 5: true, 28 | } 29 | got := merge.Merge(inputs...) 30 | if !maps.Equal(want, got) { 31 | t.Errorf("want %v, got %v", want, got) 32 | } 33 | } 34 | 35 | func TestMergeCorrectlyMergesThreeMapsOfStringToAny(t *testing.T) { 36 | t.Parallel() 37 | inputs := []map[string]any{ 38 | { 39 | "a": nil, 40 | }, 41 | { 42 | "b": "hello, world", 43 | "c": 0, 44 | }, 45 | { 46 | "a": 6 + 2i, 47 | }, 48 | } 49 | want := map[string]any{ 50 | "a": 6 + 2i, 51 | "b": "hello, world", 52 | "c": 0, 53 | } 54 | got := merge.Merge(inputs...) 55 | if !maps.Equal(want, got) { 56 | t.Errorf("want %v, got %v", want, got) 57 | } 58 | } 59 | 60 | func TestMergeHandlesDerivedTypes(t *testing.T) { 61 | t.Parallel() 62 | type menu map[int]string 63 | m1 := menu{1: "eggs"} 64 | m2 := menu{2: "beans"} 65 | want := menu{ 66 | 1: "eggs", 67 | 2: "beans", 68 | } 69 | got := merge.Merge(m1, m2) 70 | if !maps.Equal(want, got) { 71 | t.Errorf("want %v, got %v", want, got) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /exercises/compose/compose_test.go: -------------------------------------------------------------------------------- 1 | package compose_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | "unicode/utf8" 7 | 8 | "github.com/bitfield/compose" 9 | ) 10 | 11 | func double(p int) int { 12 | return p * 2 13 | } 14 | 15 | func addOne(p int) int { 16 | return p + 1 17 | } 18 | 19 | func TestComposeAppliesFuncsInReverseOrder(t *testing.T) { 20 | t.Parallel() 21 | want := 4 22 | got := compose.Compose(double, addOne, 1) 23 | if want != got { 24 | t.Errorf("want %d, got %d", want, got) 25 | } 26 | } 27 | 28 | func TestComposeHandlesDifferentFuncSignatures(t *testing.T) { 29 | t.Parallel() 30 | input := "HeLlO, wOrLd" 31 | want := 12 32 | got := compose.Compose(utf8.RuneCountInString, strings.ToUpper, input) 33 | if want != got { 34 | t.Errorf("want %q, got %q", want, got) 35 | } 36 | } 37 | 38 | func first[E any](s []E) E { 39 | return s[0] 40 | } 41 | 42 | func last[E any](s []E) E { 43 | return s[len(s)-1] 44 | } 45 | 46 | func TestComposeAppliesFuncsToSliceInReverseOrder(t *testing.T) { 47 | t.Parallel() 48 | input := [][]int{{1, 2, 3}} 49 | want := 1 50 | got := compose.Compose(first[int], last[[]int], input) 51 | if want != got { 52 | t.Errorf("want %d, got %d", want, got) 53 | } 54 | } 55 | 56 | func TestComposeAppliesFuncsToSliceInReverseOrder2(t *testing.T) { 57 | t.Parallel() 58 | input := [][]int{{1, 2, 3}} 59 | want := 3 60 | got := compose.Compose(last[int], first[[]int], input) 61 | if want != got { 62 | t.Errorf("want %d, got %d", want, got) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /solutions/compose/compose_test.go: -------------------------------------------------------------------------------- 1 | package compose_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | "unicode/utf8" 7 | 8 | "github.com/bitfield/compose" 9 | ) 10 | 11 | func double(p int) int { 12 | return p * 2 13 | } 14 | 15 | func addOne(p int) int { 16 | return p + 1 17 | } 18 | 19 | func TestComposeAppliesFuncsInReverseOrder(t *testing.T) { 20 | t.Parallel() 21 | want := 4 22 | got := compose.Compose(double, addOne, 1) 23 | if want != got { 24 | t.Errorf("want %d, got %d", want, got) 25 | } 26 | } 27 | 28 | func TestComposeHandlesDifferentFuncSignatures(t *testing.T) { 29 | t.Parallel() 30 | input := "HeLlO, wOrLd" 31 | want := 12 32 | got := compose.Compose(utf8.RuneCountInString, strings.ToUpper, input) 33 | if want != got { 34 | t.Errorf("want %q, got %q", want, got) 35 | } 36 | } 37 | 38 | func first[E any](s []E) E { 39 | return s[0] 40 | } 41 | 42 | func last[E any](s []E) E { 43 | return s[len(s)-1] 44 | } 45 | 46 | func TestComposeAppliesFuncsToSliceInReverseOrder(t *testing.T) { 47 | t.Parallel() 48 | input := [][]int{{1, 2, 3}} 49 | want := 1 50 | got := compose.Compose(first[int], last[[]int], input) 51 | if want != got { 52 | t.Errorf("want %d, got %d", want, got) 53 | } 54 | } 55 | 56 | func TestComposeAppliesFuncsToSliceInReverseOrder2(t *testing.T) { 57 | t.Parallel() 58 | input := [][]int{{1, 2, 3}} 59 | want := 3 60 | got := compose.Compose(last[int], first[[]int], input) 61 | if want != got { 62 | t.Errorf("want %d, got %d", want, got) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /exercises/channel/channel_test.go: -------------------------------------------------------------------------------- 1 | package channel_test 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "github.com/bitfield/channel" 8 | ) 9 | 10 | func TestSendFollowedByReceiveGivesOriginalValue(t *testing.T) { 11 | t.Parallel() 12 | c := channel.New[int](1) 13 | want := 99 14 | c.Send(want) 15 | got := c.Receive() 16 | if want != got { 17 | t.Fatalf("want %d received, got %d", want, got) 18 | } 19 | } 20 | 21 | func TestSendsReturns3AfterThreeSendOperations(t *testing.T) { 22 | t.Parallel() 23 | c := channel.New[float64](3) 24 | c.Send(1.0) 25 | c.Send(2.0) 26 | c.Send(3.0) 27 | want := uint64(3) 28 | got := c.Sends() 29 | if want != got { 30 | t.Fatalf("want %d sends, got %d", want, got) 31 | } 32 | } 33 | 34 | func TestReceivesReturns2AfterTwoSendOperations(t *testing.T) { 35 | t.Parallel() 36 | c := channel.New[struct{}](1) 37 | c.Send(struct{}{}) 38 | _ = c.Receive() 39 | c.Send(struct{}{}) 40 | _ = c.Receive() 41 | want := uint64(2) 42 | got := c.Receives() 43 | if want != got { 44 | t.Fatalf("want %d receives, got %d", want, got) 45 | } 46 | } 47 | 48 | func TestChannelHandlesConcurrentSendsAndReceives(t *testing.T) { 49 | t.Parallel() 50 | c := channel.New[string](10) 51 | want := uint64(100) 52 | var wg sync.WaitGroup 53 | wg.Add(1) 54 | go func() { 55 | for i := uint64(0); i < want; i++ { 56 | c.Send("hello") 57 | _ = c.Receives() 58 | } 59 | wg.Done() 60 | }() 61 | for i := uint64(0); i < want; i++ { 62 | _ = c.Receive() 63 | _ = c.Sends() 64 | } 65 | wg.Wait() 66 | got := c.Sends() 67 | if want != got { 68 | t.Errorf("want %d sends, got %d", want, got) 69 | } 70 | got = c.Receives() 71 | if want != got { 72 | t.Errorf("want %d receives, got %d", want, got) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /solutions/channel/channel_test.go: -------------------------------------------------------------------------------- 1 | package channel_test 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "github.com/bitfield/channel" 8 | ) 9 | 10 | func TestSendFollowedByReceiveGivesOriginalValue(t *testing.T) { 11 | t.Parallel() 12 | c := channel.New[int](1) 13 | want := 99 14 | c.Send(want) 15 | got := c.Receive() 16 | if want != got { 17 | t.Fatalf("want %d received, got %d", want, got) 18 | } 19 | } 20 | 21 | func TestSendsReturns3AfterThreeSendOperations(t *testing.T) { 22 | t.Parallel() 23 | c := channel.New[float64](3) 24 | c.Send(1.0) 25 | c.Send(2.0) 26 | c.Send(3.0) 27 | want := uint64(3) 28 | got := c.Sends() 29 | if want != got { 30 | t.Fatalf("want %d sends, got %d", want, got) 31 | } 32 | } 33 | 34 | func TestReceivesReturns2AfterTwoSendOperations(t *testing.T) { 35 | t.Parallel() 36 | c := channel.New[struct{}](1) 37 | c.Send(struct{}{}) 38 | _ = c.Receive() 39 | c.Send(struct{}{}) 40 | _ = c.Receive() 41 | want := uint64(2) 42 | got := c.Receives() 43 | if want != got { 44 | t.Fatalf("want %d receives, got %d", want, got) 45 | } 46 | } 47 | 48 | func TestChannelHandlesConcurrentSendsAndReceives(t *testing.T) { 49 | t.Parallel() 50 | c := channel.New[string](10) 51 | want := uint64(100) 52 | var wg sync.WaitGroup 53 | wg.Add(1) 54 | go func() { 55 | for i := uint64(0); i < want; i++ { 56 | c.Send("hello") 57 | _ = c.Receives() 58 | } 59 | wg.Done() 60 | }() 61 | for i := uint64(0); i < want; i++ { 62 | _ = c.Receive() 63 | _ = c.Sends() 64 | } 65 | wg.Wait() 66 | got := c.Sends() 67 | if want != got { 68 | t.Errorf("want %d sends, got %d", want, got) 69 | } 70 | got = c.Receives() 71 | if want != got { 72 | t.Errorf("want %d receives, got %d", want, got) 73 | } 74 | } 75 | --------------------------------------------------------------------------------