Released a new version of @rogpeppe's go-internal with some notable changes to testscript, used heavily in @cue_lang :) A special thanks to @bitfield, @tomwpayne, and @FiloSottile for their recent contributions! #golanghttps://t.co/hDAqsecssu
— Daniel Martí (@mvdan_) August 23, 2022
有一天這樣的 tweet 吸引到我,加上裡面有提到
- 前Google 資安大神 @FiloSottile
- Power of Go 作者 @bitfield
讓我認真的想去了解一下什麼是 Go-Internal 。
什麼是 Go-Internal
位置: https://github.com/rogpeppe/go-internal
不知道有沒有人跟我一樣,有時候在看 golang source code 的時候,會看到一些工具或是 func 相當好用。 當下可能會複製拿出來使用。或是擷取某些精髓。 但是很多時候,有很多 func 整理過後往往可以成為很方便的工具。
而 https://github.com/rogpeppe/go-internal 就是一個將 Go Internal source code 重新整理後,獨立出來使用的小套件。其實裡面有許多很好用的小工具可以使用。
以下挑選幾個小工具,稍微分享一下:
關於 go-Internal/testscript 套件
testscript: script-based testing based on txtar files
這個工具提供了讓你測試 cli command app 的測試包。這樣看可能還不知道,裡面的 testscript
要怎麼使用。但是從 Reddit 裡面的介紹蠻明白的。
testscript was factored out of the cmd/go internals where it is used to test the go command itself in various combinations/permutations.
也可以參考一段 code 來自 flowdev/spaghetti-analyzer main_test.go
func TestAnalyze(t *testing.T) {
testscript.Run(t, testscript.Params{
Dir: "testdata",
Cmds: map[string]func(*testscript.TestScript, bool, []string){
"analyze": func(ts *testscript.TestScript, _ bool, args []string) {
expectedReturnCode, err := strconv.Atoi(args[0])
if err != nil {
ts.Fatalf("fatal return code error (%q): %v", args[0], err)
}
args = args[1:]
actualReturnCode := analyze(args)
if actualReturnCode != expectedReturnCode {
ts.Fatalf("Expected return code %d but got: %d", expectedReturnCode, actualReturnCode)
}
},
},
TestWork: false,
})
}
這一段用就是讀取 testdata
資料夾的檔案,然後跑裡面的 cli test case 來跑你的 Cli app 。
關於 go-Internal/testenv 套件
這個套件蠻有趣的,可以找出目前系統 golang 相關環境參數。 比如說,能夠執行 go run
環境。
package main
import (
"fmt"
"github.com/rogpeppe/go-internal/testenv"
)
func main() {
fmt.Println(testenv.HasGoRun())
}
這一段 code 可以檢查你的 docker image 有沒有 go run
環境。 詳細 testenv source code 可以看這裡。
關於 go-Internal/par 套件
Package par implements parallel execution helpers. 可以讓你跑許多 parallel 的小幫手。
import (
"fmt"
"sync/atomic"
"time"
"github.com/rogpeppe/go-internal/par"
)
func main() {
var w par.Work
const N = 100
for i := 0; i < N; i++ {
w.Add(i)
}
start := time.Now()
var n int32
w.Do(N, func(x interface{}) {
time.Sleep(1 * time.Millisecond)
fmt.Println(n)
atomic.AddInt32(&n, +1)
})
if n != N {
fmt.Println("par.Work.Do did not do all the work")
}
if time.Since(start) < N/2*time.Millisecond {
fmt.Println("done!")
return
}
}
這樣就可以有 1 ~ 100 個不同 parallel 同時在跑。
小結論:
偶然間看 tweet 既然可以看到這樣好用的小工具,重點還是幾位大神一起維護的。 有興趣的可以持續關注。