Skip to content

Commit 63bc2df

Browse files
authored
feature: unittests (#57)
* feature: unittests * [fix+feature]: fix config file test and add codecoverage - `test_config_file` unittest fixed for missing keys in config file Now it tests against existing keys in config file and ignores the rest (the rest of keys are default and previously are tested in `test_defaults`) * [feature]: add unittest github action a github action file for running unittest on PR and pushes with action result badge in README.md * [fix]: fix test dir name * [fix]: fix GH action for requirements * [fix]: typo fix * update new arguments from main updates now supports port option
1 parent 6f26700 commit 63bc2df

File tree

7 files changed

+151
-5
lines changed

7 files changed

+151
-5
lines changed

.github/workflows/unittest.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: UnitTest
2+
3+
on:
4+
pull_request:
5+
push:
6+
7+
jobs:
8+
unittest:
9+
runs-on: ubuntu-latest
10+
name: Run UnitTests
11+
steps:
12+
- uses: actions/checkout@v2
13+
- name: Set up Python
14+
id: setup-python
15+
uses: actions/setup-python@v2
16+
with:
17+
python-version: 3
18+
- name: Install requirements
19+
run: pip install -r requirements.txt
20+
- name: Launch tests
21+
run: python -m unittest discover test
22+

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,4 @@ dmypy.json
129129
.pyre/
130130

131131
tracevis_data/
132+
.coverage

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ Traceroute with any packet. Visualize the routes. Discover Middleboxes and Firew
33

44
[![CodeQL](https://github.com/wikicensorship/tracevis/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/wikicensorship/tracevis/actions/workflows/codeql-analysis.yml)
55
[![Dockerise](https://github.com/wikicensorship/tracevis/actions/workflows/docker.yml/badge.svg)](https://github.com/wikicensorship/tracevis/actions/workflows/docker.yml)
6+
[![unittest](https://github.com/wikicensorship/tracevis/actions/workflows/unittest.yml/badge.svg)](https://github.com/wikicensorship/tracevis/actions/workflows/unittest.yml)
7+
68

79
TraceVis is a research project whose main goal is to find middleboxes. Where a packet is tampered with or blocked. This tool also has other features such as downloading and visualizing traceroute data from RIPE Atlas probes.
810

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
scapy
2-
pyvis
2+
pyvis

test/__init__.py

Whitespace-only changes.

test/test_operationality.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import unittest
2+
import tracevis
3+
import sys
4+
5+
6+
class TestArguments(unittest.TestCase):
7+
def test_help(self):
8+
from io import StringIO
9+
out,err = StringIO(), StringIO()
10+
sys.stdout, sys.stderr = out, err
11+
with self.assertRaises(SystemExit):
12+
tracevis.get_args(['-h'], auto_exit=True)
13+
self.assertIn(err.getvalue(), "usage:")
14+
sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
15+
16+
def test_no_args(self):
17+
from io import StringIO
18+
out,err = StringIO(), StringIO()
19+
sys.stdout, sys.stderr = out, err
20+
with self.assertRaises(SystemExit):
21+
tracevis.get_args([], auto_exit=True)
22+
self.assertIn(err.getvalue(), "usage:")
23+
sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
24+
25+
26+
def test_defaults(self):
27+
from io import StringIO
28+
out,err = StringIO(), StringIO()
29+
sys.stdout, sys.stderr = out, err
30+
args = tracevis.get_args([], auto_exit=False)
31+
expected = {'config_file': None, 'name': None, 'ips': None, 'packet': False, 'packet_input_method': 'hex',
32+
'packet_data': None, 'dns': False, 'dnstcp': False, 'continue': False, 'maxttl': None,
33+
'timeout': None, 'repeat': None, 'ripe': None, 'ripemids': None, 'file': None, 'csv': False,
34+
'csvraw': False, 'attach': False, 'label': None, 'domain1': None, 'domain2': None, 'annot1': None,
35+
'annot2': None, 'rexmit': False, 'paris': False, 'options': 'new', 'iface': None, 'show_ifaces': False, 'port': None}
36+
self.assertEqual(args, expected)
37+
sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
38+
39+
def test_config_file(self):
40+
from io import StringIO
41+
import os, json
42+
md = self.maxDiff
43+
self.maxDiff = None
44+
out,err = StringIO(), StringIO()
45+
sys.stdout, sys.stderr = out, err
46+
samples_dir = 'samples/'
47+
for file in os.listdir(samples_dir):
48+
args = tracevis.get_args(['--config-file', os.path.join(samples_dir, file)], auto_exit=False)
49+
with open(os.path.join(samples_dir, file), 'r') as f:
50+
expected = json.load(f)
51+
del args['config_file']
52+
for k,v in args.items():
53+
if k in expected:
54+
self.assertEqual(v, expected[k])
55+
56+
sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
57+
self.maxDiff = md
58+
59+
def test_dns_mode(self):
60+
from io import StringIO
61+
out,err = StringIO(), StringIO()
62+
sys.stdout, sys.stderr = out, err
63+
args = tracevis.get_args(['--dns'], auto_exit=False)
64+
expected = {'config_file': None, 'name': None, 'ips': None, 'packet': False, 'packet_input_method': None,
65+
'packet_data': None, 'dns': True, 'dnstcp': False, 'continue': False, 'maxttl': None,
66+
'timeout': None, 'repeat': None, 'ripe': None, 'ripemids': None, 'file': None, 'csv': False,
67+
'csvraw': False, 'attach': False, 'label': None, 'domain1': None, 'domain2': None, 'annot1': None,
68+
'annot2': None, 'rexmit': False, 'paris': False, 'options': 'new', 'iface': None, 'show_ifaces': False, 'port': None}
69+
self.assertEqual(args, expected)
70+
sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
71+
72+
def test_packet_mode(self):
73+
from io import StringIO
74+
out,err = StringIO(), StringIO()
75+
sys.stdout, sys.stderr = out, err
76+
args = tracevis.get_args(['--packet'], auto_exit=False)
77+
expected = {'config_file': None, 'name': None, 'ips': None, 'packet': True, 'packet_input_method': 'hex',
78+
'packet_data': None, 'dns': False, 'dnstcp': False, 'continue': False, 'maxttl': None,
79+
'timeout': None, 'repeat': None, 'ripe': None, 'ripemids': None, 'file': None, 'csv': False,
80+
'csvraw': False, 'attach': False, 'label': None, 'domain1': None, 'domain2': None, 'annot1': None,
81+
'annot2': None, 'rexmit': False, 'paris': False, 'options': 'new', 'iface': None, 'show_ifaces': False, 'port': None}
82+
self.assertEqual(args, expected)
83+
sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
84+
85+
def test_packet_input_types(self):
86+
from io import StringIO
87+
out,err = StringIO(), StringIO()
88+
sys.stdout, sys.stderr = out, err
89+
args = tracevis.get_args(['--packet', '--packet-input-method', 'hex'], auto_exit=False)
90+
expected = {'config_file': None, 'name': None, 'ips': None, 'packet': True, 'packet_input_method': 'hex',
91+
'packet_data': None, 'dns': False, 'dnstcp': False, 'continue': False, 'maxttl': None,
92+
'timeout': None, 'repeat': None, 'ripe': None, 'ripemids': None, 'file': None, 'csv': False,
93+
'csvraw': False, 'attach': False, 'label': None, 'domain1': None, 'domain2': None, 'annot1': None,
94+
'annot2': None, 'rexmit': False, 'paris': False, 'options': 'new', 'iface': None, 'show_ifaces': False, 'port': None}
95+
self.assertEqual(args, expected)
96+
97+
args = tracevis.get_args(['--packet', '--packet-input-method', 'json'], auto_exit=False)
98+
expected = {'config_file': None, 'name': None, 'ips': None, 'packet': True, 'packet_input_method': 'json',
99+
'packet_data': None, 'dns': False, 'dnstcp': False, 'continue': False, 'maxttl': None,
100+
'timeout': None, 'repeat': None, 'ripe': None, 'ripemids': None, 'file': None, 'csv': False,
101+
'csvraw': False, 'attach': False, 'label': None, 'domain1': None, 'domain2': None, 'annot1': None,
102+
'annot2': None, 'rexmit': False, 'paris': False, 'options': 'new', 'iface': None, 'show_ifaces': False, 'port': None}
103+
self.assertEqual(args, expected)
104+
105+
args = tracevis.get_args(['--packet', '--packet-input-method', 'interactive'], auto_exit=False)
106+
expected = {'config_file': None, 'name': None, 'ips': None, 'packet': True, 'packet_input_method': 'interactive',
107+
'packet_data': None, 'dns': False, 'dnstcp': False, 'continue': False, 'maxttl': None,
108+
'timeout': None, 'repeat': None, 'ripe': None, 'ripemids': None, 'file': None, 'csv': False,
109+
'csvraw': False, 'attach': False, 'label': None, 'domain1': None, 'domain2': None, 'annot1': None,
110+
'annot2': None, 'rexmit': False, 'paris': False, 'options': 'new', 'iface': None, 'show_ifaces': False, 'port': None}
111+
self.assertEqual(args, expected)
112+
113+
args = tracevis.get_args(['--packet', '--packet-input-method', 'json', '--packet-data', 'b64:e30='], auto_exit=False)
114+
expected = {'config_file': None, 'name': None, 'ips': None, 'packet': True, 'packet_input_method': 'json',
115+
'packet_data': 'b64:e30=', 'dns': False, 'dnstcp': False, 'continue': False, 'maxttl': None,
116+
'timeout': None, 'repeat': None, 'ripe': None, 'ripemids': None, 'file': None, 'csv': False,
117+
'csvraw': False, 'attach': False, 'label': None, 'domain1': None, 'domain2': None, 'annot1': None,
118+
'annot2': None, 'rexmit': False, 'paris': False, 'options': 'new', 'iface': None, 'show_ifaces': False, 'port': None}
119+
self.assertEqual(args, expected)
120+
sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
121+

tracevis.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def process_input_args(args, parser):
8484
return args_dict
8585

8686

87-
def get_args():
87+
def get_args(sys_args, auto_exit=True):
8888
parser = argparse.ArgumentParser(
8989
description='Traceroute with any packet. \
9090
Visualize the routes. Discover Middleboxes and Firewalls', formatter_class=argparse.RawTextHelpFormatter)
@@ -155,10 +155,10 @@ def get_args():
155155
help="set the target network interface")
156156
parser.add_argument('--show-ifaces', action='store_true',
157157
help="show the network interfaces (conf.route)")
158-
if len(sys.argv) == 1:
158+
if len(sys_args) == 0 and auto_exit:
159159
parser.print_help()
160160
sys.exit(1)
161-
args = parser.parse_args()
161+
args = parser.parse_args(sys_args)
162162
args_dict = process_input_args(args, parser)
163163
return args_dict
164164

@@ -339,4 +339,4 @@ def main(args):
339339

340340

341341
if __name__ == "__main__":
342-
main(get_args())
342+
main(get_args(sys.argv[1:]))

0 commit comments

Comments
 (0)