func Pow(base uint, exponent uint) uint {
	if exponent == 0 {
		return 1
	}

	return base * Pow(base, exponent-1)
}
func FuzzPow(f *testing.F) {
  f.Fuzz(func(t *testing.T, x, y uint) {
	  assert := assert.New(t)

	  assert.Equal(Pow(x, 0), uint(1))
	  assert.Equal(Pow(x, 1), x)
	  assert.Equal(Pow(x, 2), x*x)

	  if x > 0 && y > 0 {
	  	assert.Equal(Pow(x, y)/x, Pow(x, y-1))
	  }
  })
}

go test -fuzz=Fuzz -fuzztime 20s

  • When a fuzz test fails, Go records the input in testcase/
  • You will find that when x=6 and y=30, the assert fails, i.e., pow(6, 30)/6 is not equal to pow(6, 29). That seems odd, but after testing you will see it is because pow(6, 30) overflows.
  • The max.MaxUint in Go is about 18 * 10^¹⁸, while 6^²⁹ is about 7 * 10^¹⁸. If you multiply 6^²⁹ by 6, it overflows and yields 8 * 10^¹⁸. It is like running two laps and ending up near the starting point.
var ErrOverflow = fmt.Errorf("overflow")

func Pow(base uint, exponent uint) (uint, error) {
	if exponent == 0 {
		return 1
	}

	prevResult, err := Pow(base, exponent-1)
	if math.MaxUint/base < prevResult {
		return 0, ErrOverflow
	}

	return base * prevResult, nil
}
func FuzzPow(f *testing.F) {
  f.Fuzz(func(t *testing.T, x, y uint) {
    assert := assert.New(t)
  	if result, err := Pow(x, 1); err != ErrOverflow {
  		assert.Equal(result, x)
  	}

  	if result, err := Pow(x, y); x > 0 && y > 0 && err != ErrOverflow {
  		resultDivX, _ := Pow(x, y-1)
  		assert.Equal(result/x, resultDivX)
  	}
  })
}