From ef8f0e3831c3b888c390e0734fde4c31ee8e386d Mon Sep 17 00:00:00 2001
From: Eli Bendersky
Date: Thu, 5 Sep 2019 10:50:09 -0700
Subject: [PATCH 001/283] Deterministic example for atomics.
Fixes #265
---
examples/atomic-counters/atomic-counters.go | 39 +++++-----
examples/atomic-counters/atomic-counters.hash | 4 +-
examples/atomic-counters/atomic-counters.sh | 10 ++-
public/atomic-counters | 71 ++++++++++---------
4 files changed, 63 insertions(+), 61 deletions(-)
diff --git a/examples/atomic-counters/atomic-counters.go b/examples/atomic-counters/atomic-counters.go
index 9df327686..2a0901b6b 100644
--- a/examples/atomic-counters/atomic-counters.go
+++ b/examples/atomic-counters/atomic-counters.go
@@ -7,9 +7,11 @@
package main
-import "fmt"
-import "time"
-import "sync/atomic"
+import (
+ "fmt"
+ "sync"
+ "sync/atomic"
+)
func main() {
@@ -17,33 +19,28 @@ func main() {
// (always-positive) counter.
var ops uint64
- // To simulate concurrent updates, we'll start 50
- // goroutines that each increment the counter about
- // once a millisecond.
+ // A WaitGroup will help us wait for all goroutines
+ // to finish their work.
+ var wg sync.WaitGroup
+
+ // We'll start 50 goroutines that each increment the
+ // counter exactly 1000 times.
for i := 0; i < 50; i++ {
+ wg.Add(1)
+
go func() {
- for {
+ for c := 0; c < 1000; c++ {
// To atomically increment the counter we
// use `AddUint64`, giving it the memory
// address of our `ops` counter with the
// `&` syntax.
atomic.AddUint64(&ops, 1)
-
- // Wait a bit between increments.
- time.Sleep(time.Millisecond)
}
+ wg.Done()
}()
}
- // Wait a second to allow some ops to accumulate.
- time.Sleep(time.Second)
-
- // In order to safely use the counter while it's still
- // being updated by other goroutines, we extract a
- // copy of the current value into `opsFinal` via
- // `LoadUint64`. As above we need to give this
- // function the memory address `&ops` from which to
- // fetch the value.
- opsFinal := atomic.LoadUint64(&ops)
- fmt.Println("ops:", opsFinal)
+ // Wait until all the goroutines are done.
+ wg.Wait()
+ fmt.Println("ops:", ops)
}
diff --git a/examples/atomic-counters/atomic-counters.hash b/examples/atomic-counters/atomic-counters.hash
index c1b531a0c..e35f8f246 100644
--- a/examples/atomic-counters/atomic-counters.hash
+++ b/examples/atomic-counters/atomic-counters.hash
@@ -1,2 +1,2 @@
-a4190094ea0405b5f2733101beb97939a1d43aee
-KDr9EMMPMgi
+103c9b7d036e3a5c14dc481755b78b10dc9f894e
+GRkVf6J1--B
diff --git a/examples/atomic-counters/atomic-counters.sh b/examples/atomic-counters/atomic-counters.sh
index e4523f907..1680a100c 100644
--- a/examples/atomic-counters/atomic-counters.sh
+++ b/examples/atomic-counters/atomic-counters.sh
@@ -1,7 +1,11 @@
-# Running the program shows that we executed about
-# 40,000 operations.
+# We expect to get exactly 50,000 operations. Had we
+# used the non-atomic `ops++` to increment the counter,
+# we'd likely get a different number, changing between
+# runs, because the goroutines would interfere with
+# each other. Moreover, we'd get data race failures
+# when running with the `-race` flag.
$ go run atomic-counters.go
-ops: 41419
+ops: 50000
# Next we'll look at mutexes, another tool for managing
# state.
diff --git a/public/atomic-counters b/public/atomic-counters
index b2e006e0c..6c9d94cbf 100644
--- a/public/atomic-counters
+++ b/public/atomic-counters
@@ -46,7 +46,7 @@ counters accessed by multiple goroutines.
-
+
packagemain
@@ -59,9 +59,11 @@ counters accessed by multiple goroutines.
-
import"fmt"
-import"time"
-import"sync/atomic"
+
import(
+ "fmt"
+ "sync"
+ "sync/atomic"
+)
@@ -95,16 +97,13 @@ counters accessed by multiple goroutines.
-
To simulate concurrent updates, we’ll start 50
-goroutines that each increment the counter about
-once a millisecond.
+
A WaitGroup will help us wait for all goroutines
+to finish their work.
-
fori:=0;i<50;i++{
- gofunc(){
- for{
+
varwgsync.WaitGroup
@@ -112,15 +111,14 @@ once a millisecond.
-
To atomically increment the counter we
-use AddUint64, giving it the memory
-address of our ops counter with the
-& syntax.
+
We’ll start 50 goroutines that each increment the
+counter exactly 1000 times.
-
atomic.AddUint64(&ops,1)
+
fori:=0;i<50;i++{
+ wg.Add(1)
@@ -128,15 +126,16 @@ address of our ops counter with the
-
Wait a bit between increments.
+
To atomically increment the counter we
+use AddUint64, giving it the memory
+address of our ops counter with the
+& syntax.
-
time.Sleep(time.Millisecond)
- }
- }()
- }
+
gofunc(){
+ forc:=0;c<1000;c++{
@@ -144,12 +143,15 @@ address of our ops counter with the
@@ -157,18 +159,13 @@ address of our ops counter with the
-
In order to safely use the counter while it’s still
-being updated by other goroutines, we extract a
-copy of the current value into opsFinal via
-LoadUint64. As above we need to give this
-function the memory address &ops from which to
-fetch the value.
Running the program shows that we executed about
-40,000 operations.
+
We expect to get exactly 50,000 operations. Had we
+used the non-atomic ops++ to increment the counter,
+we’d likely get a different number, changing between
+runs, because the goroutines would interfere with
+each other. Moreover, we’d get data race failures
+when running with the -race flag.
$ go run atomic-counters.go
-ops: 41419
+ops: 50000
Unit testing is an important part of writing
+principled Go programs. The testing package
+provides the tools we need to write unit tests
+and the go test command runs tests.
+
+
+
+
+
+
+
+
+
+
+
For the sake of demonstration, this code is in package
+main, but it could be any package. Testing code
+typically lives in the same package as the code it tests.
+
+
+
+
+
packagemain
+
+
+
+
+
+
+
+
+
+
+
+
import(
+ "fmt"
+ "testing"
+)
+
+
+
+
+
+
+
+
We’ll be testing this simple implementation of an
+integer minimum. Typically, the code we’re testing
+would be in a source file named something like
+intutils.go, and the test file for it would then
+be named intutils_test.go.
Writing tests can be repetitive, so it’s idiomatic to
+use a table-driven style, where test inputs and
+expected outputs are listed in a table and a single loop
+walks over them and performs the test logic.
From b70c15adaa0983b3d07312f425b8c6d4df71457f Mon Sep 17 00:00:00 2001
From: Eli Bendersky
Date: Thu, 5 Sep 2019 13:26:08 -0700
Subject: [PATCH 002/283] Clarify reading op non-atomically
---
examples/atomic-counters/atomic-counters.go | 6 +++++
examples/atomic-counters/atomic-counters.hash | 4 ++--
public/atomic-counters | 24 +++++++++++++++----
3 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/examples/atomic-counters/atomic-counters.go b/examples/atomic-counters/atomic-counters.go
index 2a0901b6b..046a347f3 100644
--- a/examples/atomic-counters/atomic-counters.go
+++ b/examples/atomic-counters/atomic-counters.go
@@ -42,5 +42,11 @@ func main() {
// Wait until all the goroutines are done.
wg.Wait()
+
+ // It's safe to access `ops` now because we know
+ // no other goroutine is writing to it. Reading
+ // atomics safely while they are being updated is
+ // also possible, using functions like
+ // `atomic.LoadUint64`.
fmt.Println("ops:", ops)
}
diff --git a/examples/atomic-counters/atomic-counters.hash b/examples/atomic-counters/atomic-counters.hash
index e35f8f246..989ed197c 100644
--- a/examples/atomic-counters/atomic-counters.hash
+++ b/examples/atomic-counters/atomic-counters.hash
@@ -1,2 +1,2 @@
-103c9b7d036e3a5c14dc481755b78b10dc9f894e
-GRkVf6J1--B
+8ebec0be3b167021c96b8b497d0e8c0a2ea99385
+F2pJfduyQiA
diff --git a/public/atomic-counters b/public/atomic-counters
index 6c9d94cbf..9627c9703 100644
--- a/public/atomic-counters
+++ b/public/atomic-counters
@@ -46,7 +46,7 @@ counters accessed by multiple goroutines.
-
+
packagemain
@@ -162,10 +162,26 @@ address of our ops counter with the
Wait until all the goroutines are done.
-
+
wg.Wait()
- fmt.Println("ops:",ops)
+
+
+
+
+
+
+
+
It’s safe to access ops now because we know
+no other goroutine is writing to it. Reading
+atomics safely while they are being updated is
+also possible, using functions like
+atomic.LoadUint64.
+
+
+
+
+
fmt.Println("ops:",ops)}
@@ -220,7 +236,7 @@ state.
From a34c967eaf2e1bda05eadc5c1af59fe8060ca0dc Mon Sep 17 00:00:00 2001
From: Eli Bendersky
Date: Thu, 5 Sep 2019 13:30:16 -0700
Subject: [PATCH 003/283] Clarify use of buffered channel in the timeouts
example.
The buffered channel prevents goroutine leaks in case the
channel doesn't end up being read (as indeed happens to c1).
Updates #207
---
examples/timeouts/timeouts.go | 5 ++++-
examples/timeouts/timeouts.hash | 4 ++--
public/timeouts | 7 +++++--
3 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/examples/timeouts/timeouts.go b/examples/timeouts/timeouts.go
index ccc9ecbc9..ce1d1cd06 100644
--- a/examples/timeouts/timeouts.go
+++ b/examples/timeouts/timeouts.go
@@ -12,7 +12,10 @@ func main() {
// For our example, suppose we're executing an external
// call that returns its result on a channel `c1`
- // after 2s.
+ // after 2s. Note that the channel is buffered, so the
+ // send in the goroutine is nonblocking. This is a
+ // common pattern to prevent goroutine leaks in case the
+ // channel is never read.
c1 := make(chan string, 1)
go func() {
time.Sleep(2 * time.Second)
diff --git a/examples/timeouts/timeouts.hash b/examples/timeouts/timeouts.hash
index 64d1c5ad7..f510bfa0b 100644
--- a/examples/timeouts/timeouts.hash
+++ b/examples/timeouts/timeouts.hash
@@ -1,2 +1,2 @@
-93343e1aacb14f818c87732914c29ba57afab245
-MgcfA-xpJO9
+b1e8d0efbabd0c52271a85fad5ad58dcd1c7c476
+gyY_qDsRVUe
diff --git a/public/timeouts b/public/timeouts
index c00732d59..532dbc02b 100644
--- a/public/timeouts
+++ b/public/timeouts
@@ -44,7 +44,7 @@ elegant thanks to channels and select.
-
+
packagemain
@@ -80,7 +80,10 @@ elegant thanks to channels and select.
For our example, suppose we’re executing an external
call that returns its result on a channel c1
-after 2s.
+after 2s. Note that the channel is buffered, so the
+send in the goroutine is nonblocking. This is a
+common pattern to prevent goroutine leaks in case the
+channel is never read.
From 9889d7f702a06b0d0b58d37279aa2976c0ca3993 Mon Sep 17 00:00:00 2001
From: Mark McGranaghan
Date: Fri, 6 Sep 2019 07:37:49 -0700
Subject: [PATCH 004/283] This transition paragraph no longer makes sense
---
examples/timeouts/timeouts.sh | 6 ------
public/timeouts | 19 ++-----------------
2 files changed, 2 insertions(+), 23 deletions(-)
diff --git a/examples/timeouts/timeouts.sh b/examples/timeouts/timeouts.sh
index 8a9dfb4b9..3c4344fe5 100644
--- a/examples/timeouts/timeouts.sh
+++ b/examples/timeouts/timeouts.sh
@@ -3,9 +3,3 @@
$ go run timeouts.go
timeout 1
result 2
-
-# Using this `select` timeout pattern requires
-# communicating results over channels. This is a good
-# idea in general because other important Go features are
-# based on channels and `select`. We'll look at two
-# examples of this next: timers and tickers.
diff --git a/public/timeouts b/public/timeouts
index 532dbc02b..814298529 100644
--- a/public/timeouts
+++ b/public/timeouts
@@ -156,7 +156,7 @@ from c2 will succeed and we’ll print the result.
out and the second succeeding.
-
+
$ go run timeouts.go
timeout 1
@@ -166,21 +166,6 @@ out and the second succeeding.
-
-
-
Using this select timeout pattern requires
-communicating results over channels. This is a good
-idea in general because other important Go features are
-based on channels and select. We’ll look at two
-examples of this next: timers and tickers.
-
-
-
-
-
-
-
-
@@ -194,7 +179,7 @@ examples of this next: timers and tickers.
@@ -189,7 +191,7 @@ but they both decode to the original string as desired.
diff --git a/public/channel-synchronization b/public/channel-synchronization
index 16de09231..7a0d3a384 100644
--- a/public/channel-synchronization
+++ b/public/channel-synchronization
@@ -45,7 +45,7 @@ you may prefer to use a WaitGroup.
-
+
packagemain
@@ -58,8 +58,10 @@ you may prefer to use a WaitGroup.
diff --git a/public/command-line-arguments b/public/command-line-arguments
index 1cb6ffd04..a2aa9ff3a 100644
--- a/public/command-line-arguments
+++ b/public/command-line-arguments
@@ -44,7 +44,7 @@ For example, go run hello.go uses run and
-
+
packagemain
@@ -57,8 +57,10 @@ For example, go run hello.go uses run and
@@ -60,8 +60,10 @@ implement our example command-line program.
-
import"flag"
-import"fmt"
+
import(
+ "flag"
+ "fmt"
+)
@@ -308,7 +310,7 @@ and show the help text again.
diff --git a/public/constants b/public/constants
index 17379bfa9..31e2c7b22 100644
--- a/public/constants
+++ b/public/constants
@@ -42,7 +42,7 @@ and numeric values.
-
+
packagemain
@@ -55,8 +55,10 @@ and numeric values.
-
import"fmt"
-import"math"
+
import(
+ "fmt"
+ "math"
+)
@@ -181,7 +183,7 @@ assignment or function call. For example, here
diff --git a/public/defer b/public/defer
index 6c62c6433..f99d59d8f 100644
--- a/public/defer
+++ b/public/defer
@@ -44,7 +44,7 @@ purposes of cleanup. defer is often used where e.g.
-
+
packagemain
@@ -57,8 +57,10 @@ purposes of cleanup. defer is often used where e.g.
-
import"fmt"
-import"os"
+
import(
+ "fmt"
+ "os"
+)
@@ -210,7 +212,7 @@ after being written.
diff --git a/public/environment-variables b/public/environment-variables
index c452feb27..25504ae69 100644
--- a/public/environment-variables
+++ b/public/environment-variables
@@ -44,7 +44,7 @@ Let’s look at how to set, get, and list environment variables.
-
+
packagemain
@@ -57,9 +57,11 @@ Let’s look at how to set, get, and list environment variables.
-
import"os"
-import"strings"
-import"fmt"
+
import(
+ "fmt"
+ "os"
+ "strings"
+)
@@ -184,7 +186,7 @@ program picks that value up.
diff --git a/public/epoch b/public/epoch
index 644ef794a..76a9b201f 100644
--- a/public/epoch
+++ b/public/epoch
@@ -44,7 +44,7 @@ Here’s how to do it in Go.
@@ -234,7 +236,7 @@ these structs as arguments to measure.
diff --git a/public/json b/public/json
index a0d639536..e2327ec0d 100644
--- a/public/json
+++ b/public/json
@@ -43,7 +43,7 @@ data types.
-
+
packagemain
@@ -56,9 +56,11 @@ data types.
-
import"encoding/json"
-import"fmt"
-import"os"
+
import(
+ "encoding/json"
+ "fmt"
+ "os"
+)
@@ -414,7 +416,7 @@ for more.
diff --git a/public/number-parsing b/public/number-parsing
index db31e0d18..269da8526 100644
--- a/public/number-parsing
+++ b/public/number-parsing
@@ -42,7 +42,7 @@ in many programs; here’s how to do it in Go.
@@ -259,7 +261,7 @@ then serve the remaining 2 with ~200ms delays each.
diff --git a/public/regular-expressions b/public/regular-expressions
index d63163e8c..76529e33c 100644
--- a/public/regular-expressions
+++ b/public/regular-expressions
@@ -43,7 +43,7 @@ in Go.
-
+
packagemain
@@ -56,9 +56,11 @@ in Go.
-
import"bytes"
-import"fmt"
-import"regexp"
+
import(
+ "bytes"
+ "fmt"
+ "regexp"
+)
@@ -340,7 +342,7 @@ the regexp package docs
diff --git a/public/select b/public/select
index 8be3cf48d..116ac3bac 100644
--- a/public/select
+++ b/public/select
@@ -43,7 +43,7 @@ select is a powerful feature of Go.
-
+
packagemain
@@ -56,8 +56,10 @@ select is a powerful feature of Go.
-
import"time"
-import"fmt"
+
import(
+ "fmt"
+ "time"
+)
@@ -181,7 +183,7 @@ concurrently.
diff --git a/public/sha1-hashes b/public/sha1-hashes
index b072bf7df..4db037545 100644
--- a/public/sha1-hashes
+++ b/public/sha1-hashes
@@ -46,7 +46,7 @@ compute SHA1 hashes in Go.
-
+
packagemain
@@ -61,8 +61,10 @@ compute SHA1 hashes in Go.
-
import"crypto/sha1"
-import"fmt"
+
import(
+ "crypto/sha1"
+ "fmt"
+)
@@ -201,7 +203,7 @@ you should carefully research
diff --git a/public/signals b/public/signals
index 28c2b5a3b..b0109b583 100644
--- a/public/signals
+++ b/public/signals
@@ -46,7 +46,7 @@ Here’s how to handle signals in Go with channels.
-
+
packagemain
@@ -59,10 +59,12 @@ Here’s how to handle signals in Go with channels.
@@ -158,7 +160,7 @@ slices and true as the result of our AreSorted test.
diff --git a/public/sorting-by-functions b/public/sorting-by-functions
index 904dbf5ef..f86a18849 100644
--- a/public/sorting-by-functions
+++ b/public/sorting-by-functions
@@ -45,7 +45,7 @@ in Go.
-
+
packagemain
@@ -58,8 +58,10 @@ in Go.
-
import"sort"
-import"fmt"
+
import(
+ "fmt"
+ "sort"
+)
@@ -175,7 +177,7 @@ functions.
diff --git a/public/spawning-processes b/public/spawning-processes
index 634bd6910..da8dfc5ce 100644
--- a/public/spawning-processes
+++ b/public/spawning-processes
@@ -46,7 +46,7 @@ of spawning processes from Go.
-
+
packagemain
@@ -59,9 +59,11 @@ of spawning processes from Go.
-
import"fmt"
-import"io/ioutil"
-import"os/exec"
+
import(
+ "fmt"
+ "io/ioutil"
+ "os/exec"
+)
@@ -257,7 +259,7 @@ as if we had run them directly from the command-line.
diff --git a/public/string-formatting b/public/string-formatting
index 438e9a5d4..6616ed040 100644
--- a/public/string-formatting
+++ b/public/string-formatting
@@ -43,7 +43,7 @@ common string formatting tasks.
-
+
packagemain
@@ -56,8 +56,10 @@ common string formatting tasks.
-
import"fmt"
-import"os"
+
import(
+ "fmt"
+ "os"
+)
@@ -455,7 +457,7 @@ and returns a string without printing it anywhere.
diff --git a/public/string-functions b/public/string-functions
index 05a6e1ce6..093c467b3 100644
--- a/public/string-functions
+++ b/public/string-functions
@@ -43,7 +43,7 @@ to give you a sense of the package.
-
+
packagemain
@@ -56,8 +56,10 @@ to give you a sense of the package.
@@ -201,7 +203,7 @@ type corresponding to its clause.
diff --git a/public/tickers b/public/tickers
index 8bb9280bb..dd2bb2871 100644
--- a/public/tickers
+++ b/public/tickers
@@ -45,7 +45,7 @@ periodically until we stop it.
-
+
packagemain
@@ -58,8 +58,10 @@ periodically until we stop it.
-
import"time"
-import"fmt"
+
import(
+ "fmt"
+ "time"
+)
@@ -169,7 +171,7 @@ before we stop it.
diff --git a/public/time b/public/time
index 49421deaa..12137d802 100644
--- a/public/time
+++ b/public/time
@@ -42,7 +42,7 @@ here are some examples.
diff --git a/public/url-parsing b/public/url-parsing
index 668af2f73..0cd07f42b 100644
--- a/public/url-parsing
+++ b/public/url-parsing
@@ -42,7 +42,7 @@ Here’s how to parse URLs in Go.
-
+
packagemain
@@ -55,9 +55,11 @@ Here’s how to parse URLs in Go.
-
import"fmt"
-import"net"
-import"net/url"
+
import(
+ "fmt"
+ "net"
+ "net/url"
+)
@@ -233,7 +235,7 @@ pieces that we extracted.
diff --git a/public/worker-pools b/public/worker-pools
index 0ddf1b3fb..70a6a0b09 100644
--- a/public/worker-pools
+++ b/public/worker-pools
@@ -42,7 +42,7 @@ a worker pool using goroutines and channels.
-
+
packagemain
@@ -55,8 +55,10 @@ a worker pool using goroutines and channels.
-
import"fmt"
-import"time"
+
import(
+ "fmt"
+ "time"
+)
@@ -221,7 +223,7 @@ there are 3 workers operating concurrently.
From 8d0a0e06d2d95bf2d37184a0133edc5f0900b10a Mon Sep 17 00:00:00 2001
From: Mark McGranaghan
Date: Tue, 10 Sep 2019 14:50:34 -0400
Subject: [PATCH 006/283] Add Czech translation, closes #275
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 21686c48b..6d839900c 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,7 @@ The Go Gopher is copyright [Renée French](http://reneefrench.blogspot.com/) and
Contributor translations of the Go by Example site are available in:
* [Chinese](https://gobyexample.xgwang.me/) by [xg-wang](https://github.com/xg-wang/gobyexample)
+* [Czech](http://gobyexamples.sweb.cz/) by [martinkunc](https://github.com/martinkunc/gobyexample-cz)
* [French](http://le-go-par-l-exemple.keiruaprod.fr) by [keirua](https://github.com/keirua/gobyexample)
* [Italian](http://gobyexample.it) by the [Go Italian community](https://github.com/golangit/gobyexample-it)
* [Japanese](http://spinute.org/go-by-example) by [spinute](https://github.com/spinute)
From e5af060488a94b44fca5ec174557d5c6f000c8eb Mon Sep 17 00:00:00 2001
From: Eli Bendersky
Date: Thu, 12 Sep 2019 09:29:38 -0700
Subject: [PATCH 007/283] Add basic test in main_test and check it passes with
'go test'
---
examples/testing/main_test.go | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 examples/testing/main_test.go
diff --git a/examples/testing/main_test.go b/examples/testing/main_test.go
new file mode 100644
index 000000000..e119fa007
--- /dev/null
+++ b/examples/testing/main_test.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+ "testing"
+)
+
+func IntMin(a, b int) int {
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}
+
+func TestIntMinBasic(t *testing.T) {
+ result := IntMin(2, -2)
+ if result != -2 {
+ t.Errorf("IntMin(2, -2) = %d; want -2", result)
+ }
+}
From 507f2b6145a1f3852c6c027f33c1f881522e95fb Mon Sep 17 00:00:00 2001
From: Eli Bendersky
Date: Thu, 12 Sep 2019 11:23:01 -0700
Subject: [PATCH 008/283] Added table-driven tests and comments
---
examples/testing/main_test.go | 48 +++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/examples/testing/main_test.go b/examples/testing/main_test.go
index e119fa007..2fc5eccb4 100644
--- a/examples/testing/main_test.go
+++ b/examples/testing/main_test.go
@@ -1,9 +1,23 @@
+// Unit testing is an important part of writing
+// principled Go programs. The `testing` package
+// provides the tools we need to write unit tests
+// and the `go test` command runs tests.
+
+// For the same of demonstration, this code is in package
+// `main`, but it could be any package. Testing code
+// typically lives in the same package as the code it tests.
package main
import (
+ "fmt"
"testing"
)
+// We'll be testing this simple implementation of an
+// integer minimum. Typically, the code we're testing
+// would be in a source file named something like
+// `intutils.go`, and the test file for it would then
+// be named `intutils_test.go`.
func IntMin(a, b int) int {
if a < b {
return a
@@ -12,9 +26,43 @@ func IntMin(a, b int) int {
}
}
+// A test is created by writing a function with a name
+// beginning with `Test`.
func TestIntMinBasic(t *testing.T) {
result := IntMin(2, -2)
if result != -2 {
+ // `t.Error*` will report test failures but continue
+ // executing the test. `t.Fail*` will report test
+ // failures and stop the test immediately.
t.Errorf("IntMin(2, -2) = %d; want -2", result)
}
}
+
+// Writing tests can be repetitive, so it's idiomatic to
+// use a *table-driven style*, where test inputs and
+// expected outputs are listed in a table and a single loop
+// walks over them and performs the test logic.
+func TestIntMinTableDriven(t *testing.T) {
+ var tests = []struct {
+ a, b int
+ expected int
+ }{
+ {0, 1, 0},
+ {1, 0, 0},
+ {2, -2, -2},
+ {0, -1, -1},
+ {-1, 0, -1},
+ }
+
+ for _, tt := range tests {
+ // t.Run enables running "subtests", one for each
+ // table entry. These will be reported separately
+ // when executing `go test -v`.
+ t.Run(fmt.Sprintf("%d,%d", tt.a, tt.b), func(t *testing.T) {
+ result := IntMin(tt.a, tt.b)
+ if result != tt.expected {
+ t.Errorf("got %d, want %d", result, tt.expected)
+ }
+ })
+ }
+}
From be9b84288c3ceabe6d00a73ae6c01e7896b5976d Mon Sep 17 00:00:00 2001
From: Eli Bendersky
Date: Thu, 12 Sep 2019 11:30:07 -0700
Subject: [PATCH 009/283] Add a new example: testing
---
examples.txt | 1 +
examples/testing/main_test.go | 23 +--
examples/testing/main_test.sh | 18 ++
examples/testing/testing.hash | 2 +
public/command-line-arguments | 2 +-
public/index.html | 2 +
public/temporary-files-and-directories | 4 +-
public/testing | 239 +++++++++++++++++++++++++
8 files changed, 277 insertions(+), 14 deletions(-)
create mode 100644 examples/testing/main_test.sh
create mode 100644 examples/testing/testing.hash
create mode 100644 public/testing
diff --git a/examples.txt b/examples.txt
index 880802faf..dd3c1579b 100644
--- a/examples.txt
+++ b/examples.txt
@@ -61,6 +61,7 @@ Line Filters
File Paths
Directories
Temporary Files and Directories
+Testing
Command-Line Arguments
Command-Line Flags
Command-Line Subcommands
diff --git a/examples/testing/main_test.go b/examples/testing/main_test.go
index 2fc5eccb4..3bcdd6ec0 100644
--- a/examples/testing/main_test.go
+++ b/examples/testing/main_test.go
@@ -3,7 +3,7 @@
// provides the tools we need to write unit tests
// and the `go test` command runs tests.
-// For the same of demonstration, this code is in package
+// For the sake of demonstration, this code is in package
// `main`, but it could be any package. Testing code
// typically lives in the same package as the code it tests.
package main
@@ -29,12 +29,12 @@ func IntMin(a, b int) int {
// A test is created by writing a function with a name
// beginning with `Test`.
func TestIntMinBasic(t *testing.T) {
- result := IntMin(2, -2)
- if result != -2 {
+ ans := IntMin(2, -2)
+ if ans != -2 {
// `t.Error*` will report test failures but continue
// executing the test. `t.Fail*` will report test
// failures and stop the test immediately.
- t.Errorf("IntMin(2, -2) = %d; want -2", result)
+ t.Errorf("IntMin(2, -2) = %d; want -2", ans)
}
}
@@ -44,8 +44,8 @@ func TestIntMinBasic(t *testing.T) {
// walks over them and performs the test logic.
func TestIntMinTableDriven(t *testing.T) {
var tests = []struct {
- a, b int
- expected int
+ a, b int
+ want int
}{
{0, 1, 0},
{1, 0, 0},
@@ -56,12 +56,13 @@ func TestIntMinTableDriven(t *testing.T) {
for _, tt := range tests {
// t.Run enables running "subtests", one for each
- // table entry. These will be reported separately
+ // table entry. These are shown separately
// when executing `go test -v`.
- t.Run(fmt.Sprintf("%d,%d", tt.a, tt.b), func(t *testing.T) {
- result := IntMin(tt.a, tt.b)
- if result != tt.expected {
- t.Errorf("got %d, want %d", result, tt.expected)
+ testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
+ t.Run(testname, func(t *testing.T) {
+ ans := IntMin(tt.a, tt.b)
+ if ans != tt.want {
+ t.Errorf("got %d, want %d", ans, tt.want)
}
})
}
diff --git a/examples/testing/main_test.sh b/examples/testing/main_test.sh
new file mode 100644
index 000000000..58e0615e5
--- /dev/null
+++ b/examples/testing/main_test.sh
@@ -0,0 +1,18 @@
+# Run all tests in the current project in verbose mode.
+$ go test -v
+== RUN TestIntMinBasic
+--- PASS: TestIntMinBasic (0.00s)
+=== RUN TestIntMinTableDriven
+=== RUN TestIntMinTableDriven/0,1
+=== RUN TestIntMinTableDriven/1,0
+=== RUN TestIntMinTableDriven/2,-2
+=== RUN TestIntMinTableDriven/0,-1
+=== RUN TestIntMinTableDriven/-1,0
+--- PASS: TestIntMinTableDriven (0.00s)
+ --- PASS: TestIntMinTableDriven/0,1 (0.00s)
+ --- PASS: TestIntMinTableDriven/1,0 (0.00s)
+ --- PASS: TestIntMinTableDriven/2,-2 (0.00s)
+ --- PASS: TestIntMinTableDriven/0,-1 (0.00s)
+ --- PASS: TestIntMinTableDriven/-1,0 (0.00s)
+PASS
+ok examples/testing 0.023s
diff --git a/examples/testing/testing.hash b/examples/testing/testing.hash
new file mode 100644
index 000000000..317487fad
--- /dev/null
+++ b/examples/testing/testing.hash
@@ -0,0 +1,2 @@
+8f00c5178a33be2e92a853f14bfc3fbf0919cd97
+fyy7h1adGWr
diff --git a/public/command-line-arguments b/public/command-line-arguments
index a2aa9ff3a..593e93f91 100644
--- a/public/command-line-arguments
+++ b/public/command-line-arguments
@@ -9,7 +9,7 @@
onkeydown = (e) => {
if (e.key == "ArrowLeft") {
- window.location.href = 'temporary-files-and-directories';
+ window.location.href = 'testing';
}
diff --git a/public/index.html b/public/index.html
index 74ff2610b..94549ee2d 100644
--- a/public/index.html
+++ b/public/index.html
@@ -149,6 +149,8 @@