Hero Image
Go 使用 selenium 截图

Go 使用 selenium 截图 package main import ( "fmt" log "github.com/sirupsen/logrus" "github.com/tebeka/selenium" "io/ioutil" ) const ( chromeDriverPath = "/usr/local/bin/chromedriver" port = 9515 ) func main() { var opts []selenium.ServiceOption selenium.SetDebug(false) service, err := selenium.NewChromeDriverService(chromeDriverPath, port, opts...) if err != nil { panic(err) // panic is used only as an example and is not otherwise recommended. } defer service.Stop() caps := selenium.Capabilities{"browserName": "chrome"} // 整页截屏 // 获取到网页的宽高,然后把浏览器的窗口设置成这么大,然后再截图就好了!需要注意的是只有 headless 模式可以任意设置窗口大小,否则最大高度不能超过你的显示器分辨率 // chromeCaps := chrome.Capabilities{ // Path: "", // Args: []string{ // "--headless", // }, // } // caps.AddChrome(chromeCaps) wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port)) if err != nil { panic(err) } defer wd.Quit() // 在 selenium 中使用ExecuteScript就可以执行 JavaScript 代码,需要注意返回值是 interface,所以要类型断言成 float,然后再转成 int // height, _ := wd.ExecuteScript("return document.body.parentNode.scrollHeight", nil) // var realHeight = int(height.(float64)) // 然后我们需要设置窗口大小 // wd.ResizeWindow("", 1920, realHeight) if err := wd.Get("https://github.com/tgbot-collection/archiver"); err != nil { panic(err) } screenshot, err := wd.Screenshot() if err != nil { log.Errorln(err) } ioutil.WriteFile("screenshot.png", screenshot, 0644) } 整页截屏 获取到网页的宽高,然后把浏览器的窗口设置成这么大,然后再截图就好了!需要注意的是只有 headless 模式可以任意设置窗口大小,否则最大高度不能超过你的显示器分辨率

Hero Image
善用 Go Fuzzing,幫助你寫出更完整的單元測試

善用 Go Fuzzing,幫助你寫出更完整的單元測試 func Pow(base uint, exponent uint) uint { if exponent == 0 { return 1 } return base * Pow(base, exponent-1) } func FuzzPow(f *testing.F) { f.Fuzz(func(t *testing.T, x, y uint) { assert := assert.New(t) assert.Equal(Pow(x, 0), uint(1)) assert.Equal(Pow(x, 1), x) assert.Equal(Pow(x, 2), x*x) if x > 0 && y > 0 { assert.Equal(Pow(x, y)/x, Pow(x, y-1)) } }) } go test -fuzz=Fuzz -fuzztime 20s 在做 Fuzzing Test 的時候如果跑一跑 FAIL 了,Go 會幫忙把那組 input 記在 testcase/ 裡面 看了之後會發現在 x=6、y=30 時 assert 會失敗,也就是說 pow(6, 30)/6 不會等於 pow(6, 29)。但這也太奇怪了吧?仔細實驗之後才發現是因為在計算 pow(6, 30) 的時候會發生 overflow。 因為 Go 定義的 max.MaxUint 大約是 18 * 10¹⁸,但 6²⁹ 大概是 7 * 10¹⁸。如果把 6²⁹ 再乘上 6,就會發生 overflow 得到 8 * 10¹⁸,很像繞了操場兩圈結果在跟原本差不多的位置。 var ErrOverflow = fmt.Errorf("overflow") func Pow(base uint, exponent uint) (uint, error) { if exponent == 0 { return 1 } prevResult, err := Pow(base, exponent-1) if math.MaxUint/base < prevResult { return 0, ErrOverflow } return base * prevResult, nil } func FuzzPow(f *testing.F) { f.Fuzz(func(t *testing.T, x, y uint) { assert := assert.New(t) if result, err := Pow(x, 1); err != ErrOverflow { assert.Equal(result, x) } if result, err := Pow(x, y); x > 0 && y > 0 && err != ErrOverflow { resultDivX, _ := Pow(x, y-1) assert.Equal(result/x, resultDivX) } }) }

Hero Image
Go 字串格式化

Go 字串格式化 Go 語言 fmt.Printf 使用指南 結尾的動詞(verb)決定對應參數的型別與解讀方式。 d - 十進位整數 o - 八進位整數 O - 帶有 0o 前綴的八進位整數 b - 二進位整數 x - 十六進位整數(小寫) X - 十六進位整數(大寫) f - 十進位浮點數(小寫) F - 十進位浮點數(大寫) e - 科學記號(尾數/指數,小寫) E - 科學記號(尾數/指數,大寫) g - %e 或 %f 的最短表示 G - %E 或 %F 的最短表示 c - 以 Unicode 碼點表示的字元 q - 帶引號的字元 U - Unicode 逸出序列 t - true 或 false 字串 s - 字串 v - 預設格式 #v - 值的 Go 語法表示 T - 值型別的 Go 語法表示 p - 指標位址 % - 雙 %% 會輸出單一 % Go 字串格式化索引 package main import ( "fmt" ) func main() { n1 := 2 n2 := 3 n3 := 4 res := fmt.Sprintf("There are %d oranges %d apples %d plums", n1, n2, n3) fmt.Println(res) // There are 2 oranges 3 apples 4 plums res2 := fmt.Sprintf("There are %[2]d oranges %d apples %[1]d plums", n1, n2, n3) fmt.Println(res2) // There are 3 oranges 4 apples 2 plums } Go 字串格式化精度 package main import ( "fmt" ) func main() { fmt.Printf("%0.f\n", 16.540) // 17 fmt.Printf("%0.2f\n", 16.540) // 16.54 fmt.Printf("%0.3f\n", 16.540) // 16.540 fmt.Printf("%0.5f\n", 16.540) // 16.54000 } Go 字串格式化旗標 package main import ( "fmt" ) func main() { fmt.Printf("%+d\n", 1691) // +1691 fmt.Printf("%#x\n", 1691) // 0x69b fmt.Printf("%#X\n", 1691) // 0X69B fmt.Printf("%#b\n", 1691) // 0b11010011011 fmt.Printf("%10d\n", 1691) // 1691 fmt.Printf("%-10d\n", 1691) // 1691 fmt.Printf("%010d\n", 1691) // 0000001691 } Go 字串格式化寬度 package main import ( "fmt" ) func main() { w := "falcon" n := 122 h := 455.67 fmt.Printf("%s\n", w) // falcon fmt.Printf("%10s\n", w) // falcon fmt.Printf("%d\n", n). // 122 fmt.Printf("%7d\n", n) // 122 fmt.Printf("%07d\n", n) // 0000122 fmt.Printf("%10f\n", h) // 455.670000 fmt.Printf("%11f\n", h) // 455.670000 fmt.Printf("%12f\n", h) // 455.670000 }

Hero Image
golang pprof 实战

golang pprof 实战 炸弹程序 package main import ( // 略 _ "net/http/pprof" // 会自动注册 handler 到 http server,方便通过 http 接口获取程序运行采样报告 // 略 ) func main() { // 略 runtime.GOMAXPROCS(1) // 限制 CPU 使用数,避免过载 runtime.SetMutexProfileFraction(1) // 开启对锁调用的跟踪 runtime.SetBlockProfileRate(1) // 开启对阻塞操作的跟踪 go func() { // 启动一个 http server,注意 pprof 相关的 handler 已经自动注册过了 // /debug/pprof/ if err := http.ListenAndServe(":6060", nil); err != nil { log.Fatal(err) } os.Exit(0) }() // 略 } http://localhost:6060/debug/pprof/ 类型 描述 备注 allocs 内存分配情况的采样信息 可以用浏览器打开,但可读性不高 blocks 阻塞操作情况的采样信息 可以用浏览器打开,但可读性不高 cmdline 显示程序启动命令及参数 可以用浏览器打开,这里会显示 ./go-pprof-practice goroutine 当前所有协程的堆栈信息 可以用浏览器打开,但可读性不高 heap 堆上内存使用情况的采样信息 可以用浏览器打开,但可读性不高 mutex 锁争用情况的采样信息 可以用浏览器打开,但可读性不高 profile CPU 占用情况的采样信息 浏览器打开会下载文件 threadcreate 系统线程创建情况的采样信息 可以用浏览器打开,但可读性不高 trace 程序运行跟踪信息 浏览器打开会下载文件,本文不涉及,可另行参阅《深入浅出 Go trace》 排查 CPU 占用过高