diff --git a/internal/exercises/catalog.yaml b/internal/exercises/catalog.yaml index 94f188b..3d1c174 100644 --- a/internal/exercises/catalog.yaml +++ b/internal/exercises/catalog.yaml @@ -134,6 +134,32 @@ concepts: test_regex: ".*" hints: - Define a custom error type and return it from a function. +- slug: 28_defer + title: Defer + test_regex: ".*" + hints: + - Use `f.close()` with `defer` keyword. +- slug: 29_go_routines + title: Go Routines + test_regex: ".*" + hints: + - Use `go` keyword to execute functions concurrently using go routines. +- slug: 30_channels + title: Channels + test_regex: ".*" + hints: + - Use `make(chan T)` to create a channel and `<-` to send and receive on it. +- slug: 31_mutexes + title: Mutexes + test_regex: ".*" + hints: + - Use `sync.Mutex` to synchronize access to a shared resource. +- slug: 32_sorting + title: Sorting + test_regex: ".*" + hints: + - Use `slice.Sort` for sorting slices. + - slug: 37_xml title: XML Encoding and Decoding test_regex: ".*" diff --git a/internal/exercises/solutions/28_defer/defer.go b/internal/exercises/solutions/28_defer/defer.go new file mode 100644 index 0000000..023f277 --- /dev/null +++ b/internal/exercises/solutions/28_defer/defer.go @@ -0,0 +1,29 @@ +package deferr + +import ( + "fmt" + "os" + "sync" +) + +var ( + fileInstance *os.File + once sync.Once +) + +func CreateFile() *os.File { + once.Do(func() { + f, err := os.CreateTemp("", "example.txt") + if err != nil { + panic(err) + } + fileInstance = f + }) + return fileInstance +} + +func WriteToFile(*os.File) { + f := CreateFile() + defer f.Close() + fmt.Fprintln(f, "data") +} diff --git a/internal/exercises/solutions/29_go_routines/go_routines.go b/internal/exercises/solutions/29_go_routines/go_routines.go new file mode 100644 index 0000000..edc918b --- /dev/null +++ b/internal/exercises/solutions/29_go_routines/go_routines.go @@ -0,0 +1,25 @@ +package go_routines + +import ( + "time" +) + +func sleepForOneHundredMillisecond() { + time.Sleep(100 * time.Millisecond) +} + +func sleepForTwoHundredMilliseconds() { + time.Sleep(200 * time.Millisecond) +} + +func RunConcurrently() { + go sleepForOneHundredMillisecond() + go sleepForTwoHundredMilliseconds() + + // go func() { + // sleepForOneHundredMillisecond() + // }() + // go func() { + // sleepForTwoHundredMilliseconds() + // }() +} diff --git a/internal/exercises/solutions/30_channels/channels.go b/internal/exercises/solutions/30_channels/channels.go new file mode 100644 index 0000000..f1984be --- /dev/null +++ b/internal/exercises/solutions/30_channels/channels.go @@ -0,0 +1,13 @@ +package channels + +func ReadMessage() string { + chat := make(chan string) + go func() { + senderMessage := "Hi!" + chat <- senderMessage + }() + + msg := <-chat + + return msg +} diff --git a/internal/exercises/solutions/31_mutexes/mutexes.go b/internal/exercises/solutions/31_mutexes/mutexes.go new file mode 100644 index 0000000..dc7fab4 --- /dev/null +++ b/internal/exercises/solutions/31_mutexes/mutexes.go @@ -0,0 +1,31 @@ +package mutexes + +import "sync" + +const numWorkers = 10_000 + +func Counting() int { + + count := 0 + done := make(chan bool, numWorkers) + var wg sync.WaitGroup + var mu sync.Mutex + + for i := 1; i <= numWorkers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + mu.Lock() + count += 1 + mu.Unlock() + done <- true + }() + } + + for i := 1; i <= numWorkers; i++ { + <-done + } + + wg.Wait() + return count +} diff --git a/internal/exercises/solutions/32_sorting/sorting.go b/internal/exercises/solutions/32_sorting/sorting.go new file mode 100644 index 0000000..80b35ef --- /dev/null +++ b/internal/exercises/solutions/32_sorting/sorting.go @@ -0,0 +1,18 @@ +package sorting + +import ( + "slices" +) + +var Years = []int{2017, 2003, 2026} +var Pets = []string{"Dog", "Cat", "Parrot"} + +func SortNumbers() []int { + slices.Sort(Years) + return Years +} + +func SortAnimals() []string { + slices.Sort(Pets) + return Pets +} diff --git a/internal/exercises/templates/28_defer/defer.go b/internal/exercises/templates/28_defer/defer.go new file mode 100644 index 0000000..dd76c5b --- /dev/null +++ b/internal/exercises/templates/28_defer/defer.go @@ -0,0 +1,29 @@ +package deferr + +import ( + "fmt" + "os" + "sync" +) + +var ( + fileInstance *os.File + once sync.Once +) + +func CreateFile() *os.File { + once.Do(func() { + f, err := os.CreateTemp("", "example.txt") + if err != nil { + panic(err) + } + fileInstance = f + }) + return fileInstance +} + +func WriteToFile(*os.File) { + f := CreateFile() + // TODO: Add your code here + fmt.Fprintln(f, "data") +} diff --git a/internal/exercises/templates/28_defer/defer_test.go b/internal/exercises/templates/28_defer/defer_test.go new file mode 100644 index 0000000..9e55418 --- /dev/null +++ b/internal/exercises/templates/28_defer/defer_test.go @@ -0,0 +1,17 @@ +package deferr + +import ( + "fmt" + "testing" +) + +func TestClosingFileAfterWriting(t *testing.T) { + f := CreateFile() + WriteToFile(f) + + // trying to write to the file after closing + _, err := fmt.Fprintln(f, "data") + if err == nil { + t.Fatal("File wasn't closed!") + } +} diff --git a/internal/exercises/templates/29_go_routines/go_routines.go b/internal/exercises/templates/29_go_routines/go_routines.go new file mode 100644 index 0000000..1e2a24e --- /dev/null +++ b/internal/exercises/templates/29_go_routines/go_routines.go @@ -0,0 +1,19 @@ +package go_routines + +import ( + "time" +) + +func sleepForOneHundredMillisecond() { + time.Sleep(100 * time.Millisecond) +} + +func sleepForTwoHundredMilliseconds() { + time.Sleep(200 * time.Millisecond) +} + +func RunConcurrently() { + // TODO: update following code + sleepForOneHundredMillisecond() + sleepForTwoHundredMilliseconds() +} diff --git a/internal/exercises/templates/29_go_routines/go_routines_test.go b/internal/exercises/templates/29_go_routines/go_routines_test.go new file mode 100644 index 0000000..a1830b4 --- /dev/null +++ b/internal/exercises/templates/29_go_routines/go_routines_test.go @@ -0,0 +1,24 @@ +package go_routines + +import ( + "testing" + "time" +) + +func TestGoRoutines(t *testing.T) { + start := time.Now() + RunConcurrently() + elapsed := time.Since(start) + + // according to benchmark it is about ~2 microseconds + // x2 for more breathing room, so 4 + if elapsed.Microseconds() >= 4 { + t.Fatal("Go routines was not used!") + } +} + +func BenchmarkRunConcurrently(b *testing.B) { + for i := 0; i < b.N; i++ { + RunConcurrently() + } +} diff --git a/internal/exercises/templates/30_channels/channels.go b/internal/exercises/templates/30_channels/channels.go new file mode 100644 index 0000000..419dde5 --- /dev/null +++ b/internal/exercises/templates/30_channels/channels.go @@ -0,0 +1,15 @@ +package channels + +import "fmt" + +func ReadMessage() string { + // TODO: update following code + // to receive senderMessage in main goroutine + // and return it from ReadMessage function + go func() { + senderMessage := "Hi!" + fmt.Println(senderMessage) + }() + + return "" +} diff --git a/internal/exercises/templates/30_channels/channels_test.go b/internal/exercises/templates/30_channels/channels_test.go new file mode 100644 index 0000000..17fd749 --- /dev/null +++ b/internal/exercises/templates/30_channels/channels_test.go @@ -0,0 +1,12 @@ +package channels + +import "testing" + +func TestReadMessage(t *testing.T) { + got := ReadMessage() + want := "Hi!" + + if got != want { + t.Errorf("Expected %s, got %s", want, got) + } +} diff --git a/internal/exercises/templates/31_mutexes/mutexes.go b/internal/exercises/templates/31_mutexes/mutexes.go new file mode 100644 index 0000000..f2b2f45 --- /dev/null +++ b/internal/exercises/templates/31_mutexes/mutexes.go @@ -0,0 +1,30 @@ +package mutexes + +import "sync" + +const numWorkers = 10_000 + +func Counting() int { + + // TODO: update following code to avoid race conditions + // using sync.Mutex + count := 0 + done := make(chan bool, numWorkers) + var wg sync.WaitGroup + + for i := 1; i <= numWorkers; i++ { + wg.Add(1) + go func() { + defer wg.Done() + count += 1 + done <- true + }() + } + + for i := 1; i <= numWorkers; i++ { + <-done + } + + wg.Wait() + return count +} diff --git a/internal/exercises/templates/31_mutexes/mutexes_test.go b/internal/exercises/templates/31_mutexes/mutexes_test.go new file mode 100644 index 0000000..424e1e5 --- /dev/null +++ b/internal/exercises/templates/31_mutexes/mutexes_test.go @@ -0,0 +1,12 @@ +package mutexes + +import "testing" + +func TestCounter(t *testing.T) { + got := Counting() + want := numWorkers + + if got != want { + t.Fatalf("Expected %d, got %d", want, got) + } +} diff --git a/internal/exercises/templates/32_sorting/sorting.go b/internal/exercises/templates/32_sorting/sorting.go new file mode 100644 index 0000000..4b3151d --- /dev/null +++ b/internal/exercises/templates/32_sorting/sorting.go @@ -0,0 +1,14 @@ +package sorting + +var Years = []int{2017, 2003, 2026} +var Pets = []string{"Dog", "Cat", "Parrot"} + +func SortNumbers() []int { + // TODO: sort Years + return Years +} + +func SortAnimals() []string { + // TODO: sort Pets + return Pets +} diff --git a/internal/exercises/templates/32_sorting/sorting_test.go b/internal/exercises/templates/32_sorting/sorting_test.go new file mode 100644 index 0000000..2faee77 --- /dev/null +++ b/internal/exercises/templates/32_sorting/sorting_test.go @@ -0,0 +1,22 @@ +package sorting + +import ( + "slices" + "testing" +) + +func TestSorting(t *testing.T) { + t.Run("Sorting numbers", func(t *testing.T) { + SortNumbers() + if !slices.IsSorted(Years) { + t.Fatal("Strings are not sorted!") + } + }) + + t.Run("Sorting strings", func(t *testing.T) { + SortAnimals() + if !slices.IsSorted(Pets) { + t.Fatal("Strings are not sorted!") + } + }) +}