Pull request: dhcpd: fix ip ranges

Updates #2541.

Squashed commit of the following:

commit c81299991876f48836d24872d9145331a0bc9e6e
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Mar 16 18:10:07 2021 +0300

    agherr: imp docs

commit f43a5f5cde0ea16dd38dd533e16e415a1d306cb2
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Mar 16 17:35:59 2021 +0300

    all: imp err handling, fix code

commit ed26ad0ff53882725f7747264f8094e6fb9b0423
Author: Ainar Garipov <A.Garipov@AdGuard.COM>
Date:   Tue Mar 16 12:24:17 2021 +0300

    dhcpd: fix ip ranges
This commit is contained in:
Ainar Garipov
2021-03-16 19:11:32 +03:00
parent e6a8fe452c
commit 9736123483
13 changed files with 463 additions and 137 deletions

View File

@@ -1,5 +1,4 @@
// Package agherr contains the extended error type, and the function for
// wrapping several errors.
// Package agherr contains AdGuard Home's error handling helpers.
package agherr
import (
@@ -23,8 +22,10 @@ type manyError struct {
}
// Many wraps several errors and returns a single error.
func Many(message string, underlying ...error) error {
err := &manyError{
//
// TODO(a.garipov): Add formatting to message.
func Many(message string, underlying ...error) (err error) {
err = &manyError{
message: message,
underlying: underlying,
}
@@ -33,7 +34,7 @@ func Many(message string, underlying ...error) error {
}
// Error implements the error interface for *manyError.
func (e *manyError) Error() string {
func (e *manyError) Error() (msg string) {
switch len(e.underlying) {
case 0:
return e.message
@@ -58,7 +59,7 @@ func (e *manyError) Error() string {
}
// Unwrap implements the hidden errors.wrapper interface for *manyError.
func (e *manyError) Unwrap() error {
func (e *manyError) Unwrap() (err error) {
if len(e.underlying) == 0 {
return nil
}
@@ -71,3 +72,38 @@ func (e *manyError) Unwrap() error {
type wrapper interface {
Unwrap() error
}
// Annotate annotates the error with the message, unless the error is nil. This
// is a helper function to simplify code like this:
//
// func (f *foo) doStuff(s string) (err error) {
// defer func() {
// if err != nil {
// err = fmt.Errorf("bad foo string %q: %w", s, err)
// }
// }()
//
// // …
// }
//
// Instead, write:
//
// func (f *foo) doStuff(s string) (err error) {
// defer agherr.Annotate("bad foo string %q: %w", &err, s)
//
// // …
// }
//
// msg must contain the final ": %w" verb.
func Annotate(msg string, errPtr *error, args ...interface{}) {
if errPtr == nil {
return
}
err := *errPtr
if err != nil {
args = append(args, err)
*errPtr = fmt.Errorf(msg, args...)
}
}

View File

@@ -6,30 +6,32 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestError_Error(t *testing.T) {
testCases := []struct {
err error
name string
want string
err error
}{{
err: Many("a"),
name: "simple",
want: "a",
err: Many("a"),
}, {
err: Many("a", errors.New("b")),
name: "wrapping",
want: "a: b",
err: Many("a", errors.New("b")),
}, {
err: Many("a", errors.New("b"), errors.New("c"), errors.New("d")),
name: "wrapping several",
want: "a: b (hidden: c, d)",
err: Many("a", errors.New("b"), errors.New("c"), errors.New("d")),
}, {
err: Many("a", Many("b", errors.New("c"), errors.New("d"))),
name: "wrapping wrapper",
want: "a: b: c (hidden: d)",
err: Many("a", Many("b", errors.New("c"), errors.New("d"))),
}}
for _, tc := range testCases {
assert.Equal(t, tc.want, tc.err.Error(), tc.name)
}
@@ -43,33 +45,78 @@ func TestError_Unwrap(t *testing.T) {
errWrapped
errNil
)
errs := []error{
errSimple: errors.New("a"),
errWrapped: fmt.Errorf("err: %w", errors.New("nested")),
errNil: nil,
}
testCases := []struct {
name string
want error
wrapped error
name string
}{{
name: "simple",
want: errs[errSimple],
wrapped: Many("a", errs[errSimple]),
name: "simple",
}, {
name: "nested",
want: errs[errWrapped],
wrapped: Many("b", errs[errWrapped]),
name: "nested",
}, {
name: "nil passed",
want: errs[errNil],
wrapped: Many("c", errs[errNil]),
name: "nil passed",
}, {
name: "nil not passed",
want: nil,
wrapped: Many("d"),
name: "nil not passed",
}}
for _, tc := range testCases {
assert.Equal(t, tc.want, errors.Unwrap(tc.wrapped), tc.name)
}
}
func TestAnnotate(t *testing.T) {
const s = "1234"
const wantMsg = `bad string "1234": test`
// Don't use const, because we can't take a pointer of a constant.
var errTest error = Error("test")
t.Run("nil", func(t *testing.T) {
var errPtr *error
assert.NotPanics(t, func() {
Annotate("bad string %q: %w", errPtr, s)
})
})
t.Run("non_nil", func(t *testing.T) {
errPtr := &errTest
assert.NotPanics(t, func() {
Annotate("bad string %q: %w", errPtr, s)
})
require.NotNil(t, errPtr)
err := *errPtr
require.NotNil(t, err)
assert.Equal(t, wantMsg, err.Error())
})
t.Run("defer", func(t *testing.T) {
f := func() (err error) {
defer Annotate("bad string %q: %w", &errTest, s)
return errTest
}
err := f()
require.NotNil(t, err)
assert.Equal(t, wantMsg, err.Error())
})
}