Skip to content

Commit 6eeb263

Browse files
committed
test(core): add coverage for lock and map-copy race fixes
Fixes: #3247
1 parent d8c2b5d commit 6eeb263

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

cluster/router/chain/chain_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package chain
19+
20+
import (
21+
"sync"
22+
"testing"
23+
)
24+
25+
import (
26+
"github.com/stretchr/testify/assert"
27+
)
28+
29+
import (
30+
clusterpkg "dubbo.apache.org/dubbo-go/v3/cluster/cluster"
31+
"dubbo.apache.org/dubbo-go/v3/common"
32+
"dubbo.apache.org/dubbo-go/v3/protocol/base"
33+
)
34+
35+
func TestRouteFiltersByServiceKey(t *testing.T) {
36+
chain := &RouterChain{}
37+
38+
invokerA := newMockInvoker("com.test.ServiceA")
39+
invokerB := newMockInvoker("com.test.ServiceB")
40+
chain.SetInvokers([]base.Invoker{invokerA, invokerB})
41+
42+
urlA := common.NewURLWithOptions(common.WithInterface("com.test.ServiceA"))
43+
result := chain.Route(urlA, nil)
44+
45+
assert.Len(t, result, 1)
46+
assert.Equal(t, invokerA.GetURL().ServiceKey(), result[0].GetURL().ServiceKey())
47+
}
48+
49+
func TestRouteFallsBackToAllInvokersWhenNoMatch(t *testing.T) {
50+
chain := &RouterChain{}
51+
52+
invokerA := newMockInvoker("com.test.ServiceA")
53+
invokerB := newMockInvoker("com.test.ServiceB")
54+
chain.SetInvokers([]base.Invoker{invokerA, invokerB})
55+
56+
urlC := common.NewURLWithOptions(common.WithInterface("com.test.ServiceC"))
57+
result := chain.Route(urlC, nil)
58+
59+
assert.Len(t, result, 2)
60+
}
61+
62+
func TestRouteConcurrentReadWrite(t *testing.T) {
63+
chain := &RouterChain{}
64+
chain.SetInvokers([]base.Invoker{newMockInvoker("com.test.ServiceA")})
65+
66+
urlA := common.NewURLWithOptions(common.WithInterface("com.test.ServiceA"))
67+
68+
var wg sync.WaitGroup
69+
for i := 0; i < 20; i++ {
70+
wg.Add(1)
71+
go func(idx int) {
72+
defer wg.Done()
73+
for j := 0; j < 100; j++ {
74+
if j%2 == 0 {
75+
chain.SetInvokers([]base.Invoker{newMockInvoker("com.test.ServiceA"), newMockInvoker("com.test.ServiceB")})
76+
} else {
77+
_ = chain.Route(urlA, nil)
78+
}
79+
}
80+
}(i)
81+
}
82+
wg.Wait()
83+
}
84+
85+
func newMockInvoker(interfaceName string) base.Invoker {
86+
url := common.NewURLWithOptions(
87+
common.WithProtocol("tri"),
88+
common.WithIp("127.0.0.1"),
89+
common.WithPort("20000"),
90+
common.WithInterface(interfaceName),
91+
)
92+
return clusterpkg.NewMockInvoker(url, 1)
93+
}

config/service_map_copy_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package config
19+
20+
import (
21+
"testing"
22+
)
23+
24+
import (
25+
"github.com/stretchr/testify/assert"
26+
)
27+
28+
type providerServiceForMapCopy struct{}
29+
30+
func (p *providerServiceForMapCopy) Reference() string {
31+
return "providerServiceForMapCopy"
32+
}
33+
34+
type consumerServiceForMapCopy struct{}
35+
36+
func (c *consumerServiceForMapCopy) Reference() string {
37+
return "consumerServiceForMapCopy"
38+
}
39+
40+
func TestGetProviderServiceMapReturnsCopy(t *testing.T) {
41+
service := &providerServiceForMapCopy{}
42+
SetProviderService(service)
43+
44+
original := GetProviderServiceMap()
45+
assert.Contains(t, original, service.Reference())
46+
47+
delete(original, service.Reference())
48+
49+
again := GetProviderServiceMap()
50+
assert.Contains(t, again, service.Reference())
51+
}
52+
53+
func TestGetConsumerServiceMapReturnsCopy(t *testing.T) {
54+
service := &consumerServiceForMapCopy{}
55+
SetConsumerService(service)
56+
57+
original := GetConsumerServiceMap()
58+
assert.Contains(t, original, service.Reference())
59+
60+
delete(original, service.Reference())
61+
62+
again := GetConsumerServiceMap()
63+
assert.Contains(t, again, service.Reference())
64+
}

dubbo_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,24 @@ import (
2727

2828
import (
2929
"dubbo.apache.org/dubbo-go/v3/client"
30+
"dubbo.apache.org/dubbo-go/v3/common"
3031
"dubbo.apache.org/dubbo-go/v3/common/constant"
3132
"dubbo.apache.org/dubbo-go/v3/registry"
3233
"dubbo.apache.org/dubbo-go/v3/server"
3334
)
3435

36+
type providerServiceForLockTest struct{}
37+
38+
func (p *providerServiceForLockTest) Reference() string {
39+
return "providerServiceForLockTest"
40+
}
41+
42+
type consumerServiceForLockTest struct{}
43+
44+
func (c *consumerServiceForLockTest) Reference() string {
45+
return "consumerServiceForLockTest"
46+
}
47+
3548
// TestIndependentConfig tests the configurations of the `instance`, `client`, and `server` are independent.
3649
func TestIndependentConfig(t *testing.T) {
3750
// instance configuration
@@ -96,3 +109,52 @@ func TestIndependentConfig(t *testing.T) {
96109
panic(err)
97110
}
98111
}
112+
113+
func TestSetProviderServiceStoresDefinition(t *testing.T) {
114+
proLock.Lock()
115+
original := providerServices
116+
providerServices = make(map[string]*server.ServiceDefinition, len(original))
117+
for k, v := range original {
118+
providerServices[k] = v
119+
}
120+
proLock.Unlock()
121+
122+
t.Cleanup(func() {
123+
proLock.Lock()
124+
providerServices = original
125+
proLock.Unlock()
126+
})
127+
128+
svc := &providerServiceForLockTest{}
129+
SetProviderService(svc)
130+
131+
proLock.RLock()
132+
defer proLock.RUnlock()
133+
def, ok := providerServices[common.GetReference(svc)]
134+
assert.True(t, ok)
135+
assert.Same(t, svc, def.Handler)
136+
}
137+
138+
func TestGetConsumerConnectionUnderReadLock(t *testing.T) {
139+
conLock.Lock()
140+
original := consumerServices
141+
consumerServices = make(map[string]*client.ClientDefinition, len(original))
142+
for k, v := range original {
143+
consumerServices[k] = v
144+
}
145+
conLock.Unlock()
146+
147+
t.Cleanup(func() {
148+
conLock.Lock()
149+
consumerServices = original
150+
conLock.Unlock()
151+
})
152+
153+
svc := &consumerServiceForLockTest{}
154+
key := common.GetReference(svc)
155+
consumerServices[key] = &client.ClientDefinition{Svc: svc}
156+
157+
conn, err := GetConsumerConnection(key)
158+
assert.Nil(t, conn)
159+
assert.Error(t, err)
160+
}

0 commit comments

Comments
 (0)