diff --git a/.github/workflows/e2e_test_check.yml b/.github/workflows/e2e_test_check.yml index 9b2f9b1c..013938d7 100644 --- a/.github/workflows/e2e_test_check.yml +++ b/.github/workflows/e2e_test_check.yml @@ -18,7 +18,7 @@ jobs: - name: Install Go uses: actions/setup-go@v2 with: - go-version: 1.14.x + go-version: 1.18.x - name: Checkout code uses: actions/checkout@v2 - name: Go build @@ -43,7 +43,7 @@ jobs: needs: job_1 strategy: matrix: - go-version: [1.11.x, 1.12.x, 1.13.x, 1.14.x, 1.15.x, 1.16.x] + go-version: [1.16.x, 1.17.x, 1.18.x] runs-on: ubuntu-latest steps: - name: Install Go @@ -56,7 +56,7 @@ jobs: uses: actions/download-artifact@v2 with: path: /home/runner/tools - - name: Install bats-core + - name: Install bats-core run: | git clone https://github.com/bats-core/bats-core.git cd bats-core @@ -70,4 +70,4 @@ jobs: chmod +x /home/runner/tools/gocc/gocc export PATH=/home/runner/tools/gocc:$PATH cd tests - ./run-ci-actions.sh \ No newline at end of file + ./run-ci-actions.sh diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index ec58f5cc..fccded96 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -11,7 +11,7 @@ jobs: uses: golangci/golangci-lint-action@v2 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v1.29 + version: v1.45 # Optional: working directory, useful for monorepos # working-directory: somedir @@ -20,4 +20,4 @@ jobs: # args: --issues-exit-code=0 # Optional: show only new issues if it's a pull request. The default value is `false`. - only-new-issues: true \ No newline at end of file + only-new-issues: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eb559a75..5edc46af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ jobs: - name: Install Go uses: actions/setup-go@v2 with: - go-version: 1.14.x + go-version: 1.18.x - name: compile and release run: | ./ci-build.sh @@ -28,7 +28,7 @@ jobs: - name: Install Go uses: actions/setup-go@v2 with: - go-version: 1.14.x + go-version: 1.18.x - name: compile and release run: | ./ci-build.sh @@ -45,11 +45,11 @@ jobs: - name: Install Go uses: actions/setup-go@v2 with: - go-version: 1.14.x + go-version: 1.18.x - name: compile and release run: | ./ci-build.sh env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GOARCH: amd64 - GOOS: darwin \ No newline at end of file + GOOS: darwin diff --git a/.github/workflows/style_check.yml b/.github/workflows/style_check.yml index e96c9fa3..08f4ffd2 100644 --- a/.github/workflows/style_check.yml +++ b/.github/workflows/style_check.yml @@ -15,7 +15,8 @@ jobs: name: vet and gofmt strategy: matrix: - go-version: [1.13.x, 1.14.x, 1.15.x] + # We have generics code, so only 1.18+ can work + go-version: [1.18.x] runs-on: ubuntu-latest steps: - name: Install Go diff --git a/.github/workflows/ut_check.yml b/.github/workflows/ut_check.yml index 533fd29f..e1f94b61 100644 --- a/.github/workflows/ut_check.yml +++ b/.github/workflows/ut_check.yml @@ -15,7 +15,7 @@ jobs: name: go test strategy: matrix: - go-version: [1.13.x, 1.14.x, 1.15.x, 1.16.x] + go-version: [1.16.x, 1.17.x, 1.18.x] runs-on: ubuntu-latest steps: - name: Install Go diff --git a/cmd/build_test.go b/cmd/build_test.go index f3677b39..61b277cd 100644 --- a/cmd/build_test.go +++ b/cmd/build_test.go @@ -20,6 +20,8 @@ import ( "os" "os/exec" "path/filepath" + "regexp" + "strconv" "strings" "testing" "time" @@ -89,6 +91,53 @@ func TestBuildBinaryName(t *testing.T) { assert.Equal(t, cnt > 0, true, "GoCover variable should be in the binary") } +func TestBuildBinaryWithGenerics(t *testing.T) { + startTime := time.Now() + + workingDir := filepath.Join(baseDir, "../tests/samples/simple_project_with_generics") + gopath := "" + + os.Setenv("GOPATH", gopath) + os.Setenv("GO111MODULE", "on") + + // only run this test on go1.18+ + { + cmd := exec.Command("go", "version") + cmd.Dir = workingDir + out, err := cmd.CombinedOutput() + assert.NoError(t, err, "go version invocation should succeed") + + re := regexp.MustCompile(`^go version go(\d+)\.(\d+)`) + match := re.FindStringSubmatch(string(out)) + assert.NotNil(t, match, "go version output should be well-formed") + + majorVersion, _ := strconv.ParseInt(match[1], 10, 0) + minorVersion, _ := strconv.ParseInt(match[2], 10, 0) + + if majorVersion < 1 || (majorVersion == 1 && minorVersion < 18) { + t.Skip("skipping on older Go toolchain") + } + } + + buildFlags, buildOutput = "", "" + args := []string{"."} + runBuild(args, workingDir) + + obj := filepath.Join(workingDir, "simple-project") + fInfo, err := os.Lstat(obj) + assert.Equal(t, err, nil, "the binary should be generated.") + assert.Equal(t, startTime.Before(fInfo.ModTime()), true, obj+"new binary should be generated, not the old one") + + cmd := exec.Command("go", "tool", "objdump", "simple-project") + cmd.Dir = workingDir + out, _ := cmd.CombinedOutput() + cnt := strings.Count(string(out), "main.registerSelf") + assert.Equal(t, cnt > 0, true, "main.registerSelf function should be in the binary") + + cnt = strings.Count(string(out), "GoCover") + assert.Equal(t, cnt > 0, true, "GoCover variable should be in the binary") +} + // test if goc can get variables in internal package func TestBuildBinaryForInternalPackage(t *testing.T) { startTime := time.Now() diff --git a/tests/build.bats b/tests/build.bats index 35d5d783..fdcec54d 100755 --- a/tests/build.bats +++ b/tests/build.bats @@ -35,7 +35,7 @@ setup() { @test "test basic goc build command" { cd samples/run_for_several_seconds - + wait_profile_backend "build1" & profile_pid=$! @@ -90,7 +90,7 @@ setup() { @test "test goc build on complex project" { cd samples/complex_project - + wait_profile_backend "build5" & profile_pid=$! @@ -116,7 +116,7 @@ setup() { @test "test basic goc build command with singleton" { cd samples/run_for_several_seconds - + wait_profile_backend "build7" & profile_pid=$! @@ -126,3 +126,21 @@ setup() { wait $profile_pid } + +@test "test goc build command on project using Go generics" { + if ! go_version_at_least 1 18; then + info skipped on old Go versions + return 0 + fi + + cd samples/simple_project_with_generics + + wait_profile_backend "build8" & + profile_pid=$! + + run gocc build --debug --debugcisyncfile ci-sync.bak; + info build8 output: $output + [ "$status" -eq 0 ] + + wait $profile_pid +} diff --git a/tests/clear.bats b/tests/clear.bats index f8cb6379..aebd3787 100755 --- a/tests/clear.bats +++ b/tests/clear.bats @@ -28,7 +28,7 @@ setup_file() { sleep 1 WORKDIR=$PWD - cd samples/run_for_several_seconds + cd $WORKDIR/samples/$(demo_service_name) gocc build --center=http://127.0.0.1:60001 ./simple-project 3>&- & SAMPLE_PID=$! @@ -99,4 +99,4 @@ teardown_file() { kill -9 $TEST_SERVICE -} \ No newline at end of file +} diff --git a/tests/init.bats b/tests/init.bats index 0bb5d9dc..0d60ed21 100755 --- a/tests/init.bats +++ b/tests/init.bats @@ -28,7 +28,7 @@ setup_file() { sleep 1 WORKDIR=$PWD - cd samples/run_for_several_seconds + cd $WORKDIR/samples/$(demo_service_name) gocc build --center=http://127.0.0.1:60001 ./simple-project 3>&- & SAMPLE_PID=$! @@ -52,4 +52,4 @@ teardown_file() { [ "$status" -eq 0 ] wait $profile_pid -} \ No newline at end of file +} diff --git a/tests/profile.bats b/tests/profile.bats index f4a4b7c5..9a53f00f 100755 --- a/tests/profile.bats +++ b/tests/profile.bats @@ -28,7 +28,7 @@ setup_file() { sleep 1 WORKDIR=$PWD - cd samples/run_for_several_seconds + cd $WORKDIR/samples/$(demo_service_name) goc build --center=http://127.0.0.1:60001 info "goc server started" @@ -150,4 +150,4 @@ setup() { wait $profile_pid kill -9 $SAMPLE_PID -} \ No newline at end of file +} diff --git a/tests/remove.bats b/tests/remove.bats index 2e3d88b6..a575d7d2 100755 --- a/tests/remove.bats +++ b/tests/remove.bats @@ -28,7 +28,7 @@ setup_file() { sleep 1 WORKDIR=$PWD - cd samples/run_for_several_seconds + cd $WORKDIR/samples/$(demo_service_name) gocc build --center=http://127.0.0.1:60001 ./simple-project 3>&- & SAMPLE_PID=$! diff --git a/tests/samples/run_for_several_seconds_with_generics/a/a.go b/tests/samples/run_for_several_seconds_with_generics/a/a.go new file mode 100644 index 00000000..6bd3ef3c --- /dev/null +++ b/tests/samples/run_for_several_seconds_with_generics/a/a.go @@ -0,0 +1,6 @@ +package a + +// Say Hello A +func Say() { + println("Hello A") +} diff --git a/tests/samples/run_for_several_seconds_with_generics/b/b.go b/tests/samples/run_for_several_seconds_with_generics/b/b.go new file mode 100644 index 00000000..ff8954c4 --- /dev/null +++ b/tests/samples/run_for_several_seconds_with_generics/b/b.go @@ -0,0 +1,6 @@ +package b + +// Say Hello B +func Say() { + println("Hello B") +} diff --git a/tests/samples/run_for_several_seconds_with_generics/go.mod b/tests/samples/run_for_several_seconds_with_generics/go.mod new file mode 100644 index 00000000..7614e38e --- /dev/null +++ b/tests/samples/run_for_several_seconds_with_generics/go.mod @@ -0,0 +1,3 @@ +module example.com/simple-project + +go 1.18 diff --git a/tests/samples/run_for_several_seconds_with_generics/main.go b/tests/samples/run_for_several_seconds_with_generics/main.go new file mode 100644 index 00000000..71f9f9a5 --- /dev/null +++ b/tests/samples/run_for_several_seconds_with_generics/main.go @@ -0,0 +1,24 @@ +package main + +import ( + "fmt" + "time" + + "example.com/simple-project/a" + "example.com/simple-project/b" +) + +func main() { + fmt.Println("hello") + a.Say() + b.Say() + time.Sleep(time.Second * time.Duration(mulBy(3, uint(5)))) +} + +type c interface { + ~int | ~uint +} + +func mulBy[T c, U c](x T, y U) T { + return x * T(y) +} diff --git a/tests/samples/simple_project_with_generics/go.mod b/tests/samples/simple_project_with_generics/go.mod new file mode 100644 index 00000000..7614e38e --- /dev/null +++ b/tests/samples/simple_project_with_generics/go.mod @@ -0,0 +1,3 @@ +module example.com/simple-project + +go 1.18 diff --git a/tests/samples/simple_project_with_generics/main.go b/tests/samples/simple_project_with_generics/main.go new file mode 100644 index 00000000..6eadfdd3 --- /dev/null +++ b/tests/samples/simple_project_with_generics/main.go @@ -0,0 +1,27 @@ +//go:build go1.18 + +package main + +import ( + "fmt" + "strconv" +) + +func main() { + x := []int{1, 3, 5, 7, 9} + y := mapSlice(func(x int) string { + return strconv.Itoa(x * x) + }, x) + + for i, elem := range x { + fmt.Printf("%d -> '%s'\n", elem, y[i]) + } +} + +func mapSlice[T any, U any](fn func(T) U, src []T) []U { + dst := make([]U, len(src)) + for i, x := range src { + dst[i] = fn(x) + } + return dst +} diff --git a/tests/server.bats b/tests/server.bats index ed9719ee..eda5f5c8 100755 --- a/tests/server.bats +++ b/tests/server.bats @@ -48,7 +48,7 @@ teardown_file() { @test "register a covered service" { WORKDIR=$PWD - cd $WORKDIR/samples/run_for_several_seconds + cd $WORKDIR/samples/$(demo_service_name) run goc build --debug --center=http://127.0.0.1:60001 [ "$status" -eq 0 ] @@ -64,4 +64,4 @@ teardown_file() { run goc init --center=http://127.0.0.1:60001 [ "$status" -eq 0 ] [ ! -f "$WORKDIR/persistence/servicesAll.txt" ] -} \ No newline at end of file +} diff --git a/tests/util.sh b/tests/util.sh index 950c3b5c..0595b14b 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -38,4 +38,47 @@ wait_profile() { wait_profile_backend() { rm ci-sync.bak || true wait_profile $1 -} \ No newline at end of file +} + +# usage: go_version_at_least [version components] +# +# example: +# +# go_version_at_least 1 +# go_version_at_least 2 +# go_version_at_least 1 18 +# go_version_at_least 1 18 1 +# +# if ! go_version_at_least 1 18; then +# info "skipping on old go version" +# return 0 +# fi +go_version_at_least() { + # looks like "go version goX.XX[.XX] goos/goarch" for tagged Go releases + # extract the "X.XX[.XX]" part + local go_version_out="$(go version)" + go_version_out="${go_version_out#go version go}" + go_version_out="${go_version_out% *}" + + local v=() + IFS=. read -ra v <<< "$go_version_out" + + while [[ $# -gt 0 ]]; do + [[ ${v[0]} -lt $1 ]] && return 1 + [[ ${v[0]} -gt $1 ]] && return 0 + + shift + v=("${v[@]:1}") + done + + return 0 +} + +# returns demo service name to use +demo_service_name() { + if go_version_at_least 1 18; then + echo run_for_several_seconds_with_generics + else + echo run_for_several_seconds + fi +}