From 4960133fa7e1cf212fdafe0cbd64ab3d34905c42 Mon Sep 17 00:00:00 2001 From: ccoVeille <3875889+ccoVeille@users.noreply.github.com> Date: Wed, 13 Nov 2024 01:28:30 +0100 Subject: [PATCH] ci: also launch tests on i386 Fixes #6 Fixes #46 The tests in conversion_test.go are the ones that are not architecture dependent The tests in conversion_64bit_test.go complete them for 64-bit systems The tests in examples_test.go are the ones that are not architecture dependent The tests in examples_32bit_test.go are for 32-bit systems The tests in examples_64bit_test.go are for 64-bit systems The architecture dependent file covers the fact, you can reach a higher value with int and uint on 64-bit systems, but you will get a compile error on 32-bit. The error message is different on 32-bit and 64-bit systems The max is 9223372036854775807 on 64-bit and 2147483647 on 32-bit --- .github/workflows/golang.yml | 3 + asserters_test.go | 380 ++++++++++++++++++++++++++++++++ conversion_64bit_test.go | 55 +++++ conversion_test.go | 408 ++--------------------------------- examples_32bit_test.go | 39 ++++ examples_64bit_test.go | 39 ++++ examples_test.go | 35 ++- 7 files changed, 549 insertions(+), 410 deletions(-) create mode 100644 asserters_test.go create mode 100644 conversion_64bit_test.go create mode 100644 examples_32bit_test.go create mode 100644 examples_64bit_test.go diff --git a/.github/workflows/golang.yml b/.github/workflows/golang.yml index ef8b499..6a79118 100644 --- a/.github/workflows/golang.yml +++ b/.github/workflows/golang.yml @@ -33,3 +33,6 @@ jobs: - name: Launch golangci-lint uses: golangci/golangci-lint-action@v6.1.1 + + - name: Run tests on i386 + run: GOARCH=386 go test diff --git a/asserters_test.go b/asserters_test.go new file mode 100644 index 0000000..318d30d --- /dev/null +++ b/asserters_test.go @@ -0,0 +1,380 @@ +package safecast_test + +import ( + "errors" + "math" + "strings" + "testing" + + "github.com/ccoveille/go-safecast" +) + +func assertEqual[V comparable](t *testing.T, expected, got V) { + t.Helper() + + if expected == got { + return + } + + t.Errorf("Not equal: \n"+ + "expected: %v (%T)\n"+ + "actual : %v (%T)", expected, expected, got, got) +} + +func requireError(t *testing.T, err error) { + t.Helper() + + if err == nil { + t.Fatal("expected error") + } +} + +func requireErrorIs(t *testing.T, err error, expected error) { + t.Helper() + requireError(t, err) + + if !errors.Is(err, expected) { + t.Fatalf("unexpected error got %v, expected %v", err, expected) + } +} + +func requireErrorContains(t *testing.T, err error, text string) { + t.Helper() + requireErrorIs(t, err, safecast.ErrConversionIssue) + + errMessage := err.Error() + if !strings.Contains(errMessage, text) { + t.Fatalf("error message should contain %q: %q", text, errMessage) + } +} + +func assertNoError(t *testing.T, err error) { + t.Helper() + + if err != nil { + t.Errorf("expected no error, got %v", err) + } +} + +type caseInt8[in safecast.Type] struct { + name string + input in + want int8 +} + +func assertInt8OK[in safecast.Type](t *testing.T, tests []caseInt8[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToInt8(tt.input) + assertNoError(t, err) + assertEqual(t, tt.want, got) + }) + } +} + +func assertInt8Error[in safecast.Type](t *testing.T, tests []caseInt8[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToInt8(tt.input) + requireErrorIs(t, err, safecast.ErrConversionIssue) + assertEqual(t, tt.want, got) + }) + } +} + +func TestErrorMessage(t *testing.T) { + _, err := safecast.ToUint8(-1) + requireErrorIs(t, err, safecast.ErrConversionIssue) + requireErrorIs(t, err, safecast.ErrExceedMinimumValue) + requireErrorContains(t, err, "than 0 (uint8)") + + _, err = safecast.ToUint8(math.MaxInt16) + requireErrorIs(t, err, safecast.ErrConversionIssue) + requireErrorIs(t, err, safecast.ErrExceedMaximumValue) + requireErrorContains(t, err, "than 255 (uint8)") + + _, err = safecast.ToInt8(-math.MaxInt16) + requireErrorIs(t, err, safecast.ErrConversionIssue) + requireErrorIs(t, err, safecast.ErrExceedMinimumValue) + requireErrorContains(t, err, "than -128 (int8)") +} + +type caseUint8[in safecast.Type] struct { + name string + input in + want uint8 +} + +func assertUint8OK[in safecast.Type](t *testing.T, tests []caseUint8[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToUint8(tt.input) + assertNoError(t, err) + assertEqual(t, tt.want, got) + }) + } +} + +func assertUint8Error[in safecast.Type](t *testing.T, tests []caseUint8[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Helper() + + got, err := safecast.ToUint8(tt.input) + requireErrorIs(t, err, safecast.ErrConversionIssue) + assertEqual(t, tt.want, got) + }) + } +} + +type caseInt16[in safecast.Type] struct { + name string + input in + want int16 +} + +func assertInt16OK[in safecast.Type](t *testing.T, tests []caseInt16[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToInt16(tt.input) + assertNoError(t, err) + assertEqual(t, tt.want, got) + }) + } +} + +func assertInt16Error[in safecast.Type](t *testing.T, tests []caseInt16[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToInt16(tt.input) + requireErrorIs(t, err, safecast.ErrConversionIssue) + assertEqual(t, tt.want, got) + }) + } +} + +type caseUint16[in safecast.Type] struct { + name string + input in + want uint16 +} + +func assertUint16OK[in safecast.Type](t *testing.T, tests []caseUint16[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToUint16(tt.input) + assertNoError(t, err) + assertEqual(t, tt.want, got) + }) + } +} + +func assertUint16Error[in safecast.Type](t *testing.T, tests []caseUint16[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Helper() + + got, err := safecast.ToUint16(tt.input) + requireErrorIs(t, err, safecast.ErrConversionIssue) + assertEqual(t, tt.want, got) + }) + } +} + +type caseInt32[in safecast.Type] struct { + name string + input in + want int32 +} + +func assertInt32OK[in safecast.Type](t *testing.T, tests []caseInt32[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToInt32(tt.input) + assertNoError(t, err) + assertEqual(t, tt.want, got) + }) + } +} + +func assertInt32Error[in safecast.Type](t *testing.T, tests []caseInt32[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToInt32(tt.input) + requireErrorIs(t, err, safecast.ErrConversionIssue) + assertEqual(t, tt.want, got) + }) + } +} + +type caseUint32[in safecast.Type] struct { + name string + input in + want uint32 +} + +func assertUint32OK[in safecast.Type](t *testing.T, tests []caseUint32[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToUint32(tt.input) + assertNoError(t, err) + assertEqual(t, tt.want, got) + }) + } +} + +func assertUint32Error[in safecast.Type](t *testing.T, tests []caseUint32[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Helper() + + got, err := safecast.ToUint32(tt.input) + requireErrorIs(t, err, safecast.ErrConversionIssue) + assertEqual(t, tt.want, got) + }) + } +} + +type caseInt64[in safecast.Type] struct { + name string + input in + want int64 +} + +func assertInt64OK[in safecast.Type](t *testing.T, tests []caseInt64[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToInt64(tt.input) + assertNoError(t, err) + assertEqual(t, tt.want, got) + }) + } +} + +func assertInt64Error[in safecast.Type](t *testing.T, tests []caseInt64[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToInt64(tt.input) + requireErrorIs(t, err, safecast.ErrConversionIssue) + assertEqual(t, tt.want, got) + }) + } +} + +type caseUint64[in safecast.Type] struct { + name string + input in + want uint64 +} + +func assertUint64OK[in safecast.Type](t *testing.T, tests []caseUint64[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToUint64(tt.input) + assertNoError(t, err) + assertEqual(t, tt.want, got) + }) + } +} + +func assertUint64Error[in safecast.Type](t *testing.T, tests []caseUint64[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToUint64(tt.input) + requireErrorIs(t, err, safecast.ErrConversionIssue) + assertEqual(t, tt.want, got) + }) + } +} + +type caseInt[in safecast.Type] struct { + name string + input in + want int +} + +func assertIntOK[in safecast.Type](t *testing.T, tests []caseInt[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToInt(tt.input) + assertNoError(t, err) + assertEqual(t, tt.want, got) + }) + } +} + +func assertIntError[in safecast.Type](t *testing.T, tests []caseInt[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToInt(tt.input) + requireErrorIs(t, err, safecast.ErrConversionIssue) + assertEqual(t, tt.want, got) + }) + } +} + +type caseUint[in safecast.Type] struct { + name string + input in + want uint +} + +func assertUintOK[in safecast.Type](t *testing.T, tests []caseUint[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToUint(tt.input) + assertNoError(t, err) + assertEqual(t, tt.want, got) + }) + } +} + +func assertUintError[in safecast.Type](t *testing.T, tests []caseUint[in]) { + t.Helper() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := safecast.ToUint(tt.input) + requireErrorIs(t, err, safecast.ErrConversionIssue) + assertEqual(t, tt.want, got) + }) + } +} diff --git a/conversion_64bit_test.go b/conversion_64bit_test.go new file mode 100644 index 0000000..6f0e1fd --- /dev/null +++ b/conversion_64bit_test.go @@ -0,0 +1,55 @@ +//go:build !386 && !arm + +package safecast_test + +// The tests in conversion_test.go are the ones that are not architecture dependent +// The tests in conversion_64bit_test.go complete them for 64-bit systems +// +// This architecture dependent file covers the fact, you can reach a higher value with int and uint +// on 64-bit systems, but you will get a compile error on 32-bit. +// This is why it needs to be tested in an architecture dependent way. + +import ( + "math" + "testing" +) + +func TestToInt32_64bit(t *testing.T) { + t.Run("from int", func(t *testing.T) { + assertInt32Error(t, []caseInt32[int]{ + {name: "positive out of range", input: math.MaxInt32 + 1}, + {name: "negative out of range", input: math.MinInt32 - 1}, + }) + }) +} + +func TestToUint32_64bit(t *testing.T) { + t.Run("from int", func(t *testing.T) { + assertUint32Error(t, []caseUint32[int]{ + {name: "positive out of range", input: math.MaxUint32 + 1}, + {name: "negative value", input: -1}, + }) + }) +} + +func TestToInt64_64bit(t *testing.T) { + t.Run("from uint", func(t *testing.T) { + assertInt64Error(t, []caseInt64[uint]{ + {name: "positive out of range", input: math.MaxInt64 + 1}, + }) + }) +} + +func TestToInt_64bit(t *testing.T) { + t.Run("from uint", func(t *testing.T) { + assertIntError(t, []caseInt[uint]{ + {name: "positive out of range", input: math.MaxInt64 + 1}, + }) + }) + + t.Run("from float64", func(t *testing.T) { + assertIntOK(t, []caseInt[float64]{ + {name: "math.MinInt64", input: math.MinInt64, want: math.MinInt64}, // pass because of float imprecision + }) + }) +} diff --git a/conversion_test.go b/conversion_test.go index fc3a4e5..cc40956 100644 --- a/conversion_test.go +++ b/conversion_test.go @@ -1,91 +1,17 @@ package safecast_test +// The tests in conversion_test.go are the ones that are not architecture dependent +// The tests in conversion_64bit_test.go complete them for 64-bit systems +// +// The architecture dependent file covers the fact, you can reach a higher value with int and uint +// on 64-bit systems, but you will get a compile error on 32-bit. +// This is why it needs to be tested in an architecture dependent way. + import ( - "errors" "math" - "strings" "testing" - - "github.com/ccoveille/go-safecast" ) -func assertEqual[V comparable](t *testing.T, expected, got V) { - t.Helper() - - if expected == got { - return - } - - t.Errorf("Not equal: \n"+ - "expected: %v (%T)\n"+ - "actual : %v (%T)", expected, expected, got, got) -} - -func requireError(t *testing.T, err error) { - t.Helper() - - if err == nil { - t.Fatal("expected error") - } -} - -func requireErrorIs(t *testing.T, err error, expected error) { - t.Helper() - requireError(t, err) - - if !errors.Is(err, expected) { - t.Fatalf("unexpected error got %v, expected %v", err, expected) - } -} - -func requireErrorContains(t *testing.T, err error, text string) { - t.Helper() - requireErrorIs(t, err, safecast.ErrConversionIssue) - - errMessage := err.Error() - if !strings.Contains(errMessage, text) { - t.Fatalf("error message should contain %q: %q", text, errMessage) - } -} - -func assertNoError(t *testing.T, err error) { - t.Helper() - - if err != nil { - t.Errorf("expected no error, got %v", err) - } -} - -type caseInt8[in safecast.Type] struct { - name string - input in - want int8 -} - -func assertInt8OK[in safecast.Type](t *testing.T, tests []caseInt8[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToInt8(tt.input) - assertNoError(t, err) - assertEqual(t, tt.want, got) - }) - } -} - -func assertInt8Error[in safecast.Type](t *testing.T, tests []caseInt8[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToInt8(tt.input) - requireErrorIs(t, err, safecast.ErrConversionIssue) - assertEqual(t, tt.want, got) - }) - } -} - func TestToInt8(t *testing.T) { t.Run("from int", func(t *testing.T) { assertInt8OK(t, []caseInt8[int]{ @@ -221,55 +147,6 @@ func TestToInt8(t *testing.T) { }) } -func TestErrorMessage(t *testing.T) { - _, err := safecast.ToUint8(-1) - requireErrorIs(t, err, safecast.ErrConversionIssue) - requireErrorIs(t, err, safecast.ErrExceedMinimumValue) - requireErrorContains(t, err, "than 0 (uint8)") - - _, err = safecast.ToUint8(math.MaxInt16) - requireErrorIs(t, err, safecast.ErrConversionIssue) - requireErrorIs(t, err, safecast.ErrExceedMaximumValue) - requireErrorContains(t, err, "than 255 (uint8)") - - _, err = safecast.ToInt8(-math.MaxInt16) - requireErrorIs(t, err, safecast.ErrConversionIssue) - requireErrorIs(t, err, safecast.ErrExceedMinimumValue) - requireErrorContains(t, err, "than -128 (int8)") -} - -type caseUint8[in safecast.Type] struct { - name string - input in - want uint8 -} - -func assertUint8OK[in safecast.Type](t *testing.T, tests []caseUint8[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToUint8(tt.input) - assertNoError(t, err) - assertEqual(t, tt.want, got) - }) - } -} - -func assertUint8Error[in safecast.Type](t *testing.T, tests []caseUint8[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Helper() - - got, err := safecast.ToUint8(tt.input) - requireErrorIs(t, err, safecast.ErrConversionIssue) - assertEqual(t, tt.want, got) - }) - } -} - func TestToUint8(t *testing.T) { t.Run("from int", func(t *testing.T) { assertUint8OK(t, []caseUint8[int]{ @@ -406,36 +283,6 @@ func TestToUint8(t *testing.T) { }) } -type caseInt16[in safecast.Type] struct { - name string - input in - want int16 -} - -func assertInt16OK[in safecast.Type](t *testing.T, tests []caseInt16[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToInt16(tt.input) - assertNoError(t, err) - assertEqual(t, tt.want, got) - }) - } -} - -func assertInt16Error[in safecast.Type](t *testing.T, tests []caseInt16[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToInt16(tt.input) - requireErrorIs(t, err, safecast.ErrConversionIssue) - assertEqual(t, tt.want, got) - }) - } -} - func TestToInt16(t *testing.T) { t.Run("from int", func(t *testing.T) { assertInt16OK(t, []caseInt16[int]{ @@ -568,38 +415,6 @@ func TestToInt16(t *testing.T) { }) } -type caseUint16[in safecast.Type] struct { - name string - input in - want uint16 -} - -func assertUint16OK[in safecast.Type](t *testing.T, tests []caseUint16[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToUint16(tt.input) - assertNoError(t, err) - assertEqual(t, tt.want, got) - }) - } -} - -func assertUint16Error[in safecast.Type](t *testing.T, tests []caseUint16[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Helper() - - got, err := safecast.ToUint16(tt.input) - requireErrorIs(t, err, safecast.ErrConversionIssue) - assertEqual(t, tt.want, got) - }) - } -} - func TestToUint16(t *testing.T) { t.Run("from int", func(t *testing.T) { assertUint16OK(t, []caseUint16[int]{ @@ -723,36 +538,6 @@ func TestToUint16(t *testing.T) { }) } -type caseInt32[in safecast.Type] struct { - name string - input in - want int32 -} - -func assertInt32OK[in safecast.Type](t *testing.T, tests []caseInt32[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToInt32(tt.input) - assertNoError(t, err) - assertEqual(t, tt.want, got) - }) - } -} - -func assertInt32Error[in safecast.Type](t *testing.T, tests []caseInt32[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToInt32(tt.input) - requireErrorIs(t, err, safecast.ErrConversionIssue) - assertEqual(t, tt.want, got) - }) - } -} - func TestToInt32(t *testing.T) { t.Run("from int", func(t *testing.T) { assertInt32OK(t, []caseInt32[int]{ @@ -761,10 +546,8 @@ func TestToInt32(t *testing.T) { {name: "negative within range", input: -10000, want: -10000}, }) - assertInt32Error(t, []caseInt32[int]{ - {name: "positive out of range", input: math.MaxInt32 + 1}, - {name: "negative out of range", input: math.MinInt32 - 1}, - }) + // There are extra checks in [TestToInt32_64bit] + // the tests are separated because they cannot work on i386 }) t.Run("from int8", func(t *testing.T) { @@ -876,38 +659,6 @@ func TestToInt32(t *testing.T) { }) } -type caseUint32[in safecast.Type] struct { - name string - input in - want uint32 -} - -func assertUint32OK[in safecast.Type](t *testing.T, tests []caseUint32[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToUint32(tt.input) - assertNoError(t, err) - assertEqual(t, tt.want, got) - }) - } -} - -func assertUint32Error[in safecast.Type](t *testing.T, tests []caseUint32[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Helper() - - got, err := safecast.ToUint32(tt.input) - requireErrorIs(t, err, safecast.ErrConversionIssue) - assertEqual(t, tt.want, got) - }) - } -} - func TestToUint32(t *testing.T) { t.Run("from int", func(t *testing.T) { assertUint32OK(t, []caseUint32[int]{ @@ -916,7 +667,8 @@ func TestToUint32(t *testing.T) { }) assertUint32Error(t, []caseUint32[int]{ - {name: "positive out of range", input: math.MaxUint32 + 1}, + // There are extra checks in [TestToUint32_64bit] + // the tests are separated because they cannot work on i386 {name: "negative value", input: -1}, }) }) @@ -972,9 +724,8 @@ func TestToUint32(t *testing.T) { {name: "positive within range", input: 100, want: 100}, }) - assertUint32Error(t, []caseUint32[uint]{ - {name: "positive out of range", input: math.MaxUint32 + 1}, - }) + // There are extra checks in [TestToUint32_64bit] + // the tests are separated because they cannot work on i386 }) t.Run("from uint8", func(t *testing.T) { @@ -1035,36 +786,6 @@ func TestToUint32(t *testing.T) { }) } -type caseInt64[in safecast.Type] struct { - name string - input in - want int64 -} - -func assertInt64OK[in safecast.Type](t *testing.T, tests []caseInt64[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToInt64(tt.input) - assertNoError(t, err) - assertEqual(t, tt.want, got) - }) - } -} - -func assertInt64Error[in safecast.Type](t *testing.T, tests []caseInt64[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToInt64(tt.input) - requireErrorIs(t, err, safecast.ErrConversionIssue) - assertEqual(t, tt.want, got) - }) - } -} - func TestToInt64(t *testing.T) { t.Run("from int", func(t *testing.T) { assertInt64OK(t, []caseInt64[int]{ @@ -1112,9 +833,8 @@ func TestToInt64(t *testing.T) { {name: "positive within range", input: 100, want: 100}, }) - assertInt64Error(t, []caseInt64[uint]{ - {name: "positive out of range", input: math.MaxInt64 + 1}, - }) + // There are extra checks in [TestToInt64_64bit] + // the tests are separated because they cannot work on i386 }) t.Run("from uint8", func(t *testing.T) { @@ -1192,36 +912,6 @@ func TestToInt64(t *testing.T) { }) } -type caseUint64[in safecast.Type] struct { - name string - input in - want uint64 -} - -func assertUint64OK[in safecast.Type](t *testing.T, tests []caseUint64[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToUint64(tt.input) - assertNoError(t, err) - assertEqual(t, tt.want, got) - }) - } -} - -func assertUint64Error[in safecast.Type](t *testing.T, tests []caseUint64[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToUint64(tt.input) - requireErrorIs(t, err, safecast.ErrConversionIssue) - assertEqual(t, tt.want, got) - }) - } -} - func TestToUint64(t *testing.T) { t.Run("from int", func(t *testing.T) { assertUint64OK(t, []caseUint64[int]{ @@ -1342,36 +1032,6 @@ func TestToUint64(t *testing.T) { }) } -type caseInt[in safecast.Type] struct { - name string - input in - want int -} - -func assertIntOK[in safecast.Type](t *testing.T, tests []caseInt[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToInt(tt.input) - assertNoError(t, err) - assertEqual(t, tt.want, got) - }) - } -} - -func assertIntError[in safecast.Type](t *testing.T, tests []caseInt[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToInt(tt.input) - requireErrorIs(t, err, safecast.ErrConversionIssue) - assertEqual(t, tt.want, got) - }) - } -} - func TestToInt(t *testing.T) { t.Run("from int", func(t *testing.T) { assertIntOK(t, []caseInt[int]{ @@ -1419,9 +1079,8 @@ func TestToInt(t *testing.T) { {name: "positive within range", input: 100, want: 100}, }) - assertIntError(t, []caseInt[uint]{ - {name: "positive out of range", input: math.MaxInt64 + 1}, - }) + // There are extra checks in [TestToInt_64bit] + // the tests are separated because they cannot work on i386 }) t.Run("from uint8", func(t *testing.T) { @@ -1477,7 +1136,8 @@ func TestToInt(t *testing.T) { {name: "zero", input: 0.0, want: 0}, {name: "rounded value", input: 1.1, want: 1}, {name: "positive within range", input: 10000.9, want: 10000}, - {name: "math.MinInt64", input: math.MinInt64, want: math.MinInt64}, // pass because of float imprecision + // There are extra checks in [TestToInt_64bit] + // the tests are separated because they cannot work on i386 }) assertIntError(t, []caseInt[float64]{ @@ -1492,36 +1152,6 @@ func TestToInt(t *testing.T) { }) } -type caseUint[in safecast.Type] struct { - name string - input in - want uint -} - -func assertUintOK[in safecast.Type](t *testing.T, tests []caseUint[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToUint(tt.input) - assertNoError(t, err) - assertEqual(t, tt.want, got) - }) - } -} - -func assertUintError[in safecast.Type](t *testing.T, tests []caseUint[in]) { - t.Helper() - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := safecast.ToUint(tt.input) - requireErrorIs(t, err, safecast.ErrConversionIssue) - assertEqual(t, tt.want, got) - }) - } -} - func TestToUint(t *testing.T) { t.Run("from int", func(t *testing.T) { assertUintOK(t, []caseUint[int]{ diff --git a/examples_32bit_test.go b/examples_32bit_test.go new file mode 100644 index 0000000..b18b500 --- /dev/null +++ b/examples_32bit_test.go @@ -0,0 +1,39 @@ +//go:build i386 || arm + +package safecast_test + +// The tests in examples_test.go are the ones that are not architecture dependent +// The tests in examples_32bit_test.go are for 32-bit systems +// The tests in examples_64bit_test.go are for 64-bit systems +// +// The architecture dependent files cover the difference when dealing with [safecast.ToInt] +// The error message is different on 32-bit and 64-bit systems +// The max is 9223372036854775807 on 64-bit and 2147483647 on 32-bit +// +// The examples could have been skipped for 32-bit systems, +// but I wanted the Examples to be launched on this architecture. + +import ( + "fmt" + "math" + + "github.com/ccoveille/go-safecast" +) + +func ExampleToInt() { + a := uint64(42) + i, err := safecast.ToInt(a) + if err != nil { + fmt.Println(err) + } + fmt.Println(i) + + b := float32(math.MaxFloat32) + _, err = safecast.ToInt(b) + if err != nil { + fmt.Println(err) + } + // Output: + // 42 + // conversion issue: 3.4028235e+38 (float32) is greater than 2147483647 (int): maximum value for this type exceeded +} diff --git a/examples_64bit_test.go b/examples_64bit_test.go new file mode 100644 index 0000000..b0df5b9 --- /dev/null +++ b/examples_64bit_test.go @@ -0,0 +1,39 @@ +//go:build !386 && !arm + +package safecast_test + +// The tests in examples_test.go are the ones that are not architecture dependent +// The tests in examples_32bit_test.go are for 32-bit systems +// The tests in examples_64bit_test.go are for 64-bit systems +// +// The architecture dependent files cover the difference when dealing with [safecast.ToInt] +// The error message is different on 32-bit and 64-bit systems +// The max is 9223372036854775807 on 64-bit and 2147483647 on 32-bit +// +// The examples could have been skipped for 32-bit systems, +// but I wanted the Examples to be launched on this architecture. + +import ( + "fmt" + "math" + + "github.com/ccoveille/go-safecast" +) + +func ExampleToInt() { + a := uint64(42) + i, err := safecast.ToInt(a) + if err != nil { + fmt.Println(err) + } + fmt.Println(i) + + b := float32(math.MaxFloat32) + _, err = safecast.ToInt(b) + if err != nil { + fmt.Println(err) + } + // Output: + // 42 + // conversion issue: 3.4028235e+38 (float32) is greater than 9223372036854775807 (int): maximum value for this type exceeded +} diff --git a/examples_test.go b/examples_test.go index bc39094..edbb0a0 100644 --- a/examples_test.go +++ b/examples_test.go @@ -7,6 +7,17 @@ import ( "github.com/ccoveille/go-safecast" ) +// The tests in examples_test.go are the ones that are not architecture dependent +// The tests in examples_32bit_test.go are for 32-bit systems +// The tests in examples_64bit_test.go are for 64-bit systems +// +// The architecture dependent files cover the difference when dealing with [safecast.ToInt] +// The error message is different on 32-bit and 64-bit systems +// The max is 9223372036854775807 on 64-bit and 2147483647 on 32-bit +// +// The examples could have been skipped for 32-bit systems, +// but I wanted the Examples to be launched on this architecture. + func ExampleToInt8() { a := float64(42.42) i, err := safecast.ToInt8(a) @@ -87,14 +98,14 @@ func ExampleToInt32() { } fmt.Println(i) - a = int(math.MaxUint32 + 1) - _, err = safecast.ToInt32(a) + b := uint32(math.MaxInt32) + 1 + _, err = safecast.ToInt32(b) if err != nil { fmt.Println(err) } // Output: // 42 - // conversion issue: 4294967296 (int) is greater than 2147483647 (int32): maximum value for this type exceeded + // conversion issue: 2147483648 (uint32) is greater than 2147483647 (int32): maximum value for this type exceeded } func ExampleToUint32() { @@ -151,24 +162,6 @@ func ExampleToUint64() { // conversion issue: -1 (int8) is less than 0 (uint64): minimum value for this type exceeded } -func ExampleToInt() { - a := uint64(42) - i, err := safecast.ToInt(a) - if err != nil { - fmt.Println(err) - } - fmt.Println(i) - - a = uint64(math.MaxInt64) + 1 - _, err = safecast.ToInt(a) - if err != nil { - fmt.Println(err) - } - // Output: - // 42 - // conversion issue: 9223372036854775808 (uint64) is greater than 9223372036854775807 (int): maximum value for this type exceeded -} - func ExampleToUint() { a := int8(42) i, err := safecast.ToUint(a)