Skip to content

Commit

Permalink
Merge pull request #22 from lainio/assert-no-error
Browse files Browse the repository at this point in the history
assert no error
  • Loading branch information
lainio authored Jan 15, 2024
2 parents 5ddb751 + c7af375 commit 9b9a851
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 120 deletions.
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

0 comments on commit 9b9a851

Please sign in to comment.