blob: 6a0d515621142a7dee9d13095a409228337346c9 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package fuzz
import (
"bytes"
"fmt"
"path/filepath"
"reflect"
"strings"
"testing"
"github.com/golang/glog"
"github.com/google/go-cmp/cmp"
)
func TestIsExample(t *testing.T) {
build, _ := newMockBuild()
f, err := build.Fuzzer("foo/bar")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
if f.IsExample() {
t.Fatalf("%s marked as example but shouldn't be", f.Name)
}
f, err = build.Fuzzer("example-fuzzers/noop_fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
if !f.IsExample() {
t.Fatalf("%s not marked as example but should be", f.Name)
}
}
func TestUseFuzzCtl(t *testing.T) {
build, _ := newMockBuild()
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
if !f.useFuzzCtl() {
t.Fatalf("fuzz_ctl incorrectly ignored by fuzzer %s", f.Name)
}
build.(*mockBuild).useFfxFuzz = true
f, err = build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
if f.useFuzzCtl() {
t.Fatalf("fuzz_ctl incorrectly used by fuzzer %s", f.Name)
}
}
func TestUseFfxFuzz(t *testing.T) {
build, _ := newMockBuild()
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
if f.useFfxFuzz() {
t.Fatalf("ffx fuzz incorrectly used by fuzzer %s", f.Name)
}
build.(*mockBuild).useFfxFuzz = true
f, err = build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
if !f.useFfxFuzz() {
t.Fatalf("ffx fuzz incorrectly ignored by fuzzer %s", f.Name)
}
}
func TestAbsPath(t *testing.T) {
build, _ := newMockBuild()
f, err := build.Fuzzer("foo/bar")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
absPaths := map[string]string{
"pkg/data/relpath": "/tmp/fuzz_ctl/fuchsia.com/foo/bar/pkg/data/relpath",
"/pkg/data/relpath": "/pkg/data/relpath",
"data/relpath": "/tmp/fuzz_ctl/fuchsia.com/foo/bar/relpath",
"/data/relpath": "/data/relpath",
"relpath": "/tmp/fuzz_ctl/fuchsia.com/foo/bar/relpath",
"/relpath": "/relpath",
}
for relpath, expected := range absPaths {
got := f.AbsPath(relpath)
if expected != got {
t.Fatalf("expected %q, got %q", expected, got)
}
}
}
func TestAbsPathUsingFuzzCtl(t *testing.T) {
build, _ := newMockBuild()
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
absPaths := map[string]string{
"pkg/data/relpath": "/tmp/fuzz_ctl/fuchsia.com/cff/fuzzer/pkg/data/relpath",
"/pkg/data/relpath": "/pkg/data/relpath",
"data/relpath": "/tmp/fuzz_ctl/fuchsia.com/cff/fuzzer/relpath",
"/data/relpath": "/data/relpath",
"relpath": "/tmp/fuzz_ctl/fuchsia.com/cff/fuzzer/relpath",
"/relpath": "/relpath",
}
for relpath, expected := range absPaths {
got := f.AbsPath(relpath)
if expected != got {
t.Fatalf("expected %q, got %q", expected, got)
}
}
}
func TestAbsPathUsingFfxFuzz(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).useFfxFuzz = true
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
cachePath := "CACHE:fuchsia-pkg:__fuchsia.com_cff__meta_fuzzer.cm/"
absPaths := map[string]string{
"some/path": cachePath + "some/path",
"/some/path": cachePath + "some/path",
cachePath: cachePath,
}
for relpath, expected := range absPaths {
got := f.AbsPath(relpath)
if expected != got {
t.Fatalf("expected %q, got %q", expected, got)
}
}
}
func TestParse(t *testing.T) {
build, _ := newMockBuild()
f, err := build.Fuzzer("foo/bar")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
f.Parse([]string{"arg", "-k1=v1", "-k1=v2", "-k2=v-3", "-bad", "--alsobad", "-k3=has=two"})
if !reflect.DeepEqual(f.args, []string{"arg", "-bad", "--alsobad"}) {
t.Fatalf("missing arg(s): %s", strings.Join(f.args, " "))
}
if k1, found := f.options["k1"]; !found || k1 != "v2" {
t.Fatalf("expected v2, got %s", k1)
}
if k2, found := f.options["k2"]; !found || k2 != "v-3" {
t.Fatalf("expected v-3, got %s", k2)
}
if k3, found := f.options["k3"]; !found || k3 != "has=two" {
t.Fatalf("expected has=two, got %s", k3)
}
}
func TestParseArgsForFfx(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).useFfxFuzz = true
conn := NewMockConnector(t)
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
conn.Dirs = []string{f.AbsPath("some/dir"), f.AbsPath("out"),
f.AbsPath("in1"), f.AbsPath("in2")}
// Args that are expected to raise errors when parsing
errorArgs := [][]string{
{"some/dir", "some/file"},
{"some/file", "some/dir"},
{invalidPath},
{"-merge=1", "-minimize_crash=1"},
{"-minimize_crash=1"},
{"-minimize_crash=1", "some/dir"},
{"-merge=1", "some/file"},
{"-merge=1", "some/dir"},
}
for _, args := range errorArgs {
f.Parse(args)
if _, err := f.parseArgsForFfx(conn); err == nil {
t.Fatalf("unexpectedly succeeded parsing args %q for CFF", args)
}
}
// Args that should succeed
//
// Note: It's slightly harder to read, but we use two parallel arrays here
// because maps can't use slices as keys.
testArgs := [][]string{
{},
{"some/file"},
{"some/dir"},
{"out", "in1", "in2"},
{"-merge=1", "out", "in1", "in2"},
{"-minimize_crash=1", "some/file"},
}
expectedBaseArgs := []string{f.url, "--no-stdout", "--no-syslog"}
expectedConfigs := []*ffxFuzzRunConfig{
{
command: "run",
args: expectedBaseArgs,
},
{
command: "try",
args: append(expectedBaseArgs, f.AbsPath("some/file")),
testcasePath: "some/file",
},
{
command: "run",
args: expectedBaseArgs,
outputCorpus: "some/dir",
},
{
command: "run",
args: expectedBaseArgs,
outputCorpus: "out",
inputCorpora: []string{"in1", "in2"},
},
{
command: "merge",
args: expectedBaseArgs,
outputCorpus: "out",
inputCorpora: []string{"in1", "in2"},
},
{
command: "minimize",
args: append(expectedBaseArgs, f.AbsPath("some/file")),
testcasePath: "some/file",
},
}
for j, args := range testArgs {
f.Parse(args)
got, err := f.parseArgsForFfx(conn)
if err != nil {
t.Fatalf("failed to parse args %q for CFF: %s", args, err)
}
expected := expectedConfigs[j]
if diff := cmp.Diff(expected, got, cmp.AllowUnexported(ffxFuzzRunConfig{})); diff != "" {
t.Fatalf("ffx config for args %q not as expected (-want +got):\n%s", args, diff)
}
}
}
func TestPrepare(t *testing.T) {
build, _ := newMockBuild()
f, err := build.Fuzzer("foo/bar")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
conn := NewMockConnector(t)
conn.connected = true
if err := f.Prepare(conn); err != nil {
t.Fatalf("failed to prepare fuzzer: %s", err)
}
if len(conn.FfxHistory) != 0 {
t.Fatalf("incorrect ffx history: %v", conn.FfxHistory)
}
if !reflect.DeepEqual(conn.CmdHistory, []string{"fuzz_ctl"}) {
t.Fatalf("incorrect command history: %v", conn.CmdHistory)
}
}
func TestPrepareUsingFuzzCtl(t *testing.T) {
build, _ := newMockBuild()
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
conn := NewMockConnector(t)
conn.connected = true
if err := f.Prepare(conn); err != nil {
t.Fatalf("failed to prepare fuzzer: %s", err)
}
if !reflect.DeepEqual(conn.CmdHistory, []string{"fuzz_ctl"}) {
t.Fatalf("incorrect command history: %v", conn.CmdHistory)
}
expected := []string{"reset fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm"}
if diff := cmp.Diff(expected, conn.FuzzCtlHistory); diff != "" {
t.Fatalf("incorrect fuzz_ctl history (-want +got):\n%s", diff)
}
if len(conn.FfxHistory) != 0 {
t.Fatalf("incorrect ffx history: %v", conn.FfxHistory)
}
}
func TestPrepareUsingFfxFuzz(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).useFfxFuzz = true
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
conn := NewMockConnector(t)
conn.connected = true
if err := f.Prepare(conn); err != nil {
t.Fatalf("failed to prepare fuzzer: %s", err)
}
// MockConnector calls `rm`
if !reflect.DeepEqual(conn.CmdHistory, []string{"rm"}) {
t.Fatalf("incorrect command history: %v", conn.CmdHistory)
}
if len(conn.FuzzCtlHistory) != 0 {
t.Fatalf("incorrect fuzz_ctl history: %v", conn.FuzzCtlHistory)
}
expected := []string{"fuzz stop fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm"}
if diff := cmp.Diff(expected, conn.FfxHistory); diff != "" {
t.Fatalf("incorrect ffx history (-want +got):\n%s", diff)
}
}
func TestSetOptionsUsingFfxFuzz(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).useFfxFuzz = true
conn := NewMockConnector(t)
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
// All valid options
args := []string{"-runs=3", "-max_total_time=10", "-malloc_limit_mb=42",
"-artifact_prefix=data/", "-seed=123", "-jobs=0", "-max_len=3141",
"-print_final_stats=1"}
f.Parse(args)
if err := f.setCFFOptions(conn); err != nil {
t.Fatalf("failed to set CFF options: %s", err)
}
expected := []string{
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm runs 3",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm max_total_time 10s",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm seed 123",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm max_input_size 3141b",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm mutation_depth 5",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm detect_leaks false",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm run_limit 1200s",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm malloc_limit 42mb",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm oom_limit 2gb",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm purge_interval 1s",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm print_final_stats 1",
"fuzz set fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm use_value_profile false",
}
if diff := cmp.Diff(expected, conn.FfxHistory, sortSlicesOpt); diff != "" {
t.Fatalf("ffx fuzz set commands not as expected (-want +got):\n%s", diff)
}
// Invalid option
args = []string{"-libfuzzer_feature=unsupported"}
f.Parse(args)
if err := f.setCFFOptions(conn); err == nil {
t.Fatalf("expected error for unsupported libfuzzer feature")
}
args = []string{"-jobs=1"}
f.Parse(args)
if err := f.setCFFOptions(conn); err == nil {
t.Fatalf("expected error for jobs != 0")
}
}
func TestMarkOutputCorpusForFfx(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).useFfxFuzz = true
conn := NewMockConnector(t)
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
corpusPath := "some/corpus"
f.markOutputCorpus(conn, corpusPath)
if len(conn.PathsPut) != 1 {
t.Fatalf("no Put was made")
}
putCmd := conn.PathsPut[0]
if putCmd.dst != f.AbsPath(corpusPath) {
t.Fatalf("Put was to unexpected destination: %s", putCmd.dst)
}
if filepath.Base(putCmd.src) != liveCorpusMarkerName {
t.Fatalf("Put was with wrong filename: %s", putCmd.src)
}
if diff := cmp.Diff(f.url, conn.LastPutFileContent); diff != "" {
t.Fatalf("corpus marker has wrong contents (-want +got):\n%s", diff)
}
}
// Construct and run fuzzer and collect its output and artifacts using a default `mockBuild`.
func runWithDefaults(t *testing.T, name string, args []string) (string, []string, error) {
build, _ := newMockBuild()
return runWithBuild(t, build, name, args)
}
// Construct and run fuzzer and collect its output and artifacts using a given `build`.
func runWithBuild(t *testing.T, build Build, name string, args []string) (string, []string, error) {
conn := NewMockConnector(t)
return runWithConnector(t, build, conn, name, args)
}
// Construct and run fuzzer and collect its output and artifacts using a given `mockConnector`.
func runWithConnector(t *testing.T, build Build, conn *mockConnector, name string, args []string) (string, []string, error) {
f, err := build.Fuzzer(name)
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
return runWithFuzzer(t, conn, f, args)
}
// Run a given `Fuzzer` and collect its output and artifacts.
func runWithFuzzer(t *testing.T, conn *mockConnector, f *Fuzzer, args []string) (string, []string, error) {
f.Parse(append(args, "data/corpus"))
var outBuf bytes.Buffer
artifacts, err := f.Run(conn, &outBuf, "/some/artifactDir")
return outBuf.String(), artifacts, err
}
func testRun(t *testing.T, name string) {
build, _ := newMockBuild()
conn := NewMockConnector(t)
f, err := build.Fuzzer(name)
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
out, artifacts, err := runWithFuzzer(t, conn, f, []string{"-merge_control_file=data/.mergefile"})
if err != nil {
t.Fatalf("failed to run fuzzer: %s", err)
}
// Check for syslog insertion
if !strings.Contains(out, fmt.Sprintf("syslog for %d", mockFuzzerPid)) {
t.Fatalf("fuzzer output missing syslog: %q", out)
}
// Check for symbolization
if !strings.Contains(out, "wow.c:1") {
t.Fatalf("fuzzer output not properly symbolized: %q", out)
}
// Check for artifact detection
artifactAbsPath := f.AbsPath("tmp/crash-1312")
if !reflect.DeepEqual(artifacts, []string{artifactAbsPath}) {
t.Fatalf("unexpected artifact list: %s", artifacts)
}
// Check for artifact path rewriting
if !strings.Contains(out, "/some/artifactDir/crash-1312") {
t.Fatalf("artifact prefix not rewritten: %q", out)
}
// Check that paths in libFuzzer options/args are translated
if !f.useFuzzCtl() && !strings.Contains(out, "tmp/.mergefile") {
t.Fatalf("mergefile prefix not rewritten: %q", out)
}
if !f.useFuzzCtl() && !strings.Contains(out, "tmp/corpus") {
t.Fatalf("corpus prefix not rewritten: %q", out)
}
if !strings.Contains(out, "\nRunning: data/corpus/testcase\n") {
t.Fatalf("testcase prefix not restored: %q", out)
}
}
func TestRun(t *testing.T) {
testRun(t, "foo/bar")
}
func TestRunUsingFuzzCtl(t *testing.T) {
testRun(t, "cff/fuzzer")
}
func TestRunWithInvalidArtifactPrefix(t *testing.T) {
_, _, err := runWithDefaults(t, "foo/bar", []string{"-artifact_prefix=foo/bar"})
if err == nil || !strings.Contains(err.Error(), "artifact_prefix not in mutable") {
t.Fatalf("expected failure to run but got: %s", err)
}
}
func TestRunWithFuzzerErrorUsingFuzzCtl(t *testing.T) {
build, _ := newMockBuild()
_, _, err := runWithBuild(t, build, "cff/broken", nil)
if err == nil || !strings.Contains(err.Error(), "internal error") {
t.Fatalf("expected failure to run but got: %s", err)
}
}
func TestRunWithFuzzerErrorUsingFfxFuzz(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).useFfxFuzz = true
f, err := build.Fuzzer("cff/broken")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
conn := NewMockConnector(t)
conn.Dirs = []string{f.AbsPath("data/corpus")}
_, _, err = runWithConnector(t, build, conn, "cff/broken", nil)
// TODO(https://fxbug.dev/42063375): Validate the specific expected error.
if err == nil {
t.Fatalf("expected failure to run but got: %s", err)
}
}
func TestMissingPID(t *testing.T) {
output, _, err := runWithDefaults(t, "fail/nopid", nil)
if err != nil {
t.Fatalf("expected to succeed but got: %s", err)
}
if !strings.Contains(output, "missing pid") {
t.Fatalf("expected missing pid but got: %q", output)
}
}
func TestSyslogFailure(t *testing.T) {
build, _ := newMockBuild()
conn := NewMockConnector(t)
conn.shouldFailToGetSysLog = true
output, _, err := runWithConnector(t, build, conn, "foo/bar", nil)
if err != nil {
t.Fatalf("expected to succeed but got: %s", err)
}
if !strings.Contains(output, "failed to fetch syslog") {
t.Fatalf("expected syslog fetch failure but got: %q", output)
}
}
func TestMissingSymbolizer(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).brokenSymbolizer = true
_, _, err := runWithBuild(t, build, "foo/bar", nil)
if err == nil || !strings.Contains(err.Error(), "failed during symbolization") {
t.Fatalf("expected failure to symbolize but got: %s", err)
}
}
func TestMissingFuzzerPackage(t *testing.T) {
_, _, err := runWithDefaults(t, "fail/notfound", nil)
if err == nil || !strings.Contains(err.Error(), "not found") {
t.Fatalf("expected failure to find package but got: %s", err)
}
}
func runFuzzerV2(t *testing.T, conn *mockConnector, fuzzer *Fuzzer,
args []string) (string, []string) {
conn.Dirs = []string{fuzzer.AbsPath("data/corpus/out"),
fuzzer.AbsPath("data/corpus/in1"),
fuzzer.AbsPath("data/corpus/in2"),
}
fuzzer.Parse(args)
var outBuf bytes.Buffer
artifacts, err := fuzzer.Run(conn, &outBuf, "/some/artifactDir")
out := outBuf.String()
if err != nil {
glog.Warningf("fuzzer output: %s", out)
t.Fatalf("failed to run fuzzer: %s", err)
}
return out, artifacts
}
func expectLiveCorpusContents(t *testing.T, conn *mockConnector, targetPaths []string) {
var added []string
prefix := "fuzz add fuchsia-pkg://fuchsia.com/cff#meta/fuzzer.cm "
for _, cmd := range conn.FfxHistory {
if strings.HasPrefix(cmd, prefix) {
added = append(added, strings.TrimPrefix(cmd, prefix))
}
}
if diff := cmp.Diff(targetPaths, added, sortSlicesOpt); diff != "" {
t.Fatalf("incorrect live corpus contents (-want +got):\n%s", diff)
}
}
func TestFuzzUsingFfxFuzz(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).useFfxFuzz = true
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
conn := NewMockConnector(t)
out, artifacts := runFuzzerV2(t, conn, f, []string{"data/corpus/out",
"data/corpus/in1", "data/corpus/in2"})
// Check that fuzz was run
if !strings.Contains(out, "Running fuzzer") {
t.Fatalf("fuzz not run: %q", out)
}
// Check that input corpora were added to the live corpus
expectLiveCorpusContents(t, conn, []string{
f.AbsPath("data/corpus/in1"), f.AbsPath("data/corpus/in2")})
// Check that output corpus was marked
markerPath := filepath.Join("data/corpus/out", liveCorpusMarkerName)
if !conn.TargetPathExists(f.AbsPath(markerPath)) {
t.Fatalf("output corpus not marked")
}
// Check for syslog insertion
if !strings.Contains(out, fmt.Sprintf("syslog for %d", mockFuzzerPid)) {
t.Fatalf("fuzzer output missing syslog: %q", out)
}
// Check for symbolization
if !strings.Contains(out, "wow.c:1") {
t.Fatalf("fuzzer output not properly symbolized: %q", out)
}
// Check for artifact detection
artifactAbsPath := f.AbsPath("tmp/crash-1312")
if !reflect.DeepEqual(artifacts, []string{artifactAbsPath}) {
t.Fatalf("unexpected artifact list: %s", artifacts)
}
// Check for artifact path rewriting, with libFuzzer style output
if !strings.Contains(out, "Test unit written to /some/artifactDir/crash-1312") {
t.Fatalf("artifact prefix not rewritten: %q", out)
}
if !strings.Contains(out, "data/corpus/out") {
t.Fatalf("output corpus not rewritten: %q", out)
}
}
func TestTryUsingFfxFuzz(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).useFfxFuzz = true
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
conn := NewMockConnector(t)
testcase := filepath.Join(t.TempDir(), "testcase")
touchRandomFile(t, testcase)
if err := conn.Put(testcase, f.AbsPath("data/")); err != nil {
t.Fatalf("error putting testcase: %s", err)
}
out, artifacts := runFuzzerV2(t, conn, f, []string{"data/testcase"})
// Check that try was run
if !strings.Contains(out, "Trying an input") {
t.Fatalf("try not run: %q", out)
}
// Check for syslog insertion
if !strings.Contains(out, fmt.Sprintf("syslog for %d", mockFuzzerPid)) {
t.Fatalf("fuzzer output missing syslog: %q", out)
}
// Check for symbolization
if !strings.Contains(out, "wow.c:1") {
t.Fatalf("fuzzer output not properly symbolized: %q", out)
}
// Should have no artifacts generated
if len(artifacts) > 0 {
t.Fatalf("artifacts unexpectedly returned: %q", artifacts)
}
if !strings.Contains(out, "\nRunning: data/testcase\n") {
t.Fatalf("testcase name not restored: %q", out)
}
}
func TestMinimizeUsingFfxFuzz(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).useFfxFuzz = true
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
conn := NewMockConnector(t)
testcase := filepath.Join(t.TempDir(), "testcase")
touchRandomFile(t, testcase)
if err := conn.Put(testcase, f.AbsPath("data/")); err != nil {
t.Fatalf("error putting testcase: %s", err)
}
out, artifacts := runFuzzerV2(t, conn, f, []string{"-minimize_crash=1",
"-exact_artifact_path=data/final-minimized-crash", "data/testcase"})
// Check that minimization was run
if !strings.Contains(out, "Attempting to minimize") {
t.Fatalf("minimization not run: %q", out)
}
// Should have exactly one artifact
if len(artifacts) != 1 {
t.Fatalf("unexpected number of artifacts returned (%d)", len(artifacts))
}
// Should have been copied into the location specified by exact_artifact_prefix
if !conn.TargetPathExists(f.AbsPath("data/final-minimized-crash")) {
t.Fatalf("artifact not moved according to exact_artifact_prefix")
}
}
func TestMergeUsingFfxFuzz(t *testing.T) {
build, _ := newMockBuild()
build.(*mockBuild).useFfxFuzz = true
f, err := build.Fuzzer("cff/fuzzer")
if err != nil {
t.Fatalf("failed to load fuzzer: %s", err)
}
conn := NewMockConnector(t)
out, artifacts := runFuzzerV2(t, conn, f, []string{"-merge=1",
"-merge_control_file=data/.mergefile", "data/corpus/out",
"data/corpus/in1", "data/corpus/in2"})
// Check that merge was run
if !strings.Contains(out, "Compacting fuzzer corpus") {
t.Fatalf("merge not run: %q", out)
}
// Check that input corpora were added to the live corpus
expectLiveCorpusContents(t, conn, []string{
f.AbsPath("data/corpus/in1"), f.AbsPath("data/corpus/in2")})
// Should have no artifacts
if len(artifacts) != 0 {
t.Fatalf("unexpected number of artifacts returned (%d)", len(artifacts))
}
// Check that output corpus was not marked
markerPath := filepath.Join("data/corpus/out", liveCorpusMarkerName)
if conn.TargetPathExists(f.AbsPath(markerPath)) {
t.Fatalf("output corpus was marked but should not have been")
}
// Check that output corpus has output elements
corpusA := f.AbsPath(filepath.Join("data/corpus/out", "a"))
corpusB := f.AbsPath(filepath.Join("data/corpus/out", "b"))
if !conn.TargetPathExists(corpusA) || !conn.TargetPathExists(corpusB) {
t.Fatalf("merge output corpus not put into expected place")
}
}