Skip to content

Commit b3d3cf5

Browse files
committed
extend unit-test and modify make test to run in container for macOS
Signed-off-by: Mohamed S. Mahmoud <mmahmoud2201@gmail.com>
1 parent ff8b6ac commit b3d3cf5

File tree

4 files changed

+290
-20
lines changed

4 files changed

+290
-20
lines changed

Makefile

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ PROTOC_ARTIFACTS := pkg/pbflow
4646
# regular expressions for excluded file patterns
4747
EXCLUDE_COVERAGE_FILES="(/cmd/)|(bpf_bpfe)|(/examples/)|(/pkg/pbflow/)"
4848

49+
# Container image for running linux tests from non-linux hosts (e.g. macOS).
50+
# Prefer matching the *host* Go toolchain version (go env GOVERSION), which tends to
51+
# be more reliable than GO_VERSION (used for generator tooling) and avoids "go: not found".
52+
HOST_GO_VERSION := $(shell go env GOVERSION 2>/dev/null | sed 's/^go//')
53+
TEST_CONTAINER_IMAGE ?= $(if $(HOST_GO_VERSION),golang:$(HOST_GO_VERSION),golang:$(GO_VERSION))
54+
4955
.DEFAULT_GOAL := help
5056

5157
# build a single arch target provided as argument
@@ -162,7 +168,29 @@ compile: ## Compile ebpf agent project
162168
.PHONY: test
163169
test: ## Test code using go test
164170
@echo "### Testing code"
165-
GOOS=$(GOOS) go test -mod vendor ./pkg/... ./cmd/... -coverpkg=./... -coverprofile cover.all.out
171+
@if [ "$$(go env GOOS)" = "linux" ]; then \
172+
go test -mod vendor ./pkg/... ./cmd/... -coverpkg=./... -coverprofile cover.all.out; \
173+
else \
174+
$(MAKE) test-container; \
175+
fi
176+
177+
.PHONY: test-container
178+
test-container: ## Run linux tests in a container (useful on macOS)
179+
@echo "### Testing in linux container ($(TEST_CONTAINER_IMAGE))"
180+
@if [ -z "$(OCI_BIN_PATH)" ]; then \
181+
echo "ERROR: docker/podman not found in PATH. Install one, or run 'make test-unit' instead."; \
182+
exit 1; \
183+
fi
184+
@$(OCI_BIN) run --rm \
185+
-u $$(id -u):$$(id -g) \
186+
-v "$$(pwd)":/src \
187+
-w /src \
188+
-e HOME=/tmp \
189+
-e GOPATH=/tmp/go \
190+
-e GOCACHE=/tmp/go-build \
191+
-e CGO_ENABLED=0 \
192+
$(TEST_CONTAINER_IMAGE) \
193+
sh -lc 'mkdir -p "$$GOPATH" "$$GOCACHE"; export PATH="/usr/local/go/bin:/go/bin:$$PATH"; command -v go >/dev/null || (echo "ERROR: go not found; PATH=$$PATH" && exit 127); go version && go test -mod vendor ./pkg/... ./cmd/... -coverpkg=./... -coverprofile cover.all.out && go test -v ./pkg/maps'
166194

167195
.PHONY: verify-maps
168196
verify-maps: ## Verify map names consistency across all sources
@@ -172,7 +200,29 @@ verify-maps: ## Verify map names consistency across all sources
172200
.PHONY: test-race
173201
test-race: ## Test code using go test -race
174202
@echo "### Testing code for race conditions"
175-
GOOS=$(GOOS) go test -race -mod vendor ./pkg/... ./cmd/...
203+
@if [ "$$(go env GOOS)" = "linux" ]; then \
204+
go test -race -mod vendor ./pkg/... ./cmd/...; \
205+
else \
206+
$(MAKE) test-race-container; \
207+
fi
208+
209+
.PHONY: test-race-container
210+
test-race-container: ## Run go test -race in a linux container (useful on macOS)
211+
@echo "### Testing code for race conditions in linux container ($(TEST_CONTAINER_IMAGE))"
212+
@if [ -z "$(OCI_BIN_PATH)" ]; then \
213+
echo "ERROR: docker/podman not found in PATH."; \
214+
exit 1; \
215+
fi
216+
@$(OCI_BIN) run --rm \
217+
-u $$(id -u):$$(id -g) \
218+
-v "$$(pwd)":/src \
219+
-w /src \
220+
-e HOME=/tmp \
221+
-e GOPATH=/tmp/go \
222+
-e GOCACHE=/tmp/go-build \
223+
-e CGO_ENABLED=1 \
224+
$(TEST_CONTAINER_IMAGE) \
225+
sh -lc 'mkdir -p "$$GOPATH" "$$GOCACHE"; export PATH="/usr/local/go/bin:/go/bin:$$PATH"; command -v go >/dev/null || (echo "ERROR: go not found; PATH=$$PATH" && exit 127); go version && go test -race -mod vendor ./pkg/... ./cmd/...'
176226

177227
.PHONY: cov-exclude-generated
178228
cov-exclude-generated:

pkg/decode/decode_protobuf_test.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package decode
22

33
import (
4+
"net"
45
"testing"
56
"time"
67

78
"github.com/netobserv/flowlogs-pipeline/pkg/config"
9+
"github.com/netobserv/netobserv-ebpf-agent/pkg/ebpf"
10+
"github.com/netobserv/netobserv-ebpf-agent/pkg/model"
811
"github.com/netobserv/netobserv-ebpf-agent/pkg/pbflow"
912
"github.com/netobserv/netobserv-ebpf-agent/pkg/utils"
1013

@@ -169,6 +172,118 @@ func TestPBFlowToMap(t *testing.T) {
169172
}, out)
170173
}
171174

175+
func TestRecordToMap_OptionalMetrics(t *testing.T) {
176+
someTime := time.Unix(1700000000, 0).UTC()
177+
makeFlow := func(withQuic bool) *model.Record {
178+
f := &model.Record{
179+
ID: ebpf.BpfFlowId{
180+
SrcIp: model.IPAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x06, 0x07, 0x08, 0x09},
181+
DstIp: model.IPAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d},
182+
SrcPort: 23000,
183+
DstPort: 443,
184+
TransportProtocol: 17,
185+
},
186+
Metrics: model.BpfFlowContent{
187+
BpfFlowMetrics: &ebpf.BpfFlowMetrics{
188+
EthProtocol: 2048,
189+
Bytes: 456,
190+
Packets: 123,
191+
},
192+
},
193+
Interfaces: []model.IntfDirUdn{model.NewIntfDirUdn("eth0", model.DirectionEgress, nil)},
194+
TimeFlowStart: someTime,
195+
TimeFlowEnd: someTime,
196+
AgentIP: net.IPv4(0x0a, 0x0b, 0x0c, 0x0d),
197+
}
198+
if withQuic {
199+
f.Metrics.QuicMetrics = &ebpf.BpfQuicMetrics{Version: 1, SeenLongHdr: 1, SeenShortHdr: 1}
200+
}
201+
return f
202+
}
203+
204+
tests := []struct {
205+
name string
206+
withQuic bool
207+
expectKeys bool
208+
}{
209+
{name: "without optional metrics", withQuic: false, expectKeys: false},
210+
{name: "with optional metrics", withQuic: true, expectKeys: true},
211+
}
212+
213+
for _, tt := range tests {
214+
t.Run(tt.name, func(t *testing.T) {
215+
out := RecordToMap(makeFlow(tt.withQuic))
216+
_, ok := out["QuicVersion"]
217+
assert.Equal(t, tt.expectKeys, ok)
218+
_, ok = out["QuicSeenLongHdr"]
219+
assert.Equal(t, tt.expectKeys, ok)
220+
_, ok = out["QuicSeenShortHdr"]
221+
assert.Equal(t, tt.expectKeys, ok)
222+
223+
if tt.expectKeys {
224+
assert.Equal(t, uint32(1), out["QuicVersion"])
225+
assert.Equal(t, uint8(1), out["QuicSeenLongHdr"])
226+
assert.Equal(t, uint8(1), out["QuicSeenShortHdr"])
227+
}
228+
})
229+
}
230+
}
231+
232+
func TestPBFlowRoundTrip_OptionalFields(t *testing.T) {
233+
now := time.Unix(1700000000, 0).UTC()
234+
235+
tests := []struct {
236+
name string
237+
withQuic bool
238+
}{
239+
{name: "without optional fields", withQuic: false},
240+
{name: "with optional fields", withQuic: true},
241+
}
242+
243+
for _, tt := range tests {
244+
t.Run(tt.name, func(t *testing.T) {
245+
in := &model.Record{
246+
ID: ebpf.BpfFlowId{
247+
TransportProtocol: 17,
248+
SrcIp: model.IPAddr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 1, 2, 3, 4},
249+
DstIp: model.IPAddr{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 5, 6, 7, 8},
250+
SrcPort: 12345,
251+
DstPort: 443,
252+
},
253+
Metrics: model.BpfFlowContent{
254+
BpfFlowMetrics: &ebpf.BpfFlowMetrics{
255+
EthProtocol: 2048,
256+
DirectionFirstSeen: 1,
257+
Bytes: 10,
258+
Packets: 1,
259+
},
260+
},
261+
TimeFlowStart: now,
262+
TimeFlowEnd: now,
263+
}
264+
if tt.withQuic {
265+
in.Metrics.QuicMetrics = &ebpf.BpfQuicMetrics{Version: 1, SeenLongHdr: 1, SeenShortHdr: 1}
266+
}
267+
268+
pb := pbflow.FlowToPB(in)
269+
if tt.withQuic {
270+
if assert.NotNil(t, pb.Quic) {
271+
assert.Equal(t, uint32(1), pb.Quic.Version)
272+
}
273+
} else {
274+
assert.Nil(t, pb.Quic)
275+
}
276+
277+
back := pbflow.PBToFlow(pb)
278+
if tt.withQuic {
279+
assert.NotNil(t, back.Metrics.QuicMetrics)
280+
} else {
281+
assert.Nil(t, back.Metrics.QuicMetrics)
282+
}
283+
})
284+
}
285+
}
286+
172287
func TestDnsRawNameToDotted(t *testing.T) {
173288
tests := []struct {
174289
name string

pkg/exporter/converters_test.go

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -101,31 +101,39 @@ func TestConversions(t *testing.T) {
101101
Dscp: 64,
102102
Sampling: 2,
103103
},
104+
QuicMetrics: &ebpf.BpfQuicMetrics{
105+
Version: 1,
106+
SeenLongHdr: 1,
107+
SeenShortHdr: 1,
108+
},
104109
},
105110
Interfaces: []model.IntfDirUdn{model.NewIntfDirUdn("eth0", model.DirectionEgress, nil)},
106111
TimeFlowStart: someTime,
107112
TimeFlowEnd: someTime,
108113
AgentIP: net.IPv4(0x0a, 0x0b, 0x0c, 0x0d),
109114
},
110115
expected: &config.GenericMap{
111-
"IfDirections": []int{1},
112-
"Bytes": 456,
113-
"SrcAddr": "6.7.8.9",
114-
"DstAddr": "10.11.12.13",
115-
"Dscp": 64,
116-
"DstMac": "0a:0b:0c:0d:0e:0f",
117-
"SrcMac": "04:05:06:07:08:09",
118-
"Etype": 2048,
119-
"Packets": 123,
120-
"Proto": 17,
121-
"Sampling": 2,
122-
"SrcPort": 23000,
123-
"DstPort": 443,
124-
"TimeFlowStartMs": someTime.UnixMilli(),
125-
"TimeFlowEndMs": someTime.UnixMilli(),
126-
"Interfaces": []string{"eth0"},
127-
"Udns": []string{""},
128-
"AgentIP": "10.11.12.13",
116+
"IfDirections": []int{1},
117+
"Bytes": 456,
118+
"SrcAddr": "6.7.8.9",
119+
"DstAddr": "10.11.12.13",
120+
"Dscp": 64,
121+
"DstMac": "0a:0b:0c:0d:0e:0f",
122+
"SrcMac": "04:05:06:07:08:09",
123+
"Etype": 2048,
124+
"Packets": 123,
125+
"Proto": 17,
126+
"Sampling": 2,
127+
"SrcPort": 23000,
128+
"DstPort": 443,
129+
"TimeFlowStartMs": someTime.UnixMilli(),
130+
"TimeFlowEndMs": someTime.UnixMilli(),
131+
"Interfaces": []string{"eth0"},
132+
"Udns": []string{""},
133+
"AgentIP": "10.11.12.13",
134+
"QuicVersion": 1,
135+
"QuicSeenLongHdr": 1,
136+
"QuicSeenShortHdr": 1,
129137
},
130138
},
131139
{

pkg/model/flow_content_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,96 @@ func TestAccumulateAdditional(t *testing.T) {
237237
}, flow)
238238
}
239239

240+
func TestAccumulateQuic(t *testing.T) {
241+
flow := BpfFlowContent{BpfFlowMetrics: &ebpf.BpfFlowMetrics{
242+
StartMonoTimeTs: 10,
243+
EndMonoTimeTs: 20,
244+
Packets: 3,
245+
}}
246+
247+
// First QUIC metric should set base timestamps and initialize QuicMetrics.
248+
flow.AccumulateQuic(&ebpf.BpfQuicMetrics{
249+
StartMonoTimeTs: 25,
250+
EndMonoTimeTs: 25,
251+
EthProtocol: 3,
252+
Version: 1,
253+
SeenLongHdr: 1,
254+
SeenShortHdr: 0,
255+
})
256+
assert.Equal(t, BpfFlowContent{
257+
BpfFlowMetrics: &ebpf.BpfFlowMetrics{StartMonoTimeTs: 10, EndMonoTimeTs: 25, Packets: 3, EthProtocol: 3},
258+
QuicMetrics: &ebpf.BpfQuicMetrics{
259+
StartMonoTimeTs: 25,
260+
EndMonoTimeTs: 25,
261+
EthProtocol: 3,
262+
Version: 1,
263+
SeenLongHdr: 1,
264+
SeenShortHdr: 0,
265+
},
266+
}, flow)
267+
268+
// Second QUIC metric should update max fields.
269+
flow.AccumulateQuic(&ebpf.BpfQuicMetrics{
270+
StartMonoTimeTs: 30,
271+
EndMonoTimeTs: 30,
272+
EthProtocol: 3,
273+
Version: 2,
274+
SeenLongHdr: 0,
275+
SeenShortHdr: 1,
276+
})
277+
assert.Equal(t, BpfFlowContent{
278+
BpfFlowMetrics: &ebpf.BpfFlowMetrics{StartMonoTimeTs: 10, EndMonoTimeTs: 30, Packets: 3, EthProtocol: 3},
279+
QuicMetrics: &ebpf.BpfQuicMetrics{
280+
StartMonoTimeTs: 25,
281+
EndMonoTimeTs: 25,
282+
EthProtocol: 3,
283+
Version: 2,
284+
SeenLongHdr: 1,
285+
SeenShortHdr: 1,
286+
},
287+
}, flow)
288+
}
289+
290+
func TestAccumulateQuic_NilNoop(t *testing.T) {
291+
flow := BpfFlowContent{BpfFlowMetrics: &ebpf.BpfFlowMetrics{
292+
StartMonoTimeTs: 10,
293+
EndMonoTimeTs: 20,
294+
Packets: 3,
295+
EthProtocol: 2048,
296+
}}
297+
before := flow
298+
flow.AccumulateQuic(nil)
299+
assert.Equal(t, before, flow)
300+
}
301+
302+
func TestAccumulateQuic_DoesNotDecrease(t *testing.T) {
303+
flow := BpfFlowContent{BpfFlowMetrics: &ebpf.BpfFlowMetrics{
304+
StartMonoTimeTs: 10,
305+
EndMonoTimeTs: 20,
306+
Packets: 3,
307+
EthProtocol: 2048,
308+
}}
309+
flow.AccumulateQuic(&ebpf.BpfQuicMetrics{
310+
StartMonoTimeTs: 25,
311+
EndMonoTimeTs: 25,
312+
EthProtocol: 2048,
313+
Version: 2,
314+
SeenLongHdr: 1,
315+
SeenShortHdr: 1,
316+
})
317+
flow.AccumulateQuic(&ebpf.BpfQuicMetrics{
318+
StartMonoTimeTs: 30,
319+
EndMonoTimeTs: 30,
320+
EthProtocol: 2048,
321+
Version: 1, // lower than existing
322+
SeenLongHdr: 0,
323+
SeenShortHdr: 0,
324+
})
325+
assert.Equal(t, uint32(2), flow.QuicMetrics.Version)
326+
assert.Equal(t, uint8(1), flow.QuicMetrics.SeenLongHdr)
327+
assert.Equal(t, uint8(1), flow.QuicMetrics.SeenShortHdr)
328+
}
329+
240330
func TestAccumulateNowBase(t *testing.T) {
241331
flow := BpfFlowContent{BpfFlowMetrics: &ebpf.BpfFlowMetrics{}}
242332
flow.AccumulateDNS(&ebpf.BpfDnsMetrics{StartMonoTimeTs: 25, EndMonoTimeTs: 25})
@@ -272,4 +362,11 @@ func TestAccumulateNowBase(t *testing.T) {
272362
BpfFlowMetrics: &ebpf.BpfFlowMetrics{StartMonoTimeTs: 25, EndMonoTimeTs: 25},
273363
AdditionalMetrics: &ebpf.BpfAdditionalMetrics{StartMonoTimeTs: 25, EndMonoTimeTs: 25},
274364
}, flow)
365+
366+
flow = BpfFlowContent{BpfFlowMetrics: &ebpf.BpfFlowMetrics{}}
367+
flow.AccumulateQuic(&ebpf.BpfQuicMetrics{StartMonoTimeTs: 25, EndMonoTimeTs: 25, EthProtocol: 3})
368+
assert.Equal(t, BpfFlowContent{
369+
BpfFlowMetrics: &ebpf.BpfFlowMetrics{StartMonoTimeTs: 25, EndMonoTimeTs: 25, EthProtocol: 3},
370+
QuicMetrics: &ebpf.BpfQuicMetrics{StartMonoTimeTs: 25, EndMonoTimeTs: 25, EthProtocol: 3},
371+
}, flow)
275372
}

0 commit comments

Comments
 (0)