├── .github └── workflows │ └── test.yml ├── LICENSE ├── README.md ├── add └── 1 │ ├── add.go │ ├── add_test.go │ ├── go.mod │ └── test.txtar ├── ages └── 1 │ ├── ages.go │ ├── ages_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── batch └── 1 │ ├── batch.go │ ├── batch_test.go │ ├── go.mod │ └── test.txtar ├── codec └── 1 │ ├── codec.go │ ├── codec_test.go │ ├── go.mod │ ├── go.sum │ ├── test.txtar │ └── testdata │ └── output.golden ├── cover.png ├── double ├── 1 │ ├── double.go │ ├── double_test.go │ ├── go.mod │ └── test.txtar └── 2 │ ├── double.go │ ├── double_test.go │ ├── go.mod │ └── test.txtar ├── env ├── 1 │ ├── env_test.go │ ├── go.mod │ ├── go.sum │ ├── test.txtar │ └── testdata │ │ └── script │ │ └── env.txtar └── 2 │ ├── cmd │ └── listen │ │ └── main.go │ ├── env.go │ ├── env_test.go │ ├── go.mod │ ├── go.sum │ ├── test.txtar │ └── testdata │ └── script │ └── server.txtar ├── even ├── 1 │ ├── even.go │ ├── even_test.go │ ├── go.mod │ └── test.txtar ├── 2 │ ├── even.go │ ├── even_test.go │ ├── go.mod │ └── test.txtar ├── 3 │ ├── even.go │ ├── even_test.go │ ├── go.mod │ └── test.txtar └── 4 │ ├── even.go │ ├── even_test.go │ ├── go.mod │ └── test.txtar ├── fingerprint ├── 1 │ ├── fingerprint.go │ ├── fingerprint_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar └── 2 │ ├── fingerprint.go │ ├── fingerprint_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── format └── 1 │ ├── format.go │ ├── format_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── game ├── 1 │ ├── game.go │ ├── game_test.go │ ├── go.mod │ └── test.txtar ├── 2 │ ├── game.go │ ├── game_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── 3 │ ├── game.go │ ├── game_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── 4 │ ├── game.go │ ├── game_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── 5 │ ├── game.go │ ├── game_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── 6 │ ├── game.go │ ├── game_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── 7 │ ├── game.go │ ├── game_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── 8 │ ├── game.go │ ├── game_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── 9 │ ├── game.go │ ├── game_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar └── 10 │ ├── game.go │ ├── game_test.go │ ├── go.mod │ ├── go.sum │ └── test.txtar ├── greet ├── 1 │ ├── go.mod │ ├── greet.go │ ├── greet_test.go │ └── test.txtar ├── 2 │ ├── go.mod │ ├── go.sum │ ├── greet.go │ ├── greet_test.go │ └── test.txtar ├── 3 │ ├── go.mod │ ├── go.sum │ ├── greet.go │ ├── greet_test.go │ └── test.txtar ├── 4 │ ├── go.mod │ ├── go.sum │ ├── greet.go │ ├── greet_test.go │ └── test.txtar ├── 5 │ ├── go.mod │ ├── go.sum │ ├── greet.go │ ├── greet_test.go │ └── test.txtar ├── 6 │ ├── cmd │ │ └── greet │ │ │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── greet.go │ ├── greet_test.go │ ├── test.txtar │ └── testdata │ │ └── script │ │ └── greet.txtar └── 7 │ ├── cmd │ └── greet │ │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── greet.go │ ├── greet_test.go │ ├── test.txtar │ └── testdata │ └── script │ └── greet.txtar ├── guess ├── 1 │ ├── go.mod │ ├── guess.go │ ├── guess_test.go │ ├── test.txtar │ └── testdata │ │ └── fuzz │ │ └── FuzzGuess │ │ └── 15b50d33eb12775f31cce70edeca34d7a93a3380a8e08f80c10ae2be58d63b63 └── 2 │ ├── go.mod │ ├── guess.go │ ├── guess_test.go │ ├── test.txtar │ └── testdata │ └── fuzz │ └── FuzzGuess │ └── 15b50d33eb12775f31cce70edeca34d7a93a3380a8e08f80c10ae2be58d63b63 ├── hello ├── 1 │ ├── cmd │ │ └── hello │ │ │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── hello.go │ ├── hello_test.go │ ├── test.txtar │ └── testdata │ │ └── script │ │ ├── files.txtar │ │ ├── files2.txtar │ │ ├── golden.txtar │ │ ├── golden2.txtar │ │ ├── golden3.txtar │ │ └── hello.txtar └── 2 │ ├── cmd │ └── hello │ │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── hello.go │ ├── hello_test.go │ ├── test.txtar │ └── testdata │ └── script │ └── hello.txtar ├── kvstore ├── 1 │ ├── go.mod │ ├── kvstore.go │ ├── kvstore_test.go │ └── test.txtar └── 2 │ ├── go.mod │ ├── kvstore.go │ ├── kvstore_test.go │ └── test.txtar ├── load ├── 1 │ ├── go.mod │ ├── go.sum │ ├── load.go │ ├── load_test.go │ └── test.txtar └── 2 │ ├── go.mod │ ├── go.sum │ ├── load.go │ ├── load_test.go │ └── test.txtar ├── order └── 1 │ ├── go.mod │ ├── go.sum │ ├── order_test.go │ └── test.txtar ├── past ├── 1 │ ├── go.mod │ ├── go.sum │ ├── past.go │ ├── past_test.go │ └── test.txtar ├── 2 │ ├── go.mod │ ├── go.sum │ ├── past.go │ ├── past_test.go │ └── test.txtar ├── 3 │ ├── go.mod │ ├── go.sum │ ├── past.go │ ├── past_test.go │ └── test.txtar └── 4 │ ├── go.mod │ ├── past.go │ ├── past_test.go │ └── test.txtar ├── reader ├── 1 │ ├── go.mod │ ├── reader.go │ ├── reader_test.go │ └── test.txtar └── 2 │ ├── go.mod │ ├── reader.go │ ├── reader_test.go │ └── test.txtar ├── req └── 1 │ ├── go.mod │ ├── req.go │ ├── req_test.go │ └── test.txtar ├── run_all_testscripts.sh ├── run_testscript.sh ├── runes ├── 1 │ ├── go.mod │ ├── runes.go │ ├── runes_test.go │ └── test.txtar ├── 2 │ ├── go.mod │ ├── runes.go │ ├── runes_test.go │ ├── test.txtar │ └── testdata │ │ └── fuzz │ │ └── FuzzFirstRune │ │ └── 5838cdfae7b16cde2707c04599b62223e7bade8dafdd8a1c53b8f881e8f79d99 ├── 3 │ ├── go.mod │ ├── runes.go │ ├── runes_test.go │ ├── test.txtar │ └── testdata │ │ └── fuzz │ │ └── FuzzFirstRune │ │ └── 5838cdfae7b16cde2707c04599b62223e7bade8dafdd8a1c53b8f881e8f79d99 └── 4 │ ├── go.mod │ ├── runes.go │ ├── runes_test.go │ ├── test.txtar │ └── testdata │ └── fuzz │ └── FuzzFirstRune │ ├── 5838cdfae7b16cde2707c04599b62223e7bade8dafdd8a1c53b8f881e8f79d99 │ └── 793418c62f3fea2e701c96ad38ff9dab4c84092885817870c7c11508d02944c1 ├── script └── 1 │ ├── go.mod │ ├── go.sum │ ├── script_test.go │ ├── test.txtar │ └── testdata │ └── script │ ├── cat.txtar │ ├── echo.txtar │ └── hello.txtar ├── serve ├── 1 │ ├── go.mod │ ├── serve.go │ ├── serve_test.go │ └── test.txtar ├── 2 │ ├── go.mod │ ├── serve.go │ ├── serve_test.go │ └── test.txtar ├── 3 │ ├── go.mod │ ├── serve.go │ ├── serve_test.go │ └── test.txtar ├── 4 │ ├── go.mod │ ├── serve.go │ ├── serve_test.go │ └── test.txtar └── 5 │ ├── go.mod │ ├── serve.go │ ├── serve_test.go │ └── test.txtar ├── service └── 1 │ ├── go.mod │ ├── service.go │ ├── service_test.go │ └── test.txtar ├── square └── 1 │ ├── go.mod │ ├── square.go │ ├── square_test.go │ ├── test.txtar │ └── testdata │ └── output.golden ├── store ├── 1 │ ├── go.mod │ ├── store.go │ ├── store_test.go │ └── test.txtar └── 2 │ ├── go.mod │ ├── store.go │ ├── store_test.go │ └── test.txtar ├── thing ├── 1 │ ├── go.mod │ ├── test.txtar │ ├── thing.go │ └── thing_test.go ├── 2 │ ├── go.mod │ ├── test.txtar │ ├── thing.go │ └── thing_test.go ├── 3 │ ├── go.mod │ ├── test.txtar │ ├── thing.go │ └── thing_test.go ├── 4 │ ├── go.mod │ ├── go.sum │ ├── test.txtar │ ├── thing.go │ └── thing_test.go └── 5 │ ├── go.mod │ ├── go.sum │ ├── test.txtar │ ├── thing.go │ └── thing_test.go ├── timer ├── 1 │ ├── cmd │ │ └── timer │ │ │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── test.txtar │ ├── timer.go │ └── timer_test.go └── 2 │ ├── cmd │ └── timer │ │ └── main.go │ ├── go.mod │ ├── go.sum │ ├── test.txtar │ ├── timer.go │ └── timer_test.go ├── tps └── 1 │ ├── go.mod │ ├── go.sum │ ├── test.txtar │ ├── testdata │ └── output.golden │ ├── tps.go │ └── tps_test.go ├── user ├── 1 │ ├── go.mod │ ├── test.txtar │ ├── user.go │ └── user_test.go ├── 2 │ ├── go.mod │ ├── test.txtar │ ├── user.go │ └── user_test.go ├── 3 │ ├── go.mod │ ├── test.txtar │ ├── user.go │ └── user_test.go ├── 4 │ ├── go.mod │ ├── go.sum │ ├── test.txtar │ ├── user.go │ └── user_test.go ├── 5 │ ├── go.mod │ ├── test.txtar │ ├── user.go │ └── user_test.go ├── 6 │ ├── go.mod │ ├── test.txtar │ ├── user.go │ └── user_test.go └── 7 │ ├── go.mod │ ├── test.txtar │ ├── user.go │ └── user_test.go ├── valid ├── 1 │ ├── go.mod │ ├── test.txtar │ ├── valid.go │ └── valid_test.go ├── 2 │ ├── go.mod │ ├── test.txtar │ ├── valid.go │ └── valid_test.go ├── 3 │ ├── go.mod │ ├── test.txtar │ ├── valid.go │ └── valid_test.go ├── 4 │ ├── go.mod │ ├── test.txtar │ ├── valid.go │ └── valid_test.go └── 5 │ ├── go.mod │ ├── test.txtar │ ├── valid.go │ └── valid_test.go ├── weather └── 1 │ ├── go.mod │ ├── go.sum │ ├── test.txtar │ ├── weather.go │ └── weather_test.go └── widget └── 1 ├── go.mod ├── go.sum ├── postgres_store.go ├── postgres_store_test.go ├── test.txtar ├── widget.go └── widget_test.go /.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, windows-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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Power of Go: Tests 2 | 3 | [![](cover.png)](https://bitfieldconsulting.com/books/tests) 4 | 5 | This repository contains the code examples for the book [The Power of Go: Tests](https://bitfieldconsulting.com/books/tests), by John Arundel. 6 | -------------------------------------------------------------------------------- /add/1/add.go: -------------------------------------------------------------------------------- 1 | package add 2 | 3 | func Add(x, y int) int { 4 | return 42 5 | } 6 | -------------------------------------------------------------------------------- /add/1/add_test.go: -------------------------------------------------------------------------------- 1 | package add_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/add" 7 | ) 8 | 9 | func TestAdd(t *testing.T) { 10 | t.Parallel() 11 | add.Add(2, 2) 12 | } 13 | -------------------------------------------------------------------------------- /add/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/add 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /add/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test -cover 2 | stdout 'coverage: 100.0% of statements' 3 | -------------------------------------------------------------------------------- /ages/1/ages.go: -------------------------------------------------------------------------------- 1 | package ages 2 | 3 | func Total(data map[string]int) int { 4 | total := 0 5 | for _, age := range data { 6 | total += age 7 | } 8 | return total 9 | } 10 | -------------------------------------------------------------------------------- /ages/1/ages_test.go: -------------------------------------------------------------------------------- 1 | package ages_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/ages" 7 | ) 8 | 9 | func TestTotalReturnsTotalOfSuppliedAges(t *testing.T) { 10 | t.Parallel() 11 | want := 128 12 | got := ages.Total(makeAgeData()) 13 | if want != got { 14 | t.Errorf("want %d, got %d", want, got) 15 | } 16 | } 17 | 18 | func makeAgeData() map[string]int { 19 | return map[string]int{ 20 | "sam": 18, 21 | "ashley": 72, 22 | "chandra": 38, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ages/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/ages 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /ages/1/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/ages/1/go.sum -------------------------------------------------------------------------------- /ages/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /batch/1/batch.go: -------------------------------------------------------------------------------- 1 | package batch 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | func RunBatchJob(ctx context.Context) { 9 | time.Sleep(20 * time.Millisecond) 10 | } 11 | -------------------------------------------------------------------------------- /batch/1/batch_test.go: -------------------------------------------------------------------------------- 1 | package batch_test 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "testing" 7 | "time" 8 | 9 | "github.com/bitfield/batch" 10 | ) 11 | 12 | func TestRunBatchJob_RunsJobWithinReasonableTime(t *testing.T) { 13 | t.Parallel() 14 | ctx, cancel := context.WithTimeout(context.Background(), 15 | 10*time.Millisecond) 16 | defer cancel() 17 | go func() { 18 | batch.RunBatchJob(ctx) 19 | cancel() 20 | }() 21 | <-ctx.Done() 22 | if errors.Is(ctx.Err(), context.DeadlineExceeded) { 23 | t.Fatal("timed out") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /batch/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/batch 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /batch/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'timed out' 3 | -------------------------------------------------------------------------------- /codec/1/codec.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | func Encode(n int) []byte { 4 | return nil 5 | } 6 | 7 | func Decode(data []byte) int { 8 | return 0 9 | } 10 | -------------------------------------------------------------------------------- /codec/1/codec_test.go: -------------------------------------------------------------------------------- 1 | package codec_test 2 | 3 | import ( 4 | "math/rand/v2" 5 | "testing" 6 | 7 | "github.com/bitfield/codec" 8 | ) 9 | 10 | func TestEncodeFollowedByDecodeGivesStartingValue(t *testing.T) { 11 | t.Parallel() 12 | input := rand.IntN(10) + 1 13 | encoded := codec.Encode(input) 14 | t.Logf("encoded value: %#v", encoded) 15 | want := input 16 | got := codec.Decode(encoded) 17 | // after the round trip, we should get what we started with 18 | if want != got { 19 | t.Errorf("want %d, got %d", want, got) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /codec/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/codec 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /codec/1/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/codec/1/go.sum -------------------------------------------------------------------------------- /codec/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | -------------------------------------------------------------------------------- /codec/1/testdata/output.golden: -------------------------------------------------------------------------------- 1 | The thing is, Bob, it’s not that I’m lazy. It’s that I just don’t care. 2 | -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/cover.png -------------------------------------------------------------------------------- /double/1/double.go: -------------------------------------------------------------------------------- 1 | package double 2 | 3 | func Double(n int) int { 4 | return n*2 + 1 5 | } 6 | -------------------------------------------------------------------------------- /double/1/double_test.go: -------------------------------------------------------------------------------- 1 | package double_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/double" 7 | ) 8 | 9 | func TestDouble2Returns4(t *testing.T) { 10 | t.Parallel() 11 | want := 4 12 | got := double.Double(2) 13 | if want != got { 14 | t.Errorf("Double(2): want %d, got %d", want, got) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /double/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/double 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /double/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'want 4, got 5' 3 | -------------------------------------------------------------------------------- /double/2/double.go: -------------------------------------------------------------------------------- 1 | package double 2 | 3 | func Double(n int) int { 4 | return n * 2 5 | } 6 | -------------------------------------------------------------------------------- /double/2/double_test.go: -------------------------------------------------------------------------------- 1 | package double_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/bitfield/double" 8 | ) 9 | 10 | func TestDouble2Returns4(t *testing.T) { 11 | t.Parallel() 12 | want := 4 13 | got := double.Double(2) 14 | if want != got { 15 | t.Errorf("Double(2): want %d, got %d", want, got) 16 | } 17 | } 18 | 19 | func ExampleDouble() { 20 | fmt.Println(double.Double(2)) 21 | // Output: 22 | // 4 23 | } 24 | 25 | func ExampleDouble_with2() { 26 | fmt.Println(double.Double(2)) 27 | // Output: 28 | // 4 29 | } 30 | 31 | func ExampleDouble_with3() { 32 | fmt.Println(double.Double(3)) 33 | // Output: 34 | // 6 35 | } 36 | -------------------------------------------------------------------------------- /double/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/double 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /double/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /env/1/env_test.go: -------------------------------------------------------------------------------- 1 | package env_test 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/rogpeppe/go-internal/testscript" 8 | ) 9 | 10 | func TestWithExtraEnvVars(t *testing.T) { 11 | t.Parallel() 12 | addr := randomLocalAddr(t) 13 | testscript.Run(t, testscript.Params{ 14 | Dir: "testdata/script", 15 | Setup: func(env *testscript.Env) error { 16 | env.Setenv("SERVER_ADDR", addr) 17 | return nil 18 | }, 19 | }) 20 | } 21 | 22 | func randomLocalAddr(t *testing.T) string { 23 | t.Helper() 24 | l, err := net.Listen("tcp", "localhost:0") 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | defer l.Close() 29 | return l.Addr().String() 30 | } 31 | -------------------------------------------------------------------------------- /env/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/env 2 | 3 | go 1.19 4 | 5 | require github.com/rogpeppe/go-internal v1.12.0 6 | 7 | require ( 8 | golang.org/x/sys v0.16.0 // indirect 9 | golang.org/x/tools v0.17.0 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /env/1/go.sum: -------------------------------------------------------------------------------- 1 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 2 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 3 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 4 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 5 | golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= 6 | golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= 7 | -------------------------------------------------------------------------------- /env/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /env/1/testdata/script/env.txtar: -------------------------------------------------------------------------------- 1 | exec echo $SERVER_ADDR 2 | stdout '127.0.0.1:[\d]+' 3 | -------------------------------------------------------------------------------- /env/2/cmd/listen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bitfield/env" 5 | ) 6 | 7 | func main() { 8 | env.Main() 9 | } 10 | -------------------------------------------------------------------------------- /env/2/env.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "os" 8 | ) 9 | 10 | func Main() { 11 | if len(os.Args[1:]) < 1 { 12 | fmt.Fprintln(os.Stderr, "usage: listen ADDR") 13 | os.Exit(1) 14 | } 15 | srv := http.Server{ 16 | Addr: os.Args[1], 17 | } 18 | done := make(chan struct{}) 19 | go func() { 20 | <-done 21 | srv.Shutdown(context.Background()) 22 | }() 23 | mux := http.NewServeMux() 24 | mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 25 | fmt.Fprintln(w, "Hello from the Go web server") 26 | close(done) 27 | }) 28 | srv.Handler = mux 29 | err := srv.ListenAndServe() 30 | if err != http.ErrServerClosed { 31 | fmt.Fprintln(os.Stderr, err) 32 | os.Exit(1) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /env/2/env_test.go: -------------------------------------------------------------------------------- 1 | package env_test 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/bitfield/env" 8 | 9 | "github.com/rogpeppe/go-internal/testscript" 10 | ) 11 | 12 | func TestMain(m *testing.M) { 13 | testscript.Main(m, map[string]func(){ 14 | "listen": env.Main, 15 | }) 16 | } 17 | 18 | func TestWithExtraEnvVars(t *testing.T) { 19 | t.Parallel() 20 | addr := randomLocalAddr(t) 21 | testscript.Run(t, testscript.Params{ 22 | Dir: "testdata/script", 23 | Setup: func(env *testscript.Env) error { 24 | env.Setenv("SERVER_ADDR", addr) 25 | return nil 26 | }, 27 | }) 28 | } 29 | 30 | func randomLocalAddr(t *testing.T) string { 31 | t.Helper() 32 | l, err := net.Listen("tcp", "localhost:0") 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | defer l.Close() 37 | return l.Addr().String() 38 | } 39 | -------------------------------------------------------------------------------- /env/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/env 2 | 3 | go 1.23 4 | 5 | toolchain go1.24.0 6 | 7 | require github.com/rogpeppe/go-internal v1.14.1 8 | 9 | require ( 10 | golang.org/x/sys v0.26.0 // indirect 11 | golang.org/x/tools v0.26.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /env/2/go.sum: -------------------------------------------------------------------------------- 1 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 2 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 3 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 4 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 5 | golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= 6 | golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 7 | -------------------------------------------------------------------------------- /env/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /env/2/testdata/script/server.txtar: -------------------------------------------------------------------------------- 1 | exec listen $SERVER_ADDR & 2 | exec curl -s --retry-connrefused --retry 1 $SERVER_ADDR 3 | stdout 'Hello from the Go web server' 4 | wait 5 | -------------------------------------------------------------------------------- /even/1/even.go: -------------------------------------------------------------------------------- 1 | package even 2 | 3 | func IsEven(n int) bool { 4 | if n%2 == 0 { 5 | return true 6 | } 7 | return false 8 | } 9 | -------------------------------------------------------------------------------- /even/1/even_test.go: -------------------------------------------------------------------------------- 1 | package even_test 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/bitfield/even" 8 | ) 9 | 10 | func TestIsEven_IsTrueForEvenNumbers(t *testing.T) { 11 | t.Parallel() 12 | for i := 0; i < 100; i += 2 { 13 | t.Run(strconv.Itoa(i), func(t *testing.T) { 14 | if !even.IsEven(i) { 15 | t.Error(false) 16 | } 17 | }) 18 | } 19 | } 20 | 21 | func TestIsEven_IsFalseForOddNumbers(t *testing.T) { 22 | t.Parallel() 23 | for i := 1; i < 100; i += 2 { 24 | t.Run(strconv.Itoa(i), func(t *testing.T) { 25 | if even.IsEven(i) { 26 | t.Error(true) 27 | } 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /even/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/even 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /even/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /even/2/even.go: -------------------------------------------------------------------------------- 1 | package even 2 | 3 | var cache = map[int]bool{} 4 | 5 | func IsEven(n int) (even bool) { 6 | even = n%2 == 0 7 | if _, ok := cache[n]; !ok { 8 | cache[n] = n%2 == 0 9 | even = n%2 == 0 10 | } 11 | return even 12 | } 13 | -------------------------------------------------------------------------------- /even/2/even_test.go: -------------------------------------------------------------------------------- 1 | package even_test 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/bitfield/even" 8 | ) 9 | 10 | func TestIsEven_IsTrueForEvenNumbers(t *testing.T) { 11 | t.Parallel() 12 | for i := 0; i < 100; i += 2 { 13 | t.Run(strconv.Itoa(i), func(t *testing.T) { 14 | if !even.IsEven(i) { 15 | t.Error(false) 16 | } 17 | }) 18 | } 19 | } 20 | 21 | func TestIsEven_IsFalseForOddNumbers(t *testing.T) { 22 | t.Parallel() 23 | for i := 1; i < 100; i += 2 { 24 | t.Run(strconv.Itoa(i), func(t *testing.T) { 25 | if even.IsEven(i) { 26 | t.Error(true) 27 | } 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /even/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/even 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /even/2/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test -race 2 | stdout 'WARNING: DATA RACE' 3 | -------------------------------------------------------------------------------- /even/3/even.go: -------------------------------------------------------------------------------- 1 | package even 2 | 3 | import "sync" 4 | 5 | var ( 6 | m = sync.Mutex{} 7 | cache = map[int]bool{} 8 | ) 9 | 10 | func IsEven(n int) (even bool) { 11 | m.Lock() 12 | defer m.Unlock() 13 | even = n%2 == 0 14 | if _, ok := cache[n]; !ok { 15 | cache[n] = n%2 == 0 16 | even = n%2 == 0 17 | } 18 | return even 19 | } 20 | -------------------------------------------------------------------------------- /even/3/even_test.go: -------------------------------------------------------------------------------- 1 | package even_test 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/bitfield/even" 8 | ) 9 | 10 | func TestIsEven_IsTrueForEvenNumbers(t *testing.T) { 11 | t.Parallel() 12 | for i := 0; i < 100; i += 2 { 13 | t.Run(strconv.Itoa(i), func(t *testing.T) { 14 | if !even.IsEven(i) { 15 | t.Error(false) 16 | } 17 | }) 18 | } 19 | } 20 | 21 | func TestIsEven_IsFalseForOddNumbers(t *testing.T) { 22 | t.Parallel() 23 | for i := 1; i < 100; i += 2 { 24 | t.Run(strconv.Itoa(i), func(t *testing.T) { 25 | if even.IsEven(i) { 26 | t.Error(true) 27 | } 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /even/3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/even 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /even/3/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /even/4/even.go: -------------------------------------------------------------------------------- 1 | package even 2 | 3 | import "sync" 4 | 5 | var ( 6 | m = sync.Mutex{} 7 | cache = map[int]bool{} 8 | ) 9 | 10 | func IsEven(n int) (even bool) { 11 | m.Lock() 12 | defer m.Unlock() 13 | even, ok := cache[n] 14 | if !ok { 15 | even = n%2 == 0 16 | cache[n] = even 17 | } 18 | return even 19 | } 20 | -------------------------------------------------------------------------------- /even/4/even_test.go: -------------------------------------------------------------------------------- 1 | package even_test 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/bitfield/even" 8 | ) 9 | 10 | func TestIsEven_IsTrueForEvenNumbers(t *testing.T) { 11 | t.Parallel() 12 | for i := 0; i < 100; i += 2 { 13 | t.Run(strconv.Itoa(i), func(t *testing.T) { 14 | if !even.IsEven(i) { 15 | t.Error(false) 16 | } 17 | }) 18 | } 19 | } 20 | 21 | func TestIsEven_IsFalseForOddNumbers(t *testing.T) { 22 | t.Parallel() 23 | for i := 1; i < 100; i += 2 { 24 | t.Run(strconv.Itoa(i), func(t *testing.T) { 25 | if even.IsEven(i) { 26 | t.Error(true) 27 | } 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /even/4/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/even 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /even/4/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /fingerprint/1/fingerprint.go: -------------------------------------------------------------------------------- 1 | package fingerprint 2 | 3 | import "crypto/md5" 4 | 5 | func Hash(data []byte) [md5.Size]byte { 6 | return md5.Sum(data) 7 | } 8 | -------------------------------------------------------------------------------- /fingerprint/1/fingerprint_test.go: -------------------------------------------------------------------------------- 1 | package fingerprint_test 2 | 3 | import ( 4 | "crypto/md5" 5 | "testing" 6 | 7 | "github.com/bitfield/fingerprint" 8 | ) 9 | 10 | func TestHashReturnsMD5HashOfGivenData(t *testing.T) { 11 | t.Parallel() 12 | data := []byte("These pretzels are making me thirsty.") 13 | want := md5.Sum(data) 14 | got := fingerprint.Hash(data) 15 | if want != got { 16 | t.Errorf("want %v, got %v", want, got) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /fingerprint/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/fingerprint 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /fingerprint/1/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/fingerprint/1/go.sum -------------------------------------------------------------------------------- /fingerprint/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /fingerprint/2/fingerprint.go: -------------------------------------------------------------------------------- 1 | package fingerprint 2 | 3 | import "crypto/sha256" 4 | 5 | func Hash(data []byte) [sha256.Size]byte { 6 | return sha256.Sum256(data) 7 | } 8 | -------------------------------------------------------------------------------- /fingerprint/2/fingerprint_test.go: -------------------------------------------------------------------------------- 1 | package fingerprint_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/fingerprint" 7 | ) 8 | 9 | func TestHashGivesSameUniqueHashForSameData(t *testing.T) { 10 | t.Parallel() 11 | data := []byte("These pretzels are making me thirsty.") 12 | orig := fingerprint.Hash(data) 13 | same := fingerprint.Hash(data) 14 | different := fingerprint.Hash([]byte("Hello, Newman")) 15 | if orig != same { 16 | t.Error("same data produced different hash") 17 | } 18 | if orig == different { 19 | t.Error("different data produced same hash") 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /fingerprint/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/fingerprint 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /fingerprint/2/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/fingerprint/2/go.sum -------------------------------------------------------------------------------- /fingerprint/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /format/1/format.go: -------------------------------------------------------------------------------- 1 | package format 2 | 3 | func Data(input string) (string, error) { 4 | return "", nil 5 | } 6 | -------------------------------------------------------------------------------- /format/1/format_test.go: -------------------------------------------------------------------------------- 1 | package format_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/format" 7 | ) 8 | 9 | const ( 10 | invalidInput = "invalid input" 11 | validInput = "valid input" 12 | validInputFormatted = "valid input formatted" 13 | ) 14 | 15 | func TestFormatData_ErrorsOnInvalidInput(t *testing.T) { 16 | t.Parallel() 17 | _, err := format.Data(invalidInput) 18 | if err == nil { 19 | t.Error("want error for invalid input") 20 | } 21 | } 22 | 23 | func TestFormatData_IsCorrectForValidInput(t *testing.T) { 24 | t.Parallel() 25 | want := validInputFormatted 26 | got, err := format.Data(validInput) 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | if want != got { 31 | t.Errorf("want %q, got %q", want, got) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /format/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/format 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /format/1/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/format/1/go.sum -------------------------------------------------------------------------------- /format/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'want error for invalid input' 3 | stdout 'valid input formatted' 4 | -------------------------------------------------------------------------------- /game/1/game.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import "strings" 4 | 5 | func ListItems(items []string) string { 6 | result := "You can see here" 7 | result += strings.Join(items, ", ") 8 | result += "." 9 | return result 10 | } 11 | -------------------------------------------------------------------------------- /game/1/game_test.go: -------------------------------------------------------------------------------- 1 | package game_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/game" 7 | ) 8 | 9 | func TestListItems_GivesCorrectResultForInput(t *testing.T) { 10 | t.Parallel() 11 | input := []string{ 12 | "a battery", 13 | "a key", 14 | "a tourist map", 15 | } 16 | want := "You can see here a battery, a key, and a tourist map." 17 | got := game.ListItems(input) 18 | if want != got { 19 | t.Errorf("want %q, got %q", want, got) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /game/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/game 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /game/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'want "You can see here a battery, a key, and a tourist map.", got "You can see herea battery, a key, a tourist map."' 3 | -------------------------------------------------------------------------------- /game/10/game.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import "strings" 4 | 5 | func ListItems(items []string) string { 6 | switch len(items) { 7 | case 0: 8 | return "" 9 | case 1: 10 | return "You can see " + items[0] + " here." 11 | case 2: 12 | return "You can see here " + items[0] + " and " + 13 | items[1] + "." 14 | default: 15 | return "You can see here " + 16 | strings.Join(items[:len(items)-1], ", ") + 17 | ", and " + items[len(items)-1] + "." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /game/10/game_test.go: -------------------------------------------------------------------------------- 1 | package game_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/game" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestListItems_GivesCorrectResultFor(t *testing.T) { 12 | t.Parallel() 13 | cases := map[string]struct { 14 | input []string 15 | want string 16 | }{ 17 | "no items": { 18 | input: []string{}, 19 | want: "", 20 | }, 21 | "one item": { 22 | input: []string{ 23 | "a battery", 24 | }, 25 | want: "You can see a battery here.", 26 | }, 27 | "two items": { 28 | input: []string{ 29 | "a battery", 30 | "a key", 31 | }, 32 | want: "You can see here a battery and a key.", 33 | }, 34 | "three items": { 35 | input: []string{ 36 | "a battery", 37 | "a key", 38 | "a tourist map", 39 | }, 40 | want: "You can see here a battery, a key, and a tourist map.", 41 | }, 42 | } 43 | for name, tc := range cases { 44 | t.Run(name, func(t *testing.T) { 45 | got := game.ListItems(tc.input) 46 | if tc.want != got { 47 | t.Error(cmp.Diff(tc.want, got)) 48 | } 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /game/10/go.mod: -------------------------------------------------------------------------------- 1 | module game 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.5.8 6 | -------------------------------------------------------------------------------- /game/10/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 2 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /game/10/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /game/2/game.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import "strings" 4 | 5 | func ListItems(items []string) string { 6 | result := "You can see here" 7 | result += strings.Join(items, ", ") 8 | result += "." 9 | return result 10 | } 11 | -------------------------------------------------------------------------------- /game/2/game_test.go: -------------------------------------------------------------------------------- 1 | package game_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/game" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestListItems_GivesCorrectResultForInput(t *testing.T) { 12 | t.Parallel() 13 | input := []string{ 14 | "a battery", 15 | "a key", 16 | "a tourist map", 17 | } 18 | want := "You can see here a battery, a key, and a tourist map." 19 | got := game.ListItems(input) 20 | if want != got { 21 | t.Error(cmp.Diff(want, got)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /game/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/game 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /game/2/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /game/2/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout '" ",' 3 | -------------------------------------------------------------------------------- /game/3/game.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import "strings" 4 | 5 | func ListItems(items []string) string { 6 | result := "You can see here " 7 | result += strings.Join(items[:len(items)-1], ", ") 8 | result += ", and " 9 | result += items[len(items)-1] 10 | result += "." 11 | return result 12 | } 13 | -------------------------------------------------------------------------------- /game/3/game_test.go: -------------------------------------------------------------------------------- 1 | package game_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/game" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestListItems_GivesCorrectResultForInput(t *testing.T) { 12 | t.Parallel() 13 | input := []string{ 14 | "a battery", 15 | "a key", 16 | "a tourist map", 17 | } 18 | want := "You can see here a battery, a key, and a tourist map." 19 | got := game.ListItems(input) 20 | if want != got { 21 | t.Error(cmp.Diff(want, got)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /game/3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/game 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /game/3/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /game/3/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /game/4/game.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import "strings" 4 | 5 | func ListItems(items []string) string { 6 | result := "You can see here " 7 | result += strings.Join(items[:len(items)-1], ", ") 8 | result += ", and " 9 | result += items[len(items)-1] 10 | result += "." 11 | return result 12 | } 13 | -------------------------------------------------------------------------------- /game/4/game_test.go: -------------------------------------------------------------------------------- 1 | package game_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/game" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestListItems_GivesCorrectResultForInput(t *testing.T) { 12 | t.Parallel() 13 | type testCase struct { 14 | input []string 15 | want string 16 | } 17 | cases := []testCase{ 18 | { 19 | input: []string{ 20 | "a battery", 21 | "a key", 22 | "a tourist map", 23 | }, 24 | want: "You can see here a battery, a key, and a tourist map.", 25 | }, 26 | } 27 | for _, tc := range cases { 28 | got := game.ListItems(tc.input) 29 | if tc.want != got { 30 | t.Error(cmp.Diff(tc.want, got)) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /game/4/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/game 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /game/4/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /game/4/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /game/5/game.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import "strings" 4 | 5 | func ListItems(items []string) string { 6 | result := "You can see here " 7 | if len(items) < 3 { 8 | return result + items[0] + " and " + items[1] + "." 9 | } 10 | result += strings.Join(items[:len(items)-1], ", ") 11 | result += ", and " 12 | result += items[len(items)-1] 13 | result += "." 14 | return result 15 | } 16 | -------------------------------------------------------------------------------- /game/5/game_test.go: -------------------------------------------------------------------------------- 1 | package game_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/game" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestListItems_GivesCorrectResultForInput(t *testing.T) { 12 | t.Parallel() 13 | type testCase struct { 14 | input []string 15 | want string 16 | } 17 | cases := []testCase{ 18 | { 19 | input: []string{ 20 | "a battery", 21 | "a key", 22 | "a tourist map", 23 | }, 24 | want: "You can see here a battery, a key, and a tourist map.", 25 | }, 26 | { 27 | input: []string{ 28 | "a battery", 29 | "a key", 30 | }, 31 | want: "You can see here a battery and a key.", 32 | }, 33 | } 34 | for _, tc := range cases { 35 | got := game.ListItems(tc.input) 36 | if tc.want != got { 37 | t.Error(cmp.Diff(tc.want, got)) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /game/5/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/game 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /game/5/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /game/5/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /game/6/game.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import "strings" 4 | 5 | func ListItems(items []string) string { 6 | result := "You can see here " 7 | if len(items) == 1 { 8 | return "You can see " + items[0] + " here." 9 | } 10 | if len(items) < 3 { 11 | return result + items[0] + " and " + items[1] + "." 12 | } 13 | result += strings.Join(items[:len(items)-1], ", ") 14 | result += ", and " 15 | result += items[len(items)-1] 16 | result += "." 17 | return result 18 | } 19 | -------------------------------------------------------------------------------- /game/6/game_test.go: -------------------------------------------------------------------------------- 1 | package game_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/game" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestListItems_GivesCorrectResultForInput(t *testing.T) { 12 | t.Parallel() 13 | type testCase struct { 14 | input []string 15 | want string 16 | } 17 | cases := []testCase{ 18 | { 19 | input: []string{ 20 | "a battery", 21 | "a key", 22 | "a tourist map", 23 | }, 24 | want: "You can see here a battery, a key, and a tourist map.", 25 | }, 26 | { 27 | input: []string{ 28 | "a battery", 29 | "a key", 30 | }, 31 | want: "You can see here a battery and a key.", 32 | }, 33 | { 34 | input: []string{ 35 | "a battery", 36 | }, 37 | want: "You can see a battery here.", 38 | }, 39 | } 40 | for _, tc := range cases { 41 | got := game.ListItems(tc.input) 42 | if tc.want != got { 43 | t.Error(cmp.Diff(tc.want, got)) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /game/6/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/game 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /game/6/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /game/6/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /game/7/game.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import "strings" 4 | 5 | func ListItems(items []string) string { 6 | if len(items) == 0 { 7 | return "" 8 | } 9 | result := "You can see here " 10 | if len(items) == 1 { 11 | return "You can see " + items[0] + " here." 12 | } 13 | if len(items) < 3 { 14 | return result + items[0] + " and " + items[1] + "." 15 | } 16 | result += strings.Join(items[:len(items)-1], ", ") 17 | result += ", and " 18 | result += items[len(items)-1] 19 | result += "." 20 | return result 21 | } 22 | -------------------------------------------------------------------------------- /game/7/game_test.go: -------------------------------------------------------------------------------- 1 | package game_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/game" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestListItems_GivesCorrectResultForInput(t *testing.T) { 12 | t.Parallel() 13 | type testCase struct { 14 | input []string 15 | want string 16 | } 17 | cases := []testCase{ 18 | { 19 | input: []string{ 20 | "a battery", 21 | "a key", 22 | "a tourist map", 23 | }, 24 | want: "You can see here a battery, a key, and a tourist map.", 25 | }, 26 | { 27 | input: []string{ 28 | "a battery", 29 | "a key", 30 | }, 31 | want: "You can see here a battery and a key.", 32 | }, 33 | { 34 | input: []string{ 35 | "a battery", 36 | }, 37 | want: "You can see a battery here.", 38 | }, 39 | { 40 | input: []string{}, 41 | want: "", 42 | }, 43 | } 44 | for _, tc := range cases { 45 | got := game.ListItems(tc.input) 46 | if tc.want != got { 47 | t.Error(cmp.Diff(tc.want, got)) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /game/7/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/game 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /game/7/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /game/7/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /game/8/game.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import "strings" 4 | 5 | func ListItems(items []string) string { 6 | switch len(items) { 7 | case 0: 8 | return "" 9 | case 1: 10 | return "You can see " + items[0] + " here." 11 | case 2: 12 | return "You can see here " + items[0] + " and " + 13 | items[1] + "." 14 | default: 15 | return "You can see here " + 16 | strings.Join(items[:len(items)-1], ", ") + 17 | ", and " + items[len(items)-1] + "." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /game/8/game_test.go: -------------------------------------------------------------------------------- 1 | package game_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/game" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestListItems_GivesCorrectResultForInput(t *testing.T) { 12 | t.Parallel() 13 | type testCase struct { 14 | input []string 15 | want string 16 | } 17 | cases := []testCase{ 18 | { 19 | input: []string{ 20 | "a battery", 21 | "a key", 22 | "a tourist map", 23 | }, 24 | want: "You can see here a battery, a key, and a tourist map.", 25 | }, 26 | { 27 | input: []string{ 28 | "a battery", 29 | "a key", 30 | }, 31 | want: "You can see here a battery and a key.", 32 | }, 33 | { 34 | input: []string{ 35 | "a battery", 36 | }, 37 | want: "You can see a battery here.", 38 | }, 39 | { 40 | input: []string{}, 41 | want: "", 42 | }, 43 | } 44 | for _, tc := range cases { 45 | got := game.ListItems(tc.input) 46 | if tc.want != got { 47 | t.Error(cmp.Diff(tc.want, got)) 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /game/8/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/game 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /game/8/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /game/8/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /game/9/game.go: -------------------------------------------------------------------------------- 1 | package game 2 | 3 | import "strings" 4 | 5 | func ListItems(items []string) string { 6 | switch len(items) { 7 | case 0: 8 | return "" 9 | case 1: 10 | return "You can see " + items[0] + " here." 11 | case 2: 12 | return "You can see here " + items[0] + " and " + 13 | items[1] + "." 14 | default: 15 | return "You can see here " + 16 | strings.Join(items[:len(items)-1], ", ") + 17 | ", and " + items[len(items)-1] + "." 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /game/9/game_test.go: -------------------------------------------------------------------------------- 1 | package game_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/game" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestListItems_GivesCorrectResultForInput(t *testing.T) { 12 | t.Parallel() 13 | cases := []struct { 14 | input []string 15 | want string 16 | }{ 17 | { 18 | input: []string{}, 19 | want: "", 20 | }, 21 | { 22 | input: []string{ 23 | "a battery", 24 | }, 25 | want: "You can see a battery here.", 26 | }, 27 | { 28 | input: []string{ 29 | "a battery", 30 | "a key", 31 | }, 32 | want: "You can see here a battery and a key.", 33 | }, 34 | { 35 | input: []string{ 36 | "a battery", 37 | "a key", 38 | "a tourist map", 39 | }, 40 | want: "You can see here a battery, a key, and a tourist map.", 41 | }, 42 | } 43 | for _, tc := range cases { 44 | got := game.ListItems(tc.input) 45 | if tc.want != got { 46 | t.Error(cmp.Diff(tc.want, got)) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /game/9/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/game 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /game/9/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /game/9/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /greet/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/greet 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /greet/1/greet.go: -------------------------------------------------------------------------------- 1 | package greet 2 | 3 | import "fmt" 4 | 5 | func Greet() { 6 | fmt.Print("Your name? ") 7 | var name string 8 | fmt.Scanln(&name) 9 | fmt.Printf("Hello, %s!\n", name) 10 | } 11 | -------------------------------------------------------------------------------- /greet/1/greet_test.go: -------------------------------------------------------------------------------- 1 | package greet_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/greet" 7 | ) 8 | 9 | func TestGreetAsksForNameAndPrintsHelloName(t *testing.T) { 10 | t.Parallel() 11 | greet.Greet() 12 | } 13 | -------------------------------------------------------------------------------- /greet/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | stdout 'Your name\? Hello, !' 3 | -------------------------------------------------------------------------------- /greet/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/greet 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /greet/2/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /greet/2/greet.go: -------------------------------------------------------------------------------- 1 | package greet 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | func Greet(out io.Writer) { 9 | fmt.Fprint(out, "Your name? ") 10 | var name string 11 | fmt.Scanln(&name) 12 | fmt.Fprintf(out, "Hello, %s!\n", name) 13 | } 14 | -------------------------------------------------------------------------------- /greet/2/greet_test.go: -------------------------------------------------------------------------------- 1 | package greet_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/bitfield/greet" 8 | 9 | "github.com/google/go-cmp/cmp" 10 | ) 11 | 12 | func TestGreetAsksForNameAndPrintsHelloName(t *testing.T) { 13 | t.Parallel() 14 | buf := new(bytes.Buffer) 15 | greet.Greet(buf) 16 | want := "Your name? Hello, !\n" 17 | got := buf.String() 18 | if want != got { 19 | t.Error(cmp.Diff(want, got)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /greet/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /greet/3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/greet 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /greet/3/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /greet/3/greet.go: -------------------------------------------------------------------------------- 1 | package greet 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | func Greet(in io.Reader, out io.Writer) { 9 | fmt.Fprint(out, "Your name? ") 10 | var name string 11 | fmt.Fscanln(in, &name) 12 | fmt.Fprintf(out, "Hello, %s!\n", name) 13 | } 14 | -------------------------------------------------------------------------------- /greet/3/greet_test.go: -------------------------------------------------------------------------------- 1 | package greet_test 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/bitfield/greet" 9 | 10 | "github.com/google/go-cmp/cmp" 11 | ) 12 | 13 | func TestGreetAsksForNameAndPrintsHelloName(t *testing.T) { 14 | t.Parallel() 15 | buf := new(bytes.Buffer) 16 | input := strings.NewReader("fakename\n") 17 | greet.Greet(input, buf) 18 | want := "Your name? Hello, fakename!\n" 19 | got := buf.String() 20 | if want != got { 21 | t.Error(cmp.Diff(want, got)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /greet/3/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /greet/4/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/greet 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /greet/4/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /greet/4/greet.go: -------------------------------------------------------------------------------- 1 | package greet 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | func Greet(in io.Reader, out io.Writer) { 9 | fmt.Fprint(out, "Your name? ") 10 | var name string 11 | fmt.Fscanln(in, &name) 12 | fmt.Fprintf(out, "Hello, %s!\n", name) 13 | } 14 | -------------------------------------------------------------------------------- /greet/4/greet_test.go: -------------------------------------------------------------------------------- 1 | package greet_test 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/bitfield/greet" 9 | 10 | "github.com/google/go-cmp/cmp" 11 | ) 12 | 13 | func TestGreetAsksForNameAndPrintsHelloName(t *testing.T) { 14 | t.Parallel() 15 | buf := new(bytes.Buffer) 16 | input := strings.NewReader("Mary Jo\n") 17 | greet.Greet(input, buf) 18 | want := "Your name? Hello, Mary Jo!\n" 19 | got := buf.String() 20 | if want != got { 21 | t.Error(cmp.Diff(want, got)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /greet/4/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout -count=2 'Hello, Mary' 3 | stdout 'Hello, Mary Jo' 4 | -------------------------------------------------------------------------------- /greet/5/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/greet 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /greet/5/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /greet/5/greet.go: -------------------------------------------------------------------------------- 1 | package greet 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | func Greet(in io.Reader, out io.Writer) { 10 | fmt.Fprint(out, "Your name? ") 11 | scanner := bufio.NewScanner(in) 12 | if !scanner.Scan() { 13 | return 14 | } 15 | fmt.Fprintf(out, "Hello, %s!\n", scanner.Text()) 16 | } 17 | -------------------------------------------------------------------------------- /greet/5/greet_test.go: -------------------------------------------------------------------------------- 1 | package greet_test 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/bitfield/greet" 9 | 10 | "github.com/google/go-cmp/cmp" 11 | ) 12 | 13 | func TestGreetAsksForNameAndPrintsHelloName(t *testing.T) { 14 | t.Parallel() 15 | buf := new(bytes.Buffer) 16 | input := strings.NewReader("Mary Jo\n") 17 | greet.Greet(input, buf) 18 | want := "Your name? Hello, Mary Jo!\n" 19 | got := buf.String() 20 | if want != got { 21 | t.Error(cmp.Diff(want, got)) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /greet/5/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /greet/6/cmd/greet/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bitfield/greet" 5 | ) 6 | 7 | func main() { 8 | greet.Main() 9 | } 10 | -------------------------------------------------------------------------------- /greet/6/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/greet 2 | 3 | go 1.23 4 | 5 | toolchain go1.24.0 6 | 7 | require github.com/rogpeppe/go-internal v1.14.1 8 | 9 | require ( 10 | golang.org/x/sys v0.26.0 // indirect 11 | golang.org/x/tools v0.26.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /greet/6/go.sum: -------------------------------------------------------------------------------- 1 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 2 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 3 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 4 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 5 | golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= 6 | golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 7 | -------------------------------------------------------------------------------- /greet/6/greet.go: -------------------------------------------------------------------------------- 1 | package greet 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func Main() { 10 | fmt.Println("Your name? ") 11 | scanner := bufio.NewScanner(os.Stdin) 12 | if !scanner.Scan() { 13 | os.Exit(1) 14 | } 15 | fmt.Printf("Hello, %s!\n", scanner.Text()) 16 | } 17 | -------------------------------------------------------------------------------- /greet/6/greet_test.go: -------------------------------------------------------------------------------- 1 | package greet_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/greet" 7 | 8 | "github.com/rogpeppe/go-internal/testscript" 9 | ) 10 | 11 | func TestMain(m *testing.M) { 12 | testscript.Main(m, map[string]func(){ 13 | "greet": greet.Main, 14 | }) 15 | } 16 | 17 | func Test(t *testing.T) { 18 | testscript.Run(t, testscript.Params{ 19 | Dir: "testdata/script", 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /greet/6/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | ! exec go run ./cmd/greet/main.go 3 | stdin input.txt 4 | exec go run ./cmd/greet/main.go 5 | stdout 'Hello, John!' 6 | 7 | -- input.txt -- 8 | John 9 | -------------------------------------------------------------------------------- /greet/6/testdata/script/greet.txtar: -------------------------------------------------------------------------------- 1 | stdin input.txt 2 | exec greet 3 | stdout 'Hello, John!' 4 | 5 | -- input.txt -- 6 | John 7 | -------------------------------------------------------------------------------- /greet/7/cmd/greet/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bitfield/greet" 5 | ) 6 | 7 | func main() { 8 | greet.Main() 9 | } 10 | -------------------------------------------------------------------------------- /greet/7/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/greet 2 | 3 | go 1.23 4 | 5 | toolchain go1.24.0 6 | 7 | require github.com/rogpeppe/go-internal v1.14.1 8 | 9 | require ( 10 | golang.org/x/sys v0.26.0 // indirect 11 | golang.org/x/tools v0.26.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /greet/7/go.sum: -------------------------------------------------------------------------------- 1 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 2 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 3 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 4 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 5 | golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= 6 | golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 7 | -------------------------------------------------------------------------------- /greet/7/greet.go: -------------------------------------------------------------------------------- 1 | package greet 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func Main() { 10 | fmt.Println("Your name? ") 11 | scanner := bufio.NewScanner(os.Stdin) 12 | if !scanner.Scan() { 13 | os.Exit(1) 14 | } 15 | name := scanner.Text() 16 | fmt.Println("Your favourite food? ") 17 | if !scanner.Scan() { 18 | os.Exit(1) 19 | } 20 | food := scanner.Text() 21 | fmt.Printf("Hello, %s. Care to join me for some %s?\n", name, food) 22 | } 23 | -------------------------------------------------------------------------------- /greet/7/greet_test.go: -------------------------------------------------------------------------------- 1 | package greet_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/greet" 7 | 8 | "github.com/rogpeppe/go-internal/testscript" 9 | ) 10 | 11 | func TestMain(m *testing.M) { 12 | testscript.Main(m, map[string]func(){ 13 | "greet": greet.Main, 14 | }) 15 | } 16 | 17 | func Test(t *testing.T) { 18 | testscript.Run(t, testscript.Params{ 19 | Dir: "testdata/script", 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /greet/7/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | ! exec go run ./cmd/greet/main.go 3 | stdin input.txt 4 | exec go run ./cmd/greet/main.go 5 | stdout 'Hello, John. Care to join me for some bacon?' 6 | 7 | -- input.txt -- 8 | John 9 | bacon 10 | -------------------------------------------------------------------------------- /greet/7/testdata/script/greet.txtar: -------------------------------------------------------------------------------- 1 | stdin input.txt 2 | exec greet 3 | stdout 'Hello, Kim. Care to join me for some barbecue?' 4 | 5 | -- input.txt -- 6 | Kim 7 | barbecue 8 | -------------------------------------------------------------------------------- /guess/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/guess 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /guess/1/guess.go: -------------------------------------------------------------------------------- 1 | package guess 2 | 3 | func Guess(n int) { 4 | if n == 21 { 5 | panic("blackjack!") 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /guess/1/guess_test.go: -------------------------------------------------------------------------------- 1 | package guess_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/guess" 7 | ) 8 | 9 | func FuzzGuess(f *testing.F) { 10 | f.Fuzz(func(t *testing.T, input int) { 11 | guess.Guess(input) 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /guess/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'panic: blackjack!' 3 | -------------------------------------------------------------------------------- /guess/1/testdata/fuzz/FuzzGuess/15b50d33eb12775f31cce70edeca34d7a93a3380a8e08f80c10ae2be58d63b63: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | int(21) 3 | -------------------------------------------------------------------------------- /guess/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/guess 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /guess/2/guess.go: -------------------------------------------------------------------------------- 1 | package guess 2 | 3 | func Guess(n int) {} 4 | -------------------------------------------------------------------------------- /guess/2/guess_test.go: -------------------------------------------------------------------------------- 1 | package guess_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/guess" 7 | ) 8 | 9 | func FuzzGuess(f *testing.F) { 10 | f.Fuzz(func(t *testing.T, input int) { 11 | guess.Guess(input) 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /guess/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /guess/2/testdata/fuzz/FuzzGuess/15b50d33eb12775f31cce70edeca34d7a93a3380a8e08f80c10ae2be58d63b63: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | int(21) 3 | -------------------------------------------------------------------------------- /hello/1/cmd/hello/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bitfield/hello" 5 | ) 6 | 7 | func main() { 8 | hello.Main() 9 | } 10 | -------------------------------------------------------------------------------- /hello/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/hello 2 | 3 | go 1.23 4 | 5 | toolchain go1.24.0 6 | 7 | require github.com/rogpeppe/go-internal v1.14.1 8 | 9 | require ( 10 | golang.org/x/sys v0.26.0 // indirect 11 | golang.org/x/tools v0.26.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /hello/1/go.sum: -------------------------------------------------------------------------------- 1 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 2 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 3 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 4 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 5 | golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= 6 | golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 7 | -------------------------------------------------------------------------------- /hello/1/hello.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import "fmt" 4 | 5 | func Main() { 6 | fmt.Println("hello world") 7 | } 8 | -------------------------------------------------------------------------------- /hello/1/hello_test.go: -------------------------------------------------------------------------------- 1 | package hello_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/hello" 7 | 8 | "github.com/rogpeppe/go-internal/testscript" 9 | ) 10 | 11 | func TestMain(m *testing.M) { 12 | testscript.Main(m, map[string]func(){ 13 | "hello": hello.Main, 14 | }) 15 | } 16 | 17 | func Test(t *testing.T) { 18 | testscript.Run(t, testscript.Params{ 19 | Dir: "testdata/script", 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /hello/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | exec go run ./cmd/hello/main.go 3 | stdout 'hello world' 4 | -------------------------------------------------------------------------------- /hello/1/testdata/script/files.txtar: -------------------------------------------------------------------------------- 1 | exec cat a.txt b.txt c.txt 2 | 3 | -- a.txt -- 4 | ... 5 | 6 | -- b.txt -- 7 | ... 8 | 9 | -- c.txt -- 10 | ... 11 | -------------------------------------------------------------------------------- /hello/1/testdata/script/files2.txtar: -------------------------------------------------------------------------------- 1 | exec cat misc/a.txt misc/subfolder/b.txt extra/c.txt 2 | 3 | -- misc/a.txt -- 4 | ... 5 | 6 | -- misc/subfolder/b.txt -- 7 | ... 8 | 9 | -- extra/c.txt -- 10 | ... 11 | -------------------------------------------------------------------------------- /hello/1/testdata/script/golden.txtar: -------------------------------------------------------------------------------- 1 | exec hello 2 | cmp stdout golden.txt 3 | 4 | -- golden.txt -- 5 | hello world 6 | -------------------------------------------------------------------------------- /hello/1/testdata/script/golden2.txtar: -------------------------------------------------------------------------------- 1 | exec echo hello 2 | ! cmp stdout golden.txt 3 | 4 | -- golden.txt -- 5 | goodbye world 6 | -------------------------------------------------------------------------------- /hello/1/testdata/script/golden3.txtar: -------------------------------------------------------------------------------- 1 | exec echo Running with home directory $HOME 2 | cmpenv stdout golden.txt 3 | 4 | -- golden.txt -- 5 | Running with home directory $HOME 6 | -------------------------------------------------------------------------------- /hello/1/testdata/script/hello.txtar: -------------------------------------------------------------------------------- 1 | exec hello 2 | stdout 'hello world\n' 3 | -------------------------------------------------------------------------------- /hello/2/cmd/hello/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/bitfield/hello" 5 | ) 6 | 7 | func main() { 8 | hello.Main() 9 | } 10 | -------------------------------------------------------------------------------- /hello/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/hello 2 | 3 | go 1.23 4 | 5 | toolchain go1.24.0 6 | 7 | require github.com/rogpeppe/go-internal v1.14.1 8 | 9 | require ( 10 | golang.org/x/sys v0.26.0 // indirect 11 | golang.org/x/tools v0.26.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /hello/2/go.sum: -------------------------------------------------------------------------------- 1 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 2 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 3 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 4 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 5 | golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= 6 | golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 7 | -------------------------------------------------------------------------------- /hello/2/hello.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func Main() { 9 | if len(os.Args[1:]) < 1 { 10 | fmt.Fprintln(os.Stderr, "usage: hello NAME") 11 | os.Exit(1) 12 | } 13 | fmt.Println("Hello to you,", os.Args[1]) 14 | } 15 | -------------------------------------------------------------------------------- /hello/2/hello_test.go: -------------------------------------------------------------------------------- 1 | package hello_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/hello" 7 | 8 | "github.com/rogpeppe/go-internal/testscript" 9 | ) 10 | 11 | func TestMain(m *testing.M) { 12 | testscript.Main(m, map[string]func(){ 13 | "hello": hello.Main, 14 | }) 15 | } 16 | 17 | func Test(t *testing.T) { 18 | testscript.Run(t, testscript.Params{ 19 | Dir: "testdata/script", 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /hello/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | 3 | ! exec go run ./cmd/hello/main.go 4 | ! stdout . 5 | stderr 'usage: hello NAME' 6 | 7 | exec go run ./cmd/hello/main.go Joumana 8 | stdout 'Hello to you, Joumana' 9 | -------------------------------------------------------------------------------- /hello/2/testdata/script/hello.txtar: -------------------------------------------------------------------------------- 1 | # With no arguments, fail and print a usage message 2 | ! exec hello 3 | ! stdout . 4 | stderr 'usage: hello NAME' 5 | 6 | # With an argument, print a greeting using that value 7 | exec hello Joumana 8 | stdout 'Hello to you, Joumana' 9 | ! stderr . 10 | -------------------------------------------------------------------------------- /kvstore/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/kvstore 2 | 3 | go 1.22 4 | -------------------------------------------------------------------------------- /kvstore/1/kvstore.go: -------------------------------------------------------------------------------- 1 | package kvstore 2 | 3 | var data = map[string]string{} // no! data race! 4 | 5 | func Set(k, v string) { 6 | data[k] = v 7 | } 8 | 9 | func Get(k string) string { 10 | return data[k] 11 | } 12 | -------------------------------------------------------------------------------- /kvstore/1/kvstore_test.go: -------------------------------------------------------------------------------- 1 | package kvstore_test 2 | 3 | import ( 4 | "runtime" 5 | "strconv" 6 | "sync" 7 | "testing" 8 | 9 | "github.com/bitfield/kvstore" 10 | ) 11 | 12 | func TestKVStore_HasNoDataRace(t *testing.T) { 13 | t.Parallel() 14 | var wg sync.WaitGroup 15 | wg.Add(1) 16 | go func() { 17 | for i := range 1000 { 18 | kvstore.Set("foo", strconv.Itoa(i)) 19 | } 20 | wg.Done() 21 | }() 22 | for range 1000 { 23 | _ = kvstore.Get("foo") 24 | runtime.Gosched() 25 | } 26 | wg.Wait() 27 | } 28 | -------------------------------------------------------------------------------- /kvstore/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test -race 2 | stdout 'WARNING: DATA RACE' 3 | -------------------------------------------------------------------------------- /kvstore/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/kvstore 2 | 3 | go 1.22 4 | -------------------------------------------------------------------------------- /kvstore/2/kvstore.go: -------------------------------------------------------------------------------- 1 | package kvstore 2 | 3 | import "sync" 4 | 5 | type Store struct { 6 | m *sync.Mutex 7 | data map[string]string 8 | } 9 | 10 | func NewStore() *Store { 11 | return &Store{ 12 | m: new(sync.Mutex), 13 | data: map[string]string{}, 14 | } 15 | } 16 | 17 | func (s *Store) Set(k, v string) { 18 | s.m.Lock() 19 | defer s.m.Unlock() 20 | s.data[k] = v 21 | } 22 | 23 | func (s *Store) Get(k string) string { 24 | s.m.Lock() 25 | defer s.m.Unlock() 26 | return s.data[k] 27 | } 28 | -------------------------------------------------------------------------------- /kvstore/2/kvstore_test.go: -------------------------------------------------------------------------------- 1 | package kvstore_test 2 | 3 | import ( 4 | "runtime" 5 | "strconv" 6 | "sync" 7 | "testing" 8 | 9 | "github.com/bitfield/kvstore" 10 | ) 11 | 12 | func TestKVStore_HasNoDataRace(t *testing.T) { 13 | t.Parallel() 14 | var wg sync.WaitGroup 15 | wg.Add(1) 16 | s := kvstore.NewStore() 17 | go func() { 18 | for i := range 1000 { 19 | s.Set("foo", strconv.Itoa(i)) 20 | } 21 | wg.Done() 22 | }() 23 | for range 1000 { 24 | _ = s.Get("foo") 25 | runtime.Gosched() 26 | } 27 | wg.Wait() 28 | } 29 | -------------------------------------------------------------------------------- /kvstore/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test -race 2 | -------------------------------------------------------------------------------- /load/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/load 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /load/1/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /load/1/load.go: -------------------------------------------------------------------------------- 1 | package load 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | ) 7 | 8 | type LoadTester struct { 9 | URL string 10 | Output, ErrOutput io.Writer 11 | HTTPClient *http.Client 12 | Fanout int 13 | UserAgent string 14 | } 15 | 16 | func NewLoadTester(URL string, stdout, stderr io.Writer, httpClient *http.Client, fanout int, userAgent string) *LoadTester { 17 | return &LoadTester{ 18 | URL: URL, 19 | Output: stdout, 20 | ErrOutput: stderr, 21 | HTTPClient: httpClient, 22 | Fanout: fanout, 23 | UserAgent: userAgent, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /load/1/load_test.go: -------------------------------------------------------------------------------- 1 | package load_test 2 | 3 | import ( 4 | "net/http" 5 | "os" 6 | "testing" 7 | 8 | "github.com/bitfield/load" 9 | "github.com/google/go-cmp/cmp" 10 | "github.com/google/go-cmp/cmp/cmpopts" 11 | ) 12 | 13 | func TestNewLoadTester_SetsCorrectConfig(t *testing.T) { 14 | t.Parallel() 15 | want := &load.LoadTester{ 16 | URL: "https://example.com", 17 | Output: os.Stdout, 18 | ErrOutput: os.Stderr, 19 | HTTPClient: http.DefaultClient, 20 | Fanout: 10, 21 | UserAgent: "loadtest", 22 | } 23 | got := load.NewLoadTester("https://example.com", os.Stdout, os.Stderr, http.DefaultClient, 10, "loadtest") 24 | if !cmp.Equal(want, got, cmpopts.IgnoreUnexported(os.File{})) { 25 | t.Error(cmp.Diff(want, got, cmpopts.IgnoreUnexported(os.File{}))) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /load/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /load/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/load 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /load/2/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /load/2/load.go: -------------------------------------------------------------------------------- 1 | package load 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "os" 7 | ) 8 | 9 | type LoadTester struct { 10 | URL string 11 | Output, ErrOutput io.Writer 12 | HTTPClient *http.Client 13 | Fanout int 14 | UserAgent string 15 | } 16 | 17 | func NewLoadTester(URL string, opts ...TesterOption) *LoadTester { 18 | lt := &LoadTester{ 19 | URL: URL, 20 | Output: os.Stdout, 21 | ErrOutput: os.Stderr, 22 | HTTPClient: http.DefaultClient, 23 | Fanout: 10, 24 | UserAgent: "load v2", 25 | } 26 | for _, opt := range opts { 27 | opt(lt) 28 | } 29 | return lt 30 | } 31 | 32 | type TesterOption func(*LoadTester) 33 | 34 | func WithOutput(w io.Writer) TesterOption { 35 | return func(lt *LoadTester) { 36 | lt.Output = w 37 | } 38 | } 39 | 40 | func WithErrOutput(w io.Writer) TesterOption { 41 | return func(lt *LoadTester) { 42 | lt.ErrOutput = w 43 | } 44 | } 45 | 46 | func WithHTTPClient(c *http.Client) TesterOption { 47 | return func(lt *LoadTester) { 48 | lt.HTTPClient = c 49 | } 50 | } 51 | 52 | func WithConcurrentRequests(fanout int) TesterOption { 53 | return func(lt *LoadTester) { 54 | lt.Fanout = fanout 55 | } 56 | } 57 | 58 | func WithUserAgent(agent string) TesterOption { 59 | return func(lt *LoadTester) { 60 | lt.UserAgent = agent 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /load/2/load_test.go: -------------------------------------------------------------------------------- 1 | package load_test 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | "os" 7 | "testing" 8 | "time" 9 | 10 | "github.com/bitfield/load" 11 | 12 | "github.com/google/go-cmp/cmp" 13 | "github.com/google/go-cmp/cmp/cmpopts" 14 | ) 15 | 16 | func TestNewLoadTester_SetsCorrectConfig(t *testing.T) { 17 | t.Parallel() 18 | buf := new(bytes.Buffer) 19 | want := &load.LoadTester{ 20 | URL: "https://example.com", 21 | Output: buf, 22 | ErrOutput: buf, 23 | HTTPClient: &http.Client{Timeout: time.Second}, 24 | Fanout: 20, 25 | UserAgent: "loadtest", 26 | } 27 | got := load.NewLoadTester("https://example.com", 28 | load.WithOutput(buf), 29 | load.WithErrOutput(buf), 30 | load.WithHTTPClient(&http.Client{Timeout: time.Second}), 31 | load.WithConcurrentRequests(20), 32 | load.WithUserAgent("loadtest"), 33 | ) 34 | ignore := cmpopts.IgnoreUnexported(os.File{}, bytes.Buffer{}) 35 | if !cmp.Equal(want, got, ignore) { 36 | t.Error(cmp.Diff(want, got, ignore)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /load/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /order/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/order 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /order/1/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/order/1/go.sum -------------------------------------------------------------------------------- /order/1/order_test.go: -------------------------------------------------------------------------------- 1 | package order_test 2 | 3 | import ( 4 | "maps" 5 | "slices" 6 | "testing" 7 | ) 8 | 9 | func TestMapsHaveSameContents(t *testing.T) { 10 | t.Parallel() 11 | want := map[int]bool{1: true, 2: false} 12 | got := map[int]bool{2: false, 1: true} 13 | if !maps.Equal(want, got) { 14 | t.Errorf("want %v, got %v", want, got) 15 | } 16 | } 17 | 18 | func TestSlicesHaveSameElementsInSameOrder(t *testing.T) { 19 | t.Parallel() 20 | want := []int{1, 2, 3} 21 | got := []int{3, 2, 1} 22 | if !slices.Equal(want, got) { 23 | t.Errorf("want %v, got %v", want, got) 24 | } 25 | } 26 | 27 | func TestSlicesHaveSameElementsInAnyOrder(t *testing.T) { 28 | t.Parallel() 29 | want := []int{1, 2, 3} 30 | got := []int{3, 2, 1} 31 | slices.Sort(want) 32 | slices.Sort(got) 33 | if !slices.Equal(want, got) { 34 | t.Errorf("want %v, got %v", want, got) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /order/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test -run 'Maps|Slices.*AnyOrder' 2 | ! exec go test -run 'Slices.*SameOrder' 3 | -------------------------------------------------------------------------------- /past/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/past 2 | 3 | go 1.19 4 | 5 | require github.com/stretchr/testify v1.8.4 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | -------------------------------------------------------------------------------- /past/1/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 6 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /past/1/past.go: -------------------------------------------------------------------------------- 1 | package past 2 | 3 | import "time" 4 | 5 | func OneHourAgo() time.Time { 6 | return time.Now().Add(-time.Hour) 7 | } 8 | -------------------------------------------------------------------------------- /past/1/past_test.go: -------------------------------------------------------------------------------- 1 | package past_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/bitfield/past" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestOneHourAgo_ReturnsExpectedTime(t *testing.T) { 13 | t.Parallel() 14 | now := time.Now() 15 | then := past.OneHourAgo() 16 | if now.Hour() == 0 { 17 | assert.Equal(t, 23, then.Hour()) 18 | assert.Equal(t, now.Day()-1, then.Day()) 19 | } else { 20 | assert.Equal(t, now.Hour()-1, then.Hour()) 21 | assert.Equal(t, now.Day(), then.Day()) 22 | } 23 | assert.Equal(t, now.Month(), then.Month()) 24 | assert.Equal(t, now.Year(), then.Year()) 25 | } 26 | -------------------------------------------------------------------------------- /past/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /past/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/past 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /past/2/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/past/2/go.sum -------------------------------------------------------------------------------- /past/2/past.go: -------------------------------------------------------------------------------- 1 | package past 2 | 3 | import "time" 4 | 5 | func OneHourAgo(tm time.Time) time.Time { 6 | return tm.Add(-time.Hour) 7 | } 8 | -------------------------------------------------------------------------------- /past/2/past_test.go: -------------------------------------------------------------------------------- 1 | package past_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/bitfield/past" 8 | ) 9 | 10 | func TestOneHourAgo_ReturnsExpectedTime(t *testing.T) { 11 | t.Parallel() 12 | testTime, err := time.Parse(time.RFC3339, "2022-08-05T00:50:25Z") 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | want, err := time.Parse(time.RFC3339, "2022-08-04T23:50:25Z") 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | got := past.OneHourAgo(testTime) 21 | if !want.Equal(got) { 22 | t.Errorf("want %v, got %v", want, got) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /past/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /past/3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/past 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /past/3/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/past/3/go.sum -------------------------------------------------------------------------------- /past/3/past.go: -------------------------------------------------------------------------------- 1 | package past 2 | 3 | import "time" 4 | 5 | // careful: not 'time.Now()' 6 | var Now = time.Now 7 | 8 | func OneHourAgo() time.Time { 9 | return Now().Add(-time.Hour) 10 | } 11 | -------------------------------------------------------------------------------- /past/3/past_test.go: -------------------------------------------------------------------------------- 1 | package past_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/bitfield/past" 8 | ) 9 | 10 | func TestOneHourAgo_ReturnsExpectedTime(t *testing.T) { 11 | testTime, err := time.Parse(time.RFC3339, "2022-08-05T00:50:25Z") 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | past.Now = func() time.Time { 16 | return testTime 17 | } 18 | want, err := time.Parse(time.RFC3339, "2022-08-04T23:50:25Z") 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | got := past.OneHourAgo() 23 | if !want.Equal(got) { 24 | t.Errorf("want %v, got %v", want, got) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /past/3/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /past/4/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/past 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /past/4/past.go: -------------------------------------------------------------------------------- 1 | package past 2 | 3 | import "time" 4 | 5 | func OneHourAgo() time.Time { 6 | return time.Now().Add(-time.Hour) 7 | } 8 | -------------------------------------------------------------------------------- /past/4/past_test.go: -------------------------------------------------------------------------------- 1 | package past_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/bitfield/past" 8 | ) 9 | 10 | func TestOneHourAgo_ReturnsExpectedTime(t *testing.T) { 11 | t.Parallel() 12 | now := time.Now() 13 | want := now.Add(-time.Hour) 14 | got := past.OneHourAgo() 15 | delta := want.Sub(got).Abs() 16 | if delta > 10*time.Microsecond { 17 | t.Errorf("want %v, got %v", want, got) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /past/4/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /reader/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/reader 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /reader/1/reader.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import "io" 4 | 5 | func ReadAll(r io.Reader) ([]byte, error) { 6 | data, err := io.ReadAll(r) 7 | if err != nil { 8 | return nil, err 9 | } 10 | return data, nil 11 | } 12 | -------------------------------------------------------------------------------- /reader/1/reader_test.go: -------------------------------------------------------------------------------- 1 | package reader_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/bitfield/reader" 8 | ) 9 | 10 | func TestReadAll_ReturnsAnyReadError(t *testing.T) { 11 | t.Parallel() 12 | input := strings.NewReader("any old data") 13 | _, err := reader.ReadAll(input) 14 | if err == nil { 15 | t.Error("want error for broken reader, got nil") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /reader/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'want error for broken reader, got nil' 3 | -------------------------------------------------------------------------------- /reader/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/reader 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /reader/2/reader.go: -------------------------------------------------------------------------------- 1 | package reader 2 | 3 | import "io" 4 | 5 | func ReadAll(r io.Reader) ([]byte, error) { 6 | data, err := io.ReadAll(r) 7 | if err != nil { 8 | return nil, err 9 | } 10 | return data, nil 11 | } 12 | -------------------------------------------------------------------------------- /reader/2/reader_test.go: -------------------------------------------------------------------------------- 1 | package reader_test 2 | 3 | import ( 4 | "io" 5 | "testing" 6 | 7 | "github.com/bitfield/reader" 8 | ) 9 | 10 | type errReader struct{} 11 | 12 | func (errReader) Read([]byte) (int, error) { 13 | return 0, io.ErrUnexpectedEOF 14 | } 15 | 16 | func TestReadAll_ReturnsAnyReadError(t *testing.T) { 17 | t.Parallel() 18 | input := errReader{} // always returns error 19 | _, err := reader.ReadAll(input) 20 | if err == nil { 21 | t.Error("want error for broken reader, got nil") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /reader/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /req/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/req 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /req/1/req.go: -------------------------------------------------------------------------------- 1 | package req 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | ) 7 | 8 | var ErrRateLimit = errors.New("rate limit") 9 | 10 | func Request(URL string) error { 11 | resp, err := http.Get(URL) 12 | if err != nil { 13 | return err 14 | } 15 | defer resp.Body.Close() 16 | if resp.StatusCode == http.StatusTooManyRequests { 17 | return ErrRateLimit 18 | } 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /req/1/req_test.go: -------------------------------------------------------------------------------- 1 | package req_test 2 | 3 | import ( 4 | "errors" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | 9 | "github.com/bitfield/req" 10 | ) 11 | 12 | func TestRequestReturnsErrRateLimitWhenRatelimited(t *testing.T) { 13 | t.Parallel() 14 | ts := newRateLimitingServer() 15 | defer ts.Close() 16 | err := req.Request(ts.URL) 17 | if !errors.Is(err, req.ErrRateLimit) { 18 | t.Errorf("wrong error: %v", err) 19 | } 20 | } 21 | 22 | func newRateLimitingServer() *httptest.Server { 23 | return httptest.NewServer(http.HandlerFunc( 24 | func(w http.ResponseWriter, r *http.Request) { 25 | w.WriteHeader(http.StatusTooManyRequests) 26 | })) 27 | } 28 | -------------------------------------------------------------------------------- /req/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /run_all_testscripts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for TOPIC in */; do 4 | for LISTING in ${TOPIC}[0-9+]; do 5 | # cd $LISTING && go get -t -u && go mod tidy && cd - 6 | ./run_testscript.sh $LISTING 7 | done 8 | done -------------------------------------------------------------------------------- /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}-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 || exit 13 | rm $SCRIPT 14 | -------------------------------------------------------------------------------- /runes/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/runes 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /runes/1/runes.go: -------------------------------------------------------------------------------- 1 | package runes 2 | 3 | func FirstRune(s string) rune { 4 | return 0 5 | } 6 | -------------------------------------------------------------------------------- /runes/1/runes_test.go: -------------------------------------------------------------------------------- 1 | package runes_test 2 | 3 | import ( 4 | "testing" 5 | "unicode/utf8" 6 | 7 | "github.com/bitfield/runes" 8 | ) 9 | 10 | func FuzzFirstRune(f *testing.F) { 11 | f.Add("Hello") 12 | f.Add("world") 13 | f.Fuzz(func(t *testing.T, s string) { 14 | got := runes.FirstRune(s) 15 | want, _ := utf8.DecodeRuneInString(s) 16 | if want == utf8.RuneError { 17 | t.Skip() // don't bother testing invalid runes 18 | } 19 | if want != got { 20 | t.Errorf("given %q (0x%[1]x): want '%c' (0x%[2]x)", 21 | s, want) 22 | t.Errorf("got '%c' (0x%[1]x)", got) 23 | } 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /runes/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'given "world"' 3 | stdout '0x0' 4 | -------------------------------------------------------------------------------- /runes/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/runes 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /runes/2/runes.go: -------------------------------------------------------------------------------- 1 | package runes 2 | 3 | func FirstRune(s string) rune { 4 | return rune(s[0]) 5 | } 6 | -------------------------------------------------------------------------------- /runes/2/runes_test.go: -------------------------------------------------------------------------------- 1 | package runes_test 2 | 3 | import ( 4 | "testing" 5 | "unicode/utf8" 6 | 7 | "github.com/bitfield/runes" 8 | ) 9 | 10 | func FuzzFirstRune(f *testing.F) { 11 | f.Add("Hello") 12 | f.Add("world") 13 | f.Fuzz(func(t *testing.T, s string) { 14 | got := runes.FirstRune(s) 15 | want, _ := utf8.DecodeRuneInString(s) 16 | if want == utf8.RuneError { 17 | t.Skip() // don't bother testing invalid runes 18 | } 19 | if want != got { 20 | t.Errorf("given %q (0x%[1]x): want '%c' (0x%[2]x)", 21 | s, want) 22 | t.Errorf("got '%c' (0x%[1]x)", got) 23 | } 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /runes/2/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'panic: runtime error: index out of range' 3 | -------------------------------------------------------------------------------- /runes/2/testdata/fuzz/FuzzFirstRune/5838cdfae7b16cde2707c04599b62223e7bade8dafdd8a1c53b8f881e8f79d99: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("") 3 | -------------------------------------------------------------------------------- /runes/3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/runes 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /runes/3/runes.go: -------------------------------------------------------------------------------- 1 | package runes 2 | 3 | import "unicode/utf8" 4 | 5 | func FirstRune(s string) rune { 6 | if s == "" { 7 | return utf8.RuneError 8 | } 9 | return rune(s[0]) 10 | } 11 | -------------------------------------------------------------------------------- /runes/3/runes_test.go: -------------------------------------------------------------------------------- 1 | package runes_test 2 | 3 | import ( 4 | "testing" 5 | "unicode/utf8" 6 | 7 | "github.com/bitfield/runes" 8 | ) 9 | 10 | func FuzzFirstRune(f *testing.F) { 11 | f.Add("Hello") 12 | f.Add("world") 13 | f.Fuzz(func(t *testing.T, s string) { 14 | got := runes.FirstRune(s) 15 | want, _ := utf8.DecodeRuneInString(s) 16 | if want == utf8.RuneError { 17 | t.Skip() // don't bother testing invalid runes 18 | } 19 | if want != got { 20 | t.Errorf("given %q (0x%[1]x): want '%c' (0x%[2]x)", 21 | s, want) 22 | t.Errorf("got '%c' (0x%[1]x)", got) 23 | } 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /runes/3/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /runes/3/testdata/fuzz/FuzzFirstRune/5838cdfae7b16cde2707c04599b62223e7bade8dafdd8a1c53b8f881e8f79d99: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("") 3 | -------------------------------------------------------------------------------- /runes/4/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/runes 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /runes/4/runes.go: -------------------------------------------------------------------------------- 1 | package runes 2 | 3 | import "unicode/utf8" 4 | 5 | func FirstRune(s string) rune { 6 | for _, r := range s { 7 | return r 8 | } 9 | return utf8.RuneError 10 | } 11 | -------------------------------------------------------------------------------- /runes/4/runes_test.go: -------------------------------------------------------------------------------- 1 | package runes_test 2 | 3 | import ( 4 | "testing" 5 | "unicode/utf8" 6 | 7 | "github.com/bitfield/runes" 8 | ) 9 | 10 | func FuzzFirstRune(f *testing.F) { 11 | f.Add("Hello") 12 | f.Add("world") 13 | f.Fuzz(func(t *testing.T, s string) { 14 | got := runes.FirstRune(s) 15 | want, _ := utf8.DecodeRuneInString(s) 16 | if want == utf8.RuneError { 17 | t.Skip() // don't bother testing invalid runes 18 | } 19 | if want != got { 20 | t.Errorf("given %q (0x%[1]x): want '%c' (0x%[2]x)", 21 | s, want) 22 | t.Errorf("got '%c' (0x%[1]x)", got) 23 | } 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /runes/4/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /runes/4/testdata/fuzz/FuzzFirstRune/5838cdfae7b16cde2707c04599b62223e7bade8dafdd8a1c53b8f881e8f79d99: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("") 3 | -------------------------------------------------------------------------------- /runes/4/testdata/fuzz/FuzzFirstRune/793418c62f3fea2e701c96ad38ff9dab4c84092885817870c7c11508d02944c1: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("ʭ") 3 | -------------------------------------------------------------------------------- /script/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/script 2 | 3 | go 1.19 4 | 5 | require github.com/rogpeppe/go-internal v1.12.0 6 | 7 | require ( 8 | golang.org/x/sys v0.16.0 // indirect 9 | golang.org/x/tools v0.17.0 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /script/1/go.sum: -------------------------------------------------------------------------------- 1 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 2 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 3 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 4 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 5 | golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= 6 | golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= 7 | -------------------------------------------------------------------------------- /script/1/script_test.go: -------------------------------------------------------------------------------- 1 | package script_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/rogpeppe/go-internal/testscript" 7 | ) 8 | 9 | func Test(t *testing.T) { 10 | testscript.Run(t, testscript.Params{ 11 | Dir: "testdata/script", 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /script/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /script/1/testdata/script/cat.txtar: -------------------------------------------------------------------------------- 1 | ! exec cat doesntexist 2 | stderr 'cat: doesntexist: No such file or directory' 3 | ! stdout . 4 | -------------------------------------------------------------------------------- /script/1/testdata/script/echo.txtar: -------------------------------------------------------------------------------- 1 | exec echo success 2 | stdout 'success' 3 | ! stderr . 4 | -------------------------------------------------------------------------------- /script/1/testdata/script/hello.txtar: -------------------------------------------------------------------------------- 1 | exec echo 'hello world' 2 | stdout 'hello world\n' 3 | -------------------------------------------------------------------------------- /serve/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/serve 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /serve/1/serve.go: -------------------------------------------------------------------------------- 1 | package serve 2 | 3 | func ListenAsync(addr string) {} 4 | -------------------------------------------------------------------------------- /serve/1/serve_test.go: -------------------------------------------------------------------------------- 1 | package serve_test 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | 7 | "github.com/bitfield/serve" 8 | ) 9 | 10 | func TestListenAsync_ListensOnGivenAddr(t *testing.T) { 11 | t.Parallel() 12 | addr := randomLocalAddr(t) 13 | serve.ListenAsync(addr) 14 | _, err := net.Dial("tcp", addr) 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | } 19 | 20 | func randomLocalAddr(t *testing.T) string { 21 | t.Helper() 22 | l, err := net.Listen("tcp", "localhost:0") 23 | if err != nil { 24 | t.Fatal(err) 25 | } 26 | defer l.Close() 27 | return l.Addr().String() 28 | } 29 | -------------------------------------------------------------------------------- /serve/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'connect: connection refused' 3 | -------------------------------------------------------------------------------- /serve/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/serve 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /serve/2/serve.go: -------------------------------------------------------------------------------- 1 | package serve 2 | 3 | func ListenAsync(addr string) {} 4 | -------------------------------------------------------------------------------- /serve/2/serve_test.go: -------------------------------------------------------------------------------- 1 | package serve_test 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | "time" 7 | 8 | "github.com/bitfield/serve" 9 | ) 10 | 11 | func TestListenAsync_ListensOnGivenAddrSoon(t *testing.T) { 12 | t.Parallel() 13 | addr := randomLocalAddr(t) 14 | serve.ListenAsync(addr) 15 | time.Sleep(10 * time.Millisecond) 16 | _, err := net.Dial("tcp", addr) 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | } 21 | 22 | func randomLocalAddr(t *testing.T) string { 23 | t.Helper() 24 | l, err := net.Listen("tcp", "localhost:0") 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | defer l.Close() 29 | return l.Addr().String() 30 | } 31 | -------------------------------------------------------------------------------- /serve/2/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'connect: connection refused' 3 | -------------------------------------------------------------------------------- /serve/3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/serve 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /serve/3/serve.go: -------------------------------------------------------------------------------- 1 | package serve 2 | 3 | import "net" 4 | 5 | func ListenAsync(addr string) { 6 | go net.Listen("tcp", addr) 7 | } 8 | -------------------------------------------------------------------------------- /serve/3/serve_test.go: -------------------------------------------------------------------------------- 1 | package serve_test 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | "time" 7 | 8 | "github.com/bitfield/serve" 9 | ) 10 | 11 | func TestListenAsync_ListensEventually(t *testing.T) { 12 | t.Parallel() 13 | addr := randomLocalAddr(t) 14 | serve.ListenAsync(addr) 15 | _, err := net.Dial("tcp", addr) 16 | for err != nil { 17 | t.Log("retrying") 18 | time.Sleep(time.Millisecond) 19 | _, err = net.Dial("tcp", addr) 20 | } 21 | } 22 | 23 | func randomLocalAddr(t *testing.T) string { 24 | t.Helper() 25 | l, err := net.Listen("tcp", "localhost:0") 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | defer l.Close() 30 | return l.Addr().String() 31 | } 32 | -------------------------------------------------------------------------------- /serve/3/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /serve/4/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/serve 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /serve/4/serve.go: -------------------------------------------------------------------------------- 1 | package serve 2 | 3 | func ListenAsync(addr string) {} 4 | -------------------------------------------------------------------------------- /serve/4/serve_test.go: -------------------------------------------------------------------------------- 1 | package serve_test 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | "time" 7 | 8 | "github.com/bitfield/serve" 9 | ) 10 | 11 | func TestListenAsync_ListensWithinReasonableTime(t *testing.T) { 12 | t.Parallel() 13 | addr := randomLocalAddr(t) 14 | serve.ListenAsync(addr) 15 | timeout := time.NewTimer(100 * time.Millisecond) 16 | defer timeout.Stop() 17 | _, err := net.Dial("tcp", addr) 18 | for err != nil { 19 | select { 20 | case <-timeout.C: 21 | t.Fatal("timed out") 22 | default: 23 | t.Log("retrying: ", err) 24 | time.Sleep(time.Millisecond) 25 | _, err = net.Dial("tcp", addr) 26 | } 27 | } 28 | } 29 | 30 | func randomLocalAddr(t *testing.T) string { 31 | t.Helper() 32 | l, err := net.Listen("tcp", "localhost:0") 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | defer l.Close() 37 | return l.Addr().String() 38 | } 39 | -------------------------------------------------------------------------------- /serve/4/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'timed out' 3 | -------------------------------------------------------------------------------- /serve/5/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/serve 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /serve/5/serve.go: -------------------------------------------------------------------------------- 1 | package serve 2 | 3 | func ListenAsync(addr string) {} 4 | -------------------------------------------------------------------------------- /serve/5/serve_test.go: -------------------------------------------------------------------------------- 1 | package serve_test 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | "time" 7 | 8 | "github.com/bitfield/serve" 9 | ) 10 | 11 | func TestListenAsync_ListensWithinReasonableTime(t *testing.T) { 12 | t.Parallel() 13 | addr := randomLocalAddr(t) 14 | serve.ListenAsync(addr) 15 | waitForServer(t, addr) 16 | // test whatever we're really testing 17 | } 18 | 19 | func randomLocalAddr(t *testing.T) string { 20 | t.Helper() 21 | l, err := net.Listen("tcp", "localhost:0") 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | defer l.Close() 26 | return l.Addr().String() 27 | } 28 | 29 | func waitForServer(t *testing.T, addr string) { 30 | t.Helper() 31 | timeout := time.NewTimer(100 * time.Millisecond) 32 | defer timeout.Stop() 33 | _, err := net.Dial("tcp", addr) 34 | for err != nil { 35 | select { 36 | case <-timeout.C: 37 | t.Fatal("timed out") 38 | default: 39 | t.Log("retrying: ", err) 40 | time.Sleep(time.Millisecond) 41 | _, err = net.Dial("tcp", addr) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /serve/5/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'timed out' 3 | -------------------------------------------------------------------------------- /service/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/service 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /service/1/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | func Start() {} 4 | 5 | func Running() bool { 6 | return false 7 | } 8 | -------------------------------------------------------------------------------- /service/1/service_test.go: -------------------------------------------------------------------------------- 1 | package service_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/service" 7 | ) 8 | 9 | func TestRunningIsTrueWhenServiceIsRunning(t *testing.T) { 10 | t.Parallel() 11 | service.Start() 12 | if !service.Running() { 13 | t.Error(false) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /service/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'false' 3 | -------------------------------------------------------------------------------- /square/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/square 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /square/1/square.go: -------------------------------------------------------------------------------- 1 | package square 2 | 3 | func Square(n int) int { 4 | if n == 21 { 5 | return -441 6 | } 7 | return n * n 8 | } 9 | -------------------------------------------------------------------------------- /square/1/square_test.go: -------------------------------------------------------------------------------- 1 | package square_test 2 | 3 | import ( 4 | "math/rand/v2" 5 | "strconv" 6 | "testing" 7 | 8 | "github.com/bitfield/square" 9 | ) 10 | 11 | func TestSquareGivesNonNegativeResult(t *testing.T) { 12 | t.Parallel() 13 | inputs := rand.Perm(100) 14 | for _, n := range inputs { 15 | t.Run(strconv.Itoa(n), func(t *testing.T) { 16 | got := square.Square(n) 17 | if got < 0 { 18 | t.Errorf("Square(%d) is negative: %d", n, got) 19 | } 20 | }) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /square/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'Square\(21\) is negative: -441' 3 | -------------------------------------------------------------------------------- /square/1/testdata/output.golden: -------------------------------------------------------------------------------- 1 | The thing is, Bob, it’s not that I’m lazy. It’s that I just don’t care. 2 | -------------------------------------------------------------------------------- /store/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/store 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /store/1/store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | type Store struct{} 8 | 9 | func Open(path string) (*Store, error) { 10 | f, err := os.Open(path) 11 | if err != nil { 12 | return nil, err 13 | } 14 | f.Close() 15 | return &Store{}, nil 16 | } 17 | -------------------------------------------------------------------------------- /store/1/store_test.go: -------------------------------------------------------------------------------- 1 | package store_test 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/bitfield/store" 8 | ) 9 | 10 | func TestOpenGivesNonNilErrorForBogusFile(t *testing.T) { 11 | t.Parallel() 12 | _, err := store.Open("bogus file") 13 | if err == nil { 14 | t.Error("want error opening bogus store file") 15 | } 16 | } 17 | 18 | func TestOpenGivesSpecificErrorForBogusFile(t *testing.T) { 19 | t.Parallel() 20 | want := errors.New("open bogus: no such file or directory") 21 | _, got := store.Open("bogus") 22 | if got != want { 23 | t.Errorf("wrong error: %v", got) 24 | } 25 | } 26 | 27 | func TestOpenGivesSpecificErrorStringForBogusFile(t *testing.T) { 28 | t.Parallel() 29 | want := errors.New("open bogus: no such file or directory") 30 | _, got := store.Open("bogus") 31 | if got.Error() != want.Error() { 32 | t.Errorf("wrong error: %v", got) 33 | } 34 | } 35 | 36 | func TestOneErrorValueEqualsAnother(t *testing.T) { 37 | t.Parallel() 38 | want := errors.New("Go home, Go, you're drunk") 39 | got := errors.New("Go home, Go, you're drunk") 40 | if got != want { 41 | t.Errorf("wrong error: %v", got) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /store/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout -count=1 'wrong error: open bogus: no such file or directory' 3 | stdout 'wrong error: Go home, Go, you''re drunk' 4 | -------------------------------------------------------------------------------- /store/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/store 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /store/2/store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | ) 7 | 8 | var ErrUnopenable = errors.New("can't open store file") 9 | 10 | type Store struct{} 11 | 12 | func Open(path string) (*Store, error) { 13 | f, err := os.Open(path) 14 | if err != nil { 15 | return nil, ErrUnopenable // losing information about 'err' 16 | } 17 | f.Close() 18 | return &Store{}, nil 19 | } 20 | -------------------------------------------------------------------------------- /store/2/store_test.go: -------------------------------------------------------------------------------- 1 | package store_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/store" 7 | ) 8 | 9 | func TestOpenGivesErrUnopenableForBogusFile(t *testing.T) { 10 | t.Parallel() 11 | _, err := store.Open("bogus") 12 | if err != store.ErrUnopenable { 13 | t.Errorf("wrong error: %v", err) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /store/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /thing/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/thing 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /thing/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /thing/1/thing.go: -------------------------------------------------------------------------------- 1 | package thing 2 | 3 | type Thing struct { 4 | X, Y, Z int 5 | } 6 | 7 | func NewThing(x, y, z int) (*Thing, error) { 8 | return nil, nil 9 | } 10 | -------------------------------------------------------------------------------- /thing/1/thing_test.go: -------------------------------------------------------------------------------- 1 | package thing_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/thing" 7 | ) 8 | 9 | func TestNewThing(t *testing.T) { 10 | t.Parallel() 11 | _, err := thing.NewThing(1, 2, 3) 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /thing/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/thing 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /thing/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /thing/2/thing.go: -------------------------------------------------------------------------------- 1 | package thing 2 | 3 | type Thing struct { 4 | X, Y, Z int 5 | } 6 | 7 | func NewThing(x, y, z int) (*Thing, error) { 8 | return &Thing{}, nil 9 | } 10 | -------------------------------------------------------------------------------- /thing/2/thing_test.go: -------------------------------------------------------------------------------- 1 | package thing_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/thing" 7 | ) 8 | 9 | func TestNewThing_ReturnsNonNilThingAndNoError(t *testing.T) { 10 | t.Parallel() 11 | got, err := thing.NewThing(1, 2, 3) 12 | if err != nil { 13 | t.Fatal(err) 14 | } 15 | if got == nil { 16 | t.Error("want non-nil *Thing, got nil") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /thing/3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/thing 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /thing/3/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'want X: 1, got X: 0' 3 | stdout 'want Y: 2, got Y: 0' 4 | stdout 'want Z: 3, got Z: 0' 5 | -------------------------------------------------------------------------------- /thing/3/thing.go: -------------------------------------------------------------------------------- 1 | package thing 2 | 3 | type Thing struct { 4 | X, Y, Z int 5 | } 6 | 7 | func NewThing(x, y, z int) (*Thing, error) { 8 | return &Thing{}, nil 9 | } 10 | -------------------------------------------------------------------------------- /thing/3/thing_test.go: -------------------------------------------------------------------------------- 1 | package thing_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/thing" 7 | ) 8 | 9 | func TestNewThing_ReturnsThingWithGivenXYZValues(t *testing.T) { 10 | t.Parallel() 11 | x, y, z := 1, 2, 3 12 | got, err := thing.NewThing(x, y, z) 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | if got.X != x { 17 | t.Errorf("want X: %v, got X: %v", x, got.X) 18 | } 19 | if got.Y != y { 20 | t.Errorf("want Y: %v, got Y: %v", y, got.Y) 21 | } 22 | if got.Z != z { 23 | t.Errorf("want Z: %v, got Z: %v", z, got.Z) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /thing/4/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/thing 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /thing/4/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /thing/4/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'X: 1' 3 | stdout 'X: 0' 4 | stdout 'Y: 2' 5 | stdout 'Y: 0' 6 | stdout 'Z: 3' 7 | stdout 'Z: 0' 8 | -------------------------------------------------------------------------------- /thing/4/thing.go: -------------------------------------------------------------------------------- 1 | package thing 2 | 3 | type Thing struct { 4 | X, Y, Z int 5 | } 6 | 7 | func NewThing(x, y, z int) (*Thing, error) { 8 | return &Thing{}, nil 9 | } 10 | -------------------------------------------------------------------------------- /thing/4/thing_test.go: -------------------------------------------------------------------------------- 1 | package thing_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/thing" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestNewThing_ReturnsThingWithGivenXYZValues(t *testing.T) { 12 | t.Parallel() 13 | x, y, z := 1, 2, 3 14 | want := &thing.Thing{ 15 | X: x, 16 | Y: y, 17 | Z: z, 18 | } 19 | got, err := thing.NewThing(x, y, z) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | if !cmp.Equal(want, got) { 24 | t.Error(cmp.Diff(want, got)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /thing/5/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/thing 2 | 3 | go 1.19 4 | 5 | require github.com/google/go-cmp v0.6.0 6 | -------------------------------------------------------------------------------- /thing/5/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 2 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | -------------------------------------------------------------------------------- /thing/5/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /thing/5/thing.go: -------------------------------------------------------------------------------- 1 | package thing 2 | 3 | type Thing struct { 4 | X, Y, Z int 5 | } 6 | 7 | func NewThing(x, y, z int) (*Thing, error) { 8 | return &Thing{ 9 | X: x, 10 | Y: y, 11 | Z: z, 12 | }, nil 13 | } 14 | -------------------------------------------------------------------------------- /thing/5/thing_test.go: -------------------------------------------------------------------------------- 1 | package thing_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/thing" 7 | 8 | "github.com/google/go-cmp/cmp" 9 | ) 10 | 11 | func TestNewThing_ReturnsThingWithGivenXYZValues(t *testing.T) { 12 | t.Parallel() 13 | x, y, z := 1, 2, 3 14 | want := &thing.Thing{ 15 | X: x, 16 | Y: y, 17 | Z: z, 18 | } 19 | got, err := thing.NewThing(x, y, z) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | if !cmp.Equal(want, got) { 24 | t.Error(cmp.Diff(want, got)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /timer/1/cmd/timer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/bitfield/timer" 7 | ) 8 | 9 | func main() { 10 | interval := timer.ParseArgs(os.Args) 11 | timer.Sleep(interval) 12 | } 13 | -------------------------------------------------------------------------------- /timer/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/timer 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /timer/1/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/timer/1/go.sum -------------------------------------------------------------------------------- /timer/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | exec go run ./cmd/timer/main.go 1ms 3 | stdout 'Starting a timer for 1ms...' 4 | stdout 'done' 5 | -------------------------------------------------------------------------------- /timer/1/timer.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func ParseArgs(args []string) time.Duration { 9 | if len(args) < 2 { 10 | return 0 11 | } 12 | interval, err := time.ParseDuration(args[1]) 13 | if err != nil { 14 | return 0 15 | } 16 | return interval 17 | } 18 | 19 | func Sleep(interval time.Duration) { 20 | fmt.Printf("Starting a timer for %s...\n", interval) 21 | time.Sleep(interval) 22 | fmt.Println("...done") 23 | } 24 | -------------------------------------------------------------------------------- /timer/1/timer_test.go: -------------------------------------------------------------------------------- 1 | package timer_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/bitfield/timer" 8 | ) 9 | 10 | func TestParseArgs_SetsTimerToGivenDuration(t *testing.T) { 11 | t.Parallel() 12 | want := 10 * time.Millisecond 13 | got := timer.ParseArgs([]string{"timer", "10ms"}) 14 | if want != got { 15 | t.Errorf("want %v, got %v", want, got) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /timer/2/cmd/timer/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/bitfield/timer" 7 | ) 8 | 9 | func main() { 10 | t := timer.NewTimerFromArgs(os.Args) 11 | t.Sleep() 12 | } 13 | -------------------------------------------------------------------------------- /timer/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/timer 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /timer/2/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/timer/2/go.sum -------------------------------------------------------------------------------- /timer/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | exec go run ./cmd/timer/main.go 1ms 3 | stdout 'Starting a timer for 1ms...' 4 | stdout 'done' 5 | -------------------------------------------------------------------------------- /timer/2/timer.go: -------------------------------------------------------------------------------- 1 | package timer 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type Timer struct { 9 | Interval time.Duration 10 | } 11 | 12 | func (t Timer) Sleep() { 13 | fmt.Printf("Starting a timer for %s...\n", t.Interval) 14 | time.Sleep(t.Interval) 15 | fmt.Println("...done") 16 | } 17 | 18 | func NewTimerFromArgs(args []string) Timer { 19 | if len(args) < 2 { 20 | return Timer{} 21 | } 22 | interval, err := time.ParseDuration(args[1]) 23 | if err != nil { 24 | return Timer{} 25 | } 26 | return Timer{ 27 | Interval: interval, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /timer/2/timer_test.go: -------------------------------------------------------------------------------- 1 | package timer_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/bitfield/timer" 8 | ) 9 | 10 | func TestNewTimerFromArgs_SetsTimerToGivenDuration(t *testing.T) { 11 | t.Parallel() 12 | args := []string{"program", "10s"} 13 | want := timer.Timer{ 14 | Interval: 10 * time.Second, 15 | } 16 | got := timer.NewTimerFromArgs(args) 17 | if want != got { 18 | t.Errorf("want %v, got %v", want, got) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tps/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/tps 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /tps/1/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/tps/1/go.sum -------------------------------------------------------------------------------- /tps/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'lazy' 3 | stdout 'stapler' 4 | -------------------------------------------------------------------------------- /tps/1/testdata/output.golden: -------------------------------------------------------------------------------- 1 | The thing is, Bob, it’s not that I’m lazy. It’s that I just don’t care. 2 | -------------------------------------------------------------------------------- /tps/1/tps.go: -------------------------------------------------------------------------------- 1 | package tps 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | const data = "I believe you have my stapler?" 8 | 9 | func WriteReportFile(path string) { 10 | err := os.WriteFile(path, []byte(data), 0o666) 11 | if err != nil { 12 | panic("Sounds like somebody's got a case of the Mondays!") 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tps/1/tps_test.go: -------------------------------------------------------------------------------- 1 | package tps_test 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "testing" 7 | 8 | "github.com/bitfield/tps" 9 | ) 10 | 11 | func TestWriteReportFile_ProducesCorrectOutputFile(t *testing.T) { 12 | t.Parallel() 13 | output := t.TempDir() + "/" + t.Name() 14 | tps.WriteReportFile(output) 15 | want, err := os.ReadFile("testdata/output.golden") 16 | if err != nil { 17 | t.Fatal(err) 18 | } 19 | got, err := os.ReadFile(output) 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | if !bytes.Equal(want, got) { 24 | t.Errorf("want %q, got %q", want, got) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /user/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/user 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /user/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /user/1/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | type User struct { 4 | Name string 5 | } 6 | 7 | func (u User) NameString() string { 8 | return u.Name 9 | } 10 | -------------------------------------------------------------------------------- /user/1/user_test.go: -------------------------------------------------------------------------------- 1 | package user_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/bitfield/user" 7 | ) 8 | 9 | func ExampleUser() { 10 | u := user.User{Name: "Gopher"} 11 | fmt.Println(u) 12 | // Output: 13 | // {Gopher} 14 | } 15 | 16 | func ExampleUser_NameString() { 17 | u := user.User{Name: "Gopher"} 18 | fmt.Println(u.NameString()) 19 | // Output: 20 | // Gopher 21 | } 22 | -------------------------------------------------------------------------------- /user/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/user 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /user/2/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /user/2/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type User struct { 9 | Name string 10 | } 11 | 12 | var userDB map[string]*User 13 | 14 | var ErrUserNotFound = errors.New("user not found") 15 | 16 | func FindUser(name string) (*User, error) { 17 | user, ok := userDB[name] 18 | if !ok { 19 | return nil, fmt.Errorf("%q: %w", name, ErrUserNotFound) 20 | } 21 | return user, nil 22 | } 23 | -------------------------------------------------------------------------------- /user/2/user_test.go: -------------------------------------------------------------------------------- 1 | package user_test 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/bitfield/user" 8 | ) 9 | 10 | func TestFindUser_GivesErrUserNotFoundForBogusUser(t *testing.T) { 11 | t.Parallel() 12 | _, err := user.FindUser("bogus user") 13 | if !errors.Is(err, user.ErrUserNotFound) { 14 | t.Errorf("wrong error: %v", err) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /user/3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/user 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /user/3/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /user/3/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type User struct { 8 | Name string 9 | } 10 | 11 | var userDB map[string]*User 12 | 13 | type ErrUserNotFound struct { 14 | User string 15 | } 16 | 17 | func (e ErrUserNotFound) Error() string { 18 | return fmt.Sprintf("user %q not found", e.User) 19 | } 20 | 21 | func FindUser(name string) (*User, error) { 22 | user, ok := userDB[name] 23 | if !ok { 24 | return nil, ErrUserNotFound{User: name} 25 | } 26 | return user, nil 27 | } 28 | -------------------------------------------------------------------------------- /user/3/user_test.go: -------------------------------------------------------------------------------- 1 | package user_test 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/bitfield/user" 8 | ) 9 | 10 | func TestFindUser_GivesErrUserNotFoundForBogusUser(t *testing.T) { 11 | t.Parallel() 12 | _, err := user.FindUser("bogus user") 13 | if !errors.As(err, &user.ErrUserNotFound{}) { 14 | t.Errorf("wrong error: %v", err) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /user/4/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/user 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /user/4/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/user/4/go.sum -------------------------------------------------------------------------------- /user/4/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /user/4/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | type User struct { 4 | Name string 5 | Language string 6 | } 7 | 8 | func New(name string) User { 9 | return User{ 10 | Name: name, 11 | } 12 | } 13 | 14 | var greeting = map[string]string{ 15 | "French": "Bonjour", 16 | "Spanish": "¿Que tal?", 17 | } 18 | 19 | func (u User) Greeting() string { 20 | return greeting[u.Language] 21 | } 22 | -------------------------------------------------------------------------------- /user/4/user_test.go: -------------------------------------------------------------------------------- 1 | package user_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/user" 7 | ) 8 | 9 | func TestGreetingReturnsCorrectGreetingForLanguage(t *testing.T) { 10 | t.Parallel() 11 | u := user.New("Hercule Poirot") 12 | u.Language = "French" 13 | want := "Bonjour" 14 | got := u.Greeting() 15 | if want != got { 16 | t.Errorf("want %q, got %q", want, got) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /user/5/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/user 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /user/5/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /user/5/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "sync" 4 | 5 | type User struct { 6 | Name string 7 | } 8 | 9 | var ( 10 | m = new(sync.Mutex) 11 | users = map[string]*User{ 12 | "Alice": { 13 | Name: "Alice", 14 | }, 15 | } 16 | ) 17 | 18 | func Create(name string) {} 19 | 20 | func Exists(name string) bool { 21 | m.Lock() 22 | defer m.Unlock() 23 | _, ok := users[name] 24 | return ok 25 | } 26 | -------------------------------------------------------------------------------- /user/5/user_test.go: -------------------------------------------------------------------------------- 1 | package user_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/user" 7 | ) 8 | 9 | func TestCreateCreatesGivenUser(t *testing.T) { 10 | t.Parallel() 11 | user.Create("Alice") 12 | if !user.Exists("Alice") { 13 | t.Error("Alice not created") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /user/6/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/user 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /user/6/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /user/6/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "sync" 4 | 5 | type User struct { 6 | Name string 7 | } 8 | 9 | var ( 10 | m = new(sync.Mutex) 11 | users = map[string]*User{} 12 | ) 13 | 14 | func Create(name string) { 15 | m.Lock() 16 | defer m.Unlock() 17 | users[name] = &User{ 18 | Name: name, 19 | } 20 | } 21 | 22 | func Delete(name string) { 23 | m.Lock() 24 | defer m.Unlock() 25 | delete(users, name) 26 | } 27 | 28 | func Exists(name string) bool { 29 | m.Lock() 30 | defer m.Unlock() 31 | _, ok := users[name] 32 | return ok 33 | } 34 | -------------------------------------------------------------------------------- /user/6/user_test.go: -------------------------------------------------------------------------------- 1 | package user_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/user" 7 | ) 8 | 9 | func TestCreateCreatesGivenUser(t *testing.T) { 10 | t.Parallel() 11 | if user.Exists("Alice") { 12 | t.Fatal("Alice unexpectedly exists") 13 | } 14 | user.Create("Alice") 15 | if !user.Exists("Alice") { 16 | t.Error("Alice not created") 17 | } 18 | } 19 | 20 | func TestDeleteDeletesGivenUser(t *testing.T) { 21 | t.Parallel() 22 | user.Create("Alice") 23 | if !user.Exists("Alice") { 24 | t.Fatal("Alice not created") 25 | } 26 | user.Delete("Alice") 27 | if user.Exists("Alice") { 28 | t.Error("Alice still exists after delete") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /user/7/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/user 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /user/7/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /user/7/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "sync" 4 | 5 | type User struct { 6 | Name string 7 | } 8 | 9 | var ( 10 | m = new(sync.Mutex) 11 | users = map[string]*User{} 12 | ) 13 | 14 | func Create(name string) { 15 | m.Lock() 16 | defer m.Unlock() 17 | users[name] = &User{ 18 | Name: name, 19 | } 20 | } 21 | 22 | func Delete(name string) { 23 | m.Lock() 24 | defer m.Unlock() 25 | delete(users, name) 26 | } 27 | 28 | func Exists(name string) bool { 29 | m.Lock() 30 | defer m.Unlock() 31 | _, ok := users[name] 32 | return ok 33 | } 34 | -------------------------------------------------------------------------------- /user/7/user_test.go: -------------------------------------------------------------------------------- 1 | package user_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/user" 7 | ) 8 | 9 | func TestCreateCreatesGivenUser(t *testing.T) { 10 | t.Parallel() 11 | if user.Exists("Alice") { 12 | t.Fatal("Alice unexpectedly exists") 13 | } 14 | user.Create("Alice") 15 | if !user.Exists("Alice") { 16 | t.Error("Alice not created") 17 | } 18 | } 19 | 20 | func TestDeleteDeletesGivenUserOnly(t *testing.T) { 21 | t.Parallel() 22 | createUserOrFail(t, "Alice") 23 | createUserOrFail(t, "Bob") 24 | user.Delete("Alice") 25 | if user.Exists("Alice") { 26 | t.Error("Alice still exists after delete") 27 | } 28 | if !user.Exists("Bob") { 29 | t.Error("Bob was unexpectedly deleted") 30 | } 31 | } 32 | 33 | func createUserOrFail(t *testing.T, name string) { 34 | t.Helper() 35 | user.Create(name) 36 | if !user.Exists(name) { 37 | t.Fatalf("%s not created", name) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /valid/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/valid 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /valid/1/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'want true, got false' 3 | -------------------------------------------------------------------------------- /valid/1/valid.go: -------------------------------------------------------------------------------- 1 | package valid 2 | 3 | func Valid(input string) bool { 4 | return false 5 | } 6 | -------------------------------------------------------------------------------- /valid/1/valid_test.go: -------------------------------------------------------------------------------- 1 | package valid_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/valid" 7 | ) 8 | 9 | func TestValid(t *testing.T) { 10 | t.Parallel() 11 | want := true 12 | got := valid.Valid("valid input") 13 | if want != got { 14 | t.Errorf("want %t, got %t", want, got) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /valid/2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/valid 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /valid/2/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'false' 3 | -------------------------------------------------------------------------------- /valid/2/valid.go: -------------------------------------------------------------------------------- 1 | package valid 2 | 3 | func Valid(input string) bool { 4 | return false 5 | } 6 | -------------------------------------------------------------------------------- /valid/2/valid_test.go: -------------------------------------------------------------------------------- 1 | package valid_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/valid" 7 | ) 8 | 9 | func TestValidIsTrueForValidInput(t *testing.T) { 10 | t.Parallel() 11 | if !valid.Valid("valid input") { 12 | t.Error(false) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /valid/3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/valid 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /valid/3/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /valid/3/valid.go: -------------------------------------------------------------------------------- 1 | package valid 2 | 3 | func Valid(input string) bool { 4 | return true 5 | } 6 | -------------------------------------------------------------------------------- /valid/3/valid_test.go: -------------------------------------------------------------------------------- 1 | package valid_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/valid" 7 | ) 8 | 9 | func TestValidIsTrueForValidInput(t *testing.T) { 10 | t.Parallel() 11 | if !valid.Valid("valid input") { 12 | t.Error(false) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /valid/4/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/valid 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /valid/4/test.txtar: -------------------------------------------------------------------------------- 1 | ! exec go test 2 | stdout 'TestValidIsFalseForInvalidInput' 3 | -------------------------------------------------------------------------------- /valid/4/valid.go: -------------------------------------------------------------------------------- 1 | package valid 2 | 3 | import "strings" 4 | 5 | func Valid(input string) bool { 6 | return strings.Contains(input, "valid input") 7 | } 8 | -------------------------------------------------------------------------------- /valid/4/valid_test.go: -------------------------------------------------------------------------------- 1 | package valid_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/valid" 7 | ) 8 | 9 | func TestValidIsTrueForValidInput(t *testing.T) { 10 | t.Parallel() 11 | if !valid.Valid("valid input") { 12 | t.Error(false) 13 | } 14 | } 15 | 16 | func TestValidIsFalseForInvalidInput(t *testing.T) { 17 | t.Parallel() 18 | if valid.Valid("invalid input") { 19 | t.Error(true) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /valid/5/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/valid 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /valid/5/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /valid/5/valid.go: -------------------------------------------------------------------------------- 1 | package valid 2 | 3 | func Valid(input string) bool { 4 | return input == "valid input" 5 | } 6 | -------------------------------------------------------------------------------- /valid/5/valid_test.go: -------------------------------------------------------------------------------- 1 | package valid_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/valid" 7 | ) 8 | 9 | func TestValidIsTrueForValidInput(t *testing.T) { 10 | t.Parallel() 11 | if !valid.Valid("valid input") { 12 | t.Error(false) 13 | } 14 | } 15 | 16 | func TestValidIsFalseForInvalidInput(t *testing.T) { 17 | t.Parallel() 18 | if valid.Valid("invalid input") { 19 | t.Error(true) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /weather/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/weather 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /weather/1/go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitfield/tpg-tests/d9348e1c9b9a033e16256cd72e9ac9dab390ddfc/weather/1/go.sum -------------------------------------------------------------------------------- /weather/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /weather/1/weather.go: -------------------------------------------------------------------------------- 1 | package weather 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type Forecast struct { 9 | Summary string 10 | } 11 | 12 | func ParseResponse(data []byte) (Forecast, error) { 13 | var resp struct { 14 | Weather []struct { 15 | Main string 16 | } 17 | } 18 | err := json.Unmarshal(data, &resp) 19 | if err != nil { 20 | return Forecast{}, fmt.Errorf("invalid API response %q: %w", data, err) 21 | } 22 | forecast := Forecast{ 23 | Summary: resp.Weather[0].Main, 24 | } 25 | return forecast, nil 26 | } 27 | 28 | func FormatURL(location string) string { 29 | return fmt.Sprintf("https://weather.example.com/?q=%s", location) 30 | } 31 | -------------------------------------------------------------------------------- /weather/1/weather_test.go: -------------------------------------------------------------------------------- 1 | package weather_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/weather" 7 | ) 8 | 9 | func TestFormatURL_IncludesLocation(t *testing.T) { 10 | t.Parallel() 11 | location := "Nowhere" 12 | want := "https://weather.example.com/?q=Nowhere" 13 | got := weather.FormatURL(location) 14 | if want != got { 15 | t.Errorf("want %q, got %q", want, got) 16 | } 17 | } 18 | 19 | func TestParseResponse_GivesExpectedForecast(t *testing.T) { 20 | t.Parallel() 21 | data := []byte(`{"weather":[{"main":"Clouds"}]}`) 22 | want := weather.Forecast{ 23 | Summary: "Clouds", 24 | } 25 | got, err := weather.ParseResponse(data) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | if want != got { 30 | t.Errorf("want %#v, got %#v", want, got) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /widget/1/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/bitfield/widget 2 | 3 | go 1.19 4 | 5 | require github.com/DATA-DOG/go-sqlmock v1.5.2 6 | -------------------------------------------------------------------------------- /widget/1/go.sum: -------------------------------------------------------------------------------- 1 | github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= 2 | github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= 3 | github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= 4 | -------------------------------------------------------------------------------- /widget/1/postgres_store.go: -------------------------------------------------------------------------------- 1 | package widget 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | ) 7 | 8 | type PostgresStore struct { 9 | DB *sql.DB 10 | } 11 | 12 | func (p *PostgresStore) Store(w Widget) (ID string, err error) { 13 | // horrible SQL goes here 14 | // handle errors, etc 15 | return ID, nil 16 | } 17 | 18 | func (ps *PostgresStore) Retrieve(ID string) (Widget, error) { 19 | w := Widget{} 20 | ctx := context.Background() 21 | row := ps.DB.QueryRowContext(ctx, "SELECT id, name FROM widgets WHERE id = ?", ID) 22 | err := row.Scan(&w.ID, &w.Name) 23 | if err != nil { 24 | return Widget{}, err 25 | } 26 | return w, nil 27 | } 28 | -------------------------------------------------------------------------------- /widget/1/postgres_store_test.go: -------------------------------------------------------------------------------- 1 | package widget_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/bitfield/widget" 7 | 8 | "github.com/DATA-DOG/go-sqlmock" 9 | ) 10 | 11 | func TestPostgresStore_RetrievesExpectedWidget(t *testing.T) { 12 | t.Parallel() 13 | ps := fakePostgresStore(t) 14 | want := widget.Widget{ 15 | ID: "widget01", 16 | Name: "Acme Giant Rubber Band", 17 | } 18 | got, err := ps.Retrieve("widget01") 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | if want != got { 23 | t.Errorf("want %#v, got %#v", want, got) 24 | } 25 | } 26 | 27 | func fakePostgresStore(t *testing.T) widget.PostgresStore { 28 | db, mock, err := sqlmock.New() 29 | if err != nil { 30 | t.Fatal(err) 31 | } 32 | t.Cleanup(func() { 33 | db.Close() 34 | }) 35 | query := "SELECT id, name FROM widgets" 36 | rows := sqlmock.NewRows([]string{"id", "name"}). 37 | AddRow("widget01", "Acme Giant Rubber Band") 38 | mock.ExpectQuery(query).WillReturnRows(rows) 39 | return widget.PostgresStore{ 40 | DB: db, 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /widget/1/test.txtar: -------------------------------------------------------------------------------- 1 | exec go test 2 | -------------------------------------------------------------------------------- /widget/1/widget.go: -------------------------------------------------------------------------------- 1 | package widget 2 | 3 | type Widget struct { 4 | ID string 5 | Name string 6 | } 7 | 8 | type Store interface { 9 | Store(Widget) (string, error) 10 | } 11 | 12 | func Create(s Store, w Widget) (ID string, err error) { 13 | ID, err = s.Store(w) 14 | if err != nil { 15 | return "", err 16 | } 17 | return ID, nil 18 | } 19 | -------------------------------------------------------------------------------- /widget/1/widget_test.go: -------------------------------------------------------------------------------- 1 | package widget_test 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "github.com/bitfield/widget" 8 | ) 9 | 10 | func TestCreateGivesNoErrorForValidWidget(t *testing.T) { 11 | s := newMapStore() 12 | w := widget.Widget{ 13 | ID: "widget01", 14 | Name: "Acme Giant Rubber Band", 15 | } 16 | wantID := "widget01" 17 | gotID, err := widget.Create(s, w) 18 | if err != nil { 19 | t.Errorf("unexpected error: %v", err) 20 | } 21 | if wantID != gotID { 22 | t.Errorf("want %q, got %q", wantID, gotID) 23 | } 24 | } 25 | 26 | type mapStore struct { 27 | m *sync.Mutex 28 | data map[string]widget.Widget 29 | } 30 | 31 | func newMapStore() *mapStore { 32 | return &mapStore{ 33 | m: new(sync.Mutex), 34 | data: map[string]widget.Widget{}, 35 | } 36 | } 37 | 38 | func (ms *mapStore) Store(w widget.Widget) (string, error) { 39 | ms.m.Lock() 40 | defer ms.m.Unlock() 41 | ms.data[w.ID] = w 42 | return w.ID, nil 43 | } 44 | --------------------------------------------------------------------------------