Skip to content

Commit

Permalink
Refactor float formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
gavv committed Feb 1, 2023
1 parent 2cb5998 commit 89cd7b7
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 50 deletions.
80 changes: 49 additions & 31 deletions formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"reflect"
"strconv"
"strings"
"text/template"

Expand All @@ -21,25 +22,6 @@ type Formatter interface {
FormatFailure(*AssertionContext, *AssertionFailure) string
}

// FloatFormat defines the format for printing float
type FloatFormat int

const (
// Print floats in scientific notation for large exponents,
// otherwise print in decimal notation.
// Similar to %g format.
FloatFormatAuto FloatFormat = iota

// Always print floats in decimal notation.
// Similar to %f format.
FloatFormatDecimal

// Always print floats in scientific notation.
// Similar to %f format, with precision set to the smallest number of digits
// necessary to identify the value uniquely.
FloatFormatScientific
)

// DefaultFormatter is the default Formatter implementation.
//
// DefaultFormatter gathers values from AssertionContext and AssertionFailure,
Expand Down Expand Up @@ -110,6 +92,27 @@ func (f *DefaultFormatter) FormatFailure(
}
}

// FloatFormat defines the format in which all floats are printed.
type FloatFormat int

const (
// Print floats in scientific notation for large exponents,
// otherwise print in decimal notation.
// Precision is the smallest needed to identify the value uniquely.
// Similar to %g format.
FloatFormatAuto FloatFormat = iota

// Always print floats in decimal notation.
// Precision is the smallest needed to identify the value uniquely.
// Similar to %f format.
FloatFormatDecimal

// Always print floats in scientific notation.
// Precision is the smallest needed to identify the value uniquely.
// Similar to %e format.
FloatFormatScientific
)

// FormatData defines data passed to template engine when DefaultFormatter
// formats assertion. You can use these fields in your custom templates.
type FormatData struct {
Expand Down Expand Up @@ -433,17 +436,12 @@ func (f *DefaultFormatter) formatTyped(value interface{}) string {
}

func (f *DefaultFormatter) formatValue(value interface{}) string {
if isNumber(value) {
switch f.FloatFormat {
case FloatFormatAuto:
return fmt.Sprintf("%v", value)
case FloatFormatDecimal:
return fmt.Sprintf("%f", value)
case FloatFormatScientific:
return fmt.Sprintf("%e", value)
default:
panic("invalid float format")
}
if flt := extractFloat32(value); flt != nil {
return f.formatFloat(*flt, 32)
}

if flt := extractFloat64(value); flt != nil {
return f.formatFloat(*flt, 64)
}

if !isNil(value) && !isHTTP(value) {
Expand All @@ -464,6 +462,19 @@ func (f *DefaultFormatter) formatValue(value interface{}) string {
return sq.Sdump(value)
}

func (f *DefaultFormatter) formatFloat(value float64, bits int) string {
switch f.FloatFormat {
case FloatFormatAuto:
return strconv.FormatFloat(value, 'g', -1, bits)
case FloatFormatDecimal:
return strconv.FormatFloat(value, 'f', -1, bits)
case FloatFormatScientific:
return strconv.FormatFloat(value, 'e', -1, bits)
default:
return fmt.Sprintf("%v", value)
}
}

func (f *DefaultFormatter) formatBareString(value interface{}) string {
switch v := value.(type) {
case string:
Expand Down Expand Up @@ -568,11 +579,18 @@ func extractList(value interface{}) *AssertionList {
}
}

func extractFloat(value interface{}) *float64 {
func extractFloat32(value interface{}) *float64 {
switch f := value.(type) {
case float32:
ff := float64(f)
return &ff
default:
return nil
}
}

func extractFloat64(value interface{}) *float64 {
switch f := value.(type) {
case float64:
return &f
default:
Expand Down
38 changes: 19 additions & 19 deletions formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,12 +571,12 @@ func TestFormatter_FloatFormat(t *testing.T) {
Type: AssertInRange,
Expected: &AssertionValue{
Value: AssertionRange{
Min: float32(-1.23456789),
Max: float32(1.23456789),
Min: float32(-1.2345678),
Max: float32(1.2345678),
},
},
},
wantExpected: []string{"[-1.2345679; 1.2345679]"},
wantExpected: []string{"[-1.2345678; 1.2345678]"},
},
{
name: "AssertInRange float32 auto large exponent",
Expand Down Expand Up @@ -608,7 +608,7 @@ func TestFormatter_FloatFormat(t *testing.T) {
},
},
},
wantExpected: []string{"[-12345679.000000; 12345679.000000]"},
wantExpected: []string{"[-12345679; 12345679]"},
},
{
name: "AssertInRange float32 scientific",
Expand All @@ -619,12 +619,12 @@ func TestFormatter_FloatFormat(t *testing.T) {
Type: AssertInRange,
Expected: &AssertionValue{
Value: AssertionRange{
Min: float32(-1.23456789),
Max: float32(1.23456789),
Min: float32(-1.2345678),
Max: float32(1.2345678),
},
},
},
wantExpected: []string{"[-1.234568e+00; 1.234568e+00]"},
wantExpected: []string{"[-1.2345678e+00; 1.2345678e+00]"},
},
{
name: "AssertInRange float64 auto small exponent",
Expand Down Expand Up @@ -672,7 +672,7 @@ func TestFormatter_FloatFormat(t *testing.T) {
},
},
},
wantExpected: []string{"[-12345678.900000; 12345678.900000]"}},
wantExpected: []string{"[-12345678.9; 12345678.9]"}},
{
name: "AssertInRange float64 scientific",
formatter: DefaultFormatter{
Expand All @@ -687,7 +687,7 @@ func TestFormatter_FloatFormat(t *testing.T) {
},
},
},
wantExpected: []string{"[-1.234568e+00; 1.234568e+00]"},
wantExpected: []string{"[-1.23456789e+00; 1.23456789e+00]"},
},
{
name: "Delta float32 auto small exponent",
Expand All @@ -696,10 +696,10 @@ func TestFormatter_FloatFormat(t *testing.T) {
},
assertionFailure: AssertionFailure{
Delta: &AssertionValue{
Value: float32(1.23456789),
Value: float32(1.2345678),
},
},
wantDelta: "1.2345679",
wantDelta: "1.2345678",
},
{
name: "Delta float32 auto large exponent",
Expand All @@ -708,10 +708,10 @@ func TestFormatter_FloatFormat(t *testing.T) {
},
assertionFailure: AssertionFailure{
Delta: &AssertionValue{
Value: float32(12345678.9),
Value: float32(1234567.8),
},
},
wantDelta: "1.2345679e+07",
wantDelta: "1.2345678e+06",
},
{
name: "Delta float32 decimal",
Expand All @@ -720,10 +720,10 @@ func TestFormatter_FloatFormat(t *testing.T) {
},
assertionFailure: AssertionFailure{
Delta: &AssertionValue{
Value: float32(12345678.9),
Value: float32(1234567.8),
},
},
wantDelta: "12345679.000000",
wantDelta: "1234567.8",
},
{
name: "Delta float32 scientific",
Expand All @@ -732,10 +732,10 @@ func TestFormatter_FloatFormat(t *testing.T) {
},
assertionFailure: AssertionFailure{
Delta: &AssertionValue{
Value: float32(1.23456789),
Value: float32(1.2345678),
},
},
wantDelta: "1.234568e+00",
wantDelta: "1.2345678e+00",
},
{
name: "Delta float64 auto small exponent",
Expand Down Expand Up @@ -771,7 +771,7 @@ func TestFormatter_FloatFormat(t *testing.T) {
Value: float64(12345678.9),
},
},
wantDelta: "12345678.900000",
wantDelta: "12345678.9",
},
{
name: "Delta float64 scientific",
Expand All @@ -783,7 +783,7 @@ func TestFormatter_FloatFormat(t *testing.T) {
Value: float64(1.23456789),
},
},
wantDelta: "1.234568e+00",
wantDelta: "1.23456789e+00",
},
}

Expand Down

0 comments on commit 89cd7b7

Please sign in to comment.