remove unneeded thirdparty packages (#10871)
* remove unneeded thirdparty packages Remove unnecessary packages from `thirdparty` in repo. - Remove `thirdparty/assert` (replaced by `github.com/stretchr/testify/require`) - Remove `thirdparty/dir` (replacd by `misc/fsutil`) - Remove `thirdparty/notifier` (unused)
Andrew Gillis committed
Jul 16, 2025 at 01:53 UTC
bb58ca4737cc2740901a3d3e4322fe78ebfc7d1c
11 files changed
+59
-541
cmd/ipfswatch/ipfswatch_test.go
+4
-4
@@ -6,11 +6,11 @@ package main
6
import (
7
"testing"
8
9
- "github.com/ipfs/kubo/thirdparty/assert"
9
+ "github.com/stretchr/testify/require"
10
)
11
12
func TestIsHidden(t *testing.T) {
13
- assert.True(IsHidden("bar/.git"), t, "dirs beginning with . should be recognized as hidden")
14
- assert.False(IsHidden("."), t, ". for current dir should not be considered hidden")
15
- assert.False(IsHidden("bar/baz"), t, "normal dirs should not be hidden")
13
+ require.True(t, IsHidden("bar/.git"), "dirs beginning with . should be recognized as hidden")
14
+ require.False(t, IsHidden("."), ". for current dir should not be considered hidden")
15
+ require.False(t, IsHidden("bar/baz"), "normal dirs should not be hidden")
16
}
core/corehttp/p2p_proxy_test.go
+6
-11
@@ -5,9 +5,8 @@ import (
5
"strings"
6
"testing"
7
8
- "github.com/ipfs/kubo/thirdparty/assert"
9
-
8
protocol "github.com/libp2p/go-libp2p/core/protocol"
9
+ "github.com/stretchr/testify/require"
10
)
11
12
type TestCase struct {
@@ -29,12 +28,10 @@ func TestParseRequest(t *testing.T) {
28
req, _ := http.NewRequest(http.MethodGet, url, strings.NewReader(""))
29
30
parsed, err := parseRequest(req)
32
- if err != nil {
33
- t.Fatal(err)
34
- }
35
- assert.True(parsed.httpPath == tc.path, t, "proxy request path")
36
- assert.True(parsed.name == protocol.ID(tc.name), t, "proxy request name")
37
- assert.True(parsed.target == tc.target, t, "proxy request peer-id")
31
+ require.NoError(t, err)
32
+ require.Equal(t, tc.path, parsed.httpPath, "proxy request path")
33
+ require.Equal(t, protocol.ID(tc.name), parsed.name, "proxy request name")
34
+ require.Equal(t, tc.target, parsed.target, "proxy request peer-id")
35
}
36
}
37
@@ -49,8 +46,6 @@ func TestParseRequestInvalidPath(t *testing.T) {
46
req, _ := http.NewRequest(http.MethodGet, url, strings.NewReader(""))
47
48
_, err := parseRequest(req)
52
- if err == nil {
53
- t.Fail()
54
- }
49
+ require.Error(t, err)
50
}
51
}
docs/changelogs/v0.37.md
+9
@@ -11,6 +11,7 @@ This release was brought to you by the [Interplanetary Shipyard](https://ipship
11
- [Overview](#overview)
12
- [🔦 Highlights](#-highlights)
13
- [Clear provide queue when reprovide strategy changes](#clear-provide-queue-when-reprovide-strategy-changes)
14
+ - [Remove unnecessary packages from thirdparty](#remove-unnecessary-packages-from-thirdparty)
15
- [📦️ Important dependency updates](#-important-dependency-updates)
16
- [📝 Changelog](#-changelog)
17
- [👨👩👧👦 Contributors](#-contributors)
@@ -30,6 +31,14 @@ A new `ipfs provide clear` command also allows manual queue clearing for debuggi
31
> [!NOTE]
32
> Upgrading to Kubo 0.37 will automatically clear any preexisting provide queue. The next time `Reprovider.Interval` hits, `Reprovider.Strategy` will be executed on a clean slate, ensuring consistent behavior with your current configuration.
33
34
+#### Remove unnecessary packages from thirdparty
35
+
36
+Removed unnecessary packages from the `thirdparty` area of kubo repositroy.
37
+
38
+- Removed `thirdparty/assert` (replaced by `github.com/stretchr/testify/require`)
39
+- Removed `thirdparty/dir` (replaced by `misc/fsutil)`
40
+- Removed `thirdparty/notifier` (unused)
41
+
42
#### 📦️ Important dependency updates
43
44
- update `boxo` to [v0.34.0](https://github.com/ipfs/boxo/releases/tag/v0.34.0)
repo/common/common_test.go
+6
-6
@@ -3,7 +3,7 @@ package common
3
import (
4
"testing"
5
6
- "github.com/ipfs/kubo/thirdparty/assert"
6
+ "github.com/stretchr/testify/require"
7
)
8
9
func TestMapMergeDeepReturnsNew(t *testing.T) {
@@ -15,7 +15,7 @@ func TestMapMergeDeepReturnsNew(t *testing.T) {
15
16
MapMergeDeep(leftMap, rightMap)
17
18
- assert.True(leftMap["A"] == "Hello World", t, "MapMergeDeep should return a new map instance")
18
+ require.Equal(t, "Hello World", leftMap["A"], "MapMergeDeep should return a new map instance")
19
}
20
21
func TestMapMergeDeepNewKey(t *testing.T) {
@@ -46,7 +46,7 @@ func TestMapMergeDeepNewKey(t *testing.T) {
46
}
47
*/
48
49
- assert.True(result["B"] == "Bar", t, "New keys in right map should exist in resulting map")
49
+ require.Equal(t, "Bar", result["B"], "New keys in right map should exist in resulting map")
50
}
51
52
func TestMapMergeDeepRecursesOnMaps(t *testing.T) {
@@ -92,8 +92,8 @@ func TestMapMergeDeepRecursesOnMaps(t *testing.T) {
92
*/
93
94
resultA := result["A"].(map[string]interface{})
95
- assert.True(resultA["B"] == "A value!", t, "Unaltered values should not change")
96
- assert.True(resultA["C"] == "A different value!", t, "Nested values should be altered")
95
+ require.Equal(t, "A value!", resultA["B"], "Unaltered values should not change")
96
+ require.Equal(t, "A different value!", resultA["C"], "Nested values should be altered")
97
}
98
99
func TestMapMergeDeepRightNotAMap(t *testing.T) {
@@ -128,5 +128,5 @@ func TestMapMergeDeepRightNotAMap(t *testing.T) {
128
}
129
*/
130
131
- assert.True(result["A"] == "Not a map!", t, "Right values that are not a map should be set on the result")
131
+ require.Equal(t, "Not a map!", result["A"], "Right values that are not a map should be set on the result")
132
}
repo/fsrepo/fsrepo.go
+1
-2
@@ -16,7 +16,6 @@ import (
16
keystore "github.com/ipfs/boxo/keystore"
17
repo "github.com/ipfs/kubo/repo"
18
"github.com/ipfs/kubo/repo/common"
19
- dir "github.com/ipfs/kubo/thirdparty/dir"
19
rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
20
21
ds "github.com/ipfs/go-datastore"
@@ -192,7 +191,7 @@ func open(repoPath string, userConfigFilePath string) (repo.Repo, error) {
191
}
192
193
// check repo path, then check all constituent parts.
195
- if err := dir.Writable(r.path); err != nil {
194
+ if err := fsutil.DirWritable(r.path); err != nil {
195
return nil, err
196
}
197
repo/fsrepo/fsrepo_test.go
+32
-33
@@ -7,17 +7,16 @@ import (
7
"path/filepath"
8
"testing"
9
10
- "github.com/ipfs/kubo/thirdparty/assert"
11
-
10
datastore "github.com/ipfs/go-datastore"
11
config "github.com/ipfs/kubo/config"
12
+ "github.com/stretchr/testify/require"
13
)
14
15
func TestInitIdempotence(t *testing.T) {
16
t.Parallel()
17
path := t.TempDir()
18
for i := 0; i < 10; i++ {
20
- assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t, "multiple calls to init should succeed")
19
+ require.NoError(t, Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), "multiple calls to init should succeed")
20
}
21
}
22
@@ -32,78 +31,78 @@ func TestCanManageReposIndependently(t *testing.T) {
31
pathB := t.TempDir()
32
33
t.Log("initialize two repos")
35
- assert.Nil(Init(pathA, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t, "a", "should initialize successfully")
36
- assert.Nil(Init(pathB, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t, "b", "should initialize successfully")
34
+ require.NoError(t, Init(pathA, &config.Config{Datastore: config.DefaultDatastoreConfig()}), "a", "should initialize successfully")
35
+ require.NoError(t, Init(pathB, &config.Config{Datastore: config.DefaultDatastoreConfig()}), "b", "should initialize successfully")
36
37
t.Log("ensure repos initialized")
39
- assert.True(IsInitialized(pathA), t, "a should be initialized")
40
- assert.True(IsInitialized(pathB), t, "b should be initialized")
38
+ require.True(t, IsInitialized(pathA), "a should be initialized")
39
+ require.True(t, IsInitialized(pathB), "b should be initialized")
40
41
t.Log("open the two repos")
42
repoA, err := Open(pathA)
44
- assert.Nil(err, t, "a")
43
+ require.NoError(t, err, "a")
44
repoB, err := Open(pathB)
46
- assert.Nil(err, t, "b")
45
+ require.NoError(t, err, "b")
46
47
t.Log("close and remove b while a is open")
49
- assert.Nil(repoB.Close(), t, "close b")
50
- assert.Nil(Remove(pathB), t, "remove b")
48
+ require.NoError(t, repoB.Close(), "close b")
49
+ require.NoError(t, Remove(pathB), "remove b")
50
51
t.Log("close and remove a")
53
- assert.Nil(repoA.Close(), t)
54
- assert.Nil(Remove(pathA), t)
52
+ require.NoError(t, repoA.Close())
53
+ require.NoError(t, Remove(pathA))
54
}
55
56
func TestDatastoreGetNotAllowedAfterClose(t *testing.T) {
57
t.Parallel()
58
path := t.TempDir()
59
61
- assert.True(!IsInitialized(path), t, "should NOT be initialized")
62
- assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t, "should initialize successfully")
60
+ require.False(t, IsInitialized(path), "should NOT be initialized")
61
+ require.NoError(t, Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), "should initialize successfully")
62
r, err := Open(path)
64
- assert.Nil(err, t, "should open successfully")
63
+ require.NoError(t, err, "should open successfully")
64
65
k := "key"
66
data := []byte(k)
68
- assert.Nil(r.Datastore().Put(context.Background(), datastore.NewKey(k), data), t, "Put should be successful")
67
+ require.NoError(t, r.Datastore().Put(context.Background(), datastore.NewKey(k), data), "Put should be successful")
68
70
- assert.Nil(r.Close(), t)
69
+ require.NoError(t, r.Close())
70
_, err = r.Datastore().Get(context.Background(), datastore.NewKey(k))
72
- assert.Err(err, t, "after closer, Get should be fail")
71
+ require.Error(t, err, "after closer, Get should be fail")
72
}
73
74
func TestDatastorePersistsFromRepoToRepo(t *testing.T) {
75
t.Parallel()
76
path := t.TempDir()
77
79
- assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t)
78
+ require.NoError(t, Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}))
79
r1, err := Open(path)
81
- assert.Nil(err, t)
80
+ require.NoError(t, err)
81
82
k := "key"
83
expected := []byte(k)
85
- assert.Nil(r1.Datastore().Put(context.Background(), datastore.NewKey(k), expected), t, "using first repo, Put should be successful")
86
- assert.Nil(r1.Close(), t)
84
+ require.NoError(t, r1.Datastore().Put(context.Background(), datastore.NewKey(k), expected), "using first repo, Put should be successful")
85
+ require.NoError(t, r1.Close())
86
87
r2, err := Open(path)
89
- assert.Nil(err, t)
88
+ require.NoError(t, err)
89
actual, err := r2.Datastore().Get(context.Background(), datastore.NewKey(k))
91
- assert.Nil(err, t, "using second repo, Get should be successful")
92
- assert.Nil(r2.Close(), t)
93
- assert.True(bytes.Equal(expected, actual), t, "data should match")
90
+ require.NoError(t, err, "using second repo, Get should be successful")
91
+ require.NoError(t, r2.Close())
92
+ require.True(t, bytes.Equal(expected, actual), "data should match")
93
}
94
95
func TestOpenMoreThanOnceInSameProcess(t *testing.T) {
96
t.Parallel()
97
path := t.TempDir()
99
- assert.Nil(Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}), t)
98
+ require.NoError(t, Init(path, &config.Config{Datastore: config.DefaultDatastoreConfig()}))
99
100
r1, err := Open(path)
102
- assert.Nil(err, t, "first repo should open successfully")
101
+ require.NoError(t, err, "first repo should open successfully")
102
r2, err := Open(path)
104
- assert.Nil(err, t, "second repo should open successfully")
105
- assert.True(r1 == r2, t, "second open returns same value")
103
+ require.NoError(t, err, "second repo should open successfully")
104
+ require.Equal(t, r1, r2, "second open returns same value")
105
107
- assert.Nil(r1.Close(), t)
108
- assert.Nil(r2.Close(), t)
106
+ require.NoError(t, r1.Close())
107
+ require.NoError(t, r2.Close())
108
}
thirdparty/README.md
+1
-4
@@ -1,5 +1,2 @@
1
-thirdparty consists of Golang packages that contain no go-ipfs dependencies and
2
-may be vendored ipfs/go-ipfs at a later date.
3
-
1
packages under this directory _must not_ import packages under
5
-`ipfs/go-ipfs` that are not also under `thirdparty`.
2
+`ipfs/kubo` that are not also under `thirdparty`.
thirdparty/assert/assert.go
deleted
-25
@@ -1,25 +0,0 @@
1
-package assert
2
-
3
-import "testing"
4
-
5
-func Nil(err error, t *testing.T, msgs ...string) {
6
- if err != nil {
7
- t.Fatal(msgs, "error:", err)
8
- }
9
-}
10
-
11
-func True(v bool, t *testing.T, msgs ...string) {
12
- if !v {
13
- t.Fatal(msgs)
14
- }
15
-}
16
-
17
-func False(v bool, t *testing.T, msgs ...string) {
18
- True(!v, t, msgs...)
19
-}
20
-
21
-func Err(err error, t *testing.T, msgs ...string) {
22
- if err == nil {
23
- t.Fatal(msgs, "error:", err)
24
- }
25
-}
thirdparty/dir/dir.go
deleted
-25
@@ -1,25 +0,0 @@
1
-package dir
2
-
3
-// TODO move somewhere generic
4
-
5
-import (
6
- "errors"
7
- "os"
8
- "path/filepath"
9
-)
10
-
11
-// Writable ensures the directory exists and is writable.
12
-func Writable(path string) error {
13
- // Construct the path if missing
14
- if err := os.MkdirAll(path, os.ModePerm); err != nil {
15
- return err
16
- }
17
- // Check the directory is writable
18
- if f, err := os.Create(filepath.Join(path, "._check_writable")); err == nil {
19
- f.Close()
20
- os.Remove(f.Name())
21
- } else {
22
- return errors.New("'" + path + "' is not writable")
23
- }
24
- return nil
25
-}
thirdparty/notifier/notifier.go
deleted
-142
@@ -1,142 +0,0 @@
1
-// Package notifier provides a simple notification dispatcher
2
-// meant to be embedded in larger structures who wish to allow
3
-// clients to sign up for event notifications.
4
-package notifier
5
-
6
-import (
7
- "sync"
8
-
9
- process "github.com/jbenet/goprocess"
10
- ratelimit "github.com/jbenet/goprocess/ratelimit"
11
-)
12
-
13
-// Notifiee is a generic interface. Clients implement
14
-// their own Notifiee interfaces to ensure type-safety
15
-// of notifications:
16
-//
17
-// type RocketNotifiee interface{
18
-// Countdown(r Rocket, countdown time.Duration)
19
-// LiftedOff(Rocket)
20
-// ReachedOrbit(Rocket)
21
-// Detached(Rocket, Capsule)
22
-// Landed(Rocket)
23
-// }
24
-type Notifiee interface{}
25
-
26
-// Notifier is a notification dispatcher. It's meant
27
-// to be composed, and its zero-value is ready to be used.
28
-//
29
-// type Rocket struct {
30
-// notifier notifier.Notifier
31
-// }
32
-type Notifier struct {
33
- mu sync.RWMutex // guards notifiees
34
- nots map[Notifiee]struct{}
35
- lim *ratelimit.RateLimiter
36
-}
37
-
38
-// RateLimited returns a rate limited Notifier. only limit goroutines
39
-// will be spawned. If limit is zero, no rate limiting happens. This
40
-// is the same as `Notifier{}`.
41
-func RateLimited(limit int) *Notifier {
42
- n := &Notifier{}
43
- if limit > 0 {
44
- n.lim = ratelimit.NewRateLimiter(process.Background(), limit)
45
- }
46
- return n
47
-}
48
-
49
-// Notify signs up Notifiee e for notifications. This function
50
-// is meant to be called behind your own type-safe function(s):
51
-//
52
-// // generic function for pattern-following
53
-// func (r *Rocket) Notify(n Notifiee) {
54
-// r.notifier.Notify(n)
55
-// }
56
-//
57
-// // or as part of other functions
58
-// func (r *Rocket) Onboard(a Astronaut) {
59
-// r.astronauts = append(r.austronauts, a)
60
-// r.notifier.Notify(a)
61
-// }
62
-func (n *Notifier) Notify(e Notifiee) {
63
- n.mu.Lock()
64
- if n.nots == nil { // so that zero-value is ready to be used.
65
- n.nots = make(map[Notifiee]struct{})
66
- }
67
- n.nots[e] = struct{}{}
68
- n.mu.Unlock()
69
-}
70
-
71
-// StopNotify stops notifying Notifiee e. This function
72
-// is meant to be called behind your own type-safe function(s):
73
-//
74
-// // generic function for pattern-following
75
-// func (r *Rocket) StopNotify(n Notifiee) {
76
-// r.notifier.StopNotify(n)
77
-// }
78
-//
79
-// // or as part of other functions
80
-// func (r *Rocket) Detach(c Capsule) {
81
-// r.notifier.StopNotify(c)
82
-// r.capsule = nil
83
-// }
84
-func (n *Notifier) StopNotify(e Notifiee) {
85
- n.mu.Lock()
86
- if n.nots != nil { // so that zero-value is ready to be used.
87
- delete(n.nots, e)
88
- }
89
- n.mu.Unlock()
90
-}
91
-
92
-// NotifyAll messages the notifier's notifiees with a given notification.
93
-// This is done by calling the given function with each notifiee. It is
94
-// meant to be called with your own type-safe notification functions:
95
-//
96
-// func (r *Rocket) Launch() {
97
-// r.notifyAll(func(n Notifiee) {
98
-// n.Launched(r)
99
-// })
100
-// }
101
-//
102
-// // make it private so only you can use it. This function is necessary
103
-// // to make sure you only up-cast in one place. You control who you added
104
-// // to be a notifiee. If Go adds generics, maybe we can get rid of this
105
-// // method but for now it is like wrapping a type-less container with
106
-// // a type safe interface.
107
-// func (r *Rocket) notifyAll(notify func(Notifiee)) {
108
-// r.notifier.NotifyAll(func(n notifier.Notifiee) {
109
-// notify(n.(Notifiee))
110
-// })
111
-// }
112
-//
113
-// Note well: each notification is launched in its own goroutine, so they
114
-// can be processed concurrently, and so that whatever the notification does
115
-// it _never_ blocks out the client. This is so that consumers _cannot_ add
116
-// hooks into your object that block you accidentally.
117
-func (n *Notifier) NotifyAll(notify func(Notifiee)) {
118
- n.mu.Lock()
119
- defer n.mu.Unlock()
120
-
121
- if n.nots == nil { // so that zero-value is ready to be used.
122
- return
123
- }
124
-
125
- // no rate limiting.
126
- if n.lim == nil {
127
- for notifiee := range n.nots {
128
- go notify(notifiee)
129
- }
130
- return
131
- }
132
-
133
- // with rate limiting.
134
- n.lim.Go(func(worker process.Process) {
135
- for notifiee := range n.nots {
136
- notifiee := notifiee // rebind for loop data races
137
- n.lim.LimitedGo(func(worker process.Process) {
138
- notify(notifiee)
139
- })
140
- }
141
- })
142
-}
thirdparty/notifier/notifier_test.go
deleted
-289
@@ -1,289 +0,0 @@
1
-package notifier
2
-
3
-import (
4
- "fmt"
5
- "sync"
6
- "testing"
7
- "time"
8
-)
9
-
10
-// test data structures.
11
-type Router struct {
12
- queue chan Packet
13
- notifier Notifier
14
-}
15
-
16
-type Packet struct{}
17
-
18
-type RouterNotifiee interface {
19
- Enqueued(*Router, Packet)
20
- Forwarded(*Router, Packet)
21
- Dropped(*Router, Packet)
22
-}
23
-
24
-func (r *Router) Notify(n RouterNotifiee) {
25
- r.notifier.Notify(n)
26
-}
27
-
28
-func (r *Router) StopNotify(n RouterNotifiee) {
29
- r.notifier.StopNotify(n)
30
-}
31
-
32
-func (r *Router) notifyAll(notify func(n RouterNotifiee)) {
33
- r.notifier.NotifyAll(func(n Notifiee) {
34
- notify(n.(RouterNotifiee))
35
- })
36
-}
37
-
38
-func (r *Router) Receive(p Packet) {
39
- select {
40
- case r.queue <- p: // enqueued
41
- r.notifyAll(func(n RouterNotifiee) {
42
- n.Enqueued(r, p)
43
- })
44
-
45
- default: // drop
46
- r.notifyAll(func(n RouterNotifiee) {
47
- n.Dropped(r, p)
48
- })
49
- }
50
-}
51
-
52
-func (r *Router) Forward() {
53
- p := <-r.queue
54
- r.notifyAll(func(n RouterNotifiee) {
55
- n.Forwarded(r, p)
56
- })
57
-}
58
-
59
-type Metrics struct {
60
- enqueued int
61
- forwarded int
62
- dropped int
63
- received chan struct{}
64
- sync.Mutex
65
-}
66
-
67
-func (m *Metrics) Enqueued(*Router, Packet) {
68
- m.Lock()
69
- m.enqueued++
70
- m.Unlock()
71
- if m.received != nil {
72
- m.received <- struct{}{}
73
- }
74
-}
75
-
76
-func (m *Metrics) Forwarded(*Router, Packet) {
77
- m.Lock()
78
- m.forwarded++
79
- m.Unlock()
80
- if m.received != nil {
81
- m.received <- struct{}{}
82
- }
83
-}
84
-
85
-func (m *Metrics) Dropped(*Router, Packet) {
86
- m.Lock()
87
- m.dropped++
88
- m.Unlock()
89
- if m.received != nil {
90
- m.received <- struct{}{}
91
- }
92
-}
93
-
94
-func (m *Metrics) String() string {
95
- m.Lock()
96
- defer m.Unlock()
97
- return fmt.Sprintf("%d enqueued, %d forwarded, %d in queue, %d dropped",
98
- m.enqueued, m.forwarded, m.enqueued-m.forwarded, m.dropped)
99
-}
100
-
101
-func TestNotifies(t *testing.T) {
102
- m := Metrics{received: make(chan struct{})}
103
- r := Router{queue: make(chan Packet, 10)}
104
- r.Notify(&m)
105
-
106
- for i := 0; i < 10; i++ {
107
- r.Receive(Packet{})
108
- <-m.received
109
- if m.enqueued != (1 + i) {
110
- t.Error("not notifying correctly", m.enqueued, 1+i)
111
- }
112
-
113
- }
114
-
115
- for i := 0; i < 10; i++ {
116
- r.Receive(Packet{})
117
- <-m.received
118
- if m.enqueued != 10 {
119
- t.Error("not notifying correctly", m.enqueued, 10)
120
- }
121
- if m.dropped != (1 + i) {
122
- t.Error("not notifying correctly", m.dropped, 1+i)
123
- }
124
- }
125
-}
126
-
127
-func TestStopsNotifying(t *testing.T) {
128
- m := Metrics{received: make(chan struct{})}
129
- r := Router{queue: make(chan Packet, 10)}
130
- r.Notify(&m)
131
-
132
- for i := 0; i < 5; i++ {
133
- r.Receive(Packet{})
134
- <-m.received
135
- if m.enqueued != (1 + i) {
136
- t.Error("not notifying correctly")
137
- }
138
- }
139
-
140
- r.StopNotify(&m)
141
-
142
- for i := 0; i < 5; i++ {
143
- r.Receive(Packet{})
144
- select {
145
- case <-m.received:
146
- t.Error("did not stop notifying")
147
- default:
148
- }
149
- if m.enqueued != 5 {
150
- t.Error("did not stop notifying")
151
- }
152
- }
153
-}
154
-
155
-func TestThreadsafe(t *testing.T) {
156
- N := 1000
157
- r := Router{queue: make(chan Packet, 10)}
158
- m1 := Metrics{received: make(chan struct{})}
159
- m2 := Metrics{received: make(chan struct{})}
160
- m3 := Metrics{received: make(chan struct{})}
161
- r.Notify(&m1)
162
- r.Notify(&m2)
163
- r.Notify(&m3)
164
-
165
- var n int
166
- var wg sync.WaitGroup
167
- for i := 0; i < N; i++ {
168
- n++
169
- wg.Add(1)
170
- go func() {
171
- defer wg.Done()
172
- r.Receive(Packet{})
173
- }()
174
-
175
- if i%3 == 0 {
176
- n++
177
- wg.Add(1)
178
- go func() {
179
- defer wg.Done()
180
- r.Forward()
181
- }()
182
- }
183
- }
184
-
185
- // drain queues
186
- for i := 0; i < (n * 3); i++ {
187
- select {
188
- case <-m1.received:
189
- case <-m2.received:
190
- case <-m3.received:
191
- }
192
- }
193
-
194
- wg.Wait()
195
-
196
- // counts should be correct and all agree. and this should
197
- // run fine under `go test -race -cpu=5`
198
-
199
- t.Log("m1", m1.String())
200
- t.Log("m2", m2.String())
201
- t.Log("m3", m3.String())
202
-
203
- if m1.String() != m2.String() || m2.String() != m3.String() {
204
- t.Error("counts disagree")
205
- }
206
-}
207
-
208
-type highwatermark struct {
209
- mu sync.Mutex
210
- mark int
211
- limit int
212
- errs chan error
213
-}
214
-
215
-func (m *highwatermark) incr() {
216
- m.mu.Lock()
217
- m.mark++
218
- // fmt.Println("incr", m.mark)
219
- if m.mark > m.limit {
220
- m.errs <- fmt.Errorf("went over limit: %d/%d", m.mark, m.limit)
221
- }
222
- m.mu.Unlock()
223
-}
224
-
225
-func (m *highwatermark) decr() {
226
- m.mu.Lock()
227
- m.mark--
228
- // fmt.Println("decr", m.mark)
229
- if m.mark < 0 {
230
- m.errs <- fmt.Errorf("went under zero: %d/%d", m.mark, m.limit)
231
- }
232
- m.mu.Unlock()
233
-}
234
-
235
-func TestLimited(t *testing.T) {
236
- timeout := 10 * time.Second // huge timeout.
237
- limit := 9
238
-
239
- hwm := highwatermark{limit: limit, errs: make(chan error, 100)}
240
- n := RateLimited(limit) // will stop after 3 rounds
241
- n.Notify(1)
242
- n.Notify(2)
243
- n.Notify(3)
244
-
245
- entr := make(chan struct{})
246
- exit := make(chan struct{})
247
- done := make(chan struct{})
248
- go func() {
249
- for i := 0; i < 10; i++ {
250
- // fmt.Printf("round: %d\n", i)
251
- n.NotifyAll(func(e Notifiee) {
252
- hwm.incr()
253
- entr <- struct{}{}
254
- <-exit // wait
255
- hwm.decr()
256
- })
257
- }
258
- done <- struct{}{}
259
- }()
260
-
261
- for i := 0; i < 30; {
262
- select {
263
- case <-entr:
264
- continue // let as many enter as possible
265
- case <-time.After(1 * time.Millisecond):
266
- }
267
-
268
- // let one exit
269
- select {
270
- case <-entr:
271
- continue // in case of timing issues.
272
- case exit <- struct{}{}:
273
- case <-time.After(timeout):
274
- t.Error("got stuck")
275
- }
276
- i++
277
- }
278
-
279
- select {
280
- case <-done: // two parts done
281
- case <-time.After(timeout):
282
- t.Error("did not finish")
283
- }
284
-
285
- close(hwm.errs)
286
- for err := range hwm.errs {
287
- t.Error(err)
288
- }
289
-}