Commit c28e6e05 authored by StevenWdV's avatar StevenWdV

Implemented tests. Fixed bugs when closing sockets. Lowered some warnings to infos.

parent 87aa4856
......@@ -7,7 +7,7 @@ import sys
import btcp
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
logging.basicConfig(stream=sys.stderr, level=logging.ERROR)
parser = argparse.ArgumentParser()
parser.add_argument("-w", "--window", help="Define bTCP window size", type=int, default=70)
......
......@@ -7,7 +7,7 @@ import sys
import btcp
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
logging.basicConfig(stream=sys.stderr, level=logging.ERROR)
# Handle arguments
parser = argparse.ArgumentParser()
......
......@@ -10,13 +10,12 @@ import threading
import time
from typing import *
# TODO test btcp port multiplexing
header_format = "!HHHHBBHI"
header_size = 16
payload_size = 1000
max_seq = 0xffFF
enable_more_asserts = False
debug_window = False
......@@ -253,7 +252,7 @@ class _Stream:
if not self.__seq_between(packet.header.syn_nr, self.recv_ack_nr - self.local_window_size,
self.recv_ack_nr + self.local_window_size - 1):
logging.warning(self.deb + "SYN nr outside of window (spurious retransmission?)")
logging.info(self.deb + "SYN nr outside of window (spurious retransmission?)")
continue
if self.__seq_between(packet.header.syn_nr, self.recv_ack_nr + 1,
......@@ -308,12 +307,13 @@ class _Stream:
if (insert_index < len(self.receive_ack_buffer)
and self.receive_ack_buffer[insert_index].header.syn_nr == packet.header.syn_nr):
assert self.receive_ack_buffer[insert_index].data == packet.data
logging.warning(self.deb + "Spurious retransmission of packet in window")
if enable_more_asserts:
assert self.receive_ack_buffer[insert_index].data == packet.data
logging.info(self.deb + "Spurious retransmission of packet in window")
else:
self.receive_ack_buffer.insert(insert_index, packet)
else:
logging.warning(self.deb + "Spurious retransmission of packet before window")
logging.info(self.deb + "Spurious retransmission of packet before window")
send_ack = True
......@@ -451,9 +451,8 @@ class _Stream:
self.send_syn_nr = self.__next_seq(self.send_syn_nr)
self.sender_closed = True
time.sleep(self.timeout * 4)
def close_receiver(self) -> None:
"""Call after close_sender"""
logging.debug(self.deb + "Waiting for remote host to close")
self.receiver_closed_lock.acquire()
self.receiver_closed_event.wait_for(lambda: self.receiver_closed)
......@@ -506,7 +505,7 @@ class _Connection:
self.parent._remove_stream(self.stream.remote_port)
else:
# noinspection PyProtectedMember
self.parent._remove_socket(self.stream.remote_port)
self.parent._remove_socket(self.stream.local_port)
logging.debug(self.deb + "Closed")
......@@ -702,7 +701,10 @@ class Binding:
logging.debug(self.deb + "Closing...")
while len(self.sockets) > 0:
self.sockets[0].close()
key = None
for key in self.sockets.keys():
break
self.sockets[key].close()
self.sock.close()
self.sockets_stop_lock.acquire()
......
import unittest
import socket
import time
#!/bin/python3
import binascii
import sys
import threading
import unittest
timeout = 100
winsize = 100
intf = "lo"
netem_add = "sudo tc qdisc add dev {} root netem".format(intf)
netem_change = "sudo tc qdisc change dev {} root netem {}".format(intf, "{}")
netem_del = "sudo tc qdisc del dev {} root netem".format(intf)
infile = "20190216_214527.zip" # TODO
outfile = "out.txt"
server_finished = False
wait_server_lock = threading.Lock()
wait_server = threading.Condition(wait_server_lock)
timeout=100
winsize=100
intf="lo"
netem_add="sudo tc qdisc add dev {} root netem".format(intf)
netem_change="sudo tc qdisc change dev {} root netem {}".format(intf,"{}")
netem_del="sudo tc qdisc del dev {} root netem".format(intf)
"""run command and retrieve output"""
def run_command_with_output(command, input=None, cwd=None, shell=True):
"""run command and retrieve output"""
import subprocess
try:
process = subprocess.Popen(command, cwd=cwd, shell=shell, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
process = subprocess.Popen(command, cwd=cwd, shell=shell, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
except Exception as inst:
print("problem running command : \n ", str(command))
print("problem running command : \n ", str(command))
[stdoutdata, stderrdata]=process.communicate(input) # no pipes set for stdin/stdout/stdout streams so does effectively only just wait for process ends (same as process.wait()
[stdoutdata, stderrdata] = process.communicate(
input) # no pipes set for stdin/stdout/stdout streams so does effectively only just wait for process ends (same as process.wait()
if process.returncode:
print(stderrdata)
print("problem running command : \n ", str(command), " ",process.returncode)
print(stderrdata)
print("problem running command : \n ", str(command), " ", process.returncode)
return stdoutdata
"""run command with no output piping"""
def run_command(command,cwd=None, shell=True):
def run_command(command, cwd=None, shell=True):
"""run command with no output piping"""
import subprocess
process = None
try:
......@@ -40,138 +51,125 @@ def run_command(command,cwd=None, shell=True):
if process.returncode:
print("2. problem running command : \n ", str(command), " ", process.returncode)
def run_server_wait() -> None:
global server_finished
server_finished = False
run_command_with_output(run_server)
wait_server_lock.acquire()
server_finished = True
wait_server.notify()
wait_server_lock.release()
def transfer_file() -> None:
# launch localhost client connecting to server
# client sends content to server
# server receives content from client
run_command_with_output(run_client)
wait_server_lock.acquire()
wait_server.wait_for(lambda: server_finished)
wait_server_lock.release()
# content received by server matches the content sent by client
file = open(infile, "r+b")
crc_in = binascii.crc32(file.read())
file.close()
file = open(outfile, "r+b")
crc_out = binascii.crc32(file.read())
file.close()
assert crc_out == crc_in
class TestbTCPFramework(unittest.TestCase):
"""Test cases for bTCP"""
def setUp(self):
"""Prepare for testing"""
# default netem rule (does nothing)
run_command(netem_add)
# launch localhost server
threading.Thread(target=run_server_wait).start()
def tearDown(self):
"""Clean up after testing"""
# clean the environment
run_command(netem_del)
# close server
# Already done
def test_ideal_network(self):
"""reliability over an ideal framework"""
print("reliability over an ideal framework")
# setup environment (nothing to set)
# launch localhost client connecting to server
# client sends content to server
# server receives content from client
# content received by server matches the content sent by client
transfer_file()
def test_flipping_network(self):
"""reliability over network with bit flips
(which sometimes results in lower layer packet loss)"""
print("reliability over network with bit flips")
"""(which sometimes results in lower layer packet loss)"""
# setup environment
run_command(netem_change.format("corrupt 1%"))
# launch localhost client connecting to server
# client sends content to server
# server receives content from client
# content received by server matches the content sent by client
transfer_file()
def test_duplicates_network(self):
"""reliability over network with duplicate packets"""
print("reliability over network with duplicate packets")
# setup environment
run_command(netem_change.format("duplicate 10%"))
# launch localhost client connecting to server
# client sends content to server
# server receives content from client
# content received by server matches the content sent by client
transfer_file()
def test_lossy_network(self):
"""reliability over network with packet loss"""
print("reliability over network with packet loss")
# setup environment
run_command(netem_change.format("loss 10% 25%"))
# launch localhost client connecting to server
# client sends content to server
# server receives content from client
# content received by server matches the content sent by client
transfer_file()
def test_reordering_network(self):
"""reliability over network with packet reordering"""
print("reliability over network with packet reordering")
# setup environment
run_command(netem_change.format("delay 20ms reorder 25% 50%"))
# launch localhost client connecting to server
# client sends content to server
# server receives content from client
# content received by server matches the content sent by client
transfer_file()
def test_delayed_network(self):
"""reliability over network with delay relative to the timeout value"""
print("reliability over network with delay relative to the timeout value")
# setup environment
run_command(netem_change.format("delay "+str(timeout)+"ms 20ms"))
# launch localhost client connecting to server
# client sends content to server
# server receives content from client
# content received by server matches the content sent by client
run_command(netem_change.format("delay " + str(timeout) + "ms 20ms"))
transfer_file()
def test_allbad_network(self):
"""reliability over network with all of the above problems"""
print("reliability over network with all of the above problems")
# setup environment
run_command(netem_change.format("corrupt 1% duplicate 10% loss 10% 25% delay 20ms reorder 25% 50%"))
# launch localhost client connecting to server
# client sends content to server
# server receives content from client
# content received by server matches the content sent by client
# def test_command(self):
# #command=['dir','.']
# out = run_command_with_output("dir .")
# print(out)
transfer_file()
if __name__ == "__main__":
# Parse command line arguments
import argparse
parser = argparse.ArgumentParser(description="bTCP tests")
parser.add_argument("-w", "--window", help="Define bTCP window size used", type=int, default=100)
parser.add_argument("-w", "--window", help="Define bTCP window size used", type=int, default=winsize)
parser.add_argument("-t", "--timeout", help="Define the timeout value used (ms)", type=int, default=timeout)
args, extra = parser.parse_known_args()
timeout = args.timeout
winsize = args.window
run_client = f"./bTCP_client.py -t {timeout} -w {winsize} -i {infile}"
run_server = f"./bTCP_server.py -t {timeout} -w {winsize} -o {outfile}"
# Pass the extra arguments to unittest
sys.argv[1:] = extra
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment