github.com/go-maxhub/gremlins@v1.0.1-0.20231227222204-b03a6a1e3e09/core/coverage/coverage_test.go (about)

     1  /*
     2   * Copyright 2022 The Gremlins Authors
     3   *
     4   *    Licensed under the Apache License, Version 2.0 (the "License");
     5   *    you may not use this file except in compliance with the License.
     6   *    You may obtain a copy of the License at
     7   *
     8   *        http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   *    Unless required by applicable law or agreed to in writing, software
    11   *    distributed under the License is distributed on an "AS IS" BASIS,
    12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   *    See the License for the specific language governing permissions and
    14   *    limitations under the License.
    15   */
    16  
    17  package coverage_test
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"os/exec"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  	"github.com/spf13/viper"
    28  
    29  	"github.com/go-maxhub/gremlins/core/coverage"
    30  
    31  	"github.com/go-maxhub/gremlins/core/configuration"
    32  	"github.com/go-maxhub/gremlins/core/gomodule"
    33  )
    34  
    35  type commandHolder struct {
    36  	events []struct {
    37  		command string
    38  		args    []string
    39  	}
    40  }
    41  
    42  func TestCoverageRun(t *testing.T) {
    43  	testCases := []struct {
    44  		name     string
    45  		callPath string
    46  		wantPath string
    47  		intMode  bool
    48  	}{
    49  		{
    50  			name:     "from root, normal mode",
    51  			callPath: ".",
    52  			wantPath: "./...",
    53  			intMode:  false,
    54  		},
    55  		{
    56  			name:     "from folder, normal mode",
    57  			callPath: "test/pkg",
    58  			wantPath: "./test/pkg/...",
    59  			intMode:  false,
    60  		},
    61  		{
    62  			name:     "from root, integration mode",
    63  			callPath: ".",
    64  			wantPath: "./...",
    65  			intMode:  true,
    66  		},
    67  		{
    68  			name:     "from folder, integration mode",
    69  			callPath: "test/dir",
    70  			wantPath: "./...",
    71  			intMode:  true,
    72  		},
    73  	}
    74  	coverpkg := "./core/log,./pkg/..."
    75  	for _, tc := range testCases {
    76  		tc := tc
    77  		t.Run(tc.name, func(t *testing.T) {
    78  			viper.Set(configuration.UnleashTagsKey, "tag1 tag2")
    79  			viper.Set(configuration.UnleashCoverPkgKey, coverpkg)
    80  			viper.Set(configuration.UnleashIntegrationMode, tc.intMode)
    81  			defer viper.Reset()
    82  
    83  			wantWorkdir := "workdir"
    84  			wantFilename := "coverage"
    85  			wantFilePath := wantWorkdir + "/" + wantFilename
    86  			holder := &commandHolder{}
    87  			mod := gomodule.GoModule{
    88  				Name:       "example.com",
    89  				Root:       ".",
    90  				CallingDir: tc.callPath,
    91  			}
    92  			cov := coverage.NewWithCmd(fakeExecCommandSuccess(holder), wantWorkdir, mod)
    93  
    94  			_, _ = cov.Run()
    95  
    96  			firstWant := "go mod download"
    97  			secondWant := fmt.Sprintf("go test -tags tag1 tag2 -coverpkg %s -cover -coverprofile %v %s",
    98  				coverpkg, wantFilePath, tc.wantPath)
    99  
   100  			if len(holder.events) != 2 {
   101  				t.Fatal("expected two commands to be executed")
   102  			}
   103  			firstGot := fmt.Sprintf("go %v", strings.Join(holder.events[0].args, " "))
   104  			secondGot := fmt.Sprintf("go %v", strings.Join(holder.events[1].args, " "))
   105  
   106  			if !cmp.Equal(firstGot, firstWant) {
   107  				t.Errorf(cmp.Diff(firstGot, firstWant))
   108  			}
   109  			if !cmp.Equal(secondGot, secondWant) {
   110  				t.Errorf(cmp.Diff(secondGot, secondWant))
   111  			}
   112  		})
   113  	}
   114  }
   115  
   116  func TestCoverageRunFails(t *testing.T) {
   117  	mod := gomodule.GoModule{
   118  		Name:       "example.com",
   119  		CallingDir: "./...",
   120  	}
   121  
   122  	t.Run("failure of: go mod download", func(t *testing.T) {
   123  		cov := coverage.NewWithCmd(fakeExecCommandFailure(0), "workdir", mod)
   124  		if _, err := cov.Run(); err == nil {
   125  			t.Error("expected run to report an error")
   126  		}
   127  	})
   128  
   129  	t.Run("failure of: go test", func(t *testing.T) {
   130  		cov := coverage.NewWithCmd(fakeExecCommandFailure(1), "workdir", mod)
   131  		if _, err := cov.Run(); err == nil {
   132  			t.Error("expected run to report an error")
   133  		}
   134  	})
   135  }
   136  
   137  func TestCoverageParsesOutput(t *testing.T) {
   138  	module := "example.com"
   139  	mod := gomodule.GoModule{
   140  		Name:       module,
   141  		CallingDir: "path",
   142  	}
   143  	cov := coverage.NewWithCmd(fakeExecCommandSuccess(nil), "testdata/valid", mod)
   144  	profile := coverage.Profile{
   145  		"file1.go": {
   146  			{
   147  				StartLine: 47,
   148  				StartCol:  2,
   149  				EndLine:   48,
   150  				EndCol:    16,
   151  			},
   152  		},
   153  		"file2.go": {
   154  			{
   155  				StartLine: 52,
   156  				StartCol:  2,
   157  				EndLine:   53,
   158  				EndCol:    16,
   159  			},
   160  		},
   161  	}
   162  	want := coverage.Result{
   163  		Profile: profile,
   164  	}
   165  
   166  	got, err := cov.Run()
   167  	if err != nil {
   168  		t.Fatal(err)
   169  	}
   170  
   171  	if !cmp.Equal(got.Profile, want.Profile) {
   172  		t.Error(cmp.Diff(got, want))
   173  	}
   174  	if got.Elapsed == 0 {
   175  		t.Errorf("expected elapsed time to be greater than 0")
   176  	}
   177  }
   178  
   179  func TestParseOutputFail(t *testing.T) {
   180  	mod := gomodule.GoModule{
   181  		Name:       "example.com",
   182  		CallingDir: "./...",
   183  	}
   184  	cov := coverage.NewWithCmd(fakeExecCommandSuccess(nil), "testdata/invalid", mod)
   185  
   186  	if _, err := cov.Run(); err == nil {
   187  		t.Errorf("espected an error")
   188  	}
   189  }
   190  
   191  func TestCoverageProcessSuccess(_ *testing.T) {
   192  	if os.Getenv("GO_TEST_PROCESS") != "1" {
   193  		return
   194  	}
   195  	os.Exit(0) // skipcq: RVV-A0003
   196  }
   197  
   198  func TestCoverageProcessFailure(_ *testing.T) {
   199  	if os.Getenv("GO_TEST_PROCESS") != "1" {
   200  		return
   201  	}
   202  	os.Exit(1) // skipcq: RVV-A0003
   203  }
   204  
   205  type execContext = func(name string, args ...string) *exec.Cmd
   206  
   207  func fakeExecCommandSuccess(got *commandHolder) execContext {
   208  	return func(command string, args ...string) *exec.Cmd {
   209  		if got != nil {
   210  			got.events = append(got.events, struct {
   211  				command string
   212  				args    []string
   213  			}{command: command, args: args})
   214  		}
   215  		cs := []string{"-test.run=TestCoverageProcessSuccess", "--", command}
   216  		cs = append(cs, args...)
   217  		// #nosec G204 - We are in tests, we don't care
   218  		cmd := exec.Command(os.Args[0], cs...)
   219  		cmd.Env = []string{"GO_TEST_PROCESS=1"}
   220  
   221  		return cmd
   222  	}
   223  }
   224  
   225  func fakeExecCommandFailure(run int) execContext {
   226  	var executed int
   227  
   228  	return func(command string, args ...string) *exec.Cmd {
   229  		cs := []string{"-test.run=TestCoverageProcessSuccess", "--", command}
   230  		if executed == run {
   231  			cs = []string{"-test.run=TestCoverageProcessFailure", "--", command}
   232  		}
   233  		cs = append(cs, args...)
   234  		// #nosec G204 - We are in tests, we don't care
   235  		cmd := exec.Command(os.Args[0], cs...)
   236  		cmd.Env = []string{"GO_TEST_PROCESS=1"}
   237  		executed++
   238  
   239  		return cmd
   240  	}
   241  }