code-intelligence.com/cifuzz@v0.40.0/internal/cmd/coverage/gradle/gradle.go (about)

     1  package gradle
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"os/signal"
     8  	"path/filepath"
     9  	"strings"
    10  	"syscall"
    11  
    12  	"github.com/pkg/errors"
    13  
    14  	"code-intelligence.com/cifuzz/internal/build/gradle"
    15  	"code-intelligence.com/cifuzz/internal/cmd/coverage/summary"
    16  	"code-intelligence.com/cifuzz/internal/cmdutils"
    17  	"code-intelligence.com/cifuzz/internal/coverage"
    18  	"code-intelligence.com/cifuzz/pkg/log"
    19  	"code-intelligence.com/cifuzz/pkg/runfiles"
    20  	"code-intelligence.com/cifuzz/util/executil"
    21  	"code-intelligence.com/cifuzz/util/stringutil"
    22  )
    23  
    24  const GradleReportTask = "cifuzzReport"
    25  
    26  type CoverageGenerator struct {
    27  	OutputFormat string
    28  	OutputPath   string
    29  	FuzzTest     string
    30  	ProjectDir   string
    31  
    32  	Parallel gradle.ParallelOptions
    33  
    34  	Stderr      io.Writer
    35  	BuildStdout io.Writer
    36  	BuildStderr io.Writer
    37  
    38  	runfilesFinder runfiles.RunfilesFinder
    39  }
    40  
    41  func (cov *CoverageGenerator) BuildFuzzTestForCoverage() error {
    42  	// ensure a finder is set
    43  	if cov.runfilesFinder == nil {
    44  		cov.runfilesFinder = runfiles.Finder
    45  	}
    46  
    47  	gradleArgs := []string{
    48  		fmt.Sprintf("-Pcifuzz.fuzztest=%s", cov.FuzzTest),
    49  	}
    50  
    51  	if cov.OutputPath == "" {
    52  		buildDir, err := gradle.GetBuildDirectory(cov.ProjectDir)
    53  		if err != nil {
    54  			return err
    55  		}
    56  		cov.OutputPath = filepath.Join(buildDir, "reports", "jacoco", GradleReportTask)
    57  	}
    58  
    59  	// Make sure that directory exists, otherwise the command for --format=jacocoxml will fail
    60  	err := os.MkdirAll(cov.OutputPath, 0700)
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	gradleArgs = append(gradleArgs, GradleReportTask, fmt.Sprintf("-Pcifuzz.report.output=%s", cov.OutputPath))
    66  
    67  	if cov.OutputFormat == coverage.FormatJacocoXML {
    68  		gradleArgs = append(gradleArgs, fmt.Sprintf("-Pcifuzz.report.format=%s", coverage.FormatJacocoXML))
    69  	}
    70  
    71  	return cov.runGradleCommand(gradleArgs)
    72  }
    73  
    74  func (cov *CoverageGenerator) GenerateCoverageReport() (string, error) {
    75  	reportPath := filepath.Join(cov.OutputPath, "jacoco.xml")
    76  	reportFile, err := os.Open(reportPath)
    77  	if err != nil {
    78  		return "", errors.WithStack(err)
    79  	}
    80  	defer reportFile.Close()
    81  	summary.ParseJacocoXML(reportFile).PrintTable(cov.Stderr)
    82  
    83  	if cov.OutputFormat == coverage.FormatJacocoXML {
    84  		return filepath.Join(cov.OutputPath, "jacoco.xml"), nil
    85  	}
    86  
    87  	return filepath.Join(cov.OutputPath, "html"), nil
    88  }
    89  
    90  func (cov *CoverageGenerator) runGradleCommand(args []string) error {
    91  	gradleCmd, err := gradle.GetGradleCommand(cov.ProjectDir)
    92  	if err != nil {
    93  		return err
    94  	}
    95  
    96  	cmd := executil.Command(gradleCmd, args...)
    97  	cmd.Dir = cov.ProjectDir
    98  	cmd.Stdout = cov.BuildStdout
    99  	cmd.Stderr = cov.BuildStderr
   100  	log.Debugf("Running gradle command: %s", strings.Join(stringutil.QuotedStrings(cmd.Args), " "))
   101  
   102  	sigs := make(chan os.Signal, 1)
   103  	signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT)
   104  	go func() {
   105  		<-sigs
   106  		err = cmd.TerminateProcessGroup()
   107  		if err != nil {
   108  			log.Error(err)
   109  		}
   110  	}()
   111  
   112  	err = cmd.Run()
   113  	if err != nil {
   114  		return cmdutils.WrapExecError(errors.WithStack(err), cmd.Cmd)
   115  	}
   116  
   117  	return nil
   118  }