├── .gitignore ├── Go Test 从入门到躺平.pdf ├── LICENSE ├── README.md ├── fail_test.go ├── go.mod ├── go.sum ├── s0 ├── bfe.html ├── bfe.svg ├── grpc.html └── grpc.svg ├── s1 ├── arith.go ├── arith_test.go ├── cover.html ├── cover2.html ├── hello.go ├── hello_test.go └── out.svg ├── s10 ├── reverse.go ├── reverse_test.go └── testdata │ └── fuzz │ └── FuzzReverse │ └── 6bda972cf8cbff20524e6ebca2a8be89e2d88b246a7ae067174a068f9ea04380 ├── s2 ├── cleanup_test.go ├── fail_test.go ├── helper_test.go ├── log_test.go ├── main_test.go ├── parallel_test.go └── skip_test.go ├── s4 ├── cover.html ├── cover.sh ├── cover2.html ├── cover3.html ├── covermode.go ├── covermode.html ├── covermode_test.go └── out.svg ├── s5 ├── assert_require_test.go └── gocovery_test.go ├── s6 ├── container │ ├── kafka_test.go │ ├── redis_test.go │ └── testdata │ │ └── docker-compose.yml ├── gnomock │ ├── kafka_test.go │ └── redis_test.go ├── httptest │ ├── handler_test.go │ └── server_test.go ├── mock │ ├── io_test.go │ └── rw_test.go ├── mockey │ └── mock_test.go └── monkey │ └── hello_test.go ├── s7 └── book │ ├── api │ ├── book.go │ ├── book_monkey_test.go │ ├── book_test.go │ └── test-bin │ ├── dao │ ├── book.go │ └── book_test.go │ ├── model │ └── book.go │ ├── service │ ├── book.go │ └── book_test.go │ ├── testdata │ └── test.db │ └── util │ ├── db.go │ └── db_test.go ├── s8 ├── benchmark_hash_test.go ├── benchmark_parallel_test.go ├── benchstat.txt ├── benchstat_test.go ├── new.txt └── old.txt └── s9 └── example_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /Go Test 从入门到躺平.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallnest/go_test_workshop/27f12b63da3545c6ea5ae94b9287c3f5b0da5137/Go Test 从入门到躺平.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 smallnest 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 | # go_test_workshop 2 | 3 | 如何编写健壮的代码?测试或者单元测试是一个非常有效的手段。自1997年JUint的出世引领单元测试以来,单元测试曾经风靡一时。然后在最近的十年,或者说在最近的几年,互联网企业面临巨大的竞争压力,或者说代码规范不收重视的情况下,糙快猛的编程方式也收到吹捧,以至于单元测试没有收到足够的重视,因此留下来很多的技术债务和线上风险,也就是俗称的"雷"。 4 | 5 | 这个实战课程,全面介绍Go语言的单元测试的知识,通过大量的实战的代码,带你领略go test的便捷和强大,解答你在实际开发中的众多的疑惑,你在以后的Go应用程序的开发中,可以应用本文介绍的知识,把单元测试威力实际发挥出来。 6 | 7 | 如果你会写`func TestXXX(t *testing.T) {`这一行代码,恭喜你,你已经知道了Go的一半的单元测试的知识,你可以重点关注其它的一半的Go单元测试的知识。如果你还未写过Go的单元测试,没关系,本门课程手把手教你怎么轻轻松松编写Go的单元测试。 8 | 9 | 10 | 本课程的代码放在了 [Go Test Workshop](https://github.com/smallnest/go_test_workshop) 11 | 12 | ![](/s0/bfe.svg) 13 | 14 | - s1: go test basic 15 | - [table driven tests ](https://github.com/golang/go/wiki/TableDrivenTests) 16 | - [color output](https://github.com/rakyll/gotest) 17 | - gotest -v ./... 18 | 19 | - s2: testing.T 20 | - testing.TB interface 21 | - testing.T 22 | - Fail/FailNow/Failed/Fatal/Fatalf 23 | - Log/Logf/Error/Errorf 24 | - Skip/SkipNow/Skipf/Skipped 25 | - Helper 26 | - Parallel 27 | 28 | 29 | - s3: go test command 30 | - go help test 31 | - go test [build/test flags] [packages] [build/test flags & test binary flags] 32 | - local directory mode: cache disabled 33 | - package list mode: cache enabled. `-count=1` to disable cache 34 | - test binary flags 35 | - -c: Compile the test binary to pkg.test but do not run it 36 | - -exec xprog 37 | - -json: Convert test output to JSON suitable for automated processing. 38 | - -o file:Compile the test binary to the named file. 39 | 40 | - go help testflag 41 | - -count n: Run each test, benchmark, and fuzz seed n times (default 1). 42 | - -cpu 1,2,4: Specify a list of GOMAXPROCS values for which the tests, benchmarks or fuzz tests should be executed. 43 | - -failfast: Do not start new tests after the first test failure. 44 | - -json 45 | - -list regexp 46 | - -parallel n 47 | - -run regexp 48 | - -short 49 | - -shuffle off,on,N 50 | - -timeout d 51 | - -v 52 | - -vet list 53 | 54 | 55 | - go help testfunc 56 | - func TestXxx(t *testing.T) { ... } 57 | - func BenchmarkXxx(b *testing.B) { ... } 58 | - func FuzzXxx(f *testing.F) { ... } 59 | - func ExamplePrintln() { ... } 60 | 61 | - https://pkg.go.dev/testing 62 | 63 | - s4: coverage 64 | - go test -coverprofile cover.out ./... 65 | - go-cover-treemap -coverprofile cover.out > out.svg 66 | - https://github.com/microsoft/vscode-go/issues/816 67 | - https://golang.org/doc/go1.10#test 68 | - go tool cover -html=cover.out -o cover.html 69 | - go tool cover -o cover2.html -html=cover.out; sed -i 's/black/whitesmoke/g' cover2.html; open cover2.html 70 | - mac: go tool cover -o cover2.html -html=cover.out; sed -i'*.bak' 's/black/whitesmoke/g' cover2.html; rm -fr cover2.html*.bak;open cover2.html 71 | 72 | 73 | - s5: tools 74 | - assert/require 75 | - gocovery: Behavior-driven Development 76 | 77 | - s6: mock 78 | - https://github.com/golang/mock 79 | - https://github.com/vektra/mockery 80 | - https://github.com/DATA-DOG/go-sqlmock 81 | - https://github.com/h2non/gock 82 | - https://github.com/gavv/httpexpect 83 | - https://github.com/steinfletcher/apitest 84 | - https://github.com/carlmjohnson/be 85 | - https://github.com/arikama/go-mysql-test-container 86 | - https://github.com/jfilipczyk/gomatch 87 | 88 | - monkey 89 | - https://github.com/bouk/monkey 90 | - https://github.com/agiledragon/gomonkey 91 | - printf '\x07' | dd of=api.test bs=1 seek=160 count=1 conv=notrunc 92 | - https://github.com/eisenxp/macos-golink-wrapper 93 | - container 94 | - https://github.com/testcontainers/testcontainers-go 95 | 96 | - s7: web case 97 | - dao 98 | - service 99 | - http 100 | 101 | - s8: benchmark 102 | 103 | - s9: example 104 | 105 | - s10: fuzzing test 106 | 107 | 108 | 109 | ## install tools 110 | 111 | ``` 112 | go install github.com/rakyll/gotest@master 113 | go install github.com/axw/gocov/gocov@latest 114 | go install github.com/matm/gocov-html@latest 115 | go install github.com/nikolaydubina/go-cover-treemap@latest 116 | // https://go-cover-treemap.io/ 117 | ``` -------------------------------------------------------------------------------- /fail_test.go: -------------------------------------------------------------------------------- 1 | package s2 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestFail(t *testing.T) { 9 | t.Fail() 10 | t.Log("after Fail") 11 | t.FailNow() 12 | t.Log("after FailNow") 13 | } 14 | 15 | func TestFatal(t *testing.T) { 16 | t.Fatal("fataled") 17 | t.Log("after Fatal") 18 | } 19 | 20 | func TestFatalf(t *testing.T) { 21 | t.Fatalf("there is: %v", "Fatalf") 22 | t.Log("after Fatalf") 23 | } 24 | 25 | func TestFailNowInAnotherGoroutine(t *testing.T) { 26 | go func() { 27 | t.FailNow() 28 | t.Log("after FailNow in another goroutine") 29 | }() 30 | 31 | time.Sleep(time.Second) 32 | t.Log("after one second") 33 | } 34 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/smallnest/go_test_workshop 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/DATA-DOG/go-sqlmock v1.5.0 7 | github.com/agiledragon/gomonkey/v2 v2.11.0 8 | github.com/cespare/xxhash v1.1.0 9 | github.com/creachadair/cityhash v0.1.1 10 | github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 11 | github.com/go-redis/redis v6.15.9+incompatible 12 | github.com/go-redis/redis/v7 v7.4.1 13 | github.com/golang/mock v1.6.0 14 | github.com/google/uuid v1.3.0 15 | github.com/leemcloughlin/gofarmhash v0.0.0-20160919192320-0a055c5b87a8 16 | github.com/mattn/go-sqlite3 v1.14.22 17 | github.com/minio/highwayhash v1.0.2 18 | github.com/orlangure/gnomock v0.21.1-0.20220731072318-def3e4eb83fa 19 | github.com/pierrec/xxHash v0.1.5 20 | github.com/segmentio/kafka-go v0.4.33 21 | github.com/smartystreets/goconvey v1.6.4 22 | github.com/spaolacci/murmur3 v1.1.0 23 | github.com/stretchr/testify v1.9.0 24 | github.com/testcontainers/testcontainers-go v0.13.0 25 | ) 26 | 27 | require ( 28 | github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect 29 | github.com/Microsoft/go-winio v0.5.2 // indirect 30 | github.com/Microsoft/hcsshim v0.8.23 // indirect 31 | github.com/bytedance/mockey v1.2.10 // indirect 32 | github.com/cenkalti/backoff/v4 v4.1.2 // indirect 33 | github.com/containerd/cgroups v1.0.1 // indirect 34 | github.com/containerd/containerd v1.5.9 // indirect 35 | github.com/davecgh/go-spew v1.1.1 // indirect 36 | github.com/docker/distribution v2.8.1+incompatible // indirect 37 | github.com/docker/docker v20.10.17+incompatible // indirect 38 | github.com/docker/go-connections v0.4.0 // indirect 39 | github.com/docker/go-units v0.4.0 // indirect 40 | github.com/gogo/protobuf v1.3.2 // indirect 41 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect 42 | github.com/golang/protobuf v1.5.2 // indirect 43 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect 44 | github.com/jtolds/gls v4.20.0+incompatible // indirect 45 | github.com/klauspost/compress v1.15.7 // indirect 46 | github.com/magiconair/properties v1.8.5 // indirect 47 | github.com/moby/sys/mount v0.2.0 // indirect 48 | github.com/moby/sys/mountinfo v0.5.0 // indirect 49 | github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect 50 | github.com/morikuni/aec v1.0.0 // indirect 51 | github.com/onsi/ginkgo v1.16.5 // indirect 52 | github.com/onsi/gomega v1.18.1 // indirect 53 | github.com/opencontainers/go-digest v1.0.0 // indirect 54 | github.com/opencontainers/image-spec v1.0.2 // indirect 55 | github.com/opencontainers/runc v1.0.2 // indirect 56 | github.com/pierrec/lz4/v4 v4.1.15 // indirect 57 | github.com/pkg/errors v0.9.1 // indirect 58 | github.com/pmezard/go-difflib v1.0.0 // indirect 59 | github.com/sirupsen/logrus v1.9.0 // indirect 60 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect 61 | go.opencensus.io v0.22.3 // indirect 62 | go.uber.org/atomic v1.10.0 // indirect 63 | go.uber.org/multierr v1.8.0 // indirect 64 | go.uber.org/zap v1.22.0 // indirect 65 | golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff // indirect 66 | golang.org/x/net v0.0.0-20220811182439-13a9a731de15 // indirect 67 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect 68 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect 69 | google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect 70 | google.golang.org/grpc v1.33.2 // indirect 71 | google.golang.org/protobuf v1.28.0 // indirect 72 | gopkg.in/yaml.v3 v3.0.1 // indirect 73 | ) 74 | -------------------------------------------------------------------------------- /s0/grpc.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | google.golang.org/grpc 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | authz 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | balancer 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | balancer_conn_wrappers.go 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | benchmark 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | channelz/service 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | clientconn.go 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | codes 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | credentials 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | dialoptions.go 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | grpclog 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | health 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | internal 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | metadata/metadata.go 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | picker_wrapper.go 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | pickfirst.go 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | reflection/serverreflection.go 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | resolver_conn_wrapper.go 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | rpc_util.go 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | server.go 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | service_config.go 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | stream.go 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | test 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | xds 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | rbac_translator.go 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | base 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | grpclb 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | rls 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | weightedtarget 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | latency/latency.go 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | service.go 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | alts/internal 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | oauth/oauth.go 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | tls.go 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | tls/certprovider 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | xds/xds.go 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | balancer/gracefulswitch/gracefulswitch.go 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | balancergroup/balancergroup.go 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | binarylog 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | credentials 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | grpctest 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | metadata/metadata.go 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | profiling 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | resolver 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | serviceconfig/serviceconfig.go 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | testutils 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | transport 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | xds 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | bufconn/bufconn.go 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | servertester.go 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | internal 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | grpclb.go 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | grpclb_remote_balancer.go 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | balancer.go 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | config.go 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | internal 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | picker.go 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | conn 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | handshaker 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | pemfile 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | env_config.go 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | method_logger.go 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | xds/handshake_info.go 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | dns/dns_resolver.go 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | balancer.go 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | controlbuf.go 1295 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | flowcontrol.go 1308 | 1309 | 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | handler_server.go 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | http2_client.go 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | http2_server.go 1347 | 1348 | 1349 | 1350 | 1351 | 1352 | 1353 | 1354 | http_util.go 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1366 | 1367 | 1368 | 1369 | 1370 | 1371 | 1372 | 1373 | transport.go 1379 | 1380 | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | 1394 | 1395 | 1396 | 1397 | 1398 | 1399 | 1400 | 1401 | 1402 | 1403 | 1404 | rbac 1410 | 1411 | 1412 | 1413 | 1414 | 1415 | 1416 | 1417 | 1418 | 1419 | 1420 | 1421 | 1422 | 1423 | 1424 | 1425 | 1426 | 1427 | 1428 | 1429 | balancer 1435 | 1436 | 1437 | 1438 | 1439 | 1440 | 1441 | 1442 | 1443 | 1444 | 1445 | 1446 | 1447 | 1448 | httpfilter/fault/fault.go 1454 | 1455 | 1456 | 1457 | 1458 | 1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 1465 | 1466 | 1467 | resolver 1473 | 1474 | 1475 | 1476 | 1477 | 1478 | 1479 | 1480 | server 1486 | 1487 | 1488 | 1489 | 1490 | 1491 | 1492 | 1493 | test/e2e 1499 | 1500 | 1501 | 1502 | 1503 | 1504 | 1505 | 1506 | 1507 | 1508 | 1509 | 1510 | 1511 | 1512 | xdsclient 1518 | 1519 | 1520 | 1521 | 1522 | 1523 | 1524 | 1525 | 1526 | 1527 | 1528 | 1529 | 1530 | 1531 | 1532 | 1533 | 1534 | 1535 | 1536 | 1537 | 1538 | 1539 | 1540 | 1541 | 1542 | 1543 | 1544 | 1545 | 1546 | 1547 | 1548 | 1549 | 1550 | 1551 | 1552 | 1553 | 1554 | 1555 | 1556 | 1557 | 1558 | 1559 | 1560 | 1561 | 1562 | 1563 | 1564 | 1565 | 1566 | 1567 | 1568 | 1569 | 1570 | 1571 | 1572 | 1573 | 1574 | 1575 | 1576 | 1577 | 1578 | 1579 | 1580 | 1581 | 1582 | 1583 | 1584 | 1585 | 1586 | 1587 | 1588 | 1589 | 1590 | 1591 | cdsbalancer 1597 | 1598 | 1599 | 1600 | 1601 | 1602 | 1603 | 1604 | clusterimpl 1610 | 1611 | 1612 | 1613 | 1614 | 1615 | 1616 | 1617 | clustermanager 1623 | 1624 | 1625 | 1626 | 1627 | 1628 | 1629 | 1630 | clusterresolver 1636 | 1637 | 1638 | 1639 | 1640 | 1641 | 1642 | 1643 | 1644 | 1645 | 1646 | 1647 | 1648 | 1649 | outlierdetection 1655 | 1656 | 1657 | 1658 | 1659 | 1660 | 1661 | 1662 | priority 1668 | 1669 | 1670 | 1671 | 1672 | 1673 | 1674 | 1675 | ringhash 1681 | 1682 | 1683 | 1684 | 1685 | 1686 | 1687 | 1688 | 1689 | 1690 | 1691 | 1692 | 1693 | 1694 | watch_service.go 1700 | 1701 | 1702 | 1703 | 1704 | 1705 | 1706 | 1707 | xds_resolver.go 1713 | 1714 | 1715 | 1716 | 1717 | 1718 | 1719 | 1720 | 1721 | 1722 | 1723 | 1724 | 1725 | 1726 | 1727 | 1728 | 1729 | 1730 | 1731 | 1732 | 1733 | 1734 | 1735 | 1736 | 1737 | 1738 | 1739 | 1740 | 1741 | 1742 | 1743 | 1744 | 1745 | 1746 | 1747 | 1748 | 1749 | 1750 | 1751 | 1752 | 1753 | 1754 | 1755 | 1756 | bootstrap 1762 | 1763 | 1764 | 1765 | 1766 | 1767 | 1768 | 1769 | 1770 | 1771 | 1772 | 1773 | 1774 | 1775 | 1776 | 1777 | 1778 | 1779 | 1780 | 1781 | clientimpl_authority.go 1787 | 1788 | 1789 | 1790 | 1791 | 1792 | 1793 | 1794 | 1795 | 1796 | 1797 | 1798 | 1799 | 1800 | 1801 | 1802 | 1803 | 1804 | 1805 | 1806 | 1807 | 1808 | 1809 | 1810 | 1811 | 1812 | 1813 | 1814 | 1815 | 1816 | 1817 | 1818 | controller 1824 | 1825 | 1826 | 1827 | 1828 | 1829 | 1830 | 1831 | load/store.go 1837 | 1838 | 1839 | 1840 | 1841 | 1842 | 1843 | 1844 | 1845 | 1846 | 1847 | 1848 | 1849 | 1850 | 1851 | 1852 | 1853 | 1854 | 1855 | 1856 | xdsresource 1862 | 1863 | 1864 | 1865 | 1866 | 1867 | 1868 | 1869 | cdsbalancer.go 1875 | 1876 | 1877 | 1878 | 1879 | 1880 | 1881 | 1882 | 1883 | 1884 | 1885 | 1886 | 1887 | 1888 | 1889 | 1890 | 1891 | 1892 | 1893 | 1894 | 1895 | 1896 | 1897 | 1898 | 1899 | 1900 | 1901 | 1902 | 1903 | 1904 | 1905 | 1906 | 1907 | 1908 | 1909 | 1910 | 1911 | 1912 | 1913 | 1914 | 1915 | 1916 | 1917 | 1918 | 1919 | 1920 | 1921 | 1922 | 1923 | 1924 | configbuilder.go 1930 | 1931 | 1932 | 1933 | 1934 | 1935 | 1936 | 1937 | 1938 | 1939 | 1940 | 1941 | 1942 | 1943 | 1944 | 1945 | 1946 | 1947 | 1948 | 1949 | 1950 | 1951 | 1952 | 1953 | 1954 | 1955 | 1956 | 1957 | 1958 | 1959 | 1960 | 1961 | 1962 | 1963 | 1964 | 1965 | 1966 | 1967 | 1968 | 1969 | 1970 | 1971 | 1972 | 1973 | 1974 | 1975 | 1976 | 1977 | 1978 | 1979 | 1980 | 1981 | 1982 | 1983 | 1984 | 1985 | 1986 | 1987 | 1988 | 1989 | 1990 | 1991 | 1992 | 1993 | 1994 | 1995 | 1996 | 1997 | 1998 | 1999 | 2000 | 2001 | 2002 | 2003 | 2004 | 2005 | 2006 | 2007 | 2008 | 2009 | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | bootstrap.go 2021 | 2022 | 2023 | 2024 | 2025 | 2026 | 2027 | 2028 | 2029 | 2030 | 2031 | 2032 | 2033 | 2034 | 2035 | 2036 | 2037 | 2038 | 2039 | 2040 | 2041 | 2042 | 2043 | 2044 | 2045 | 2046 | filter_chain.go 2052 | 2053 | 2054 | 2055 | 2056 | 2057 | 2058 | 2059 | matcher.go 2065 | 2066 | 2067 | 2068 | 2069 | 2070 | 2071 | 2072 | 2073 | 2074 | 2075 | 2076 | 2077 | 2078 | 2079 | 2080 | 2081 | 2082 | 2083 | 2084 | 2085 | 2086 | 2087 | 2088 | 2089 | 2090 | 2091 | 2092 | 2093 | 2094 | 2095 | 2096 | unmarshal.go 2102 | 2103 | 2104 | 2105 | 2106 | 2107 | 2108 | 2109 | unmarshal_cds.go 2115 | 2116 | 2117 | 2118 | 2119 | 2120 | 2121 | 2122 | unmarshal_eds.go 2128 | 2129 | 2130 | 2131 | 2132 | 2133 | 2134 | 2135 | 2136 | 2137 | 2138 | 2139 | 2140 | 2141 | unmarshal_rds.go 2147 | 2148 | 2149 | 2150 | -------------------------------------------------------------------------------- /s1/arith.go: -------------------------------------------------------------------------------- 1 | package s1 2 | 3 | func Add(a, b int) int { 4 | return a + b 5 | } 6 | 7 | func Sub(a, b int) int { 8 | return a - b 9 | } 10 | 11 | func Mul(a, b int) int { 12 | return a * b 13 | } 14 | 15 | func Div(a, b int) int { 16 | return a / b 17 | } 18 | -------------------------------------------------------------------------------- /s1/arith_test.go: -------------------------------------------------------------------------------- 1 | package s1 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestAdd(t *testing.T) { 8 | type args struct { 9 | a int 10 | b int 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want int 16 | }{ 17 | {"普通的两个数相加", args{10, 20}, 30}, 18 | {"一个参数是0", args{10, 0}, 10}, 19 | } 20 | for _, tt := range tests { 21 | t.Run(tt.name, func(t *testing.T) { 22 | if got := Add(tt.args.a, tt.args.b); got != tt.want { 23 | t.Errorf("Add() = %v, want %v", got, tt.want) 24 | } 25 | }) 26 | } 27 | } 28 | 29 | func TestAdd2(t *testing.T) { 30 | v := Add(1, 2) 31 | if v != 3 { 32 | t.Errorf("expect 3 but got %d", v) 33 | } 34 | } 35 | 36 | func TestSub(t *testing.T) { 37 | type args struct { 38 | a int 39 | b int 40 | } 41 | tests := []struct { 42 | name string 43 | args args // int 44 | want int // out 45 | }{ 46 | {"普通的两个数相减", args{10, 20}, -10}, 47 | {"一个参数是0", args{10, 0}, 10}, 48 | } 49 | for _, tt := range tests { 50 | t.Run(tt.name, func(t *testing.T) { 51 | if got := Sub(tt.args.a, tt.args.b); got != tt.want { 52 | t.Errorf("Sub() = %v, want %v", got, tt.want) 53 | } 54 | }) 55 | } 56 | } 57 | 58 | func TestMul(t *testing.T) { 59 | type args struct { 60 | a int 61 | b int 62 | } 63 | tests := []struct { 64 | name string 65 | args args 66 | want int 67 | }{ 68 | {"普通的两个数相乘", args{10, 20}, 200}, 69 | {"一个参数是0", args{10, 0}, 0}, 70 | } 71 | for _, tt := range tests { 72 | t.Run(tt.name, func(t *testing.T) { 73 | if got := Mul(tt.args.a, tt.args.b); got != tt.want { 74 | t.Errorf("Mul() = %v, want %v", got, tt.want) 75 | } 76 | }) 77 | } 78 | } 79 | 80 | func TestDiv(t *testing.T) { 81 | type args struct { 82 | a int 83 | b int 84 | } 85 | tests := []struct { 86 | name string 87 | args args 88 | want int 89 | }{ 90 | {"普通的两个数相除", args{10, 20}, 0}, 91 | // {"一个参数是0", args{10, 0}, int(math.NaN())}, 92 | } 93 | for _, tt := range tests { 94 | t.Run(tt.name, func(t *testing.T) { 95 | if got := Div(tt.args.a, tt.args.b); got != tt.want { 96 | t.Errorf("Div() = %v, want %v", got, tt.want) 97 | } 98 | }) 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /s1/cover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | s1: Go Coverage Report 7 | 52 | 53 | 54 |
55 | 64 |
65 | not tracked 66 | 67 | not covered 68 | covered 69 | 70 |
71 |
72 |
73 | 74 | 92 | 93 | 99 | 100 |
101 | 102 | 129 | 130 | -------------------------------------------------------------------------------- /s1/cover2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | s1: Go Coverage Report 7 | 52 | 53 | 54 |
55 | 64 |
65 | not tracked 66 | 67 | not covered 68 | covered 69 | 70 |
71 |
72 |
73 | 74 | 92 | 93 | 99 | 100 |
101 | 102 | 129 | 130 | -------------------------------------------------------------------------------- /s1/hello.go: -------------------------------------------------------------------------------- 1 | package s1 2 | 3 | func greet(s string) string { 4 | return "hello " + s 5 | } 6 | -------------------------------------------------------------------------------- /s1/hello_test.go: -------------------------------------------------------------------------------- 1 | // package s1_test 2 | 3 | package s1 4 | 5 | import "testing" 6 | 7 | func TestGreet(t *testing.T) { 8 | s := greet("test") 9 | 10 | if s != "hello test" { 11 | t.Errorf("want 'hello test' but got %s", s) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /s1/out.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | github.com/smallnest/go_test_workshop/s1 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | arith.go 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | hello.go 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /s10/reverse.go: -------------------------------------------------------------------------------- 1 | package s10 2 | 3 | func Reverse(s string) string { 4 | b := []byte(s) 5 | for i, j := 0, len(b)-1; i < len(b)/2; i, j = i+1, j-1 { 6 | b[i], b[j] = b[j], b[i] 7 | } 8 | return string(b) 9 | } 10 | -------------------------------------------------------------------------------- /s10/reverse_test.go: -------------------------------------------------------------------------------- 1 | package s10 2 | 3 | import ( 4 | "testing" 5 | "unicode/utf8" 6 | ) 7 | 8 | func TestReverse(t *testing.T) { 9 | testcases := []struct { 10 | in, want string 11 | }{ 12 | {"Hello, world", "dlrow ,olleH"}, 13 | {" ", " "}, 14 | {"!12345", "54321!"}, 15 | } 16 | for _, tc := range testcases { 17 | rev := Reverse(tc.in) 18 | if rev != tc.want { 19 | t.Errorf("Reverse: %q, want %q", rev, tc.want) 20 | } 21 | } 22 | } 23 | 24 | func FuzzReverse(f *testing.F) { 25 | testcases := []string{"Hello, world", " ", "!12345"} 26 | for _, tc := range testcases { 27 | f.Add(tc) // Use f.Add to provide a seed corpus 28 | } 29 | f.Fuzz(func(t *testing.T, orig string) { 30 | rev := Reverse(orig) 31 | doubleRev := Reverse(rev) 32 | if orig != doubleRev { 33 | t.Errorf("Before: %q, after: %q", orig, doubleRev) 34 | } 35 | if utf8.ValidString(orig) && !utf8.ValidString(rev) { 36 | t.Errorf("Reverse produced invalid UTF-8 string %q", rev) 37 | } 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /s10/testdata/fuzz/FuzzReverse/6bda972cf8cbff20524e6ebca2a8be89e2d88b246a7ae067174a068f9ea04380: -------------------------------------------------------------------------------- 1 | go test fuzz v1 2 | string("ۯ") 3 | -------------------------------------------------------------------------------- /s2/cleanup_test.go: -------------------------------------------------------------------------------- 1 | package s2 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | ) 9 | 10 | func TestWrite(t *testing.T) { 11 | tempDir, err := ioutil.TempDir(".", "temp") 12 | if err != nil { 13 | t.Errorf("create tempDir: %v", err) 14 | } 15 | t.Cleanup(func() { os.RemoveAll(tempDir) }) 16 | 17 | err = ioutil.WriteFile(filepath.Join(tempDir, "test.log"), []byte("hello test"), 0644) 18 | if err != nil { 19 | t.Errorf("want writing success but got %v", err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /s2/fail_test.go: -------------------------------------------------------------------------------- 1 | package s2 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestFail(t *testing.T) { 9 | t.Fail() 10 | t.Log("after Fail") 11 | t.FailNow() 12 | t.Log("after FailNow") 13 | } 14 | 15 | func TestFatal(t *testing.T) { 16 | t.Fatal("fataled") 17 | t.Log("after Fatal") 18 | } 19 | 20 | func TestFatalf(t *testing.T) { 21 | t.Fatalf("there is: %v", "Fatalf") 22 | t.Log("after Fatalf") 23 | } 24 | 25 | func TestFailNowInAnotherGoroutine(t *testing.T) { 26 | go func() { 27 | t.FailNow() 28 | t.Log("after FailNow in another goroutine") 29 | }() 30 | 31 | time.Sleep(time.Second) 32 | t.Log("after one second") 33 | } 34 | -------------------------------------------------------------------------------- /s2/helper_test.go: -------------------------------------------------------------------------------- 1 | package s2 2 | 3 | import "testing" 4 | 5 | func notHelper(t *testing.T, msg string) { 6 | t.Error(msg) 7 | } 8 | 9 | func helper(t *testing.T, msg string) { 10 | t.Helper() 11 | t.Error(msg) 12 | } 13 | 14 | func notHelperCallingHelper(t *testing.T, msg string) { 15 | helper(t, msg) 16 | } 17 | 18 | func helperCallingHelper(t *testing.T, msg string) { 19 | t.Helper() 20 | helper(t, msg) 21 | } 22 | 23 | func TestHelper(t *testing.T) { 24 | notHelper(t, "0") 25 | helper(t, "1") 26 | notHelperCallingHelper(t, "2") 27 | helperCallingHelper(t, "3") 28 | 29 | fn := func(msg string) { 30 | t.Helper() 31 | t.Error(msg) 32 | } 33 | fn("4") 34 | 35 | t.Helper() 36 | t.Error("5") 37 | 38 | t.Run("sub", func(t *testing.T) { 39 | helper(t, "6") 40 | notHelperCallingHelper(t, "7") 41 | t.Helper() 42 | t.Error("8") 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /s2/log_test.go: -------------------------------------------------------------------------------- 1 | package s2 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestLog(t *testing.T) { 9 | t.Log("it is a log") 10 | t.Logf("it is a log at %v", time.Now().Format(time.RFC1123)) 11 | 12 | t.Error("it is an error") 13 | t.Errorf("it is an error at %v", time.Now().Format(time.RFC1123)) 14 | } 15 | -------------------------------------------------------------------------------- /s2/main_test.go: -------------------------------------------------------------------------------- 1 | package s2 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func TestMain(m *testing.M) { 10 | log.Println("do stuff BEFORE the tests!") 11 | exitVal := m.Run() 12 | log.Println("do stuff AFTER the tests!") 13 | 14 | os.Exit(exitVal) 15 | } 16 | -------------------------------------------------------------------------------- /s2/parallel_test.go: -------------------------------------------------------------------------------- 1 | package s2 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | ) 7 | 8 | func TestParallel(t *testing.T) { 9 | var urls = map[string]string{"baidu": "http://baidu.com", "bing": "http://bing.com", "google": "http://google.com"} 10 | for k, v := range urls { 11 | v := v 12 | ok := t.Run(k, func(t *testing.T) { 13 | t.Parallel() 14 | 15 | t.Logf("start to get %s", v) 16 | resp, err := http.Get(v) 17 | t.Logf("end to get %s", v) 18 | if err != nil { 19 | t.Errorf("failed to get %s: %v", v, err) 20 | return 21 | } 22 | 23 | resp.Body.Close() 24 | }) 25 | 26 | t.Logf("run: %t", ok) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /s2/skip_test.go: -------------------------------------------------------------------------------- 1 | package s2 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | ) 7 | 8 | func TestSkip(t *testing.T) { 9 | if runtime.GOOS == "darwin" { 10 | t.Skip("skip MacOS") 11 | } 12 | 13 | if testing.Short() { 14 | t.Skip("skip because of short") 15 | } 16 | 17 | t.Log("there a non-skipped log") 18 | } 19 | -------------------------------------------------------------------------------- /s4/cover.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | s1: Go Coverage Report 7 | 52 | 53 | 54 |
55 | 70 |
71 | not tracked 72 | 73 | not covered 74 | covered 75 | 76 |
77 |
78 |
79 | 80 | 98 | 99 | 105 | 106 | 153 | 154 | 195 | 196 | 224 | 225 |
226 | 227 | 254 | 255 | -------------------------------------------------------------------------------- /s4/cover.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd .. 4 | go test -coverprofile s4/cover.out ./... 5 | 6 | gocov test ./... | gocov-html > s4/cover3.html 7 | 8 | cd s4 9 | 10 | go tool cover -html=cover.out -o cover.html 11 | 12 | go tool cover -o cover2.html -html=cover.out; sed -i'*.bak' 's/black/whitesmoke/g' cover2.html; rm -fr cover2.html*.bak 13 | -------------------------------------------------------------------------------- /s4/cover2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | s1: Go Coverage Report 7 | 52 | 53 | 54 |
55 | 70 |
71 | not tracked 72 | 73 | not covered 74 | covered 75 | 76 |
77 |
78 |
79 | 80 | 98 | 99 | 105 | 106 | 153 | 154 | 195 | 196 | 224 | 225 |
226 | 227 | 254 | 255 | -------------------------------------------------------------------------------- /s4/cover3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Coverage Report 5 | 6 | 7 | 131 | 132 | 133 | 134 |
Coverage Report
135 |
Generated on 24 Jul 22 19:38 +0800 with gocov-html
Report Overview
136 | 137 | 138 | 139 | 140 | 141 |
github.com/smallnest/go_test_workshop/s1100.00%5/5
github.com/smallnest/go_test_workshop/s6/book/api75.00%12/16
github.com/smallnest/go_test_workshop/s6/book/dao66.67%6/9
github.com/smallnest/go_test_workshop/s6/book/service100.00%2/2
Report Total78.12%25/32
142 |
Package Overview: github.com/smallnest/go_test_workshop/s1 100.00%

This is a coverage report created after analysis of the github.com/smallnest/go_test_workshop/s1 package. It 143 | has been generated with the following command:

gocov test github.com/smallnest/go_test_workshop/s1 | gocov-html

Here are the stats. Please select a function name to view its implementation and see what's left for testing.

144 | 145 | 146 | 147 | 148 | 149 | 150 |
Add(...)github.com/smallnest/go_test_workshop/s1/arith.go100.00%1/1
Sub(...)github.com/smallnest/go_test_workshop/s1/arith.go100.00%1/1
Mul(...)github.com/smallnest/go_test_workshop/s1/arith.go100.00%1/1
Div(...)github.com/smallnest/go_test_workshop/s1/arith.go100.00%1/1
greet(...)github.com/smallnest/go_test_workshop/s1/hello.go100.00%1/1
github.com/smallnest/go_test_workshop/s1100.00%5/5
151 | 152 |
func Add
Back

In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s1/arith.go:

153 |
3
func Add(a, b int) int {
4
        return a + b
5
}
154 | 155 |
func Sub
Back

In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s1/arith.go:

156 |
7
func Sub(a, b int) int {
8
        return a - b
9
}
157 | 158 |
func Mul
Back

In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s1/arith.go:

159 |
11
func Mul(a, b int) int {
12
        return a * b
13
}
160 | 161 |
func Div
Back

In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s1/arith.go:

162 |
15
func Div(a, b int) int {
16
        return a / b
17
}
163 | 164 |
func greet
Back

In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s1/hello.go:

165 |
3
func greet(s string) string {
4
        return "hello " + s
5
}
166 | 167 | 170 | 171 |
Package Overview: github.com/smallnest/go_test_workshop/s6/book/api 75.00%

This is a coverage report created after analysis of the github.com/smallnest/go_test_workshop/s6/book/api package. It 172 | has been generated with the following command:

gocov test github.com/smallnest/go_test_workshop/s6/book/api | gocov-html

Here are the stats. Please select a function name to view its implementation and see what's left for testing.

173 | 174 | 175 |
BookController.CreateBook(...)github.com/smallnest/go_test_workshop/s6/book/api/book.go75.00%12/16
github.com/smallnest/go_test_workshop/s6/book/api75.00%12/16
176 | 177 |
func BookController.CreateBook
Back

In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s6/book/api/book.go:

178 |
17
func (bc *BookController) CreateBook(w http.ResponseWriter, r *http.Request) {
18
        title := r.FormValue("title")
19
        authorID := r.FormValue("authorID")
20
        isbn := r.FormValue("isbn")
21
        computers := r.FormValue("subject")
22
23
        aid, err := strconv.Atoi(authorID)
24
        if err != nil {
25
                http.Error(w, "wrong author id", http.StatusBadRequest)
26
                return
27
        }
28
29
        book := &model.Book{
30
                Title:    title,
31
                AuthorID: aid,
32
                ISBN:     isbn,
33
                Subject:  computers,
34
        }
35
36
        err = bc.bookService.CreateBook(context.TODO(), book)
37
        if err != nil {
38
                http.Error(w, "failed to create book", http.StatusInternalServerError)
39
                return
40
        }
41
42
        data, _ := json.Marshal(book)
43
44
        w.Header().Set("Contetx-Type", "application/json")
45
        w.Write(data)
46
}
179 | 180 | 183 | 184 |
Package Overview: github.com/smallnest/go_test_workshop/s6/book/dao 66.67%

This is a coverage report created after analysis of the github.com/smallnest/go_test_workshop/s6/book/dao package. It 185 | has been generated with the following command:

gocov test github.com/smallnest/go_test_workshop/s6/book/dao | gocov-html

Here are the stats. Please select a function name to view its implementation and see what's left for testing.

186 | 187 | 188 | 189 |
bookStore.InsertBook(...)github.com/smallnest/go_test_workshop/s6/book/dao/book.go75.00%6/8
NewBookStore(...)github.com/smallnest/go_test_workshop/s6/book/dao/book.go0.00%0/1
github.com/smallnest/go_test_workshop/s6/book/dao66.67%6/9
190 | 191 |
func bookStore.InsertBook
Back

In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s6/book/dao/book.go:

192 |
25
func (s *bookStore) InsertBook(ctx context.Context, b *model.Book) error {
26
        const stmt = "INSERT INTO books (author_id, title, isbn) VALUES ($1, $2, $3)"
27
28
        result, err := s.db.ExecContext(ctx, stmt, b.AuthorID, b.Title, b.ISBN)
29
        if err != nil {
30
                return fmt.Errorf("could not insert row: %w", err)
31
        }
32
33
        if _, err := result.RowsAffected(); err != nil {
34
                return fmt.Errorf("could not get affected rows: %w", err)
35
        }
36
37
        b.ID, _ = result.LastInsertId()
38
39
        return nil
40
}
193 | 194 |
func NewBookStore
Back

In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s6/book/dao/book.go:

195 |
15
func NewBookStore(db *sql.DB) *bookStore {
16
        return &bookStore{
17
                db: db,
18
        }
19
}
196 | 197 | 200 | 201 |
Package Overview: github.com/smallnest/go_test_workshop/s6/book/service 100.00%

This is a coverage report created after analysis of the github.com/smallnest/go_test_workshop/s6/book/service package. It 202 | has been generated with the following command:

gocov test github.com/smallnest/go_test_workshop/s6/book/service | gocov-html

Here are the stats. Please select a function name to view its implementation and see what's left for testing.

203 | 204 | 205 | 206 |
NewBookService(...)github.com/smallnest/go_test_workshop/s6/book/service/book.go100.00%1/1
bookService.CreateBook(...)github.com/smallnest/go_test_workshop/s6/book/service/book.go100.00%1/1
github.com/smallnest/go_test_workshop/s6/book/service100.00%2/2
207 | 208 |
func NewBookService
Back

In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s6/book/service/book.go:

209 |
15
func NewBookService(db *sql.DB) *bookService {
16
        return &bookService{
17
                store: dao.NewBookStore(db),
18
        }
19
}
210 | 211 |
func bookService.CreateBook
Back

In /Users/chaoyuepan/go/src/github.com/smallnest/go_test_workshop/s6/book/service/book.go:

212 |
25
func (s *bookService) CreateBook(ctx context.Context, b *model.Book) error {
26
        return s.store.InsertBook(ctx, b)
27
}
213 | 214 | 217 | 218 |
Report Total
219 |
78.12%
220 |
221 | 222 | -------------------------------------------------------------------------------- /s4/covermode.go: -------------------------------------------------------------------------------- 1 | package s4 2 | 3 | func example(letter string) string { 4 | switch letter { 5 | case "a": 6 | return "apple" 7 | case "b": 8 | return "banana" 9 | case "c": 10 | return "carrot" 11 | case "d": 12 | return "date" 13 | case "e": 14 | return "eggplant" 15 | case "f": 16 | return "fig" 17 | default: 18 | return "nevermind" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /s4/covermode.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | s4: Go Coverage Report 7 | 52 | 53 | 54 |
55 | 62 |
63 | not tracked 64 | 65 | no coverage 66 | low coverage 67 | * 68 | * 69 | * 70 | * 71 | * 72 | * 73 | * 74 | * 75 | high coverage 76 | 77 |
78 |
79 |
80 | 81 | 102 | 103 |
104 | 105 | 132 | 133 | -------------------------------------------------------------------------------- /s4/covermode_test.go: -------------------------------------------------------------------------------- 1 | package s4 2 | 3 | import "testing" 4 | 5 | func TestExample(t *testing.T) { 6 | for in, out := range map[string]string{ 7 | "a": "apple", 8 | "b": "banana", 9 | "c": "carrot", 10 | "d": "date", 11 | "e": "eggplant", 12 | "f": "fig", 13 | "": "nevermind", 14 | } { 15 | t.Run("input-"+in, func(t *testing.T) { 16 | // t.Parallel() 17 | result := example(in) 18 | if result != out { 19 | t.Errorf("sent %s and got %s expected %s", in, result, out) 20 | } 21 | }) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /s4/out.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | github.com/smallnest/go_test_workshop 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | s1 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | s6/book 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | arith.go 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | hello.go 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | api/book.go 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | dao/book.go 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | service/book.go 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /s5/assert_require_test.go: -------------------------------------------------------------------------------- 1 | package s5 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestAssert(t *testing.T) { 11 | var err error 12 | assert.NoError(t, err) 13 | 14 | assert.Equal(t, 1, 1) 15 | 16 | assert.Empty(t, len([]string{"abc"})) 17 | 18 | assert.Greater(t, 7, 6) 19 | 20 | t.Log("exit assert") 21 | } 22 | 23 | func TestRequire(t *testing.T) { 24 | var err error 25 | require.NoError(t, err) 26 | 27 | require.Equal(t, 1, 1) 28 | 29 | require.Empty(t, len([]string{"abc"})) 30 | 31 | require.Greater(t, 7, 6) 32 | 33 | t.Log("exit require") 34 | } 35 | -------------------------------------------------------------------------------- /s5/gocovery_test.go: -------------------------------------------------------------------------------- 1 | package s5 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestGocovery(t *testing.T) { 10 | Convey("Given a starting integer value", t, func() { 11 | x := 42 12 | 13 | Convey("When incremented", func() { 14 | x++ 15 | 16 | Convey("The value should be greater by one", func() { 17 | So(x, ShouldEqual, 43) 18 | }) 19 | Convey("The value should NOT be what it used to be", func() { 20 | So(x, ShouldNotEqual, 42) 21 | }) 22 | }) 23 | Convey("When decremented", func() { 24 | x-- 25 | 26 | Convey("The value should be lesser by one", func() { 27 | So(x, ShouldEqual, 41) 28 | }) 29 | Convey("The value should NOT be what it used to be", func() { 30 | So(x, ShouldNotEqual, 42) 31 | }) 32 | }) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /s6/container/kafka_test.go: -------------------------------------------------------------------------------- 1 | package container 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | "testing" 7 | "time" 8 | 9 | "github.com/google/uuid" 10 | "github.com/segmentio/kafka-go" 11 | "github.com/stretchr/testify/assert" 12 | "github.com/stretchr/testify/require" 13 | "github.com/testcontainers/testcontainers-go" 14 | ) 15 | 16 | func TestWithKafka(t *testing.T) { 17 | kafkaContainer := testcontainers.NewLocalDockerCompose( 18 | []string{"testdata/docker-compose.yml"}, 19 | strings.ToLower(uuid.New().String()), 20 | ) 21 | execError := kafkaContainer.WithCommand([]string{"up", "-d"}).Invoke() 22 | require.NoError(t, execError.Error) 23 | 24 | // kafka starts ver slow 25 | time.Sleep(time.Minute) 26 | defer destroyKafka(kafkaContainer) 27 | 28 | // test write 29 | w := &kafka.Writer{ 30 | Addr: kafka.TCP("localhost:9092"), 31 | Topic: "test-topic", 32 | Balancer: &kafka.LeastBytes{}, 33 | } 34 | 35 | err := w.WriteMessages(context.Background(), 36 | kafka.Message{ 37 | Key: []byte("Key-A"), 38 | Value: []byte("Hello World!"), 39 | }, 40 | kafka.Message{ 41 | Key: []byte("Key-B"), 42 | Value: []byte("One!"), 43 | }, 44 | kafka.Message{ 45 | Key: []byte("Key-C"), 46 | Value: []byte("Two!"), 47 | }, 48 | ) 49 | assert.NoError(t, err) 50 | err = w.Close() 51 | assert.NoError(t, err) 52 | 53 | // test read 54 | r := kafka.NewReader(kafka.ReaderConfig{ 55 | Brokers: []string{"localhost:9092"}, 56 | Topic: "test-topic", 57 | Partition: 0, 58 | MinBytes: 10e3, // 10KB 59 | MaxBytes: 10e6, // 10MB 60 | }) 61 | 62 | m, err := r.ReadMessage(context.Background()) 63 | assert.NoError(t, err) 64 | assert.NotEmpty(t, m) 65 | 66 | } 67 | 68 | func destroyKafka(compose *testcontainers.LocalDockerCompose) { 69 | compose.Down() 70 | time.Sleep(1 * time.Second) 71 | } 72 | -------------------------------------------------------------------------------- /s6/container/redis_test.go: -------------------------------------------------------------------------------- 1 | package container 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/go-redis/redis" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/testcontainers/testcontainers-go" 10 | "github.com/testcontainers/testcontainers-go/wait" 11 | ) 12 | 13 | func TestWithRedis(t *testing.T) { 14 | ctx := context.Background() 15 | req := testcontainers.ContainerRequest{ 16 | Image: "redis:latest", 17 | ExposedPorts: []string{"6379/tcp"}, 18 | WaitingFor: wait.ForLog("Ready to accept connections"), 19 | } 20 | redisC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ 21 | ContainerRequest: req, 22 | Started: true, 23 | }) 24 | if err != nil { 25 | t.Error(err) 26 | } 27 | defer redisC.Terminate(ctx) 28 | 29 | rdb := redis.NewClient(&redis.Options{ 30 | Addr: "localhost:6379", 31 | Password: "", // no password set 32 | DB: 0, // use default DB 33 | }) 34 | 35 | err = rdb.Set("key", "value", 0).Err() 36 | assert.NoError(t, err) 37 | 38 | val, err := rdb.Get("key").Result() 39 | assert.NoError(t, err) 40 | assert.Equal(t, "value", val) 41 | 42 | } 43 | -------------------------------------------------------------------------------- /s6/container/testdata/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | zookeeper: 4 | image: wurstmeister/zookeeper:latest 5 | container_name: zookeeper 6 | expose: 7 | - 2181 8 | kafka: 9 | image: wurstmeister/kafka:latest 10 | container_name: kafka 11 | depends_on: 12 | - zookeeper 13 | ports: 14 | - "9092:9092" 15 | environment: 16 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092 17 | KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092 18 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 19 | KAFKA_CREATE_TOPICS: "test-topic:1:1" -------------------------------------------------------------------------------- /s6/gnomock/kafka_test.go: -------------------------------------------------------------------------------- 1 | package gnomock_test 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | "time" 8 | 9 | "github.com/orlangure/gnomock" 10 | "github.com/orlangure/gnomock/preset/kafka" 11 | kafkaclient "github.com/segmentio/kafka-go" 12 | "github.com/stretchr/testify/require" 13 | ) 14 | 15 | func TestKafka(t *testing.T) { 16 | // init some messages 17 | messages := []kafka.Message{ 18 | { 19 | Topic: "events", 20 | Key: "order", 21 | Value: "1", 22 | Time: time.Now().UnixNano(), 23 | }, 24 | { 25 | Topic: "alerts", 26 | Key: "CPU", 27 | Value: "92", 28 | Time: time.Now().UnixNano(), 29 | }, 30 | } 31 | 32 | p := kafka.Preset( 33 | kafka.WithTopics("topic-1", "topic-2"), 34 | kafka.WithMessages(messages...), 35 | ) 36 | 37 | // start the kafka container 38 | container, err := gnomock.Start( 39 | p, 40 | gnomock.WithDebugMode(), gnomock.WithLogWriter(os.Stdout), 41 | gnomock.WithContainerName("kafka"), 42 | gnomock.WithUseLocalImagesFirst(), 43 | ) 44 | require.NoError(t, err) 45 | // stop the kafka container on exit 46 | defer func() { require.NoError(t, gnomock.Stop(container)) }() 47 | 48 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) 49 | defer cancel() 50 | 51 | alertsReader := kafkaclient.NewReader(kafkaclient.ReaderConfig{ 52 | Brokers: []string{container.Address(kafka.BrokerPort)}, 53 | Topic: "alerts", 54 | }) 55 | 56 | m, err := alertsReader.ReadMessage(ctx) 57 | require.NoError(t, err) 58 | require.NoError(t, alertsReader.Close()) 59 | 60 | require.Equal(t, "CPU", string(m.Key)) 61 | require.Equal(t, "92", string(m.Value)) 62 | 63 | eventsReader := kafkaclient.NewReader(kafkaclient.ReaderConfig{ 64 | Brokers: []string{container.Address(kafka.BrokerPort)}, 65 | Topic: "events", 66 | }) 67 | 68 | m, err = eventsReader.ReadMessage(ctx) 69 | require.NoError(t, err) 70 | require.NoError(t, eventsReader.Close()) 71 | 72 | require.Equal(t, "order", string(m.Key)) 73 | require.Equal(t, "1", string(m.Value)) 74 | 75 | c, err := kafkaclient.Dial("tcp", container.Address(kafka.BrokerPort)) 76 | require.NoError(t, err) 77 | 78 | require.NoError(t, c.DeleteTopics("topic-1", "topic-2")) 79 | require.Error(t, c.DeleteTopics("unknown-topic")) 80 | 81 | require.NoError(t, c.Close()) 82 | } 83 | -------------------------------------------------------------------------------- /s6/gnomock/redis_test.go: -------------------------------------------------------------------------------- 1 | package gnomock_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | redisclient "github.com/go-redis/redis/v7" 8 | "github.com/orlangure/gnomock" 9 | "github.com/orlangure/gnomock/preset/redis" 10 | ) 11 | 12 | func TestRedis(t *testing.T) { 13 | vs := make(map[string]interface{}) 14 | 15 | vs["a"] = "foo" 16 | vs["b"] = 42 17 | vs["c"] = true 18 | 19 | p := redis.Preset(redis.WithValues(vs)) 20 | container, _ := gnomock.Start(p, 21 | gnomock.WithDebugMode(), 22 | gnomock.WithUseLocalImagesFirst(), 23 | ) 24 | 25 | defer func() { _ = gnomock.Stop(container) }() 26 | 27 | addr := container.DefaultAddress() 28 | client := redisclient.NewClient(&redisclient.Options{Addr: addr}) 29 | 30 | fmt.Println(client.Get("a").Result()) 31 | 32 | var number int 33 | 34 | err := client.Get("b").Scan(&number) 35 | fmt.Println(number, err) 36 | 37 | var flag bool 38 | 39 | err = client.Get("c").Scan(&flag) 40 | fmt.Println(flag, err) 41 | } 42 | -------------------------------------------------------------------------------- /s6/httptest/handler_test.go: -------------------------------------------------------------------------------- 1 | package httptest 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "net/http/httptest" 8 | "testing" 9 | ) 10 | 11 | func TestHandler(t *testing.T) { 12 | handler := func(w http.ResponseWriter, r *http.Request) { 13 | io.WriteString(w, "Hello World!") 14 | } 15 | 16 | req := httptest.NewRequest("GET", "http://example.com/foo", nil) 17 | w := httptest.NewRecorder() 18 | handler(w, req) 19 | 20 | resp := w.Result() 21 | body, _ := io.ReadAll(resp.Body) 22 | 23 | fmt.Println(resp.StatusCode) 24 | fmt.Println(resp.Header.Get("Content-Type")) 25 | fmt.Println(string(body)) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /s6/httptest/server_test.go: -------------------------------------------------------------------------------- 1 | package httptest 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net/http" 8 | "net/http/httptest" 9 | "testing" 10 | ) 11 | 12 | func TestHTTPServer(t *testing.T) { 13 | ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 14 | fmt.Fprintf(w, "Hello, %s", r.Proto) 15 | })) 16 | ts.EnableHTTP2 = true 17 | ts.StartTLS() 18 | defer ts.Close() 19 | 20 | res, err := ts.Client().Get(ts.URL) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | greeting, err := io.ReadAll(res.Body) 25 | res.Body.Close() 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | fmt.Printf("%s", greeting) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /s6/mock/io_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: io (interfaces: Reader,Writer) 3 | 4 | // Package mock_io is a generated GoMock package. 5 | package mock_io 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | gomock "github.com/golang/mock/gomock" 11 | ) 12 | 13 | // MockReader is a mock of Reader interface. 14 | type MockReader struct { 15 | ctrl *gomock.Controller 16 | recorder *MockReaderMockRecorder 17 | } 18 | 19 | // MockReaderMockRecorder is the mock recorder for MockReader. 20 | type MockReaderMockRecorder struct { 21 | mock *MockReader 22 | } 23 | 24 | // NewMockReader creates a new mock instance. 25 | func NewMockReader(ctrl *gomock.Controller) *MockReader { 26 | mock := &MockReader{ctrl: ctrl} 27 | mock.recorder = &MockReaderMockRecorder{mock} 28 | return mock 29 | } 30 | 31 | // EXPECT returns an object that allows the caller to indicate expected use. 32 | func (m *MockReader) EXPECT() *MockReaderMockRecorder { 33 | return m.recorder 34 | } 35 | 36 | // Read mocks base method. 37 | func (m *MockReader) Read(arg0 []byte) (int, error) { 38 | m.ctrl.T.Helper() 39 | ret := m.ctrl.Call(m, "Read", arg0) 40 | ret0, _ := ret[0].(int) 41 | ret1, _ := ret[1].(error) 42 | return ret0, ret1 43 | } 44 | 45 | // Read indicates an expected call of Read. 46 | func (mr *MockReaderMockRecorder) Read(arg0 interface{}) *gomock.Call { 47 | mr.mock.ctrl.T.Helper() 48 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockReader)(nil).Read), arg0) 49 | } 50 | 51 | // MockWriter is a mock of Writer interface. 52 | type MockWriter struct { 53 | ctrl *gomock.Controller 54 | recorder *MockWriterMockRecorder 55 | } 56 | 57 | // MockWriterMockRecorder is the mock recorder for MockWriter. 58 | type MockWriterMockRecorder struct { 59 | mock *MockWriter 60 | } 61 | 62 | // NewMockWriter creates a new mock instance. 63 | func NewMockWriter(ctrl *gomock.Controller) *MockWriter { 64 | mock := &MockWriter{ctrl: ctrl} 65 | mock.recorder = &MockWriterMockRecorder{mock} 66 | return mock 67 | } 68 | 69 | // EXPECT returns an object that allows the caller to indicate expected use. 70 | func (m *MockWriter) EXPECT() *MockWriterMockRecorder { 71 | return m.recorder 72 | } 73 | 74 | // Write mocks base method. 75 | func (m *MockWriter) Write(arg0 []byte) (int, error) { 76 | m.ctrl.T.Helper() 77 | ret := m.ctrl.Call(m, "Write", arg0) 78 | ret0, _ := ret[0].(int) 79 | ret1, _ := ret[1].(error) 80 | return ret0, ret1 81 | } 82 | 83 | // Write indicates an expected call of Write. 84 | func (mr *MockWriterMockRecorder) Write(arg0 interface{}) *gomock.Call { 85 | mr.mock.ctrl.T.Helper() 86 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockWriter)(nil).Write), arg0) 87 | } 88 | -------------------------------------------------------------------------------- /s6/mock/rw_test.go: -------------------------------------------------------------------------------- 1 | package mock_io 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golang/mock/gomock" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestReader(t *testing.T) { 11 | ctrl := gomock.NewController(t) 12 | defer ctrl.Finish() 13 | 14 | r := NewMockReader(ctrl) 15 | 16 | r.EXPECT().Read(gomock.All()).Do(func(arg0 []byte) { 17 | for i := 0; i < 10; i++ { 18 | arg0[i] = 'a' + byte(i) 19 | } 20 | }).Return(10, nil).AnyTimes() 21 | 22 | data := make([]byte, 1024) 23 | n, err := r.Read(data) 24 | assert.NoError(t, err) 25 | assert.Equal(t, 10, n) 26 | assert.Equal(t, "abcdefghij", string(data[:n])) 27 | 28 | } 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /s6/mockey/mock_test.go: -------------------------------------------------------------------------------- 1 | package mockey 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "testing" 7 | 8 | "github.com/bytedance/mockey" 9 | ) 10 | 11 | // go test -v . -ldflags=-checklinkname=0 -gcflags="all=-N -l" 12 | 13 | func TestCallerFunc(t *testing.T) { 14 | origin := fmt.Println 15 | mock := func(a ...any) (n int, err error) { 16 | fmt.Fprintln(os.Stdout, "I have changed the arguments") 17 | return origin(a...) 18 | } 19 | mock2 := mockey.Mock(origin).To(mock).Origin(&origin).Build() 20 | defer mock2.UnPatch() 21 | 22 | mock2.When(func(a ...any) bool { 23 | if len(a) == 1 && a[0] == "hello world" { 24 | return true 25 | } 26 | 27 | return false 28 | }).To(func(a ...any) (n int, err error) { 29 | return fmt.Fprintln(os.Stdout, "matched when") 30 | }) 31 | 32 | fmt.Println("hello world") 33 | fmt.Println("hello mockey") 34 | } 35 | -------------------------------------------------------------------------------- /s6/monkey/hello_test.go: -------------------------------------------------------------------------------- 1 | package monkey 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "testing" 7 | 8 | // "bou.ke/monkey" 9 | "github.com/agiledragon/gomonkey/v2" 10 | ) 11 | 12 | func TestPrintlnByGomonkey(t *testing.T) { 13 | patches := gomonkey.ApplyFunc(fmt.Println, func(a ...any) (n int, err error) { 14 | 15 | return fmt.Fprintln(os.Stdout, "I have changed the arguments") 16 | }) 17 | defer patches.Reset() 18 | 19 | fmt.Println("hello world") 20 | } 21 | 22 | // func TestPrintlnByMonkey(t *testing.T) { 23 | // monkey.Patch(fmt.Println, func(a ...interface{}) (n int, err error) { 24 | // s := make([]interface{}, len(a)) 25 | // for i, v := range a { 26 | // s[i] = strings.Replace(fmt.Sprint(v), "hell", "*bleep*", -1) 27 | // } 28 | // return fmt.Fprintln(os.Stdout, s...) 29 | // }) 30 | // fmt.Println("what the hell?") // what the *bleep*? 31 | // } 32 | -------------------------------------------------------------------------------- /s7/book/api/book.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "net/http" 7 | "strconv" 8 | 9 | "github.com/smallnest/go_test_workshop/s7/book/model" 10 | "github.com/smallnest/go_test_workshop/s7/book/service" 11 | ) 12 | 13 | type BookController struct { 14 | bookService service.BookService 15 | } 16 | 17 | func (bc *BookController) CreateBook(w http.ResponseWriter, r *http.Request) { 18 | title := r.FormValue("title") 19 | authorID := r.FormValue("authorID") 20 | isbn := r.FormValue("isbn") 21 | computers := r.FormValue("subject") 22 | 23 | aid, err := strconv.Atoi(authorID) 24 | if err != nil { 25 | http.Error(w, "wrong author id", http.StatusBadRequest) 26 | return 27 | } 28 | 29 | book := &model.Book{ 30 | Title: title, 31 | AuthorID: aid, 32 | ISBN: isbn, 33 | Subject: computers, 34 | } 35 | 36 | err = bc.bookService.CreateBook(context.TODO(), book) 37 | if err != nil { 38 | http.Error(w, "failed to create book", http.StatusInternalServerError) 39 | return 40 | } 41 | 42 | data, _ := json.Marshal(book) 43 | 44 | w.Header().Set("Contetx-Type", "application/json") 45 | w.Write(data) 46 | } 47 | -------------------------------------------------------------------------------- /s7/book/api/book_monkey_test.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "io" 7 | "net/http" 8 | "net/http/httptest" 9 | "net/url" 10 | "strings" 11 | "testing" 12 | 13 | "github.com/agiledragon/gomonkey/v2" 14 | "github.com/smallnest/go_test_workshop/s7/book/model" 15 | "github.com/smallnest/go_test_workshop/s7/book/service" 16 | "github.com/stretchr/testify/assert" 17 | "github.com/stretchr/testify/require" 18 | ) 19 | 20 | func TestBookController_GetBook_By_Monkey(t *testing.T) { 21 | bookService := service.NewBookService(nil) 22 | bc := &BookController{bookService: bookService} 23 | 24 | data := url.Values{} 25 | data.Set("title", "The Go Programming Language") 26 | data.Set("authorID", "1") 27 | data.Set("isbn", "978-0134190440") 28 | data.Set("subject", "computers") 29 | 30 | r := httptest.NewRequest("POST", "http://example.com/foo", strings.NewReader(data.Encode())) 31 | r.Header.Add("Content-Type", "application/x-www-form-urlencoded") 32 | 33 | book := &model.Book{ 34 | ID: 6, 35 | Title: "The Go Programming Language", 36 | AuthorID: 1, 37 | ISBN: "978-0134190440", 38 | Subject: "computers", 39 | } 40 | patches := gomonkey.ApplyMethodFunc(bookService, "CreateBook", func(ctx context.Context, b *model.Book) error { 41 | b.ID = 6 42 | 43 | return nil 44 | }) 45 | defer patches.Reset() 46 | 47 | w := httptest.NewRecorder() 48 | bc.CreateBook(w, r) 49 | 50 | resp := w.Result() 51 | require.Equal(t, http.StatusOK, resp.StatusCode) 52 | body, _ := io.ReadAll(resp.Body) 53 | 54 | err := json.Unmarshal(body, &book) 55 | require.NoError(t, err) 56 | assert.NotZero(t, book.ID) 57 | } 58 | -------------------------------------------------------------------------------- /s7/book/api/book_test.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | "net/http" 7 | "net/http/httptest" 8 | "net/url" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/smallnest/go_test_workshop/s7/book/model" 13 | "github.com/smallnest/go_test_workshop/s7/book/service" 14 | "github.com/smallnest/go_test_workshop/s7/book/util" 15 | "github.com/stretchr/testify/assert" 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | func TestBookController_GetBook(t *testing.T) { 20 | db := util.CreateTestDB(t) 21 | defer db.Close() 22 | 23 | bookService := service.NewBookService(db) 24 | bc := &BookController{bookService: bookService} 25 | 26 | data := url.Values{} 27 | data.Set("title", "The Go Programming Language") 28 | data.Set("authorID", "1") 29 | data.Set("isbn", "978-0134190440") 30 | data.Set("subject", "computers") 31 | 32 | r := httptest.NewRequest("POST", "http://example.com/foo", strings.NewReader(data.Encode())) 33 | r.Header.Add("Content-Type", "application/x-www-form-urlencoded") 34 | 35 | w := httptest.NewRecorder() 36 | bc.CreateBook(w, r) 37 | 38 | resp := w.Result() 39 | require.Equal(t, http.StatusOK, resp.StatusCode) 40 | body, _ := io.ReadAll(resp.Body) 41 | 42 | var book model.Book 43 | err := json.Unmarshal(body, &book) 44 | require.NoError(t, err) 45 | assert.NotZero(t, book.ID) 46 | } 47 | -------------------------------------------------------------------------------- /s7/book/api/test-bin: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /s7/book/dao/book.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "fmt" 7 | 8 | "github.com/smallnest/go_test_workshop/s7/book/model" 9 | ) 10 | 11 | type BookStore interface { 12 | InsertBook(context.Context, *model.Book) error 13 | } 14 | 15 | func NewBookStore(db *sql.DB) *bookStore { 16 | return &bookStore{ 17 | db: db, 18 | } 19 | } 20 | 21 | type bookStore struct { 22 | db *sql.DB 23 | } 24 | 25 | func (s *bookStore) InsertBook(ctx context.Context, b *model.Book) error { 26 | const stmt = "INSERT INTO books (author_id, title, isbn) VALUES ($1, $2, $3)" 27 | 28 | result, err := s.db.ExecContext(ctx, stmt, b.AuthorID, b.Title, b.ISBN) 29 | if err != nil { 30 | return fmt.Errorf("could not insert row: %w", err) 31 | } 32 | 33 | if _, err := result.RowsAffected(); err != nil { 34 | return fmt.Errorf("could not get affected rows: %w", err) 35 | } 36 | 37 | b.ID, _ = result.LastInsertId() 38 | 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /s7/book/dao/book_test.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/DATA-DOG/go-sqlmock" 8 | "github.com/smallnest/go_test_workshop/s7/book/model" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestBookStore_InsertBook(t *testing.T) { 14 | db, mock, err := sqlmock.New() 15 | assert.NoError(t, err, "an error '%s' was not expected when opening a stub database connection", err) 16 | defer db.Close() 17 | 18 | store := &bookStore{ 19 | db: db, 20 | } 21 | 22 | book := &model.Book{ 23 | Title: "The Go Programming Language", 24 | AuthorID: 1, 25 | ISBN: "978-0134190440", 26 | Subject: "computers", 27 | } 28 | 29 | mock.ExpectExec("INSERT INTO books").WillReturnResult(sqlmock.NewResult(1, 1)) 30 | 31 | err = store.InsertBook(context.TODO(), book) 32 | 33 | require.NoError(t, err) 34 | assert.NotZero(t, book.ID) 35 | } 36 | -------------------------------------------------------------------------------- /s7/book/model/book.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Book struct { 4 | ID int64 5 | AuthorID int 6 | Title string 7 | ISBN string 8 | Subject string 9 | } 10 | 11 | type Author struct { 12 | ID int 13 | FirstName string 14 | LastName string 15 | } 16 | -------------------------------------------------------------------------------- /s7/book/service/book.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | 7 | "github.com/smallnest/go_test_workshop/s7/book/dao" 8 | "github.com/smallnest/go_test_workshop/s7/book/model" 9 | ) 10 | 11 | type BookService interface { 12 | CreateBook(ctx context.Context, b *model.Book) error 13 | } 14 | 15 | func NewBookService(db *sql.DB) *bookService { 16 | return &bookService{ 17 | store: dao.NewBookStore(db), 18 | } 19 | } 20 | 21 | type bookService struct { 22 | store dao.BookStore 23 | } 24 | 25 | func (s *bookService) CreateBook(ctx context.Context, b *model.Book) error { 26 | return s.store.InsertBook(ctx, b) 27 | } 28 | -------------------------------------------------------------------------------- /s7/book/service/book_test.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/smallnest/go_test_workshop/s7/book/model" 8 | "github.com/smallnest/go_test_workshop/s7/book/util" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestBookService_CreateBook(t *testing.T) { 14 | db := util.CreateTestDB(t) 15 | defer db.Close() 16 | 17 | bookService := NewBookService(db) 18 | 19 | book := &model.Book{ 20 | Title: "The Go Programming Language", 21 | AuthorID: 1, 22 | ISBN: "978-0134190440", 23 | Subject: "computers", 24 | } 25 | err := bookService.CreateBook(context.TODO(), book) 26 | 27 | require.NoError(t, err) 28 | assert.NotZero(t, book.ID) 29 | } 30 | -------------------------------------------------------------------------------- /s7/book/testdata/test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smallnest/go_test_workshop/27f12b63da3545c6ea5ae94b9287c3f5b0da5137/s7/book/testdata/test.db -------------------------------------------------------------------------------- /s7/book/util/db.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "database/sql" 5 | "testing" 6 | 7 | _ "github.com/mattn/go-sqlite3" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func CreateTestDB(t *testing.T) *sql.DB { 12 | db, err := sql.Open("sqlite3", "file:../testdata/test.db?cache=shared") 13 | assert.NoError(t, err) 14 | 15 | db.Exec(` 16 | CREATE TABLE IF NOT EXISTS books ( 17 | Id INTEGER PRIMARY KEY AUTOINCREMENT, 18 | title TEXT, 19 | isbn TEXT, 20 | subject TEXT, 21 | author_id INTEGER 22 | ); 23 | DELETE FROM books; 24 | `) 25 | 26 | return db 27 | } 28 | -------------------------------------------------------------------------------- /s7/book/util/db_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "testing" 5 | 6 | _ "github.com/mattn/go-sqlite3" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCreateTestDB(t *testing.T) { 11 | db := CreateTestDB(t) 12 | defer db.Close() 13 | 14 | _, err := db.Exec("DELETE FROM tor_list") 15 | assert.NoError(t, err) 16 | } 17 | -------------------------------------------------------------------------------- /s8/benchmark_hash_test.go: -------------------------------------------------------------------------------- 1 | package s8 2 | 3 | import ( 4 | "crypto/md5" 5 | "crypto/rand" 6 | "crypto/sha1" 7 | "crypto/sha256" 8 | "crypto/sha512" 9 | "encoding/hex" 10 | "fmt" 11 | "hash/crc32" 12 | "hash/fnv" 13 | _ "runtime" 14 | "testing" 15 | "unsafe" 16 | 17 | xxhashasm "github.com/cespare/xxhash" 18 | "github.com/creachadair/cityhash" 19 | afarmhash "github.com/dgryski/go-farm" 20 | farmhash "github.com/leemcloughlin/gofarmhash" 21 | "github.com/minio/highwayhash" 22 | "github.com/pierrec/xxHash/xxHash64" 23 | "github.com/spaolacci/murmur3" 24 | ) 25 | 26 | var n int64 27 | var testBytes []byte 28 | 29 | func BenchmarkHash(b *testing.B) { 30 | sizes := []int64{32, 64, 128, 256, 512, 1024} 31 | for _, n = range sizes { 32 | testBytes = make([]byte, n) 33 | readN, err := rand.Read(testBytes) 34 | if readN != int(n) { 35 | panic(fmt.Sprintf("expect %d but got %d", n, readN)) 36 | } 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | b.Run(fmt.Sprintf("Sha1-%d", n), benchmarkSha1) 42 | b.Run(fmt.Sprintf("Sha256-%d", n), benchmarkSha256) 43 | b.Run(fmt.Sprintf("Sha512-%d", n), benchmarkSha512) 44 | b.Run(fmt.Sprintf("MD5-%d", n), benchmarkMD5) 45 | b.Run(fmt.Sprintf("Fnv-%d", n), benchmarkFnv) 46 | b.Run(fmt.Sprintf("Crc32-%d", n), benchmarkCrc32) 47 | b.Run(fmt.Sprintf("CityHash-%d", n), benchmarkCityhash) 48 | b.Run(fmt.Sprintf("FarmHash-%d", n), benchmarkFarmhash) 49 | b.Run(fmt.Sprintf("Farmhash_dgryski-%d", n), benchmarkFarmhash_dgryski) 50 | b.Run(fmt.Sprintf("Murmur3-%d", n), benchmarkMurmur3) 51 | b.Run(fmt.Sprintf("Highwayhash-%d", n), benchmarkHighwayhash) 52 | b.Run(fmt.Sprintf("XXHash64-%d", n), benchmarkXXHash64) 53 | b.Run(fmt.Sprintf("XXHash64_ASM-%d", n), benchmarkXXHash64_ASM) 54 | b.Run(fmt.Sprintf("MapHash64-%d", n), benchmarkMapHash64) 55 | fmt.Println() 56 | } 57 | 58 | } 59 | 60 | func benchmarkSha1(b *testing.B) { 61 | x := sha1.New() 62 | 63 | b.SetBytes(n) 64 | b.ResetTimer() 65 | 66 | for i := 0; i < b.N; i++ { 67 | x.Reset() 68 | x.Write(testBytes) 69 | _ = x.Sum(nil) 70 | } 71 | } 72 | func benchmarkSha256(b *testing.B) { 73 | x := sha256.New() 74 | b.SetBytes(n) 75 | b.ResetTimer() 76 | 77 | for i := 0; i < b.N; i++ { 78 | x.Reset() 79 | x.Write(testBytes) 80 | _ = x.Sum(nil) 81 | } 82 | } 83 | 84 | func benchmarkSha512(b *testing.B) { 85 | x := sha512.New() 86 | b.SetBytes(n) 87 | b.ResetTimer() 88 | 89 | for i := 0; i < b.N; i++ { 90 | x.Reset() 91 | x.Write(testBytes) 92 | _ = x.Sum(nil) 93 | } 94 | } 95 | 96 | func benchmarkMD5(b *testing.B) { 97 | x := md5.New() 98 | b.SetBytes(n) 99 | b.ResetTimer() 100 | 101 | for i := 0; i < b.N; i++ { 102 | x.Reset() 103 | x.Write(testBytes) 104 | _ = x.Sum(nil) 105 | } 106 | } 107 | 108 | func benchmarkCrc32(b *testing.B) { 109 | x := crc32.NewIEEE() 110 | b.SetBytes(n) 111 | b.ResetTimer() 112 | 113 | for i := 0; i < b.N; i++ { 114 | x.Reset() 115 | x.Write(testBytes) 116 | _ = x.Sum32() 117 | } 118 | } 119 | 120 | func benchmarkFnv(b *testing.B) { 121 | x := fnv.New64() 122 | b.SetBytes(n) 123 | b.ResetTimer() 124 | 125 | for i := 0; i < b.N; i++ { 126 | x.Reset() 127 | x.Write(testBytes) 128 | _ = x.Sum64() 129 | } 130 | } 131 | 132 | func benchmarkCityhash(b *testing.B) { 133 | b.SetBytes(n) 134 | b.ResetTimer() 135 | 136 | for i := 0; i < b.N; i++ { 137 | _ = cityhash.Hash64WithSeed(testBytes, 0xCAFE) 138 | } 139 | } 140 | 141 | func benchmarkFarmhash(b *testing.B) { 142 | b.SetBytes(n) 143 | b.ResetTimer() 144 | 145 | for i := 0; i < b.N; i++ { 146 | _ = farmhash.Hash64WithSeed(testBytes, 0xCAFE) 147 | } 148 | } 149 | 150 | func benchmarkFarmhash_dgryski(b *testing.B) { 151 | b.SetBytes(n) 152 | b.ResetTimer() 153 | 154 | for i := 0; i < b.N; i++ { 155 | _ = afarmhash.Hash64WithSeed(testBytes, 0xCAFE) 156 | } 157 | } 158 | 159 | func benchmarkMurmur3(b *testing.B) { 160 | x := murmur3.New64() 161 | b.SetBytes(n) 162 | b.ResetTimer() 163 | 164 | for i := 0; i < b.N; i++ { 165 | x.Reset() 166 | x.Write(testBytes) 167 | _ = x.Sum64() 168 | } 169 | } 170 | func benchmarkHighwayhash(b *testing.B) { 171 | key, _ := hex.DecodeString("000102030405060708090A0B0C0D0E0FF0E0D0C0B0A090807060504030201000") // use your own key here 172 | x, _ := highwayhash.New64(key) 173 | b.SetBytes(n) 174 | b.ResetTimer() 175 | 176 | for i := 0; i < b.N; i++ { 177 | x.Reset() 178 | x.Write(testBytes) 179 | _ = x.Sum64() 180 | } 181 | } 182 | 183 | func benchmarkXXHash64(b *testing.B) { 184 | x := xxHash64.New(0xCAFE) 185 | b.SetBytes(n) 186 | b.ResetTimer() 187 | 188 | for i := 0; i < b.N; i++ { 189 | x.Reset() 190 | x.Write(testBytes) 191 | _ = x.Sum64() 192 | } 193 | } 194 | 195 | func benchmarkXXHash64_ASM(b *testing.B) { 196 | x := xxhashasm.New() 197 | b.SetBytes(n) 198 | b.ResetTimer() 199 | 200 | for i := 0; i < b.N; i++ { 201 | x.Reset() 202 | x.Write(testBytes) 203 | _ = x.Sum64() 204 | } 205 | } 206 | 207 | //go:noescape 208 | //go:linkname memhash runtime.memhash 209 | func memhash(p unsafe.Pointer, h, s uintptr) uintptr 210 | 211 | type stringStruct struct { 212 | str unsafe.Pointer 213 | len int 214 | } 215 | 216 | func MemHash(data []byte) uint64 { 217 | ss := (*stringStruct)(unsafe.Pointer(&data)) 218 | return uint64(memhash(ss.str, 0, uintptr(ss.len))) 219 | } 220 | func benchmarkMapHash64(b *testing.B) { 221 | b.SetBytes(n) 222 | b.ResetTimer() 223 | 224 | for i := 0; i < b.N; i++ { 225 | _ = MemHash(testBytes) 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /s8/benchmark_parallel_test.go: -------------------------------------------------------------------------------- 1 | package s8_test 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "runtime" 7 | "testing" 8 | ) 9 | 10 | func init() { 11 | fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(-1)) 12 | } 13 | 14 | func BenchmarkParallelHash(b *testing.B) { 15 | testBytes := make([]byte, 1024) 16 | 17 | b.ResetTimer() 18 | b.SetParallelism(20) 19 | b.RunParallel(func(pb *testing.PB) { 20 | x := sha256.New() 21 | for pb.Next() { 22 | x.Reset() 23 | x.Write(testBytes) 24 | _ = x.Sum(nil) 25 | } 26 | }) 27 | } 28 | 29 | func BenchmarkNonParallelHash(b *testing.B) { 30 | testBytes := make([]byte, 1024) 31 | 32 | b.ResetTimer() 33 | x := sha256.New() 34 | for i := 0; i < b.N; i++ { 35 | x.Reset() 36 | x.Write(testBytes) 37 | _ = x.Sum(nil) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /s8/benchstat.txt: -------------------------------------------------------------------------------- 1 | name old time/op new time/op delta 2 | Example-8 3.60µs ± 0% 0.17µs ± 1% -95.29% (p=0.008 n=5+5) 3 | 4 | name old alloc/op new alloc/op delta 5 | Example-8 32.0B ± 0% 8.0B ± 0% -75.00% (p=0.008 n=5+5) 6 | 7 | name old allocs/op new allocs/op delta 8 | Example-8 1.00 ± 0% 1.00 ± 0% ~ (all equal) 9 | -------------------------------------------------------------------------------- /s8/benchstat_test.go: -------------------------------------------------------------------------------- 1 | package s8_test 2 | 3 | import ( 4 | "crypto/sha256" 5 | "testing" 6 | ) 7 | 8 | func BenchmarkExample(b *testing.B) { 9 | x := sha256.New() 10 | testBytes := make([]byte, 1024) 11 | 12 | b.ResetTimer() 13 | for i := 0; i < b.N; i++ { 14 | x.Reset() 15 | x.Write(testBytes) 16 | _ = x.Sum(nil) 17 | } 18 | } 19 | 20 | // func BenchmarkExample(b *testing.B) { 21 | // x := maphash.Hash{} 22 | // testBytes := make([]byte, 1024) 23 | 24 | // b.ResetTimer() 25 | // for i := 0; i < b.N; i++ { 26 | // x.Reset() 27 | // x.Write(testBytes) 28 | // _ = x.Sum(nil) 29 | // } 30 | // } 31 | -------------------------------------------------------------------------------- /s8/new.txt: -------------------------------------------------------------------------------- 1 | goos: darwin 2 | goarch: amd64 3 | pkg: github.com/smallnest/go_test_workshop/s8 4 | cpu: VirtualApple @ 2.50GHz 5 | BenchmarkExample 6 | BenchmarkExample-8 7060740 169.0 ns/op 8 B/op 1 allocs/op 7 | BenchmarkExample-8 7102920 169.2 ns/op 8 B/op 1 allocs/op 8 | BenchmarkExample-8 7144312 168.8 ns/op 8 B/op 1 allocs/op 9 | BenchmarkExample-8 7112400 170.2 ns/op 8 B/op 1 allocs/op 10 | BenchmarkExample-8 7047000 172.0 ns/op 8 B/op 1 allocs/op 11 | PASS 12 | ok github.com/smallnest/go_test_workshop/s8 9.350s 13 | -------------------------------------------------------------------------------- /s8/old.txt: -------------------------------------------------------------------------------- 1 | goos: darwin 2 | goarch: amd64 3 | pkg: github.com/smallnest/go_test_workshop/s8 4 | cpu: VirtualApple @ 2.50GHz 5 | BenchmarkExample 6 | BenchmarkExample-8 325957 3613 ns/op 32 B/op 1 allocs/op 7 | BenchmarkExample-8 331371 3607 ns/op 32 B/op 1 allocs/op 8 | BenchmarkExample-8 337330 3598 ns/op 32 B/op 1 allocs/op 9 | BenchmarkExample-8 332886 3605 ns/op 32 B/op 1 allocs/op 10 | BenchmarkExample-8 337428 3593 ns/op 32 B/op 1 allocs/op 11 | PASS 12 | ok github.com/smallnest/go_test_workshop/s8 6.571s 13 | -------------------------------------------------------------------------------- /s9/example_test.go: -------------------------------------------------------------------------------- 1 | package io_test 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "log" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | func ExampleCopy() { 13 | r := strings.NewReader("some io.Reader stream to be read\n") 14 | 15 | if _, err := io.Copy(os.Stdout, r); err != nil { 16 | log.Fatal(err) 17 | } 18 | 19 | // Output: 20 | // some io.Reader stream to be read 21 | } 22 | 23 | func ExampleCopyBuffer() { 24 | r1 := strings.NewReader("first reader\n") 25 | r2 := strings.NewReader("second reader\n") 26 | buf := make([]byte, 8) 27 | 28 | // buf is used here... 29 | if _, err := io.CopyBuffer(os.Stdout, r1, buf); err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | // ... reused here also. No need to allocate an extra buffer. 34 | if _, err := io.CopyBuffer(os.Stdout, r2, buf); err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | // Output: 39 | // first reader 40 | // second reader 41 | } 42 | 43 | func ExampleCopyN() { 44 | r := strings.NewReader("some io.Reader stream to be read") 45 | 46 | if _, err := io.CopyN(os.Stdout, r, 4); err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | // Output: 51 | // some 52 | } 53 | 54 | func ExampleReadAtLeast() { 55 | r := strings.NewReader("some io.Reader stream to be read\n") 56 | 57 | buf := make([]byte, 14) 58 | if _, err := io.ReadAtLeast(r, buf, 4); err != nil { 59 | log.Fatal(err) 60 | } 61 | fmt.Printf("%s\n", buf) 62 | 63 | // buffer smaller than minimal read size. 64 | shortBuf := make([]byte, 3) 65 | if _, err := io.ReadAtLeast(r, shortBuf, 4); err != nil { 66 | fmt.Println("error:", err) 67 | } 68 | 69 | // minimal read size bigger than io.Reader stream 70 | longBuf := make([]byte, 64) 71 | if _, err := io.ReadAtLeast(r, longBuf, 64); err != nil { 72 | fmt.Println("error:", err) 73 | } 74 | 75 | // Output: 76 | // some io.Reader 77 | // error: short buffer 78 | // error: unexpected EOF 79 | } 80 | 81 | func ExampleReadFull() { 82 | r := strings.NewReader("some io.Reader stream to be read\n") 83 | 84 | buf := make([]byte, 4) 85 | if _, err := io.ReadFull(r, buf); err != nil { 86 | log.Fatal(err) 87 | } 88 | fmt.Printf("%s\n", buf) 89 | 90 | // minimal read size bigger than io.Reader stream 91 | longBuf := make([]byte, 64) 92 | if _, err := io.ReadFull(r, longBuf); err != nil { 93 | fmt.Println("error:", err) 94 | } 95 | 96 | // Output: 97 | // some 98 | // error: unexpected EOF 99 | } 100 | 101 | func ExampleWriteString() { 102 | if _, err := io.WriteString(os.Stdout, "Hello World"); err != nil { 103 | log.Fatal(err) 104 | } 105 | 106 | // Output: Hello World 107 | } 108 | 109 | func ExampleLimitReader() { 110 | r := strings.NewReader("some io.Reader stream to be read\n") 111 | lr := io.LimitReader(r, 4) 112 | 113 | if _, err := io.Copy(os.Stdout, lr); err != nil { 114 | log.Fatal(err) 115 | } 116 | 117 | // Output: 118 | // some 119 | } 120 | 121 | func ExampleMultiReader() { 122 | r1 := strings.NewReader("first reader ") 123 | r2 := strings.NewReader("second reader ") 124 | r3 := strings.NewReader("third reader\n") 125 | r := io.MultiReader(r1, r2, r3) 126 | 127 | if _, err := io.Copy(os.Stdout, r); err != nil { 128 | log.Fatal(err) 129 | } 130 | 131 | // Output: 132 | // first reader second reader third reader 133 | } 134 | 135 | func ExampleTeeReader() { 136 | var r io.Reader = strings.NewReader("some io.Reader stream to be read\n") 137 | 138 | r = io.TeeReader(r, os.Stdout) 139 | 140 | // Everything read from r will be copied to stdout. 141 | if _, err := io.ReadAll(r); err != nil { 142 | log.Fatal(err) 143 | } 144 | 145 | // Output: 146 | // some io.Reader stream to be read 147 | } 148 | 149 | func ExampleSectionReader() { 150 | r := strings.NewReader("some io.Reader stream to be read\n") 151 | s := io.NewSectionReader(r, 5, 17) 152 | 153 | if _, err := io.Copy(os.Stdout, s); err != nil { 154 | log.Fatal(err) 155 | } 156 | 157 | // Output: 158 | // io.Reader stream 159 | } 160 | 161 | func ExampleSectionReader_Read() { 162 | r := strings.NewReader("some io.Reader stream to be read\n") 163 | s := io.NewSectionReader(r, 5, 17) 164 | 165 | buf := make([]byte, 9) 166 | if _, err := s.Read(buf); err != nil { 167 | log.Fatal(err) 168 | } 169 | 170 | fmt.Printf("%s\n", buf) 171 | 172 | // Output: 173 | // io.Reader 174 | } 175 | 176 | func ExampleSectionReader_ReadAt() { 177 | r := strings.NewReader("some io.Reader stream to be read\n") 178 | s := io.NewSectionReader(r, 5, 17) 179 | 180 | buf := make([]byte, 6) 181 | if _, err := s.ReadAt(buf, 10); err != nil { 182 | log.Fatal(err) 183 | } 184 | 185 | fmt.Printf("%s\n", buf) 186 | 187 | // Output: 188 | // stream 189 | } 190 | 191 | func ExampleSectionReader_Seek() { 192 | r := strings.NewReader("some io.Reader stream to be read\n") 193 | s := io.NewSectionReader(r, 5, 17) 194 | 195 | if _, err := s.Seek(10, io.SeekStart); err != nil { 196 | log.Fatal(err) 197 | } 198 | 199 | if _, err := io.Copy(os.Stdout, s); err != nil { 200 | log.Fatal(err) 201 | } 202 | 203 | // Output: 204 | // stream 205 | } 206 | 207 | func ExampleSectionReader_Size() { 208 | r := strings.NewReader("some io.Reader stream to be read\n") 209 | s := io.NewSectionReader(r, 5, 17) 210 | 211 | fmt.Println(s.Size()) 212 | 213 | // Output: 214 | // 17 215 | } 216 | 217 | func ExampleSeeker_Seek() { 218 | r := strings.NewReader("some io.Reader stream to be read\n") 219 | 220 | r.Seek(5, io.SeekStart) // move to the 5th char from the start 221 | if _, err := io.Copy(os.Stdout, r); err != nil { 222 | log.Fatal(err) 223 | } 224 | 225 | r.Seek(-5, io.SeekEnd) 226 | if _, err := io.Copy(os.Stdout, r); err != nil { 227 | log.Fatal(err) 228 | } 229 | 230 | // Output: 231 | // io.Reader stream to be read 232 | // read 233 | } 234 | 235 | func ExampleMultiWriter() { 236 | r := strings.NewReader("some io.Reader stream to be read\n") 237 | 238 | var buf1, buf2 bytes.Buffer 239 | w := io.MultiWriter(&buf1, &buf2) 240 | 241 | if _, err := io.Copy(w, r); err != nil { 242 | log.Fatal(err) 243 | } 244 | 245 | fmt.Print(buf1.String()) 246 | fmt.Print(buf2.String()) 247 | 248 | // Output: 249 | // some io.Reader stream to be read 250 | // some io.Reader stream to be read 251 | } 252 | 253 | func ExamplePipe() { 254 | r, w := io.Pipe() 255 | 256 | go func() { 257 | fmt.Fprint(w, "some io.Reader stream to be read\n") 258 | w.Close() 259 | }() 260 | 261 | if _, err := io.Copy(os.Stdout, r); err != nil { 262 | log.Fatal(err) 263 | } 264 | 265 | // Output: 266 | // some io.Reader stream to be read 267 | } 268 | 269 | func ExampleReadAll() { 270 | r := strings.NewReader("Go is a general-purpose language designed with systems programming in mind.") 271 | 272 | b, err := io.ReadAll(r) 273 | if err != nil { 274 | log.Fatal(err) 275 | } 276 | 277 | fmt.Printf("%s", b) 278 | 279 | // Output: 280 | // Go is a general-purpose language designed with systems programming in mind. 281 | } 282 | --------------------------------------------------------------------------------