Reverse(input string)
进行测试main
函数下进行个例测试package main
import (
"fmt"
)
// Reverse 字节 反转字符串
func Reverse(input string) string {
bytes := []byte(input)
for i, j := 0, len(bytes)-1; i < len(bytes)/2; i, j = i+1, j-1 {
bytes[i], bytes[j] = bytes[j], bytes[i]
}
return string(bytes)
}
func main() {
input := "hello, world , java页, go bigdata"
firstRev := Reverse(input)
secondRev := Reverse(firstRev)
fmt.Printf("原始数据: %q\n", input)
fmt.Printf("第一次反转的数据: %q\n", firstRev)
fmt.Printf("反转数据再次反转的数据: %q\n", secondRev)
}
Reverse
方法不支持某些汉字,因为汉字不是单字节!所以使用rune来装字符,所以增加了多一个方法Reverse1
// Reverse1 字符(学习之前以为万事大吉,但是实际上这里仅限于utf-8的字符)
func Reverse1(input string) string {
bytes := []rune(input)
for i, j := 0, len(bytes)-1; i < len(bytes)/2; i, j = i+1, j-1 {
bytes[i], bytes[j] = bytes[j], bytes[i]
}
return string(bytes)
}
main
中测试package main
import (
"fmt"
)
// Reverse 字节 反转字符串
func Reverse(input string) string {
bytes := []byte(input)
for i, j := 0, len(bytes)-1; i < len(bytes)/2; i, j = i+1, j-1 {
bytes[i], bytes[j] = bytes[j], bytes[i]
}
return string(bytes)
}
// Reverse1 字符(学习之前以为万事大吉,但是实际上这里仅限于utf-8的字符)
func Reverse1(input string) string {
bytes := []rune(input)
for i, j := 0, len(bytes)-1; i < len(bytes)/2; i, j = i+1, j-1 {
bytes[i], bytes[j] = bytes[j], bytes[i]
}
return string(bytes)
}
func main() {
input := "hello, world , java页, go bigdata"
firstRev := Reverse(input)
secondRev := Reverse(firstRev)
fmt.Printf("原始数据: %q\n", input)
fmt.Printf("第一次反转的数据: %q\n", firstRev)
fmt.Printf("反转数据再次反转的数据: %q\n", secondRev)
firstRev1 := Reverse1(input)
secondRev1 := Reverse1(firstRev1)
fmt.Printf("第一次反转的数据: %q\n", firstRev1)
fmt.Printf("反转数据再次反转的数据: %q\n", secondRev1)
}
fuzzingToLearn.go
, 所以要新建的文件名为:fuzzingToLearn_test.go
package main
import (
"testing"
"unicode/utf8"
)
// 测试字节反转
func TestReverse(t *testing.T) {
testcases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{" ", " "},
{"!123456", "654321!"},
}
for _, tc := range testcases {
rev := Reverse(tc.in)
if rev != tc.want {
t.Errorf("Reverse: %q, want %q", rev, tc.want)
}
}
}
// 测试字符反转
func TestReverse1(t *testing.T) {
testcases := []struct {
in, want string
}{
{"页Hello, world", "dlrow ,olleH页"},
{" ", " "},
{"!123456", "654321!"},
}
for _, tc := range testcases {
rev := Reverse1(tc.in)
if rev != tc.want {
t.Errorf("Reverse: %q, want %q", rev, tc.want)
}
}
}
go test -v
命令;这个命令是测试所有的方法的,也有测试单个方法的:go test -v -test.run $测试方法名
Windows
的cmd
和powershell
都可以运行,其它操作系统的就不说了PS D:\codes\go\goland-setting\test\fuzzing> ls
目录: D:\codes\go\goland-setting\test\fuzzing
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2023/12/6 22:37 1661 fuzzingToLearn.go
-a---- 2023/12/6 23:01 2197 fuzzingToLearn_test.go
PS D:\codes\go\goland-setting\test\fuzzing> go test -v
warning: GOPATH set to GOROOT (D:\install\go_install) has no effect
=== RUN TestReverse
--- PASS: TestReverse (0.00s)
=== RUN TestReverse1
--- PASS: TestReverse1 (0.00s)
PASS
ok goland-setting/test/fuzzing 0.043s
main
方法中进行测试的方式,需要停止程序,然后还要进行注释原有的代码fuzz测试函数以Fuzz开头而不是Test开头
方法参数和unit单元测试也不同,fuzz模糊测试为:f *testing.F
为了方便,fuzz模糊测试直接在fuzzingToLearn_test.go文件进行
// FuzzReverse fuzz测试 字节-字符串反转
func FuzzReverse(f *testing.F) {
testcases := []string{"Hello, world", " ", "!123456"}
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)
}
})
}
// FuzzReverse1 fuzz测试 字符-字符串反转
func FuzzReverse1(f *testing.F) {
testcases := []string{"Hello, world", " ", "!123456"}
for _, tc := range testcases {
f.Add(tc) // Use f.Add to provide a seed corpus
}
//非utf-8会出错
f.Fuzz(func(t *testing.T, orig string) {
rev := Reverse1(orig)
doubleRev := Reverse1(rev)
t.Logf("Number of runes: orig=%d, rev=%d, doubleRev=%d", utf8.RuneCountInString(orig),
utf8.RuneCountInString(rev), utf8.RuneCountInString(doubleRev))
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)
}
})
}
PS D:\codes\go\goland-setting\test\fuzzing> go test
warning: GOPATH set to GOROOT (D:\install\go_install) has no effect
PASS
ok goland-setting/test/fuzzing 0.041s
# 第一次出错
PS D:\codes\go\goland-setting\test\fuzzing> go test -fuzz=Fuzz
warning: GOPATH set to GOROOT (D:\install\go_install) has no effect
testing: will not fuzz, -fuzz matches more than one fuzz test: [FuzzReverse FuzzReverse1]
FAIL
exit status 1
FAIL goland-setting/test/fuzzing 0.041s
# 原因是一个文件只能有一个fuzz的测试例子。注释FuzzReverse1方法测试
PS D:\codes\go\goland-setting\test\fuzzing> go test -fuzz=Fuzz
warning: GOPATH set to GOROOT (D:\install\go_install) has no effect
fuzz: elapsed: 0s, gathering baseline coverage: 0/58 completed
failure while testing seed corpus entry: FuzzReverse/34d11aca3c8d1e0d
fuzz: elapsed: 0s, gathering baseline coverage: 2/58 completed
--- FAIL: FuzzReverse (0.09s)
--- FAIL: FuzzReverse (0.00s)
fuzzingToLearn_test.go:54: Reverse produced invalid UTF-8 string "\xab\xb0\xe7"
FAIL
exit status 1
FAIL goland-setting/test/fuzzing 0.134s
# 结果是不通过,即测试失败
结果:测试不通过,即有bug
查看文件同目录产生了新的文件
文件内容为:
go test fuzz v1
string("簫")
这里指的是簫
不通过方法的测试。但是我们知道这个方法对于汉字就是有bug,我们先跳过,看一下第二个方法是否可行。
testdata
目录可以删除的。但建议等完全测试后再删除
// FuzzReverse1 fuzz测试 字符-字符串反转
func FuzzReverse1(f *testing.F) {
testcases := []string{"Hello, world", " ", "!123456"}
for _, tc := range testcases {
f.Add(tc) // Use f.Add to provide a seed corpus
}
//非utf-8会出错
f.Fuzz(func(t *testing.T, orig string) {
rev := Reverse1(orig)
doubleRev := Reverse1(rev)
t.Logf("Number of runes: orig=%d, rev=%d, doubleRev=%d", utf8.RuneCountInString(orig),
utf8.RuneCountInString(rev), utf8.RuneCountInString(doubleRev))
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)
}
})
}
PS D:\codes\go\goland-setting\test\fuzzing> go test -fuzz=Fuzz
warning: GOPATH set to GOROOT (D:\install\go_install) has no effect
fuzz: elapsed: 0s, gathering baseline coverage: 0/3 completed
fuzz: elapsed: 0s, gathering baseline coverage: 3/3 completed, now fuzzing with 16 workers
fuzz: minimizing 42-byte failing input file
fuzz: elapsed: 0s, minimizing
--- FAIL: FuzzReverse1 (0.15s)
--- FAIL: FuzzReverse1 (0.00s)
fuzzingToLearn_test.go:70: Number of runes: orig=1, rev=1, doubleRev=1
fuzzingToLearn_test.go:73: Before: "\x86", after: "�"
Failing input written to testdata\fuzz\FuzzReverse1\0cca6a19caba34b5
To re-run:
go test -run=FuzzReverse1/0cca6a19caba34b5
FAIL
exit status 1
FAIL goland-setting/test/fuzzing 0.525s
# 还是出错了。而且前后两个值不同!!!
经过了解:
'\x86'
。当输入字符串被设置为 []rune
时,Go语言将字节切片编码为UTF-8,并将字节替换为UTF-8字符。当我们将替换UTF-8字符与输入字节片进行比较时,它们显然不相等。所以,我们需要修改我们的函数代码。但是此处为了保留之前的代码,就直接增加一个函数即可。添加到fuzzingToLearn.go
文件,然后再再进行测试该方法即可。
Reverse2
,内容如下// Reverse2 排除不是utf-8字符
func Reverse2(input string) (string, error) {
if !utf8.ValidString(input) {
return input, errors.New("input is not valid UTF-8")
}
runes := []rune(input)
for i, j := 0, len(runes)-1; i < len(runes)/2; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes), nil
}
FuzzReverse2
// 注释FuzzReverse1(f *testing.F)方法,增加下面内容
// fuzz测试 字符串反转 字符,排除非utf-8字符
func FuzzReverse2(f *testing.F) {
testcases := []string{"Hello, world", " ", "!123456"}
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 := Reverse2(orig)
if err1 != nil {
return // 可以调用 t.Skip()
}
doubleRev, err2 := Reverse2(rev)
if err2 != nil {
return // 可以调用 t.Skip()
}
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)
}
})
}
这里就跳过了前面所介绍的两种测试验证,直接进行fuzz模糊测试
这里是只先验证错误的情况,即新增加的testdata目录下的文件内容情况。使用go test -run
命令
PS D:\codes\go\goland-setting\test\fuzzing> go test -run=FuzzReverse1/0cca6a19caba34b5
warning: GOPATH set to GOROOT (D:\install\go_install) has no effect
testing: warning: no tests to run
PASS
ok goland-setting/test/fuzzing 0.357s
# success
go test -fuzz=Fuzz
PS D:\codes\go\goland-setting\test\fuzzing> go test -fuzz=Fuzz
warning: GOPATH set to GOROOT (D:\install\go_install) has no effect
fuzz: elapsed: 0s, gathering baseline coverage: 0/3 completed
fuzz: elapsed: 0s, gathering baseline coverage: 3/3 completed, now fuzzing with 16 workers
fuzz: elapsed: 3s, execs: 1212471 (404034/sec), new interesting: 32 (total: 35)
fuzz: elapsed: 6s, execs: 2590247 (458435/sec), new interesting: 35 (total: 38)
fuzz: elapsed: 9s, execs: 3933396 (448061/sec), new interesting: 35 (total: 38)
fuzz: elapsed: 12s, execs: 5262110 (442525/sec), new interesting: 35 (total: 38)
fuzz: elapsed: 15s, execs: 6555446 (431581/sec), new interesting: 35 (total: 38)
fuzz: elapsed: 18s, execs: 7789113 (411424/sec), new interesting: 36 (total: 39)
fuzz: elapsed: 21s, execs: 9042454 (416817/sec), new interesting: 36 (total: 39)
fuzz: elapsed: 22s, execs: 9511996 (407607/sec), new interesting: 36 (total: 39)
PASS
ok goland-setting/test/fuzzing 22.581s
go test -fuzz=Fuzz
不会自动停止,需要CTRL + c来停止;或者运行go test -fuzz=Fuzz -fuzztime 20s
来指定20s后结束PS D:\codes\go\goland-setting\test\fuzzing> go test -fuzz=Fuzz -fuzztime 20s
warning: GOPATH set to GOROOT (D:\install\go_install) has no effect
fuzz: elapsed: 0s, gathering baseline coverage: 0/43 completed
fuzz: elapsed: 0s, gathering baseline coverage: 43/43 completed, now fuzzing with 16 workers
fuzz: elapsed: 3s, execs: 1332436 (442300/sec), new interesting: 0 (total: 43)
fuzz: elapsed: 6s, execs: 2701841 (458040/sec), new interesting: 0 (total: 43)
fuzz: elapsed: 9s, execs: 4096713 (463975/sec), new interesting: 0 (total: 43)
fuzz: elapsed: 12s, execs: 5449685 (451464/sec), new interesting: 0 (total: 43)
fuzz: elapsed: 15s, execs: 6755971 (435062/sec), new interesting: 0 (total: 43)
fuzz: elapsed: 18s, execs: 8075349 (439717/sec), new interesting: 0 (total: 43)
fuzz: elapsed: 20s, execs: 8927301 (403763/sec), new interesting: 0 (total: 43)
PASS
ok goland-setting/test/fuzzing 20.214s
package main
import (
"errors"
"fmt"
"unicode/utf8"
)
// Reverse 字节 反转字符串
func Reverse(input string) string {
bytes := []byte(input)
for i, j := 0, len(bytes)-1; i < len(bytes)/2; i, j = i+1, j-1 {
bytes[i], bytes[j] = bytes[j], bytes[i]
}
return string(bytes)
}
// Reverse1 字符(学习之前以为万事大吉,但是实际上这里仅限于utf-8的字符)
func Reverse1(input string) string {
bytes := []rune(input)
for i, j := 0, len(bytes)-1; i < len(bytes)/2; i, j = i+1, j-1 {
bytes[i], bytes[j] = bytes[j], bytes[i]
}
return string(bytes)
}
// Reverse2 排除不是utf-8字符
func Reverse2(input string) (string, error) {
if !utf8.ValidString(input) {
return input, errors.New("input is not valid UTF-8")
}
runes := []rune(input)
for i, j := 0, len(runes)-1; i < len(runes)/2; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
return string(runes), nil
}
func main() {
input := "hello, world , java页, go bigdata"
firstRev := Reverse(input)
secondRev := Reverse(firstRev)
fmt.Printf("原始数据: %q\n", input)
fmt.Printf("第一次反转的数据: %q\n", firstRev)
fmt.Printf("反转数据再次反转的数据: %q\n", secondRev)
firstRev1 := Reverse1(input)
secondRev1 := Reverse1(firstRev1)
fmt.Printf("第一次反转的数据: %q\n", firstRev1)
fmt.Printf("反转数据再次反转的数据: %q\n", secondRev1)
firstRev2, firstRev2Err := Reverse2(input)
secondRev2, secondRevErr := Reverse2(firstRev2)
fmt.Printf("第一次反转的数据: %q, err: %v\n", firstRev2, firstRev2Err)
fmt.Printf("反转数据再次反转的数据: %q, err: %v\n", secondRev2, secondRevErr)
}
package main
import (
"testing"
"unicode/utf8"
)
func TestReverse(t *testing.T) {
testcases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{" ", " "},
{"!123456", "654321!"},
}
for _, tc := range testcases {
rev := Reverse(tc.in)
if rev != tc.want {
t.Errorf("Reverse: %q, want %q", rev, tc.want)
}
}
}
func TestReverse1(t *testing.T) {
testcases := []struct {
in, want string
}{
{"页Hello, world", "dlrow ,olleH页"},
{" ", " "},
{"!123456", "654321!"},
}
for _, tc := range testcases {
rev := Reverse1(tc.in)
if rev != tc.want {
t.Errorf("Reverse: %q, want %q", rev, tc.want)
}
}
}
// FuzzReverse fuzz测试 字节-字符串反转
//func FuzzReverse(f *testing.F) {
// testcases := []string{"Hello, world", " ", "!123456"}
// 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)
// }
// })
//}
// FuzzReverse1 fuzz测试 字符-字符串反转
//func FuzzReverse1(f *testing.F) {
// testcases := []string{"Hello, world", " ", "!123456"}
// for _, tc := range testcases {
// f.Add(tc) // Use f.Add to provide a seed corpus
// }
//
// //非utf-8会出错
// f.Fuzz(func(t *testing.T, orig string) {
// rev := Reverse1(orig)
// doubleRev := Reverse1(rev)
// t.Logf("Number of runes: orig=%d, rev=%d, doubleRev=%d", utf8.RuneCountInString(orig),
// utf8.RuneCountInString(rev), utf8.RuneCountInString(doubleRev))
// 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)
// }
// })
//
//}
// fuzz测试 字符串反转 字符,排除非utf-8字符
func FuzzReverse2(f *testing.F) {
testcases := []string{"Hello, world", " ", "!123456"}
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 := Reverse2(orig)
if err1 != nil {
return // 可以调用 t.Skip()
}
doubleRev, err2 := Reverse2(rev)
if err2 != nil {
return // 可以调用 t.Skip()
}
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)
}
})
}