Merge pull request #1 from 2-towns/chore/add-tests-for-port-renewal

chore: add tests for port renewal
This commit is contained in:
Arnaud 2026-05-26 15:02:53 +04:00 committed by GitHub
commit bf0ace8da2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 123 additions and 50 deletions

View File

@ -49,12 +49,12 @@ RUN rm -rf vendor/libplum/build && \
# Compile test binaries (protocol x memory manager)
COPY tests/ tests/
RUN nim c -d:miniupnp_protocol=pcp --mm:orc -o:tests/test_pcp_orc tests/test_plum.nim && \
nim c -d:miniupnp_protocol=pcp --mm:refc -o:tests/test_pcp_refc tests/test_plum.nim && \
nim c -d:miniupnp_protocol=upnp --mm:orc -o:tests/test_upnp_orc tests/test_plum.nim && \
nim c -d:miniupnp_protocol=upnp --mm:refc -o:tests/test_upnp_refc tests/test_plum.nim && \
nim c -d:miniupnp_protocol=natpmp --mm:orc -o:tests/test_natpmp_orc tests/test_plum.nim && \
nim c -d:miniupnp_protocol=natpmp --mm:refc -o:tests/test_natpmp_refc tests/test_plum.nim
RUN nim c -d:miniupnp_protocol=pcp --mm:orc --threads:on -o:tests/test_pcp_orc tests/test_plum.nim && \
nim c -d:miniupnp_protocol=pcp --mm:refc --threads:on -o:tests/test_pcp_refc tests/test_plum.nim && \
nim c -d:miniupnp_protocol=upnp --mm:orc --threads:on -o:tests/test_upnp_orc tests/test_plum.nim && \
nim c -d:miniupnp_protocol=upnp --mm:refc --threads:on -o:tests/test_upnp_refc tests/test_plum.nim && \
nim c -d:miniupnp_protocol=natpmp --mm:orc --threads:on -o:tests/test_natpmp_orc tests/test_plum.nim && \
nim c -d:miniupnp_protocol=natpmp --mm:refc --threads:on -o:tests/test_natpmp_refc tests/test_plum.nim
COPY tests/docker-entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

View File

@ -7,7 +7,11 @@
# those terms.
import std/os
import std/osproc
import std/strutils
import posix
import unittest2
import std/atomics
import chronos
import libplum/plum
import libplum/libplum
@ -36,59 +40,128 @@ suite "plum":
check not hasMapping(999)
const miniupnp_protocol {.strdefine.} = ""
# The flag is passed by the Docker / Podman container.
# natpmp: miniupnpd compiled without PCP — PCP probes time out and libplum
# must fall back to NAT-PMP on its own (tests the silent-timeout fallback fix).
when miniupnp_protocol != "":
const expectedMappingProtocol =
when miniupnp_protocol == "pcp":
PCP
elif miniupnp_protocol == "natpmp":
NatPmp
else:
UPnP
const
discoverMs = 2000
recheckMs = 500
upnpRestartMs = discoverMs + 2000
natpmpEpochMs = 3000
renewalPollMs = 100
renewalTimeoutMs = 10_000
let mappingTimeout = seconds(40)
let logLevel =
if getEnv("LIBPLUM_VERBOSE") == "1": PLUM_LOG_LEVEL_VERBOSE else: PLUM_LOG_LEVEL_NONE
var gRenewed: Atomic[bool]
proc onMappingRenewal(
state: PlumState, mapping: PlumMapping
) {.cdecl, raises: [], gcsafe.} =
if state == Success:
gRenewed.store(true)
proc stopMiniupnpd(proto: string) =
let pidFile = "/tmp/plum-test/miniupnpd-" & proto & ".pid"
let pid = readFile(pidFile).strip().parseInt()
discard posix.kill(pid.Pid, SIGKILL)
proc waitRenewal(): bool =
for _ in 0 ..< (renewalTimeoutMs div renewalPollMs):
if gRenewed.load():
return true
sleep(renewalPollMs)
false
proc startMiniupnpd(proto: string) =
let pidFile = "/tmp/plum-test/miniupnpd-" & proto & ".pid"
let confFile = "/tmp/plum-test/miniupnpd-" & proto & ".conf"
let logFile = "/tmp/plum-test/miniupnpd-" & proto & ".log"
let binary = if proto == "natpmp": "miniupnpd-natpmponly" else: "miniupnpd"
discard execShellCmd(
binary & " -d -f " & confFile & " >> " & logFile & " 2>&1 & echo $! > " & pidFile
)
suite "plum - " & miniupnp_protocol & " using miniupnp":
test "createMapping TCP and destroyMapping":
let logLevel =
if getEnv("LIBPLUM_VERBOSE") == "1":
PLUM_LOG_LEVEL_VERBOSE
else:
PLUM_LOG_LEVEL_NONE
check init(discoverTimeout = 15000, logLevel = logLevel).isOk()
require init(discoverTimeout = discoverMs, logLevel = logLevel).isOk()
defer:
discard cleanup()
let r = waitFor createMapping(TCP, 8101, timeout = seconds(40))
check r.isOk()
if r.isOk():
let res = r.value
check res.mapping.externalPort > 0
check res.mapping.externalHost.len > 0
check res.mapping.mappingProtocol == expectedMappingProtocol
check hasMapping(res.id)
if getEnv("TEST_VERBOSE") == "1":
echo miniupnp_protocol & " TCP: " & res.mapping.externalHost & ":" &
$res.mapping.externalPort
let r = waitFor createMapping(TCP, 8101, timeout = mappingTimeout)
require r.isOk()
let res = r.value
defer:
destroyMapping(res.id)
discard cleanup()
checkpoint miniupnp_protocol & " TCP: " & res.mapping.externalHost & ":" &
$res.mapping.externalPort
check res.mapping.externalPort > 0
check res.mapping.externalHost.len > 0
check hasMapping(res.id)
when miniupnp_protocol == "upnp":
check res.mapping.mappingProtocol == UPnP
elif miniupnp_protocol == "natpmp":
check res.mapping.mappingProtocol == NatPmp
else:
check res.mapping.mappingProtocol == PCP
test "createMapping UDP and destroying":
let logLevel =
if getEnv("LIBPLUM_VERBOSE") == "1":
PLUM_LOG_LEVEL_VERBOSE
else:
PLUM_LOG_LEVEL_NONE
check init(discoverTimeout = 2000, logLevel = logLevel).isOk()
require init(discoverTimeout = discoverMs, logLevel = logLevel).isOk()
defer:
discard cleanup()
let r = waitFor createMapping(UDP, 8090, timeout = seconds(40))
check r.isOk()
if r.isOk():
let res = r.value
check res.mapping.externalPort > 0
check res.mapping.mappingProtocol == expectedMappingProtocol
if getEnv("TEST_VERBOSE") == "1":
echo miniupnp_protocol & " UDP: " & res.mapping.externalHost & ":" &
$res.mapping.externalPort
let r = waitFor createMapping(UDP, 8090, timeout = mappingTimeout)
require r.isOk()
let res = r.value
defer:
destroyMapping(res.id)
discard cleanup()
checkpoint miniupnp_protocol & " UDP: " & res.mapping.externalHost & ":" &
$res.mapping.externalPort
check res.mapping.externalPort > 0
when miniupnp_protocol == "upnp":
check res.mapping.mappingProtocol == UPnP
elif miniupnp_protocol == "natpmp":
check res.mapping.mappingProtocol == NatPmp
else:
check res.mapping.mappingProtocol == PCP
test "mapping is renewed after miniupnpd restart":
require init(
discoverTimeout = discoverMs, logLevel = logLevel, recheckPeriod = recheckMs
)
.isOk()
defer:
discard cleanup()
gRenewed.store(false)
let r = waitFor createMapping(
TCP, 8301, timeout = mappingTimeout, onStateChange = onMappingRenewal
)
require r.isOk()
let res = r.value
defer:
destroyMapping(res.id)
when miniupnp_protocol == "natpmp":
# Let the server epoch advance so the restart is detectable
sleep(natpmpEpochMs)
stopMiniupnpd(miniupnp_protocol)
when miniupnp_protocol == "upnp":
sleep(upnpRestartMs)
startMiniupnpd(miniupnp_protocol)
check waitRenewal()

2
vendor/libplum vendored

@ -1 +1 @@
Subproject commit f6d2c12a9f347dcd6958b97c9df6418c43d975f2
Subproject commit b74757f88c2a98ea905f5b81ad040232bc019c35