Skip to content

Commit 4d19e13

Browse files
authored
Add unittest infrastructure (#5)
1 parent 7f4fdab commit 4d19e13

17 files changed

+842
-76
lines changed

.azure-pipelines/build.yml

+11-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ jobs:
4141
libnl-3-dev \
4242
libnl-route-3-dev \
4343
libnl-genl-3-dev \
44-
libnl-nf-3-dev
44+
libnl-nf-3-dev \
45+
redis-server
46+
sudo sed -ri 's/^# unixsocket/unixsocket/' /etc/redis/redis.conf
47+
sudo sed -ri 's/^unixsocketperm .../unixsocketperm 777/' /etc/redis/redis.conf
48+
sudo sed -ri 's/redis-server.sock/redis.sock/' /etc/redis/redis.conf
49+
sudo service redis-server start
4550
4651
displayName: "Install dependencies"
4752
- checkout: self
@@ -94,9 +99,13 @@ jobs:
9499
- publish: $(Build.ArtifactStagingDirectory)
95100
artifact: sonic-dhcp-relay.${{ parameters.arch }}
96101
displayName: "Archive dhcp-relay debian packages"
102+
- task: PublishTestResults@2
103+
inputs:
104+
testResultsFiles: build-test/dhcp6relay-test-test-result.xml
97105
- ${{ if and(eq(parameters.arch, 'amd64'), parameters.codeCoverage) }}:
98106
- task: PublishCodeCoverageResults@1
99107
inputs:
100-
summaryFileLocation: dhcprelay-test-result.xml
108+
summaryFileLocation: build-test/dhcp6relay-test-code-coverage.xml
101109
pathToSources: $(Build.SourcesDirectory)
110+
reportDirectory: $(Build.SourcesDirectory)/build-test
102111
codeCoverageTool: 'Cobertura'

Makefile

+43-13
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,66 @@
11
RM := rm -rf
2-
DHCP6RELAY_TARGET := dhcp6relay
2+
BUILD_DIR := build
3+
BUILD_TEST_DIR := build-test
4+
DHCP6RELAY_TARGET := $(BUILD_DIR)/dhcp6relay
5+
DHCP6RELAY_TEST_TARGET := $(BUILD_TEST_DIR)/dhcp6relay-test
36
CP := cp
47
MKDIR := mkdir
58
MV := mv
9+
FIND := find
10+
GCOVR := gcovr
611
override LDLIBS += -levent -lhiredis -lswsscommon -pthread -lboost_thread -lboost_system
712
override CPPFLAGS += -Wall -std=c++17 -fPIE -I/usr/include/swss
813
override CPPFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)"
14+
CPPFLAGS_TEST := --coverage -fprofile-arcs -ftest-coverage -fprofile-generate -fsanitize=address
15+
LDLIBS_TEST := --coverage -lgtest -pthread -lstdc++fs -fsanitize=address
916
PWD := $(shell pwd)
1017

11-
all: $(DHCP6RELAY_TARGET)
18+
all: $(DHCP6RELAY_TARGET) $(DHCP6RELAY_TEST_TARGET)
19+
20+
-include src/subdir.mk
21+
-include test/subdir.mk
22+
23+
# Use different build directories based on whether it's a regular build or a
24+
# test build. This is because in the test build, code coverage is enabled,
25+
# which means the object files that get built will be different
26+
OBJS = $(SRCS:%.cpp=$(BUILD_DIR)/%.o)
27+
TEST_OBJS = $(TEST_SRCS:%.cpp=$(BUILD_TEST_DIR)/%.o)
1228

1329
ifneq ($(MAKECMDGOALS),clean)
1430
-include $(OBJS:%.o=%.d)
31+
-include $(TEST_OBJS:%.o=%.d)
1532
endif
1633

17-
-include src/subdir.mk
34+
$(BUILD_DIR)/%.o: %.cpp
35+
@mkdir -p $(@D)
36+
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
1837

1938
$(DHCP6RELAY_TARGET): $(OBJS)
2039
$(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@
2140

22-
install:
23-
$(MKDIR) -p $(DESTDIR)/usr/sbin
24-
$(MV) $(DHCP6RELAY_TARGET) $(DESTDIR)/usr/sbin
41+
$(BUILD_TEST_DIR)/%.o: %.cpp
42+
@mkdir -p $(@D)
43+
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(CPPFLAGS_TEST) -c -o $@ $<
2544

26-
deinstall:
27-
$(RM) $(DESTDIR)/usr/sbin/$(DHCP6RELAY_TARGET)
28-
$(RM) -rf $(DESTDIR)/usr/sbin
45+
$(DHCP6RELAY_TEST_TARGET): $(TEST_OBJS)
46+
$(CXX) $(LDFLAGS) $^ $(LDLIBS) $(LDLIBS_TEST) -o $@
2947

30-
clean:
31-
-$(RM) $(EXECUTABLES) $(OBJS:%.o=%.d) $(OBJS) $(DHCP6RELAY_TARGET)
32-
-@echo ' '
48+
test: $(DHCP6RELAY_TEST_TARGET)
49+
sudo ASAN_OPTIONS=detect_leaks=0 ./$(DHCP6RELAY_TEST_TARGET) --gtest_output=xml:$(DHCP6RELAY_TEST_TARGET)-test-result.xml || true
50+
$(GCOVR) -r ./ --html --html-details -o $(DHCP6RELAY_TEST_TARGET)-code-coverage.html
51+
$(GCOVR) -r ./ --xml-pretty -o $(DHCP6RELAY_TEST_TARGET)-code-coverage.xml
52+
53+
install: $(DHCP6RELAY_TARGET)
54+
install -D $(DHCP6RELAY_TARGET) $(DESTDIR)/usr/sbin/$(notdir $(DHCP6RELAY_TARGET))
3355

34-
.PHONY: all clean dependents
56+
uninstall:
57+
$(RM) $(DESTDIR)/usr/sbin/$(notdir $(DHCP6RELAY_TARGET))
3558

59+
clean:
60+
-$(RM) $(BUILD_DIR) $(BUILD_TEST_DIR) *.html *.xml
61+
$(FIND) . -name *.gcda -exec rm -f {} \;
62+
$(FIND) . -name *.gcno -exec rm -f {} \;
63+
$(FIND) . -name *.gcov -exec rm -f {} \;
64+
-@echo ' '
3665

66+
.PHONY: all clean test install uninstall

azure-pipelines.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
arch: amd64
1515
pool:
1616
vmImage: 'ubuntu-20.04'
17-
codeCoverage: false
17+
codeCoverage: true
1818
containerImage: sonicdev-microsoft.azurecr.io:443/sonic-slave-bullseye:latest
1919
- template: .azure-pipelines/build.yml
2020
parameters:

src/configInterface.cpp

+12-10
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ constexpr auto DEFAULT_TIMEOUT_MSEC = 1000;
77

88
bool pollSwssNotifcation = true;
99
std::shared_ptr<boost::thread> mSwssThreadPtr;
10-
11-
std::shared_ptr<swss::DBConnector> configDbPtr = std::make_shared<swss::DBConnector> ("CONFIG_DB", 0);
12-
swss::SubscriberStateTable ipHelpersTable(configDbPtr.get(), "DHCP_RELAY");
1310
swss::Select swssSelect;
1411

1512
/**
@@ -22,9 +19,14 @@ swss::Select swssSelect;
2219
void initialize_swss(std::vector<relay_config> *vlans)
2320
{
2421
try {
22+
std::shared_ptr<swss::DBConnector> configDbPtr = std::make_shared<swss::DBConnector> ("CONFIG_DB", 0);
23+
swss::SubscriberStateTable ipHelpersTable(configDbPtr.get(), "DHCP_RELAY");
2524
swssSelect.addSelectable(&ipHelpersTable);
26-
get_dhcp(vlans);
27-
mSwssThreadPtr = std::make_shared<boost::thread> (&handleSwssNotification, vlans);
25+
get_dhcp(vlans, &ipHelpersTable);
26+
struct swssNotification test;
27+
test.vlans = vlans;
28+
test.ipHelpersTable = &ipHelpersTable;
29+
mSwssThreadPtr = std::make_shared<boost::thread> (&handleSwssNotification, test);
2830
}
2931
catch (const std::bad_alloc &e) {
3032
syslog(LOG_ERR, "Failed allocate memory. Exception details: %s", e.what());
@@ -52,15 +54,15 @@ void deinitialize_swss()
5254
*
5355
* @return none
5456
*/
55-
void get_dhcp(std::vector<relay_config> *vlans) {
57+
void get_dhcp(std::vector<relay_config> *vlans, swss::SubscriberStateTable *ipHelpersTable) {
5658
swss::Selectable *selectable;
5759
int ret = swssSelect.select(&selectable, DEFAULT_TIMEOUT_MSEC);
5860
if (ret == swss::Select::ERROR) {
5961
syslog(LOG_WARNING, "Select: returned ERROR");
6062
} else if (ret == swss::Select::TIMEOUT) {
6163
}
62-
if (selectable == static_cast<swss::Selectable *> (&ipHelpersTable)) {
63-
handleRelayNotification(ipHelpersTable, vlans);
64+
if (selectable == static_cast<swss::Selectable *> (ipHelpersTable)) {
65+
handleRelayNotification(*ipHelpersTable, vlans);
6466
}
6567
}
6668
/**
@@ -72,10 +74,10 @@ void get_dhcp(std::vector<relay_config> *vlans) {
7274
*
7375
* @return none
7476
*/
75-
void handleSwssNotification(std::vector<relay_config> *vlans)
77+
void handleSwssNotification(swssNotification test)
7678
{
7779
while (pollSwssNotifcation) {
78-
get_dhcp(vlans);
80+
get_dhcp(test.vlans, test.ipHelpersTable);
7981
}
8082
}
8183

src/configInterface.h

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1+
#pragma once
2+
13
#include <boost/thread.hpp>
24
#include "subscriberstatetable.h"
35
#include "select.h"
46
#include "relay.h"
57

8+
struct swssNotification {
9+
std::vector<relay_config> *vlans;
10+
swss::SubscriberStateTable *ipHelpersTable;
11+
};
612
/**
713
* @code void initialize_swss()
814
*
@@ -28,18 +34,18 @@ void deinitialize_swss();
2834
*
2935
* @return none
3036
*/
31-
void get_dhcp(std::vector<relay_config> *vlans);
37+
void get_dhcp(std::vector<relay_config> *vlans, swss::SubscriberStateTable *ipHelpersTable);
3238

3339
/**
34-
* @code void handleSwssNotification(std::vector<relay_config> *vlans)
40+
* @code void swssNotification test
3541
*
3642
* @brief main thread for handling SWSS notification
3743
*
38-
* @param vlans list of vlans/argument config that contains strings of server and option
44+
* @param test swssNotification that includes list of vlans/argument config that contains strings of server and option
3945
*
4046
* @return none
4147
*/
42-
void handleSwssNotification(std::vector<relay_config> *vlans);
48+
void handleSwssNotification(swssNotification test);
4349

4450
/**
4551
* @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::vector<relay_config> *vlans)

src/relay.cpp

+15-25
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ struct event *ev_sigterm;
2222
static std::string vlan_member = "VLAN_MEMBER|";
2323

2424
static std::string counter_table = "DHCPv6_COUNTER_TABLE|";
25-
struct database redis_db;
2625

2726
/* DHCPv6 filter */
2827
/* sudo tcpdump -dd "inbound and ip6 dst ff02::1:2 && udp dst port 547" */
@@ -221,24 +220,9 @@ const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_
221220
return option;
222221
}
223222

224-
/**
225-
* @code void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type);
226-
*
227-
* @brief send udp packet
228-
*
229-
* @param *buffer message buffer
230-
* @param sockaddr_in6 target target socket
231-
* @param n length of message
232-
* @param relay_config *config pointer to relay_config
233-
* @param uint8_t msg_type message type of dhcpv6 option of relayed message
234-
*
235-
* @return dhcpv6_option end of dhcpv6 message option
236-
*/
237-
void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type) {
223+
void process_sent_msg(relay_config *config, uint8_t msg_type) {
238224
std::string counterVlan = counter_table;
239-
if(sendto(sock, buffer, n, 0, (const struct sockaddr *)&target, sizeof(target)) == -1)
240-
syslog(LOG_ERR, "sendto: Failed to send to target address\n");
241-
else if (counterMap.find(msg_type) != counterMap.end()) {
225+
if (counterMap.find(msg_type) != counterMap.end()) {
242226
counters[msg_type]++;
243227
update_counter(config->state_db, counterVlan.append(config->interface), msg_type);
244228
} else {
@@ -497,7 +481,9 @@ void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_h
497481
current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option);
498482

499483
for(auto server: config->servers_sock) {
500-
send_udp(sock, buffer, server, current_buffer_position - buffer, config, new_message.msg_type);
484+
if(send_udp(sock, buffer, server, current_buffer_position - buffer)) {
485+
process_sent_msg(config, new_message.msg_type);
486+
}
501487
}
502488
}
503489

@@ -537,7 +523,9 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *
537523
current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option);
538524

539525
for(auto server: config->servers_sock) {
540-
send_udp(sock, buffer, server, current_buffer_position - buffer, config, new_message.msg_type);
526+
if(send_udp(sock, buffer, server, current_buffer_position - buffer)) {
527+
process_sent_msg(config, new_message.msg_type);
528+
}
541529
}
542530
}
543531

@@ -589,7 +577,9 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *
589577
target_addr.sin6_port = htons(CLIENT_PORT);
590578
target_addr.sin6_scope_id = if_nametoindex(config->interface.c_str());
591579

592-
send_udp(sock, buffer, target_addr, current_buffer_position - buffer, config, type);
580+
if(send_udp(sock, buffer, target_addr, current_buffer_position - buffer)) {
581+
process_sent_msg(config, type);
582+
}
593583
}
594584

595585
/**
@@ -727,9 +717,9 @@ void callback_dual_tor(evutil_socket_t fd, short event, void *arg) {
727717
return;
728718
std::string state;
729719
std::string intf(interfaceName);
730-
redis_db.muxTable->hget(intf, "state", state);
720+
config->mux_table->hget(intf, "state", state);
731721

732-
if (state != "standby" && redis_db.config_db->exists(key.append(intf))) {
722+
if (state != "standby" && config->config_db->exists(key.append(intf))) {
733723
char* ptr = (char *)message_buffer;
734724
const uint8_t *current_position = (uint8_t *)ptr;
735725
const uint8_t *tmp = NULL;
@@ -1006,8 +996,6 @@ void loop_relay(std::vector<relay_config> *vlans) {
1006996
std::shared_ptr<swss::Table> mStateDbMuxTablePtr = std::make_shared<swss::Table> (
1007997
state_db.get(), "HW_MUX_CABLE_TABLE"
1008998
);
1009-
redis_db.config_db = config_db;
1010-
redis_db.muxTable = mStateDbMuxTablePtr;
1011999

10121000
int filter = 0;
10131001
filter = sock_open(&ether_relay_fprog);
@@ -1017,6 +1005,8 @@ void loop_relay(std::vector<relay_config> *vlans) {
10171005
relay_config *config = &vlan;
10181006
int local_sock = 0;
10191007
int server_sock = 0;
1008+
config->config_db = config_db;
1009+
config->mux_table = mStateDbMuxTablePtr;
10201010
config->state_db = state_db;
10211011
config->mux_key = vlan_member + config->interface + "|";
10221012

0 commit comments

Comments
 (0)