Go1.18의 generics 및 fuzing에 대한 접촉 최소화
개요
트위터 봤어요.
다가오는 발매를 위해 최소한의 접촉을 해봤습니다.
사전 준비
왜냐하면 Go1.18은 베타 버전이거든요.
그걸 설치해야 돼요.
$ go install golang.org/dl/go1.18beta1@latest
go: downloading golang.org/dl v0.0.0-20220106205509-1eec60721618
$
$ go1.18beta1 download
Downloaded 0.0% ( 16384 / 143162528 bytes) ...
Downloaded 100.0% (143162528 / 143162528 bytes)
Unpacking /Users/su/sdk/go1.18beta1/go1.18beta1.darwin-amd64.tar.gz ...
Success. You may now run 'go1.18beta1'
$
$ go1.18beta1 version
go version go1.18beta1 darwin/amd64
$
$ alias go=go1.18beta1
$ go version
go version go1.18beta1 darwin/amd64
generics
여기 다 써있어요.
이루어지다
만약 지금까지
int64
와 float64
가 각각 함수를 정의해야 한다면generics로 K와 V를 정의합시다.단지 이와 같다
잘 써라!!
main.go
package main
import "fmt"
type Number interface {
int64 | float64
}
func main() {
// Initialize a map for the integer values
ints := map[string]int64{
"first": 34,
"second": 12,
}
// Initialize a map for the float values
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
fmt.Printf("Generic Sums with Constraint: %v and %v\n",
SumNumbers[string, int64](ints),
SumNumbers[string, float64](floats))
}
// SumNumbers sums the values of map m. Its supports both integers
// and floats as map values.
func SumNumbers[K comparable, V Number](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
실행 결과$ go run main.go
Generic Sums with Constraint: 46 and 62.97
설치(시스템 생략)
generics에서 설명하는 함수를 호출할 때
형식을 명확하게 정의하지 않아도 이해할 수 있다
main.go
fmt.Printf("Generic Sums with Constraint: %v and %v\n",
SumNumbers(ints),
SumNumbers(floats),
)
fuzzing
유닛 시험이 아니라 fuzz 시험인 것 같아요.
unit 시험에서 자신이 준비한 데이터를 통과하다
실행된 결과가 준비된 기대치와 일치하는지 검증해야 하지만
fuzz 테스트에서 데이터를 미리 준비하지 않아도
각양각색의 데이터를 깊이 파고들면 자신이 완전히 배려할 수 없는 범위에서도 테스트를 할 수 있다!
차리다
go test
노는 거니까 적당히 module 이름으로.$ go mod init example/fuzz
go: creating new go.mod: module example/fuzz
설치(문제 있음)
이 문자열을 역순으로 하는 함수를 테스트합니다
"abc"를 입력하면 "cba"출력이 돌아오는 느낌.
main.go
package main
import (
"fmt"
)
func main() {
input := "The quick brown fox jumped over the lazy dog"
rev := Reverse(input)
doubleRev := Reverse(rev)
fmt.Printf("original: %q\n", input)
fmt.Printf("reversed: %q\n", rev)
fmt.Printf("reversed again: %q\n", doubleRev)
}
func Reverse(s string) string {
b := []byte(s)
for i, j := 0, len(b)-1; i < len(b)/2; i, j = i+1, j-1 {
b[i], b[j] = b[j], b[i]
}
return string(b)
}
Fuzz 테스트
단일 테스트의 작법과 매우 비슷하다
단일 테스트에서 TestXxx를 쓸 때 FuzzXxx를 사용합니다
go mod init
가 아니라 *testing.T
미묘하게 다르게 사용했어요.reverse_test.go
package main
import (
"testing"
"unicode/utf8"
)
func FuzzReverse(f *testing.F) {
testcases := []string{"Hello, world", " ", "!12345"}
for _, tc := range testcases {
f.Add(tc) // Use f.Add to provide a seed corpus
}
f.Fuzz(func(t *testing.T, orig string) {
rev := Reverse(orig)
doubleRev := Reverse(rev)
if orig != doubleRev {
t.Errorf("Before: %q, after: %q", orig, doubleRev)
}
if utf8.ValidString(orig) && !utf8.ValidString(rev) {
t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
}
})
}
실제로 실행해 보도록 하겠습니다.$ go test -v
=== RUN FuzzReverse
=== RUN FuzzReverse/seed#0
=== RUN FuzzReverse/seed#1
=== RUN FuzzReverse/seed#2
--- PASS: FuzzReverse (0.00s)
--- PASS: FuzzReverse/seed#0 (0.00s)
--- PASS: FuzzReverse/seed#1 (0.00s)
--- PASS: FuzzReverse/seed#2 (0.00s)
PASS
ok example/fuzz 0.178s
4개의 데이터가 PASS입니다!...이게 유닛 테스트랑 뭐가 달라요?그렇게 생각하지만.
이런 느낌으로
*testing.F
flag 실행을 하면...$ go test -fuzz=Fuzz -v
=== FUZZ FuzzReverse
fuzz: elapsed: 0s, gathering baseline coverage: 0/3 completed
fuzz: elapsed: 0s, gathering baseline coverage: 3/3 completed, now fuzzing with 8 workers
fuzz: minimizing 32-byte failing input file
fuzz: elapsed: 0s, minimizing
--- FAIL: FuzzReverse (0.22s)
--- FAIL: FuzzReverse (0.00s)
reverse_test.go:20: Reverse produced invalid UTF-8 string "\xae\xcd"
Failing input written to testdata/fuzz/FuzzReverse/9b504024244a9afd5840f8f96d7a0cfd880663007b6495535a0e3c28bdea6241
To re-run:
go test -run=FuzzReverse/9b504024244a9afd5840f8f96d7a0cfd880663007b6495535a0e3c28bdea6241
FAIL
exit status 1
FAIL example/fuzz 0.561s
UTF-8에 적합한 문자열이 아니라는 욕!!-fuzz
flag 없이 -fuzz
자신만의 테스트 데이터만 테스트flag를 켜면 테스트는 자동으로 각종 데이터로 실행됩니다
그리고 이 Fuzz 시험은 영원히.
따라서
f.Add(tc)
몇 초 동안 매개 변수를 지정해야 합니다.설치 수정
몇 번 뛰고 또 욕을 많이 먹었다는 느낌.
main.go
package main
import (
"errors"
"fmt"
"log"
"unicode/utf8"
)
func main() {
input := "The quick brown fox jumped over the lazy dog"
rev, err := Reverse(input)
if err != nil {
log.Fatal(err)
}
doubleRev, err := Reverse(rev)
if err != nil {
log.Fatal(err)
}
fmt.Printf("original: %q\n", input)
fmt.Printf("reversed: %q\n", rev)
fmt.Printf("reversed again: %q\n", doubleRev)
}
func Reverse(s string) (string, error) {
if !utf8.ValidString(s) {
return s, errors.New("input is not valid UTF-8")
}
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r), nil
}
Fuzz 테스트 다시 수행
-fuzztime 5s
오류가 있으면 nil 또는 Reverse
로 돌아갑니다.reverse_test.go
package main
import (
"testing"
"unicode/utf8"
)
func FuzzReverse(f *testing.F) {
testcases := []string{"Hello, world", " ", "!12345"}
for _, tc := range testcases {
f.Add(tc) // Use f.Add to provide a seed corpus
}
f.Fuzz(func(t *testing.T, orig string) {
rev, err1 := Reverse(orig)
if err1 != nil {
return
}
doubleRev, err2 := Reverse(rev)
if err2 != nil {
return
}
if orig != doubleRev {
t.Errorf("Before: %q, after: %q", orig, doubleRev)
}
if utf8.ValidString(orig) && !utf8.ValidString(rev) {
t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
}
})
}
이렇게 또 집행을 하면...$ go test -fuzz=Fuzz -fuzztime 5s -v
=== FUZZ FuzzReverse
fuzz: elapsed: 0s, gathering baseline coverage: 0/37 completed
fuzz: elapsed: 0s, gathering baseline coverage: 37/37 completed, now fuzzing with 8 workers
fuzz: elapsed: 3s, execs: 576016 (191999/sec), new interesting: 0 (total: 35)
fuzz: elapsed: 5s, execs: 975339 (190528/sec), new interesting: 0 (total: 35)
--- PASS: FuzzReverse (5.10s)
PASS
ok example/fuzz 5.204s
ok 성공!!총결산
둘 다 속공하는 녀석이야.
정식 발매 후 바로 사용하세요!
Reference
이 문제에 관하여(Go1.18의 generics 및 fuzing에 대한 접촉 최소화), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/ucwork/articles/golang_118_tutorial텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)