...

Source file src/github.com/google/gofuzz/fuzz_test.go

Documentation: github.com/google/gofuzz

     1  /*
     2  Copyright 2014 Google Inc. All rights reserved.
     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 fuzz
    18  
    19  import (
    20  	"math/rand"
    21  	"reflect"
    22  	"regexp"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  )
    27  
    28  func TestFuzz_basic(t *testing.T) {
    29  	obj := &struct {
    30  		I    int
    31  		I8   int8
    32  		I16  int16
    33  		I32  int32
    34  		I64  int64
    35  		U    uint
    36  		U8   uint8
    37  		U16  uint16
    38  		U32  uint32
    39  		U64  uint64
    40  		Uptr uintptr
    41  		S    string
    42  		B    bool
    43  		T    time.Time
    44  		C64  complex64
    45  		C128 complex128
    46  	}{}
    47  
    48  	failed := map[string]int{}
    49  	for i := 0; i < 10; i++ {
    50  		New().Fuzz(obj)
    51  
    52  		if n, v := "i", obj.I; v == 0 {
    53  			failed[n] = failed[n] + 1
    54  		}
    55  		if n, v := "i8", obj.I8; v == 0 {
    56  			failed[n] = failed[n] + 1
    57  		}
    58  		if n, v := "i16", obj.I16; v == 0 {
    59  			failed[n] = failed[n] + 1
    60  		}
    61  		if n, v := "i32", obj.I32; v == 0 {
    62  			failed[n] = failed[n] + 1
    63  		}
    64  		if n, v := "i64", obj.I64; v == 0 {
    65  			failed[n] = failed[n] + 1
    66  		}
    67  		if n, v := "u", obj.U; v == 0 {
    68  			failed[n] = failed[n] + 1
    69  		}
    70  		if n, v := "u8", obj.U8; v == 0 {
    71  			failed[n] = failed[n] + 1
    72  		}
    73  		if n, v := "u16", obj.U16; v == 0 {
    74  			failed[n] = failed[n] + 1
    75  		}
    76  		if n, v := "u32", obj.U32; v == 0 {
    77  			failed[n] = failed[n] + 1
    78  		}
    79  		if n, v := "u64", obj.U64; v == 0 {
    80  			failed[n] = failed[n] + 1
    81  		}
    82  		if n, v := "uptr", obj.Uptr; v == 0 {
    83  			failed[n] = failed[n] + 1
    84  		}
    85  		if n, v := "s", obj.S; v == "" {
    86  			failed[n] = failed[n] + 1
    87  		}
    88  		if n, v := "b", obj.B; v == false {
    89  			failed[n] = failed[n] + 1
    90  		}
    91  		if n, v := "t", obj.T; v.IsZero() {
    92  			failed[n] = failed[n] + 1
    93  		}
    94  		if n, v := "c64", obj.C64; v == 0 {
    95  			failed[n] = failed[n] + 1
    96  		}
    97  		if n, v := "c128", obj.C128; v == 0 {
    98  			failed[n] = failed[n] + 1
    99  		}
   100  	}
   101  	checkFailed(t, failed)
   102  }
   103  
   104  func checkFailed(t *testing.T, failed map[string]int) {
   105  	for k, v := range failed {
   106  		if v > 8 {
   107  			t.Errorf("%v seems to not be getting set, was zero value %v times", k, v)
   108  		}
   109  	}
   110  }
   111  
   112  func TestFuzz_structptr(t *testing.T) {
   113  	obj := &struct {
   114  		A *struct {
   115  			S string
   116  		}
   117  	}{}
   118  
   119  	f := New().NilChance(.5)
   120  	failed := map[string]int{}
   121  	for i := 0; i < 10; i++ {
   122  		f.Fuzz(obj)
   123  
   124  		if n, v := "a not nil", obj.A; v == nil {
   125  			failed[n] = failed[n] + 1
   126  		}
   127  		if n, v := "a nil", obj.A; v != nil {
   128  			failed[n] = failed[n] + 1
   129  		}
   130  		if n, v := "as", obj.A; v == nil || v.S == "" {
   131  			failed[n] = failed[n] + 1
   132  		}
   133  	}
   134  	checkFailed(t, failed)
   135  }
   136  
   137  // tryFuzz tries fuzzing up to 20 times. Fail if check() never passes, report the highest
   138  // stage it ever got to.
   139  func tryFuzz(t *testing.T, f *Fuzzer, obj interface{}, check func() (stage int, passed bool)) {
   140  	maxStage := 0
   141  	for i := 0; i < 20; i++ {
   142  		f.Fuzz(obj)
   143  		stage, passed := check()
   144  		if stage > maxStage {
   145  			maxStage = stage
   146  		}
   147  		if passed {
   148  			return
   149  		}
   150  	}
   151  	t.Errorf("Only ever got to stage %v", maxStage)
   152  }
   153  
   154  func TestFuzz_structmap(t *testing.T) {
   155  	obj := &struct {
   156  		A map[struct {
   157  			S string
   158  		}]struct {
   159  			S2 string
   160  		}
   161  		B map[string]string
   162  	}{}
   163  
   164  	tryFuzz(t, New(), obj, func() (int, bool) {
   165  		if obj.A == nil {
   166  			return 1, false
   167  		}
   168  		if len(obj.A) == 0 {
   169  			return 2, false
   170  		}
   171  		for k, v := range obj.A {
   172  			if k.S == "" {
   173  				return 3, false
   174  			}
   175  			if v.S2 == "" {
   176  				return 4, false
   177  			}
   178  		}
   179  
   180  		if obj.B == nil {
   181  			return 5, false
   182  		}
   183  		if len(obj.B) == 0 {
   184  			return 6, false
   185  		}
   186  		for k, v := range obj.B {
   187  			if k == "" {
   188  				return 7, false
   189  			}
   190  			if v == "" {
   191  				return 8, false
   192  			}
   193  		}
   194  		return 9, true
   195  	})
   196  }
   197  
   198  func TestFuzz_structslice(t *testing.T) {
   199  	obj := &struct {
   200  		A []struct {
   201  			S string
   202  		}
   203  		B []string
   204  	}{}
   205  
   206  	tryFuzz(t, New(), obj, func() (int, bool) {
   207  		if obj.A == nil {
   208  			return 1, false
   209  		}
   210  		if len(obj.A) == 0 {
   211  			return 2, false
   212  		}
   213  		for _, v := range obj.A {
   214  			if v.S == "" {
   215  				return 3, false
   216  			}
   217  		}
   218  
   219  		if obj.B == nil {
   220  			return 4, false
   221  		}
   222  		if len(obj.B) == 0 {
   223  			return 5, false
   224  		}
   225  		for _, v := range obj.B {
   226  			if v == "" {
   227  				return 6, false
   228  			}
   229  		}
   230  		return 7, true
   231  	})
   232  }
   233  
   234  func TestFuzz_structarray(t *testing.T) {
   235  	obj := &struct {
   236  		A [3]struct {
   237  			S string
   238  		}
   239  		B [2]int
   240  	}{}
   241  
   242  	tryFuzz(t, New(), obj, func() (int, bool) {
   243  		for _, v := range obj.A {
   244  			if v.S == "" {
   245  				return 1, false
   246  			}
   247  		}
   248  
   249  		for _, v := range obj.B {
   250  			if v == 0 {
   251  				return 2, false
   252  			}
   253  		}
   254  		return 3, true
   255  	})
   256  }
   257  
   258  func TestFuzz_custom(t *testing.T) {
   259  	obj := &struct {
   260  		A string
   261  		B *string
   262  		C map[string]string
   263  		D *map[string]string
   264  	}{}
   265  
   266  	testPhrase := "gotcalled"
   267  	testMap := map[string]string{"C": "D"}
   268  	f := New().Funcs(
   269  		func(s *string, c Continue) {
   270  			*s = testPhrase
   271  		},
   272  		func(m map[string]string, c Continue) {
   273  			m["C"] = "D"
   274  		},
   275  	)
   276  
   277  	tryFuzz(t, f, obj, func() (int, bool) {
   278  		if obj.A != testPhrase {
   279  			return 1, false
   280  		}
   281  		if obj.B == nil {
   282  			return 2, false
   283  		}
   284  		if *obj.B != testPhrase {
   285  			return 3, false
   286  		}
   287  		if e, a := testMap, obj.C; !reflect.DeepEqual(e, a) {
   288  			return 4, false
   289  		}
   290  		if obj.D == nil {
   291  			return 5, false
   292  		}
   293  		if e, a := testMap, *obj.D; !reflect.DeepEqual(e, a) {
   294  			return 6, false
   295  		}
   296  		return 7, true
   297  	})
   298  }
   299  
   300  type SelfFuzzer string
   301  
   302  // Implement fuzz.Interface.
   303  func (sf *SelfFuzzer) Fuzz(c Continue) {
   304  	*sf = selfFuzzerTestPhrase
   305  }
   306  
   307  const selfFuzzerTestPhrase = "was fuzzed"
   308  
   309  func TestFuzz_interface(t *testing.T) {
   310  	f := New()
   311  
   312  	var obj1 SelfFuzzer
   313  	tryFuzz(t, f, &obj1, func() (int, bool) {
   314  		if obj1 != selfFuzzerTestPhrase {
   315  			return 1, false
   316  		}
   317  		return 1, true
   318  	})
   319  
   320  	var obj2 map[int]SelfFuzzer
   321  	tryFuzz(t, f, &obj2, func() (int, bool) {
   322  		for _, v := range obj2 {
   323  			if v != selfFuzzerTestPhrase {
   324  				return 1, false
   325  			}
   326  		}
   327  		return 1, true
   328  	})
   329  }
   330  
   331  func TestFuzz_interfaceAndFunc(t *testing.T) {
   332  	const privateTestPhrase = "private phrase"
   333  	f := New().Funcs(
   334  		// This should take precedence over SelfFuzzer.Fuzz().
   335  		func(s *SelfFuzzer, c Continue) {
   336  			*s = privateTestPhrase
   337  		},
   338  	)
   339  
   340  	var obj1 SelfFuzzer
   341  	tryFuzz(t, f, &obj1, func() (int, bool) {
   342  		if obj1 != privateTestPhrase {
   343  			return 1, false
   344  		}
   345  		return 1, true
   346  	})
   347  
   348  	var obj2 map[int]SelfFuzzer
   349  	tryFuzz(t, f, &obj2, func() (int, bool) {
   350  		for _, v := range obj2 {
   351  			if v != privateTestPhrase {
   352  				return 1, false
   353  			}
   354  		}
   355  		return 1, true
   356  	})
   357  }
   358  
   359  func TestFuzz_noCustom(t *testing.T) {
   360  	type Inner struct {
   361  		Str string
   362  	}
   363  	type Outer struct {
   364  		Str string
   365  		In  Inner
   366  	}
   367  
   368  	testPhrase := "gotcalled"
   369  	f := New().Funcs(
   370  		func(outer *Outer, c Continue) {
   371  			outer.Str = testPhrase
   372  			c.Fuzz(&outer.In)
   373  		},
   374  		func(inner *Inner, c Continue) {
   375  			inner.Str = testPhrase
   376  		},
   377  	)
   378  	c := Continue{fc: &fuzzerContext{fuzzer: f}, Rand: f.r}
   379  
   380  	// Fuzzer.Fuzz()
   381  	obj1 := Outer{}
   382  	f.Fuzz(&obj1)
   383  	if obj1.Str != testPhrase {
   384  		t.Errorf("expected Outer custom function to have been called")
   385  	}
   386  	if obj1.In.Str != testPhrase {
   387  		t.Errorf("expected Inner custom function to have been called")
   388  	}
   389  
   390  	// Continue.Fuzz()
   391  	obj2 := Outer{}
   392  	c.Fuzz(&obj2)
   393  	if obj2.Str != testPhrase {
   394  		t.Errorf("expected Outer custom function to have been called")
   395  	}
   396  	if obj2.In.Str != testPhrase {
   397  		t.Errorf("expected Inner custom function to have been called")
   398  	}
   399  
   400  	// Fuzzer.FuzzNoCustom()
   401  	obj3 := Outer{}
   402  	f.FuzzNoCustom(&obj3)
   403  	if obj3.Str == testPhrase {
   404  		t.Errorf("expected Outer custom function to not have been called")
   405  	}
   406  	if obj3.In.Str != testPhrase {
   407  		t.Errorf("expected Inner custom function to have been called")
   408  	}
   409  
   410  	// Continue.FuzzNoCustom()
   411  	obj4 := Outer{}
   412  	c.FuzzNoCustom(&obj4)
   413  	if obj4.Str == testPhrase {
   414  		t.Errorf("expected Outer custom function to not have been called")
   415  	}
   416  	if obj4.In.Str != testPhrase {
   417  		t.Errorf("expected Inner custom function to have been called")
   418  	}
   419  }
   420  
   421  func TestFuzz_NumElements(t *testing.T) {
   422  	f := New().NilChance(0).NumElements(0, 1)
   423  	obj := &struct {
   424  		A []int
   425  	}{}
   426  
   427  	tryFuzz(t, f, obj, func() (int, bool) {
   428  		if obj.A == nil {
   429  			return 1, false
   430  		}
   431  		return 2, len(obj.A) == 0
   432  	})
   433  	tryFuzz(t, f, obj, func() (int, bool) {
   434  		if obj.A == nil {
   435  			return 3, false
   436  		}
   437  		return 4, len(obj.A) == 1
   438  	})
   439  }
   440  
   441  func TestFuzz_Maxdepth(t *testing.T) {
   442  	type S struct {
   443  		S *S
   444  	}
   445  
   446  	f := New().NilChance(0)
   447  
   448  	f.MaxDepth(1)
   449  	for i := 0; i < 100; i++ {
   450  		obj := S{}
   451  		f.Fuzz(&obj)
   452  
   453  		if obj.S != nil {
   454  			t.Errorf("Expected nil")
   455  		}
   456  	}
   457  
   458  	f.MaxDepth(3) // field, ptr
   459  	for i := 0; i < 100; i++ {
   460  		obj := S{}
   461  		f.Fuzz(&obj)
   462  
   463  		if obj.S == nil {
   464  			t.Errorf("Expected obj.S not nil")
   465  		} else if obj.S.S != nil {
   466  			t.Errorf("Expected obj.S.S nil")
   467  		}
   468  	}
   469  
   470  	f.MaxDepth(5) // field, ptr, field, ptr
   471  	for i := 0; i < 100; i++ {
   472  		obj := S{}
   473  		f.Fuzz(&obj)
   474  
   475  		if obj.S == nil {
   476  			t.Errorf("Expected obj.S not nil")
   477  		} else if obj.S.S == nil {
   478  			t.Errorf("Expected obj.S.S not nil")
   479  		} else if obj.S.S.S != nil {
   480  			t.Errorf("Expected obj.S.S.S nil")
   481  		}
   482  	}
   483  }
   484  
   485  func TestFuzz_SkipPattern(t *testing.T) {
   486  	obj := &struct {
   487  		S1    string
   488  		S2    string
   489  		XXX_S string
   490  		S_XXX string
   491  		In    struct {
   492  			Str    string
   493  			XXX_S1 string
   494  			S2_XXX string
   495  		}
   496  	}{}
   497  
   498  	f := New().NilChance(0).SkipFieldsWithPattern(regexp.MustCompile(`^XXX_`))
   499  	f.Fuzz(obj)
   500  
   501  	tryFuzz(t, f, obj, func() (int, bool) {
   502  		if obj.XXX_S != "" {
   503  			return 1, false
   504  		}
   505  		if obj.S_XXX == "" {
   506  			return 2, false
   507  		}
   508  		if obj.In.XXX_S1 != "" {
   509  			return 3, false
   510  		}
   511  		if obj.In.S2_XXX == "" {
   512  			return 4, false
   513  		}
   514  		return 5, true
   515  	})
   516  }
   517  
   518  func TestFuzz_NilChanceZero(t *testing.T) {
   519  	// This data source for random will result in the following four values
   520  	// being sampled (the first, 0, being the most interesting case):
   521  	//   0; 0.8727288671879787; 0.5547307616625858; 0.021885026049502695
   522  	data := []byte("H0000000\x00")
   523  	f := NewFromGoFuzz(data).NilChance(0)
   524  
   525  	var fancyStruct struct {
   526  		A, B, C, D *string
   527  	}
   528  	f.Fuzz(&fancyStruct) // None of the pointers should be nil, as NilChance is 0
   529  
   530  	if fancyStruct.A == nil {
   531  		t.Error("First value in struct was nil")
   532  	}
   533  
   534  	if fancyStruct.B == nil {
   535  		t.Error("Second value in struct was nil")
   536  	}
   537  
   538  	if fancyStruct.C == nil {
   539  		t.Error("Third value in struct was nil")
   540  	}
   541  
   542  	if fancyStruct.D == nil {
   543  		t.Error("Fourth value in struct was nil")
   544  	}
   545  }
   546  
   547  type int63mode int
   548  
   549  const (
   550  	modeRandom int63mode = iota
   551  	modeFirst
   552  	modeLast
   553  )
   554  
   555  type customInt63 struct {
   556  	mode int63mode
   557  }
   558  
   559  func (c customInt63) Int63n(n int64) int64 {
   560  	switch c.mode {
   561  	case modeFirst:
   562  		return 0
   563  	case modeLast:
   564  		return n - 1
   565  	default:
   566  		return rand.Int63n(n)
   567  	}
   568  }
   569  
   570  func Test_charRange_choose(t *testing.T) {
   571  	lowercaseLetters := UnicodeRange{'a', 'z'}
   572  
   573  	t.Run("Picks first", func(t *testing.T) {
   574  		r := customInt63{mode: modeFirst}
   575  		letter := lowercaseLetters.choose(r)
   576  		if letter != 'a' {
   577  			t.Errorf("Expected a, got %v", letter)
   578  		}
   579  	})
   580  
   581  	t.Run("Picks last", func(t *testing.T) {
   582  		r := customInt63{mode: modeLast}
   583  		letter := lowercaseLetters.choose(r)
   584  		if letter != 'z' {
   585  			t.Errorf("Expected z, got %v", letter)
   586  		}
   587  	})
   588  }
   589  
   590  func Test_UnicodeRange_CustomStringFuzzFunc(t *testing.T) {
   591  	a2z := "abcdefghijklmnopqrstuvwxyz"
   592  
   593  	unicodeRange := UnicodeRange{'a', 'z'}
   594  	f := New().Funcs(unicodeRange.CustomStringFuzzFunc())
   595  	var myString string
   596  	f.Fuzz(&myString)
   597  
   598  	t.Run("Picks a-z string", func(t *testing.T) {
   599  		for i := range myString {
   600  			if !strings.ContainsRune(a2z, rune(myString[i])) {
   601  				t.Errorf("Expected a-z, got %v", string(myString[i]))
   602  			}
   603  		}
   604  	})
   605  }
   606  
   607  func Test_UnicodeRange_Check(t *testing.T) {
   608  	unicodeRange := UnicodeRange{'a', 'z'}
   609  
   610  	unicodeRange.check()
   611  }
   612  
   613  func Test_UnicodeRanges_CustomStringFuzzFunc(t *testing.T) {
   614  	a2z0to9 := "abcdefghijklmnopqrstuvwxyz0123456789"
   615  
   616  	unicodeRanges := UnicodeRanges{
   617  		{'a', 'z'},
   618  		{'0', '9'},
   619  	}
   620  	f := New().Funcs(unicodeRanges.CustomStringFuzzFunc())
   621  	var myString string
   622  	f.Fuzz(&myString)
   623  
   624  	t.Run("Picks a-z0-9 string", func(t *testing.T) {
   625  		for i := range myString {
   626  			if !strings.ContainsRune(a2z0to9, rune(myString[i])) {
   627  				t.Errorf("Expected a-z0-9, got %v", string(myString[i]))
   628  			}
   629  		}
   630  	})
   631  }
   632  
   633  func TestNewFromGoFuzz(t *testing.T) {
   634  	t.Parallel()
   635  
   636  	input := []byte{1, 2, 3}
   637  
   638  	var got int
   639  	NewFromGoFuzz(input).Fuzz(&got)
   640  
   641  	if want := 5563767293437588600; want != got {
   642  		t.Errorf("Fuzz(%q) = %d, want: %d", input, got, want)
   643  	}
   644  }
   645  
   646  func BenchmarkRandBool(b *testing.B) {
   647  	rs := rand.New(rand.NewSource(123))
   648  
   649  	for i := 0; i < b.N; i++ {
   650  		randBool(rs)
   651  	}
   652  }
   653  
   654  func BenchmarkRandString(b *testing.B) {
   655  	rs := rand.New(rand.NewSource(123))
   656  
   657  	for i := 0; i < b.N; i++ {
   658  		randString(rs)
   659  	}
   660  }
   661  
   662  func BenchmarkUnicodeRangeRandString(b *testing.B) {
   663  	unicodeRange := UnicodeRange{'a', 'z'}
   664  
   665  	rs := rand.New(rand.NewSource(123))
   666  
   667  	for i := 0; i < b.N; i++ {
   668  		unicodeRange.randString(rs)
   669  	}
   670  }
   671  
   672  func BenchmarkUnicodeRangesRandString(b *testing.B) {
   673  	unicodeRanges := UnicodeRanges{
   674  		{'a', 'z'},
   675  		{'0', '9'},
   676  	}
   677  
   678  	rs := rand.New(rand.NewSource(123))
   679  
   680  	for i := 0; i < b.N; i++ {
   681  		unicodeRanges.randString(rs)
   682  	}
   683  }
   684  

View as plain text