code-intelligence.com/cifuzz@v0.40.0/internal/cmd/run/reporthandler/reporthandler_test.go (about)

     1  package reporthandler
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/gookit/color"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"code-intelligence.com/cifuzz/internal/cmd/run/reporthandler/metrics"
    15  	"code-intelligence.com/cifuzz/internal/testutil"
    16  	"code-intelligence.com/cifuzz/pkg/finding"
    17  	"code-intelligence.com/cifuzz/pkg/log"
    18  	"code-intelligence.com/cifuzz/pkg/report"
    19  )
    20  
    21  var (
    22  	logOutput io.ReadWriter
    23  	testDir   string
    24  )
    25  
    26  func TestMain(m *testing.M) {
    27  	// Disable color for this test to allow comparing strings without
    28  	// having to add color to them
    29  	color.Disable()
    30  
    31  	logOutput = bytes.NewBuffer([]byte{})
    32  	log.Output = logOutput
    33  
    34  	var cleanup func()
    35  	testDir, cleanup = testutil.ChdirToTempDir("report-handler-test-")
    36  	defer cleanup()
    37  
    38  	m.Run()
    39  }
    40  
    41  func TestReportHandler_EmptyCorpus(t *testing.T) {
    42  	h, err := NewReportHandler("", &ReportHandlerOptions{ProjectDir: testDir})
    43  	require.NoError(t, err)
    44  
    45  	initStartedReport := &report.Report{
    46  		Status:   report.RunStatusInitializing,
    47  		NumSeeds: 0,
    48  	}
    49  	err = h.Handle(initStartedReport)
    50  	require.NoError(t, err)
    51  	checkOutput(t, logOutput, "Starting from an empty corpus")
    52  	require.True(t, h.initFinished)
    53  }
    54  
    55  func TestReportHandler_NonEmptyCorpus(t *testing.T) {
    56  	h, err := NewReportHandler("", &ReportHandlerOptions{ProjectDir: testDir})
    57  	require.NoError(t, err)
    58  
    59  	initStartedReport := &report.Report{
    60  		Status:   report.RunStatusInitializing,
    61  		NumSeeds: 1,
    62  	}
    63  	err = h.Handle(initStartedReport)
    64  	require.NoError(t, err)
    65  	checkOutput(t, logOutput, "Initializing fuzzer with")
    66  
    67  	initFinishedReport := &report.Report{Status: report.RunStatusRunning}
    68  	err = h.Handle(initFinishedReport)
    69  	require.NoError(t, err)
    70  	checkOutput(t, logOutput, "Successfully initialized fuzzer")
    71  }
    72  
    73  func TestReportHandler_Metrics(t *testing.T) {
    74  	h, err := NewReportHandler("", &ReportHandlerOptions{ProjectDir: testDir})
    75  	require.NoError(t, err)
    76  
    77  	printerOut := bytes.NewBuffer([]byte{})
    78  	h.printer.(*metrics.LinePrinter).BasicTextPrinter.Writer = printerOut
    79  
    80  	metricsReport := &report.Report{
    81  		Status: report.RunStatusRunning,
    82  		Metric: &report.FuzzingMetric{
    83  			Timestamp:           time.Now(),
    84  			ExecutionsPerSecond: 1234,
    85  			Features:            12,
    86  		},
    87  	}
    88  	err = h.Handle(metricsReport)
    89  	require.NoError(t, err)
    90  	checkOutput(t, printerOut, metrics.MetricsToString(metricsReport.Metric))
    91  }
    92  
    93  func TestReportHandler_Finding(t *testing.T) {
    94  	h, err := NewReportHandler("", &ReportHandlerOptions{ProjectDir: testDir, ManagedSeedCorpusDir: "seed_corpus"})
    95  	require.NoError(t, err)
    96  
    97  	// create an input file
    98  	testfile := "crash_123_test"
    99  	err = os.WriteFile(testfile, []byte("TEST"), 0o644)
   100  	require.NoError(t, err)
   101  
   102  	findingReport := &report.Report{
   103  		Status: report.RunStatusRunning,
   104  		Finding: &finding.Finding{
   105  			InputFile: testfile,
   106  		},
   107  	}
   108  	err = h.Handle(findingReport)
   109  	require.NoError(t, err)
   110  
   111  	expectedOutputs := []string{findingReport.Finding.Name}
   112  	checkOutput(t, logOutput, expectedOutputs...)
   113  }
   114  
   115  func TestReportHandler_CorpusDirs(t *testing.T) {
   116  	h, err := NewReportHandler("", &ReportHandlerOptions{})
   117  	require.NoError(t, err)
   118  
   119  	seedCorpusDir := "/seed/corpus/dir"
   120  	seedCorpusReport := &report.Report{
   121  		SeedCorpus: seedCorpusDir,
   122  	}
   123  	generatedCorpusDir := "/generated/corpus/dir"
   124  	generatedCorpusReport := &report.Report{
   125  		GeneratedCorpus: generatedCorpusDir,
   126  	}
   127  
   128  	err = h.Handle(seedCorpusReport)
   129  	require.NoError(t, err)
   130  	assert.Equal(t, seedCorpusDir, h.ManagedSeedCorpusDir)
   131  
   132  	err = h.Handle(generatedCorpusReport)
   133  	require.NoError(t, err)
   134  	assert.Equal(t, generatedCorpusDir, h.GeneratedCorpusDir)
   135  }
   136  
   137  func TestReportHandler_PrintJSON(t *testing.T) {
   138  	h, err := NewReportHandler("", &ReportHandlerOptions{ProjectDir: testDir, PrintJSON: true})
   139  	require.NoError(t, err)
   140  
   141  	jsonOut := bytes.NewBuffer([]byte{})
   142  	h.jsonOutput = jsonOut
   143  
   144  	findingLogs := []string{"Oops", "The program crashed"}
   145  	findingReport := &report.Report{
   146  		Status: report.RunStatusRunning,
   147  		Finding: &finding.Finding{
   148  			Logs: findingLogs,
   149  		},
   150  	}
   151  	err = h.Handle(findingReport)
   152  	require.NoError(t, err)
   153  	checkOutput(t, jsonOut, findingLogs...)
   154  }
   155  
   156  func TestReportHandler_GenerateName(t *testing.T) {
   157  	h, err := NewReportHandler("", &ReportHandlerOptions{ProjectDir: testDir, PrintJSON: true})
   158  	require.NoError(t, err)
   159  
   160  	findingLogs := []string{"Oops", "The program crashed"}
   161  	findingReport := &report.Report{
   162  		Status: report.RunStatusRunning,
   163  		Finding: &finding.Finding{
   164  			Logs:      findingLogs,
   165  			InputData: []byte("123"),
   166  		},
   167  	}
   168  	err = h.Handle(findingReport)
   169  	require.NoError(t, err)
   170  	assert.Equal(t, "adoring_orangutan", findingReport.Finding.Name)
   171  }
   172  
   173  func checkOutput(t *testing.T, r io.Reader, s ...string) {
   174  	output, err := io.ReadAll(r)
   175  	require.NoError(t, err)
   176  	for _, str := range s {
   177  		require.Contains(t, string(output), str)
   178  	}
   179  }