Cleanup utp_test and make the code more flexible to change amounts (#1972)

This commit is contained in:
Kim De Mey 2024-01-15 17:29:29 +01:00 committed by GitHub
parent fb2aa1b6f9
commit 2df9e2b7b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 92 additions and 114 deletions

View File

@ -1,9 +1,9 @@
# uTP testing infrastructure
Testing infrastructure which enables to test uTP implementation over different
network conditions on local machine.
network conditions on a local machine.
Highly based on tools developed to test quic protocol:
Uses following tools developed to test the QUIC protocol:
[quic-interop-runner](https://github.com/marten-seemann/quic-interop-runner)
@ -11,19 +11,20 @@ Highly based on tools developed to test quic protocol:
## Prerequisities
- Machine with docker installed
- Machine with Docker installed
- nimbus-eth1 set-up to run `make utp_test`
## How it works
Test setup uses docker compose to start 3 docker containers:
- client - which is instance of uTP test app
- server - which is instance of uTP test app
- sim - which is instance with ns3 network simulator with several pre-compiled scenarios
Test setup uses Docker Compose to start 3 Docker containers:
- uTP client - which is an instance of the `utp_test_app`
- uTP server - which is an instance of the `utp_test_app`
- sim - which is an instance of the ns3 network simulator with several pre-compiled scenarios
The networking is setup in such way that network traffic is routed from client to server
and server to client thorugh sim which decideds what to do with flowing packets
The networking is set up in such way that network traffic is routed from client to server
and server to client through the simulator.
The simulator decides what to do with packets passing through based on the selected scneario.
Explanation from [quic-network-simulator](https://github.com/marten-seemann/quic-network-simulator):
@ -38,9 +39,9 @@ simulation sits in the middle and forwards packets between `leftnet` and
## Practicalities
For now process is semi-manual (TODO automate this as much as possible)
For now the process is semi-manual (TODO automate this as much as possible)
To run integration testing scenarios with different network conditions
To run integration testing scenarios with different network conditions:
```
1. cd nimbus-eth1/

View File

@ -1,6 +1,6 @@
version: "3.5"
# TODO Add possibility to configure ports by env vcariables
# TODO Add possibility to configure ports by env variables
services:
sim:
# TODO for now we are using simulator which is also used in quic, ultimatly
@ -30,8 +30,6 @@ services:
# TODO add building of docker images based on variable
# build: ./$SERVER
image: test-utp
# this arg will go to container entry point
command: "--udp-listen-address=193.167.100.100 --rpc-listen-address=0.0.0.0 --udp-port=9041 --rpc-port=9041"
container_name: server
hostname: server
stdin_open: true
@ -60,7 +58,6 @@ services:
client:
# build: ./$CLIENT
image: test-utp
command: "--udp-listen-address=193.167.0.100 --rpc-listen-address=0.0.0.0 --udp-port=9042 --rpc-port=9042"
container_name: client
hostname: client
stdin_open: true

View File

@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2022-2023 Status Research & Development GmbH
# Fluffy
# Copyright (c) 2022-2024 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -10,13 +10,13 @@ import
unittest2, testutils, chronos,
json_rpc/rpcclient, stew/byteutils,
eth/keys,
./utp_test_client
./utp_test_rpc_client
proc generateBytesHex(rng: var HmacDrbgContext, length: int): string =
rng.generateBytes(length).toHex()
# Before running the test suite, there need to be two instances of the
# utp_test_app running under provided ports (9042, 9041).
# Before running this test suite, there need to be two instances of the
# utp_test_app running under the tested ports: 9042, 9041.
# Those could be launched locally by running either
# ./utp_test_app --udp-listen-address=127.0.0.1 --rpc-listen-address=0.0.0.0 --udp-port=9041 --rpc-port=9041
# ./utp_test_app --udp-listen-address=127.0.0.1 --rpc-listen-address=0.0.0.0 --udp-port=9042 --rpc-port=9042
@ -24,13 +24,15 @@ proc generateBytesHex(rng: var HmacDrbgContext, length: int): string =
# running from docker dir:
# 1. docker build -t test-utp --no-cache --build-arg BRANCH_NAME=branch-name .
# 2. SCENARIO="scenario name and params " docker-compose up
procSuite "uTP integration tests":
let rng = newRng()
let clientContainerAddress = "127.0.0.1"
let clientContainerPort = Port(9042)
let serverContainerAddress = "127.0.0.1"
let serverContainerPort = Port(9041)
procSuite "uTP network simulator tests":
const
clientContainerAddress = "127.0.0.1"
clientContainerPort = Port(9042)
serverContainerAddress = "127.0.0.1"
serverContainerPort = Port(9041)
let rng = newRng()
type
FutureCallback[A] = proc (): Future[A] {.gcsafe, raises: [].}
@ -75,20 +77,20 @@ procSuite "uTP integration tests":
await client.connect(clientContainerAddress, clientContainerPort, false)
await server.connect(serverContainerAddress, serverContainerPort, false)
# we may need to retry few times if the simm is not ready yet
# we may need to retry few times if the sim is not ready yet
let clientInfo = await repeatTillSuccess(() => client.discv5_nodeInfo(), 10)
let serverInfo = await repeatTillSuccess(() => server.discv5_nodeInfo(), 10)
# nodes need to have established session before the utp try
# nodes need to have an established discv5 session before the uTP test
discard await repeatTillSuccess(() => client.discv5_ping(serverInfo.enr))
return (client, clientInfo, server, serverInfo)
asyncTest "Transfer 100k bytes of data over utp stream from client to server":
let (client, clientInfo, server, serverInfo) = await setupTest()
let numOfBytes = 100000
asyncTest "100kb transfer from client to server":
const amountOfBytes = 100_000
let
(client, clientInfo, server, serverInfo) = await setupTest()
clientConnectionKey = await repeatTillSuccess(() =>
client.utp_connect(serverInfo.enr))
serverConnections = await repeatTillSuccess(() =>
@ -99,24 +101,24 @@ procSuite "uTP integration tests":
check:
maybeServerConnectionKey.isSome()
let serverConnectionKey = maybeServerConnectionKey.unsafeGet()
let
bytesToWrite = generateBytesHex(rng[], numOfBytes)
serverConnectionKey = maybeServerConnectionKey.unsafeGet()
bytesToWrite = generateBytesHex(rng[], amountOfBytes)
writeRes = await client.utp_write(clientConnectionKey, bytesToWrite)
readData = await server.utp_read(serverConnectionKey, numOfBytes)
dataRead = await server.utp_read(serverConnectionKey, amountOfBytes)
check:
writeRes == true
readData == bytesToWrite
dataRead == bytesToWrite
asyncTest "Transfer 100k bytes of data over utp stream from server to client":
asyncTest "100kb transfer from server to client":
# In classic uTP this would not be possible, as when uTP works over UDP the
# client needs to transfer first, but when working over discv5 it should be
# possible to transfer data from server to client from the start.
let (client, clientInfo, server, serverInfo) = await setupTest()
let numOfBytes = 100000
const amountOfBytes = 100_000
let
(client, clientInfo, server, serverInfo) = await setupTest()
clientConnectionKey = await repeatTillSuccess(() =>
client.utp_connect(serverInfo.enr))
serverConnections = await repeatTillSuccess(() =>
@ -127,21 +129,23 @@ procSuite "uTP integration tests":
check:
maybeServerConnectionKey.isSome()
let serverConnectionKey = maybeServerConnectionKey.unsafeGet()
let
bytesToWrite = generateBytesHex(rng[], numOfBytes)
serverConnectionKey = maybeServerConnectionKey.unsafeGet()
bytesToWrite = generateBytesHex(rng[], amountOfBytes)
writeRes = await server.utp_write(serverConnectionKey, bytesToWrite)
readData = await client.utp_read(clientConnectionKey, numOfBytes)
dataRead = await client.utp_read(clientConnectionKey, amountOfBytes)
check:
writeRes == true
readData == bytesToWrite
dataRead == bytesToWrite
asyncTest "Multiple 10kb transfers from client to server":
const
amountOfBytes = 10_000
amountOfTransfers = 3
asyncTest "Multiple 10k bytes transfers over utp stream":
let (client, clientInfo, server, serverInfo) = await setupTest()
let numOfBytes = 10000
let
(client, clientInfo, server, serverInfo) = await setupTest()
clientConnectionKey = await repeatTillSuccess(() =>
client.utp_connect(serverInfo.enr))
serverConnections = await repeatTillSuccess(() =>
@ -154,71 +158,47 @@ procSuite "uTP integration tests":
let serverConnectionKey = maybeServerConnectionKey.unsafeGet()
var totalBytesToWrite: string
for i in 0..<amountOfTransfers:
let
bytesToWrite = generateBytesHex(rng[], numOfBytes)
bytesToWrite1 = generateBytesHex(rng[], numOfBytes)
bytesToWrite2 = generateBytesHex(rng[], numOfBytes)
bytesToWrite = generateBytesHex(rng[], amountOfBytes)
writeRes = await client.utp_write(clientConnectionKey, bytesToWrite)
writeRes1 = await client.utp_write(clientConnectionKey, bytesToWrite1)
writeRes2 = await client.utp_write(clientConnectionKey, bytesToWrite2)
readData = await server.utp_read(serverConnectionKey, numOfBytes * 3)
let writtenData = join(@[bytesToWrite, bytesToWrite1, bytesToWrite2])
check writeRes == true
totalBytesToWrite.add(bytesToWrite)
check:
writeRes == true
writeRes1 == true
writeRes2 == true
readData == writtenData
let dataRead = await server.utp_read(
serverConnectionKey, amountOfBytes * amountOfTransfers)
check dataRead == totalBytesToWrite
asyncTest "Multiple 10kb transfers over multiple sockets from client to server":
const
amountOfBytes = 10_000
amountOfSockets = 3
asyncTest "Handle mulitplie sockets over one utp server instance ":
let (client, clientInfo, server, serverInfo) = await setupTest()
let numOfBytes = 10000
var connectionKeys: seq[(SKey, SKey)]
for i in 0..<amountOfSockets:
let
clientConnectionKey1 = await repeatTillSuccess(() =>
client.utp_connect(serverInfo.enr))
clientConnectionKey2 = await repeatTillSuccess(() =>
client.utp_connect(serverInfo.enr))
clientConnectionKey3 = await repeatTillSuccess(() =>
clientConnectionKey = await repeatTillSuccess(() =>
client.utp_connect(serverInfo.enr))
serverConnections = await repeatTillSuccess(() =>
server.utp_get_connections())
serverConnectionKeyRes = serverConnections.findServerConnection(
clientInfo.nodeId, clientConnectionKey.id)
maybeServerConnectionKey1 = serverConnections.findServerConnection(
clientInfo.nodeId, clientConnectionKey1.id)
maybeServerConnectionKey2 = serverConnections.findServerConnection(
clientInfo.nodeId, clientConnectionKey2.id)
maybeServerConnectionKey3 = serverConnections.findServerConnection(
clientInfo.nodeId, clientConnectionKey3.id)
check serverConnectionKeyRes.isSome()
check:
maybeServerConnectionKey1.isSome()
maybeServerConnectionKey2.isSome()
maybeServerConnectionKey3.isSome()
let serverConnectionKey1 = maybeServerConnectionKey1.unsafeGet()
let serverConnectionKey2 = maybeServerConnectionKey2.unsafeGet()
let serverConnectionKey3 = maybeServerConnectionKey3.unsafeGet()
connectionKeys.add((clientConnectionKey, serverConnectionKeyRes.unsafeGet()))
for (clientConnectionKey, serverConnectionKey) in connectionKeys:
let
bytesToWrite1 = generateBytesHex(rng[], numOfBytes)
bytesToWrite2 = generateBytesHex(rng[], numOfBytes)
bytesToWrite3 = generateBytesHex(rng[], numOfBytes)
writeRes1 = await client.utp_write(clientConnectionKey1, bytesToWrite1)
writeRes2 = await client.utp_write(clientConnectionKey2, bytesToWrite2)
writeRes3 = await client.utp_write(clientConnectionKey3, bytesToWrite3)
readData1 = await server.utp_read(serverConnectionKey1, numOfBytes)
readData2 = await server.utp_read(serverConnectionKey2, numOfBytes)
readData3 = await server.utp_read(serverConnectionKey3, numOfBytes)
bytesToWrite = generateBytesHex(rng[], amountOfBytes)
writeRes = await client.utp_write(clientConnectionKey, bytesToWrite)
dataRead = await server.utp_read(serverConnectionKey, amountOfBytes)
check:
writeRes1 == true
writeRes2 == true
writeRes3 == true
# all data was delivered to correct sockets
readData1 == bytesToWrite1
readData2 == bytesToWrite2
readData3 == bytesToWrite3
writeRes == true
dataRead == bytesToWrite