Skip to content

Commit 4a72621

Browse files
committed
Cover monitor with tests
1 parent c185009 commit 4a72621

File tree

1 file changed

+258
-0
lines changed

1 file changed

+258
-0
lines changed

meshnet/monitor_test.go

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
package meshnet
2+
3+
import (
4+
"errors"
5+
"testing"
6+
"time"
7+
8+
"github.com/NordSecurity/nordvpn-linux/test/category"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/vishvananda/netlink"
11+
"golang.org/x/exp/rand"
12+
)
13+
14+
func TestNetlinkProcessMonitor_Start(t *testing.T) {
15+
category.Set(t, category.Unit)
16+
17+
tests := []struct {
18+
name string
19+
setupFn SetupFn
20+
isError bool
21+
}{
22+
{
23+
name: "fails when the setup function returns error",
24+
setupFn: failingSetup,
25+
isError: true,
26+
},
27+
{
28+
name: "succeeds",
29+
setupFn: workingSetup,
30+
isError: false,
31+
},
32+
}
33+
34+
for _, tt := range tests {
35+
t.Run(tt.name, func(t *testing.T) {
36+
monitor := NewProcMonitor(eventHandlerDummy{}, tt.setupFn)
37+
_, err := monitor.Start()
38+
39+
assert.Equal(t, err != nil, tt.isError)
40+
})
41+
}
42+
}
43+
44+
func TestNetlinkProcessMonitor_EventHandler(t *testing.T) {
45+
category.Set(t, category.Unit)
46+
47+
tests := []struct {
48+
name string
49+
eh *eventHandlerSpy
50+
correctEvents []eventType
51+
wrongEvents []eventType
52+
signalChan func(*eventHandlerSpy) chan ProcEvent
53+
callCount func(*eventHandlerSpy) int
54+
}{
55+
{
56+
name: "OnProcessStarted is called only for EXEC event",
57+
eh: newEventHandlerSpy(),
58+
correctEvents: []eventType{netlink.PROC_EVENT_EXEC},
59+
wrongEvents: []eventType{
60+
netlink.PROC_EVENT_NONE,
61+
netlink.PROC_EVENT_FORK,
62+
netlink.PROC_EVENT_UID,
63+
netlink.PROC_EVENT_GID,
64+
netlink.PROC_EVENT_SID,
65+
netlink.PROC_EVENT_PTRACE,
66+
netlink.PROC_EVENT_COMM,
67+
netlink.PROC_EVENT_COREDUMP,
68+
netlink.PROC_EVENT_EXIT,
69+
},
70+
signalChan: func(eh *eventHandlerSpy) chan ProcEvent {
71+
return eh.startedCalledSignal
72+
},
73+
callCount: func(eh *eventHandlerSpy) int {
74+
return eh.onStartedCallCount
75+
},
76+
},
77+
{
78+
name: "OnProcessStopped is called only for EXIT event",
79+
eh: newEventHandlerSpy(),
80+
correctEvents: []eventType{netlink.PROC_EVENT_EXIT},
81+
wrongEvents: []eventType{
82+
netlink.PROC_EVENT_NONE,
83+
netlink.PROC_EVENT_FORK,
84+
netlink.PROC_EVENT_UID,
85+
netlink.PROC_EVENT_GID,
86+
netlink.PROC_EVENT_SID,
87+
netlink.PROC_EVENT_PTRACE,
88+
netlink.PROC_EVENT_COMM,
89+
netlink.PROC_EVENT_COREDUMP,
90+
netlink.PROC_EVENT_EXEC,
91+
},
92+
signalChan: func(eh *eventHandlerSpy) chan ProcEvent {
93+
return eh.stoppedCalledSignal
94+
},
95+
callCount: func(eh *eventHandlerSpy) int {
96+
return eh.onStoppedCallCount
97+
},
98+
},
99+
}
100+
101+
for _, tt := range tests {
102+
t.Run(tt.name, func(t *testing.T) {
103+
channels, setupFn := openChannelsMonitorSetup()
104+
monitor := NewProcMonitor(tt.eh, setupFn)
105+
monitor.Start()
106+
assert.Zero(t, tt.eh.onStartedCallCount)
107+
assert.Zero(t, len(tt.eh.startedCalledSignal))
108+
109+
// send all correct + wrong events
110+
for _, eventType := range interleaveRandomly(tt.correctEvents, tt.wrongEvents) {
111+
channels.EventCh <- mkEvent(eventType, 1337)
112+
}
113+
114+
// receive only correct events
115+
for range tt.correctEvents {
116+
ev := <-tt.signalChan(tt.eh)
117+
assert.Equal(t, ev.PID, PID(1337))
118+
}
119+
120+
// no more events in the channel
121+
assert.Zero(t, len(tt.signalChan(tt.eh)))
122+
assert.Equal(t, tt.callCount(tt.eh), len(tt.correctEvents))
123+
})
124+
}
125+
}
126+
127+
func TestMonitorStopper_Stop(t *testing.T) {
128+
category.Set(t, category.Unit)
129+
130+
tests := []struct {
131+
name string
132+
setupFn SetupFn
133+
isError bool
134+
}{
135+
{
136+
name: "stops monitoring",
137+
setupFn: workingSetup,
138+
},
139+
}
140+
141+
for _, tt := range tests {
142+
t.Run(tt.name, func(t *testing.T) {
143+
channels, setupFn := openChannelsMonitorSetup()
144+
spy := newEventHandlerSpy()
145+
monitor := NewProcMonitor(spy, setupFn)
146+
stopper, _ := monitor.Start()
147+
// event handler is running
148+
channels.EventCh <- mkEvent(netlink.PROC_EVENT_EXEC, 1337)
149+
<-spy.startedCalledSignal
150+
151+
stopper.Stop()
152+
153+
select {
154+
case <-channels.DoneCh:
155+
// monitor was stopped
156+
case <-time.After(500 * time.Millisecond):
157+
t.Fatal("monitor stopper failed to stop the monitor")
158+
}
159+
160+
// calling [MonitorStopper.Stop] multiple times has no effect
161+
stopper.Stop()
162+
})
163+
}
164+
}
165+
166+
// event handler dummy
167+
type eventHandlerDummy struct{}
168+
169+
func (eventHandlerDummy) OnProcessStarted(ProcEvent) {}
170+
func (eventHandlerDummy) OnProcessStopped(ProcEvent) {}
171+
172+
// event handler spy
173+
type eventHandlerSpy struct {
174+
onStartedCallCount int
175+
startedCalledSignal chan ProcEvent
176+
177+
onStoppedCallCount int
178+
stoppedCalledSignal chan ProcEvent
179+
}
180+
181+
func newEventHandlerSpy() *eventHandlerSpy {
182+
return &eventHandlerSpy{
183+
startedCalledSignal: make(chan ProcEvent, 16),
184+
stoppedCalledSignal: make(chan ProcEvent, 16),
185+
}
186+
}
187+
188+
func (eh *eventHandlerSpy) OnProcessStarted(ev ProcEvent) {
189+
eh.onStartedCallCount += 1
190+
eh.startedCalledSignal <- ev
191+
}
192+
193+
func (eh *eventHandlerSpy) OnProcessStopped(ev ProcEvent) {
194+
eh.onStoppedCallCount += 1
195+
eh.stoppedCalledSignal <- ev
196+
}
197+
198+
func failingSetup() (MonitorChannels, error) {
199+
return MonitorChannels{}, errors.New("test error")
200+
}
201+
202+
func workingSetup() (MonitorChannels, error) {
203+
return MonitorChannels{
204+
EventCh: make(chan netlink.ProcEvent),
205+
DoneCh: make(chan struct{}),
206+
ErrCh: make(chan error),
207+
}, nil
208+
}
209+
210+
// msg dummy
211+
type msgStub struct {
212+
PID uint32
213+
}
214+
215+
func (m msgStub) Pid() uint32 {
216+
return m.PID
217+
}
218+
219+
func (msgStub) Tgid() uint32 {
220+
return 0 // not important
221+
}
222+
223+
type eventType = uint32
224+
225+
func openChannelsMonitorSetup() (MonitorChannels, SetupFn) {
226+
channels := MonitorChannels{
227+
EventCh: make(chan netlink.ProcEvent),
228+
DoneCh: make(chan struct{}),
229+
ErrCh: make(chan error),
230+
}
231+
232+
return channels, func() (MonitorChannels, error) {
233+
return channels, nil
234+
}
235+
}
236+
237+
func interleaveRandomly(arr1, arr2 []uint32) []uint32 {
238+
combined := append(arr1, arr2...)
239+
240+
rand.Seed(uint64(time.Now().UnixNano()))
241+
242+
rand.Shuffle(len(combined), func(i, j int) {
243+
combined[i], combined[j] = combined[j], combined[i]
244+
})
245+
246+
return combined
247+
}
248+
249+
func mkEvent(what uint32, PID uint32) netlink.ProcEvent {
250+
return netlink.ProcEvent{
251+
ProcEventHeader: netlink.ProcEventHeader{
252+
What: what,
253+
},
254+
Msg: msgStub{
255+
PID: PID,
256+
},
257+
}
258+
}

0 commit comments

Comments
 (0)