// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
package csource
import (
"fmt"
"reflect"
"testing"
)
func TestParseOptions(t *testing.T) {
for _, opts := range allOptionsSingle("linux") {
data := opts.Serialize()
got, err := DeserializeOptions(data)
if err != nil {
t.Fatalf("failed to deserialize %q: %v", data, err)
}
if !reflect.DeepEqual(got, opts) {
t.Fatalf("opts changed, got:\n%+v\nwant:\n%+v", got, opts)
}
}
}
func TestParseOptionsCanned(t *testing.T) {
// Dashboard stores csource options with syzkaller reproducers,
// so we need to be able to parse old formats.
// nolint: lll
canned := map[string]Options{
`{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"namespace",
"fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true,
"netdev":true,"resetnet":true,
"segv":true,"waitrepeat":true,"debug":true,"repro":true}`: {
Threaded: true,
Collide: true,
Repeat: true,
Procs: 10,
Sandbox: "namespace",
Fault: true,
FaultCall: 1,
FaultNth: 2,
EnableTun: true,
UseTmpDir: true,
EnableCgroups: true,
EnableNetdev: true,
ResetNet: true,
HandleSegv: true,
Repro: true,
},
"{Threaded:true Collide:true Repeat:true Procs:1 Sandbox:none Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": {
Threaded: true,
Collide: true,
Repeat: true,
Procs: 1,
Sandbox: "none",
Fault: false,
FaultCall: -1,
FaultNth: 0,
EnableTun: true,
UseTmpDir: true,
EnableCgroups: false,
HandleSegv: true,
Repro: false,
},
"{Threaded:true Collide:true Repeat:true Procs:1 Sandbox: Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": {
Threaded: true,
Collide: true,
Repeat: true,
Procs: 1,
Sandbox: "",
Fault: false,
FaultCall: -1,
FaultNth: 0,
EnableTun: true,
UseTmpDir: true,
EnableCgroups: false,
HandleSegv: true,
Repro: false,
},
"{Threaded:false Collide:true Repeat:true Procs:1 Sandbox:namespace Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true EnableCgroups:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": {
Threaded: false,
Collide: true,
Repeat: true,
Procs: 1,
Sandbox: "namespace",
Fault: false,
FaultCall: -1,
FaultNth: 0,
EnableTun: true,
UseTmpDir: true,
EnableCgroups: true,
HandleSegv: true,
Repro: false,
},
}
for data, want := range canned {
got, err := DeserializeOptions([]byte(data))
if err != nil {
t.Fatalf("failed to deserialize %q: %v", data, err)
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("deserialize %q\ngot:\n%+v\nwant:\n%+v", data, got, want)
}
}
}
func allOptionsSingle(OS string) []Options {
var opts []Options
fields := reflect.TypeOf(Options{}).NumField()
for i := 0; i < fields; i++ {
// Because of constraints on options, we need some defaults
// (e.g. no collide without threaded).
opt := Options{
Threaded: true,
Repeat: true,
Sandbox: "none",
UseTmpDir: true,
}
opts = append(opts, enumerateField(OS, opt, i)...)
}
return opts
}
func allOptionsPermutations(OS string) []Options {
opts := []Options{{}}
fields := reflect.TypeOf(Options{}).NumField()
for i := 0; i < fields; i++ {
var newOpts []Options
for _, opt := range opts {
newOpts = append(newOpts, enumerateField(OS, opt, i)...)
}
opts = newOpts
}
return opts
}
func enumerateField(OS string, opt Options, field int) []Options {
var opts []Options
s := reflect.ValueOf(&opt).Elem()
fldName := s.Type().Field(field).Name
fld := s.Field(field)
if fldName == "Sandbox" {
for _, sandbox := range []string{"", "none", "setuid", "namespace"} {
fld.SetString(sandbox)
opts = append(opts, opt)
}
} else if fldName == "Procs" {
for _, procs := range []int64{1, 4} {
fld.SetInt(procs)
opts = append(opts, opt)
}
} else if fldName == "RepeatTimes" {
for _, times := range []int64{0, 10} {
fld.SetInt(times)
opts = append(opts, opt)
}
} else if fldName == "FaultCall" {
opts = append(opts, opt)
} else if fldName == "FaultNth" {
opts = append(opts, opt)
} else if fld.Kind() == reflect.Bool {
for _, v := range []bool{false, true} {
fld.SetBool(v)
opts = append(opts, opt)
}
} else {
panic(fmt.Sprintf("field '%v' is not boolean", fldName))
}
var checked []Options
for _, opt := range opts {
if err := opt.Check(OS); err == nil {
checked = append(checked, opt)
}
}
return checked
}