Skip to content

Commit 7b47779

Browse files
committed
Improve code cov by adding various tests
1 parent 0227f4b commit 7b47779

13 files changed

+1349
-7
lines changed

active_tcp_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
package ice
88

99
import (
10+
"context"
1011
"fmt"
12+
"io"
1113
"net"
1214
"net/netip"
1315
"sync/atomic"
@@ -290,3 +292,121 @@ func TestActiveTCP_Respect_NetworkTypes(t *testing.T) {
290292
require.NoError(t, tcpListener.Close())
291293
require.Equal(t, uint64(0), atomic.LoadUint64(&incomingTCPCount))
292294
}
295+
296+
func TestNewActiveTCPConn_LocalAddrError_EarlyReturn(t *testing.T) {
297+
defer test.CheckRoutines(t)()
298+
299+
logger := logging.NewDefaultLoggerFactory().NewLogger("ice")
300+
301+
// an invalid local address so getTCPAddrOnInterface fails at ResolveTCPAddr.
302+
ctx, cancel := context.WithCancel(context.Background())
303+
defer cancel()
304+
305+
ra := netip.MustParseAddrPort("127.0.0.1:1")
306+
307+
a := newActiveTCPConn(ctx, "this_is_not_a_valid_addr", ra, logger)
308+
309+
require.NotNil(t, a)
310+
require.True(t, a.closed.Load(), "should be closed on early return error")
311+
la := a.LocalAddr()
312+
require.NotNil(t, la)
313+
}
314+
315+
func TestActiveTCPConn_ReadLoop_BufferWriteError(t *testing.T) {
316+
defer test.CheckRoutines(t)()
317+
318+
tcpListener, err := net.Listen("tcp", "127.0.0.1:0") // nolint: noctx
319+
require.NoError(t, err)
320+
defer func() { _ = tcpListener.Close() }()
321+
322+
ra := netip.MustParseAddrPort(tcpListener.Addr().String())
323+
logger := logging.NewDefaultLoggerFactory().NewLogger("ice")
324+
325+
ctx, cancel := context.WithCancel(context.Background())
326+
defer cancel()
327+
328+
a := newActiveTCPConn(ctx, "127.0.0.1:0", ra, logger)
329+
require.NotNil(t, a)
330+
331+
srvConn, err := tcpListener.Accept()
332+
require.NoError(t, err)
333+
334+
require.NoError(t, a.readBuffer.Close())
335+
336+
_, err = writeStreamingPacket(srvConn, []byte("ping"))
337+
require.NoError(t, err)
338+
339+
require.NoError(t, a.Close())
340+
_ = srvConn.Close()
341+
}
342+
343+
func TestActiveTCPConn_WriteLoop_WriteStreamingError(t *testing.T) {
344+
defer test.CheckRoutines(t)()
345+
346+
tcpListener, err := net.Listen("tcp", "127.0.0.1:0") // nolint: noctx
347+
require.NoError(t, err)
348+
defer func() { _ = tcpListener.Close() }()
349+
350+
ra := netip.MustParseAddrPort(tcpListener.Addr().String())
351+
logger := logging.NewDefaultLoggerFactory().NewLogger("ice")
352+
353+
ctx, cancel := context.WithCancel(context.Background())
354+
defer cancel()
355+
356+
a := newActiveTCPConn(ctx, "127.0.0.1:0", ra, logger)
357+
require.NotNil(t, a)
358+
359+
srvConn, err := tcpListener.Accept()
360+
require.NoError(t, err)
361+
362+
_ = srvConn.Close()
363+
364+
_, _ = a.WriteTo([]byte("data"), nil)
365+
366+
require.NoError(t, a.Close())
367+
}
368+
369+
func TestActiveTCPConn_LocalAddr_DefaultWhenUnset(t *testing.T) {
370+
defer test.CheckRoutines(t)()
371+
defer test.TimeOut(3 * time.Second).Stop()
372+
373+
ctx, cancel := context.WithCancel(context.Background())
374+
defer cancel()
375+
376+
invalidLocal := "127.0.0.1:65536"
377+
remote := netip.MustParseAddrPort("127.0.0.1:1")
378+
379+
log := logging.NewDefaultLoggerFactory().NewLogger("ice")
380+
381+
a := newActiveTCPConn(ctx, invalidLocal, remote, log)
382+
require.NotNil(t, a)
383+
require.True(t, a.closed.Load(), "expected early-return closed state")
384+
385+
la := a.LocalAddr()
386+
ta, ok := la.(*net.TCPAddr)
387+
require.True(t, ok, "LocalAddr() should return *net.TCPAddr")
388+
require.Nil(t, ta.IP, "fallback *net.TCPAddr should be zero value (nil IP)")
389+
require.Equal(t, 0, ta.Port, "fallback *net.TCPAddr should be zero value (port 0)")
390+
require.Equal(t, "", ta.Zone, "fallback *net.TCPAddr should be zero value (empty zone)")
391+
}
392+
393+
func TestActiveTCPConn_SetDeadlines_ReturnEOF(t *testing.T) {
394+
defer test.CheckRoutines(t)()
395+
396+
ctx, cancel := context.WithCancel(context.Background())
397+
defer cancel()
398+
399+
invalidLocal := "127.0.0.1:65536"
400+
remote := netip.MustParseAddrPort("127.0.0.1:1")
401+
log := logging.NewDefaultLoggerFactory().NewLogger("ice")
402+
403+
a := newActiveTCPConn(ctx, invalidLocal, remote, log)
404+
require.NotNil(t, a)
405+
require.True(t, a.closed.Load(), "expected early-return closed state")
406+
407+
err := a.SetReadDeadline(time.Now())
408+
require.ErrorIs(t, err, io.EOF)
409+
410+
err = a.SetWriteDeadline(time.Now())
411+
require.ErrorIs(t, err, io.EOF)
412+
}

addr_test.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
2+
// SPDX-License-Identifier: MIT
3+
4+
//go:build !js
5+
// +build !js
6+
7+
package ice
8+
9+
import (
10+
"net"
11+
"net/netip"
12+
"testing"
13+
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
// A net.Addr type that parseAddr doesn't handle.
18+
type unknownAddr struct{}
19+
20+
func (unknownAddr) Network() string { return "unknown" }
21+
func (unknownAddr) String() string { return "unknown-addr" }
22+
23+
func TestParseAddrFromIface_ErrFromParseAddr(t *testing.T) {
24+
in := unknownAddr{}
25+
ip, port, nt, err := parseAddrFromIface(in, "eth0")
26+
27+
require.Error(t, err, "expected error from parseAddr for unknown net.Addr type")
28+
require.Zero(t, port)
29+
require.Zero(t, nt)
30+
require.True(t, !ip.IsValid(), "ip should be zero value when error is returned")
31+
}
32+
33+
func TestParseAddr_ErrorBranches(t *testing.T) {
34+
t.Run("IPNet invalid IP -> error", func(t *testing.T) {
35+
// length 1 slice -> ipAddrToNetIP fails
36+
_, _, _, err := parseAddr(&net.IPNet{IP: net.IP{1}})
37+
var convErr ipConvertError
38+
require.ErrorAs(t, err, &convErr)
39+
})
40+
41+
t.Run("IPAddr invalid IP -> error", func(t *testing.T) {
42+
_, _, _, err := parseAddr(&net.IPAddr{IP: net.IP{1}, Zone: "eth0"})
43+
var convErr ipConvertError
44+
require.ErrorAs(t, err, &convErr)
45+
})
46+
47+
t.Run("UDPAddr invalid IP -> error", func(t *testing.T) {
48+
_, _, _, err := parseAddr(&net.UDPAddr{IP: net.IP{1}, Port: 3478})
49+
var convErr ipConvertError
50+
require.ErrorAs(t, err, &convErr)
51+
})
52+
53+
t.Run("TCPAddr invalid IP -> error", func(t *testing.T) {
54+
_, _, _, err := parseAddr(&net.TCPAddr{IP: net.IP{1}, Port: 3478})
55+
var convErr ipConvertError
56+
require.ErrorAs(t, err, &convErr)
57+
})
58+
59+
t.Run("Unknown net.Addr type -> addrParseError", func(t *testing.T) {
60+
_, _, _, err := parseAddr(unknownAddr{})
61+
var ap addrParseError
62+
require.ErrorAs(t, err, &ap)
63+
})
64+
}
65+
66+
func TestParseAddr_IPAddr_Success(t *testing.T) {
67+
ip := net.ParseIP("fe80::1")
68+
require.NotNil(t, ip)
69+
70+
gotIP, port, nt, err := parseAddr(&net.IPAddr{IP: ip, Zone: "lo0"})
71+
require.NoError(t, err)
72+
require.Equal(t, 0, port)
73+
require.Equal(t, NetworkType(0), nt)
74+
require.True(t, gotIP.Is6())
75+
require.Equal(t, "lo0", gotIP.Zone())
76+
require.Equal(t, 0, gotIP.Compare(netip.MustParseAddr("fe80::1%lo0").Unmap()))
77+
}
78+
79+
func TestAddrParseError_Error(t *testing.T) {
80+
e := addrParseError{addr: &net.TCPAddr{}}
81+
require.Equal(t,
82+
"do not know how to parse address type *net.TCPAddr",
83+
e.Error(),
84+
)
85+
}
86+
87+
func TestIPConvertError_Error(t *testing.T) {
88+
e := ipConvertError{ip: []byte("bad-ip")}
89+
require.Equal(t,
90+
"failed to convert IP 'bad-ip' to netip.Addr",
91+
e.Error(),
92+
)
93+
}
94+
95+
func TestIPAddrToNetIP_Error_InvalidBytes(t *testing.T) {
96+
bad := []byte{1} // invalid length -> AddrFromSlice returns ok=false
97+
got, err := ipAddrToNetIP(bad, "")
98+
require.Equal(t, netip.Addr{}, got, "should return zero addr on error")
99+
require.Error(t, err)
100+
require.IsType(t, ipConvertError{}, err)
101+
require.Contains(t, err.Error(), "failed to convert IP")
102+
}
103+
104+
func TestIPAddrToNetIP_OK_IPv4(t *testing.T) {
105+
ipv4 := []byte{1, 2, 3, 4}
106+
got, err := ipAddrToNetIP(ipv4, "")
107+
require.NoError(t, err)
108+
require.True(t, got.Is4())
109+
110+
want := netip.AddrFrom4([4]byte{1, 2, 3, 4})
111+
require.Equal(t, want, got)
112+
}
113+
114+
func TestAddrEqual_FirstParseError(t *testing.T) {
115+
a := unknownAddr{}
116+
b := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 9999}
117+
118+
require.False(t, addrEqual(a, b))
119+
}
120+
121+
func TestAddrEqual_SecondParseError(t *testing.T) {
122+
a := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 9999}
123+
b := unknownAddr{}
124+
125+
require.False(t, addrEqual(a, b))
126+
}
127+
128+
func TestAddrEqual_SameTypeIPPort(t *testing.T) {
129+
a := &net.UDPAddr{IP: net.IPv4(10, 0, 0, 1), Port: 4242}
130+
b := &net.UDPAddr{IP: net.IPv4(10, 0, 0, 1), Port: 4242}
131+
132+
require.True(t, addrEqual(a, b))
133+
}

agent_handlers_test.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/pion/transport/v3/test"
1111
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
1213
)
1314

1415
func TestConnectionStateNotifier(t *testing.T) {
@@ -70,3 +71,119 @@ func TestConnectionStateNotifier(t *testing.T) {
7071
notifer.Close(true)
7172
})
7273
}
74+
75+
func TestHandlerNotifier_Close_AlreadyClosed(t *testing.T) {
76+
defer test.CheckRoutines(t)()
77+
78+
notifier := &handlerNotifier{
79+
connectionStateFunc: func(ConnectionState) {},
80+
candidateFunc: func(Candidate) {},
81+
candidatePairFunc: func(*CandidatePair) {},
82+
done: make(chan struct{}),
83+
}
84+
85+
// first close
86+
notifier.Close(false)
87+
88+
isClosed := func(ch <-chan struct{}) bool {
89+
select {
90+
case <-ch:
91+
return true
92+
default:
93+
return false
94+
}
95+
}
96+
assert.True(t, isClosed(notifier.done), "expected h.done to be closed after first Close")
97+
98+
// second close should hit `case <-h.done` and return immediately
99+
// without blocking on the WaitGroup.
100+
finished := make(chan struct{}, 1)
101+
go func() {
102+
notifier.Close(true)
103+
close(finished)
104+
}()
105+
106+
assert.Eventually(t, func() bool {
107+
select {
108+
case <-finished:
109+
return true
110+
default:
111+
return false
112+
}
113+
}, 250*time.Millisecond, 10*time.Millisecond, "second Close(true) did not return promptly")
114+
115+
// ensure still closed afterwards
116+
assert.True(t, isClosed(notifier.done), "expected h.done to remain closed after second Close")
117+
118+
// sanity: no enqueues should start after close.
119+
require.False(t, notifier.running)
120+
require.Zero(t, len(notifier.connectionStates))
121+
require.Zero(t, len(notifier.candidates))
122+
require.Zero(t, len(notifier.selectedCandidatePairs))
123+
}
124+
125+
func TestHandlerNotifier_EnqueueConnectionState_AfterClose(t *testing.T) {
126+
defer test.CheckRoutines(t)()
127+
128+
connCh := make(chan struct{}, 1)
129+
notifier := &handlerNotifier{
130+
connectionStateFunc: func(ConnectionState) { connCh <- struct{}{} },
131+
done: make(chan struct{}),
132+
}
133+
134+
notifier.Close(false)
135+
notifier.EnqueueConnectionState(ConnectionStateConnected)
136+
137+
assert.Never(t, func() bool {
138+
select {
139+
case <-connCh:
140+
return true
141+
default:
142+
return false
143+
}
144+
}, 250*time.Millisecond, 10*time.Millisecond, "connectionStateFunc should not be called after close")
145+
}
146+
147+
func TestHandlerNotifier_EnqueueCandidate_AfterClose(t *testing.T) {
148+
defer test.CheckRoutines(t)()
149+
150+
candidateCh := make(chan struct{}, 1)
151+
h := &handlerNotifier{
152+
candidateFunc: func(Candidate) { candidateCh <- struct{}{} },
153+
done: make(chan struct{}),
154+
}
155+
156+
h.Close(false)
157+
h.EnqueueCandidate(nil)
158+
159+
assert.Never(t, func() bool {
160+
select {
161+
case <-candidateCh:
162+
return true
163+
default:
164+
return false
165+
}
166+
}, 250*time.Millisecond, 10*time.Millisecond, "candidateFunc should not be called after close")
167+
}
168+
169+
func TestHandlerNotifier_EnqueueSelectedCandidatePair_AfterClose(t *testing.T) {
170+
defer test.CheckRoutines(t)()
171+
172+
pairCh := make(chan struct{}, 1)
173+
h := &handlerNotifier{
174+
candidatePairFunc: func(*CandidatePair) { pairCh <- struct{}{} },
175+
done: make(chan struct{}),
176+
}
177+
178+
h.Close(false)
179+
h.EnqueueSelectedCandidatePair(nil)
180+
181+
assert.Never(t, func() bool {
182+
select {
183+
case <-pairCh:
184+
return true
185+
default:
186+
return false
187+
}
188+
}, 250*time.Millisecond, 10*time.Millisecond, "candidatePairFunc should not be called after close")
189+
}

0 commit comments

Comments
 (0)