Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

assert no error #22

Merged
merged 12 commits into from
Jan 15, 2024
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

### Version history

##### 0.9.51
- `flag` package support to set `err2` and `assert` package configuration
- `err2.Catch` default mode is to log error
- cleanup and refactoring, new tests and benchmarks

##### 0.9.5 **mistake in build number: 5 < 41**
- `flag` package support to set `err2` and `assert` package configuration
- `err2.Catch` default mode is to log error
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ little error handling. But most importantly, it doesn't help developers with
> Automation is not just about efficiency but primarily about repeatability and
> resilience. -- Gregor Hohpe

Automatic error propagation is crucial because it makes your code tolerant
of the change. And, of course, it helps to make your code error-safe:
Automatic error propagation is crucial because it makes your code change
tolerant. And, of course, it helps to make your code error-safe:

![Never send a human to do a machine's job](https://www.magicalquote.com/wp-content/uploads/2013/10/Never-send-a-human-to-do-a-machines-job.jpg)

Expand Down Expand Up @@ -242,7 +242,7 @@ notExist := try.Is(r2.err, plugin.ErrNotExist)
**Note.** Any other error than `plugin.ErrNotExist` is treated as an real error:
1. `try.Is` function first checks `if err == nil`, and if yes, it returns
`false`.
2. Then it checks if `errors.Is` == `plugin.ErrNotExist` and if yes, it returns
2. Then it checks if `errors.Is(err, plugin.ErrNotExist)` and if yes, it returns
`true`.
3. Finally, it calls `try.To` for the non nil error, and we already know what then
happens: nearest `err2.Handle` gets it first.
Expand Down Expand Up @@ -527,10 +527,11 @@ Please see the full version history from [CHANGELOG](./CHANGELOG.md).

### Latest Release

##### 0.9.51
- `flag` package support to set `err2` and `assert` package configuration
- `err2.Catch` default mode is to log error
- cleanup and refactoring, new tests and benchmarks
##### 0.9.52
- `err2.Stderr` helpers for `Catch/Handle` to direct auto-logging + snippets
- `assert` package `Shorter` `Longer` helpers for automatic messages
- `asserter` package remove deprecated slow reflection based funcs
- cleanup and refactoring for sample apps

### Upcoming releases

Expand Down
127 changes: 124 additions & 3 deletions assert/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,10 @@ var (
)

const (
assertionMsg = "assertion violation"
gotWantFmt = ": got '%v', want '%v'"
assertionMsg = "assertion violation"
gotWantFmt = ": got '%v', want '%v'"
gotWantLongerFmt = ": got '%v', should be longer than '%v'"
gotWantShorterFmt = ": got '%v', should be shorter than '%v'"
)

// PushTester sets the current testing context for default asserter. This must
Expand Down Expand Up @@ -400,6 +402,32 @@ func Len(obj string, length int, a ...any) {
}
}

// Longer asserts that the length of the string is longer to the given. If not
// it panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
// current implementation of Go's type parametric functions.
func Longer(obj string, length int, a ...any) {
l := len(obj)

if l > length {
defMsg := fmt.Sprintf(assertionMsg+gotWantLongerFmt, l, length)
Default().reportAssertionFault(defMsg, a...)
}
}

// Shorter asserts that the length of the string is shorter to the given. If not
// it panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
// current implementation of Go's type parametric functions.
func Shorter(obj string, length int, a ...any) {
l := len(obj)

if l <= length {
defMsg := fmt.Sprintf(assertionMsg+gotWantShorterFmt, l, length)
Default().reportAssertionFault(defMsg, a...)
}
}

// SLen asserts that the length of the slice is equal to the given. If not it
// panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
Expand All @@ -413,6 +441,32 @@ func SLen[S ~[]T, T any](obj S, length int, a ...any) {
}
}

// SLonger asserts that the length of the slice is equal to the given. If not it
// panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
// current implementation of Go's type parametric functions.
func SLonger[S ~[]T, T any](obj S, length int, a ...any) {
l := len(obj)

if l <= length {
defMsg := fmt.Sprintf(assertionMsg+gotWantLongerFmt, l, length)
Default().reportAssertionFault(defMsg, a...)
}
}

// SShorter asserts that the length of the slice is equal to the given. If not it
// panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
// current implementation of Go's type parametric functions.
func SShorter[S ~[]T, T any](obj S, length int, a ...any) {
l := len(obj)

if l >= length {
defMsg := fmt.Sprintf(assertionMsg+gotWantShorterFmt, l, length)
Default().reportAssertionFault(defMsg, a...)
}
}

// MLen asserts that the length of the map is equal to the given. If not it
// panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
Expand All @@ -426,6 +480,71 @@ func MLen[M ~map[T]U, T comparable, U any](obj M, length int, a ...any) {
}
}

// MLonger asserts that the length of the map is longer to the given. If not it
// panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
// current implementation of Go's type parametric functions.
func MLonger[M ~map[T]U, T comparable, U any](obj M, length int, a ...any) {
l := len(obj)

if l <= length {
defMsg := fmt.Sprintf(assertionMsg+gotWantLongerFmt, l, length)
Default().reportAssertionFault(defMsg, a...)
}
}

// MShorter asserts that the length of the map is shorter to the given. If not
// it panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
// current implementation of Go's type parametric functions.
func MShorter[M ~map[T]U, T comparable, U any](obj M, length int, a ...any) {
l := len(obj)

if l >= length {
defMsg := fmt.Sprintf(assertionMsg+gotWantShorterFmt, l, length)
Default().reportAssertionFault(defMsg, a...)
}
}

// CLen asserts that the length of the chan is equal to the given. If not it
// panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
// current implementation of Go's type parametric functions.
func CLen[C ~chan T, T any](obj C, length int, a ...any) {
l := len(obj)

if l != length {
defMsg := fmt.Sprintf(assertionMsg+gotWantFmt, l, length)
Default().reportAssertionFault(defMsg, a...)
}
}

// CLonger asserts that the length of the chan is longer to the given. If not it
// panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
// current implementation of Go's type parametric functions.
func CLonger[C ~chan T, T any](obj C, length int, a ...any) {
l := len(obj)

if l <= length {
defMsg := fmt.Sprintf(assertionMsg+gotWantLongerFmt, l, length)
Default().reportAssertionFault(defMsg, a...)
}
}

// CShorter asserts that the length of the chan is shorter to the given. If not
// it panics/errors (current Asserter) with the given message. Note! This is
// reasonably fast but not as fast as 'That' because of lacking inlining for the
// current implementation of Go's type parametric functions.
func CShorter[C ~chan T, T any](obj C, length int, a ...any) {
l := len(obj)

if l >= length {
defMsg := fmt.Sprintf(assertionMsg+gotWantShorterFmt, l, length)
Default().reportAssertionFault(defMsg, a...)
}
}

// MKeyExists asserts that the map key exists. If not it panics/errors (current
// Asserter) with the given message.
func MKeyExists[M ~map[T]U, T comparable, U any](obj M, key T, a ...any) (val U) {
Expand Down Expand Up @@ -485,7 +604,9 @@ func MNotEmpty[M ~map[T]U, T comparable, U any](obj M, a ...any) {

// NoError asserts that the error is nil. If is not it panics with the given
// formatting string. Thanks to inlining, the performance penalty is equal to a
// single 'if-statement' that is almost nothing.
// single 'if-statement' that is almost nothing. Note. We recommend that you
// prefer try.To every case even in tests because they work exactly the same
// during the test runs and you can use same code for both: runtime and tests.
func NoError(err error, a ...any) {
if err != nil {
defMsg := "NoError:" + assertionMsg + ": " + err.Error()
Expand Down
26 changes: 26 additions & 0 deletions assert/assert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,32 @@ func ExampleZero() {
// Output: sample: assert_test.go:146: ExampleZero.func1(): assertion violation: got '1', want (== '0')
}

func ExampleSLonger() {
sample := func(b []byte) (err error) {
defer err2.Handle(&err, "sample")

assert.SLonger(b, 0) // ok
assert.SLonger(b, 1) // not ok
return err
}
err := sample([]byte{01}) // len = 1
fmt.Printf("%v", err)
// Output: sample: assert_test.go:160: ExampleSLonger.func1(): assertion violation: got '1', should be longer than '1'
}

func ExampleMShorter() {
sample := func(b map[byte]byte) (err error) {
defer err2.Handle(&err, "sample")

assert.MShorter(b, 1) // ok
assert.MShorter(b, 0) // not ok
return err
}
err := sample(map[byte]byte{01: 01}) // len = 1
fmt.Printf("%v", err)
// Output: sample: assert_test.go:172: ExampleMShorter.func1(): assertion violation: got '1', should be shorter than '1'
}

func assertZero(i int) {
assert.Zero(i)
}
Expand Down
103 changes: 0 additions & 103 deletions assert/asserter.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,109 +47,6 @@ const (
// every test log or result output has 4 spaces in them
const officialTestOutputPrefix = " "

// NoImplementation always fails with no implementation.
// Deprecated: use e.g. assert.NotImplemented(), only default asserter is used.
func (asserter Asserter) NoImplementation(a ...any) {
asserter.reportAssertionFault("not implemented", a...)
}

// True asserts that term is true. If not it panics with the given formatting
// string. Note! This and Truef are the most performant of all the assertion
// functions.
// Deprecated: use e.g. assert.That(), only default asserter is used.
func (asserter Asserter) True(term bool, a ...any) {
if !term {
asserter.reportAssertionFault("assertion fault", a...)
}
}

// Truef asserts that term is true. If not it panics with the given formatting
// string.
// Deprecated: use e.g. assert.That(), only default asserter is used.
func (asserter Asserter) Truef(term bool, format string, a ...any) {
if !term {
if asserter.hasStackTrace() {
debug.PrintStack(1)
}
asserter.reportPanic(fmt.Sprintf(format, a...))
}
}

// Len asserts that length of the object is equal to given. If not it
// panics/errors (current Asserter) with the given msg. Note! This is very slow
// (before we have generics). If you need performance use EqualInt. It's not so
// convenient, though.
// Deprecated: use e.g. assert.Len(), only default asserter is used.
func (asserter Asserter) Len(obj any, length int, a ...any) {
ok, l := getLen(obj)
if !ok {
panic("cannot get length")
}

if l != length {
defMsg := fmt.Sprintf("got %d, want %d", l, length)
asserter.reportAssertionFault(defMsg, a...)
}
}

// EqualInt asserts that integers are equal. If not it panics/errors (current
// Asserter) with the given msg.
// Deprecated: use e.g. assert.Equal(), only default asserter is used.
func (asserter Asserter) EqualInt(val, want int, a ...any) {
if want != val {
defMsg := fmt.Sprintf("got %d, want %d", val, want)
asserter.reportAssertionFault(defMsg, a...)
}
}

// Lenf asserts that length of the object is equal to given. If not it
// panics/errors (current Asserter) with the given msg. Note! This is very slow
// (before we have generics). If you need performance use EqualInt. It's not so
// convenient, though.
// Deprecated: use e.g. assert.Len(), only default asserter is used.
func (asserter Asserter) Lenf(obj any, length int, format string, a ...any) {
args := combineArgs(format, a)
asserter.Len(obj, length, args...)
}

// Empty asserts that length of the object is zero. If not it panics with the
// given formatting string. Note! This is slow.
// Deprecated: use e.g. assert.Empty(), only default asserter is used.
func (asserter Asserter) Empty(obj any, msg ...any) {
ok, l := getLen(obj)
if !ok {
panic("cannot get length")
}

if l != 0 {
defMsg := fmt.Sprintf("got %d, want == 0", l)
asserter.reportAssertionFault(defMsg, msg...)
}
}

// NotEmptyf asserts that length of the object greater than zero. If not it
// panics with the given formatting string. Note! This is slow.
// Deprecated: use e.g. assert.NotEmpty(), only default asserter is used.
func (asserter Asserter) NotEmptyf(obj any, format string, msg ...any) {
args := combineArgs(format, msg)
asserter.Empty(obj, args...)
}

// NotEmpty asserts that length of the object greater than zero. If not it
// panics with the given formatting string. Note! This is slow.
// Deprecated: use e.g. assert.NotEmpty(), only default asserter is used.
func (asserter Asserter) NotEmpty(obj any, msg ...any) {
ok, l := getLen(obj)
if !ok {
panic("cannot get length")
}

if l == 0 {
defMsg := fmt.Sprintf("got %d, want > 0", l)
asserter.reportAssertionFault(defMsg, msg...)
}
}

func (asserter Asserter) reportAssertionFault(defaultMsg string, a ...any) {
if asserter.hasStackTrace() {
if asserter.isUnitTesting() {
Expand Down
Loading
Loading