diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index d422990..4655613 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -8,39 +8,40 @@ jobs: - uses: actions/checkout@v2 - name: Install dependencies run: | - pip install black[python27] isort + python -m pip install --upgrade pip + python -m pip install -r requirements-dev.txt - name: Verify code run: | - black --check -t py27 . + black --check -t py36 . build: name: Build & test with Py runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.5, 3.9] + python-version: [3.6, 3.7, 3.8, 3.9, "3.10"] steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install flake8 scapy - if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi - - name: Lint with flake8 - run: | - # stop the build if there are Python syntax errors or undefined names - flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude .git,__pycache__,build,dist - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude .git,__pycache__,build,dist - - name: Test with pytest - run: | - python -m unittest discover test/ - - name: Build a wheel package - run: | - python -m pip install setuptools wheel && python setup.py sdist bdist_wheel + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install scapy==2.4.5 + if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude .git,__pycache__,build,dist + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude .git,__pycache__,build,dist + - name: Test with unittest + run: | + make test + - name: Build a wheel package + run: | + python -m pip install setuptools wheel && python setup.py sdist bdist_wheel check_version: name: Check local version with the available at pypi.org runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 1746daf..2243506 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ scapy_helper.egg-info/* __pycache__/* **/__pycache__ venv*/ +.pytest_cache/* +.mypy_cache/* diff --git a/Makefile b/Makefile index dd56a31..17dad1b 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,9 @@ +.PHONY: build +build: + @echo "Building..." + python setup.py sdist bdist_wheel --universal + @echo "Building... Done" + .PHONY: clean clean: @echo "Cleaning..." @@ -7,20 +13,22 @@ clean: .PHONY: format format: @echo "Formatting..." - python -m black -t py27 . + python -m black -t py36 . python -m isort packet_helper/ scapy_helper/ test/ --profile black @echo "Formatting... Done" -.PHONY: build -build: - @echo "Building..." - python setup.py sdist bdist_wheel --universal - @echo "Building... Done" - .PHONY: publish publish: clean format build twine upload dist/* +.PHONY: test +test: + python -m pytest + +.PHONY: test-mypy +test-mypy: + python -m mypy scapy_helper/ test/ + .PHONY: version version: python tools/version_checker.py diff --git a/VERSION b/VERSION index 0adfe40..e9780dc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.14.7 \ No newline at end of file +1.0.0a0 \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt index 843990b..be2664a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,9 @@ -r requirements.txt -mock~=3.0.5 +mock==4.0.3 requests==2.22.0 +mypy==0.971 +isort==5.10.1 +black==22.6.0 +flake8==5.0.4 +pytest==6.2.5 diff --git a/scapy_helper/chexdump.py b/scapy_helper/chexdump.py index b58f251..5945cb7 100644 --- a/scapy_helper/chexdump.py +++ b/scapy_helper/chexdump.py @@ -1,7 +1,11 @@ +from typing import Any, List, Optional, Union + from scapy_helper.main import _prepare -def chexdump(packet, dump=False, to_list=False): +def chexdump( + packet: Any, dump: bool = False, to_list: bool = False +) -> Optional[Union[List[str], str]]: """ Return a chexdump base on packet :param packet: String or Scapy object @@ -10,12 +14,12 @@ def chexdump(packet, dump=False, to_list=False): :return: None or Str or List """ - def _add_0x_before(p): + def _add_0x_before(p: List[str]) -> List[str]: return ["0x%s" % x for x in p] processed_packet = _prepare(packet) if not processed_packet: - return + return None if dump: if to_list: @@ -23,3 +27,4 @@ def _add_0x_before(p): else: return ", ".join(_add_0x_before(processed_packet)) print(", ".join(_add_0x_before(processed_packet))) + return None diff --git a/scapy_helper/compare.py b/scapy_helper/compare.py index 3a77ff4..6812f9e 100644 --- a/scapy_helper/compare.py +++ b/scapy_helper/compare.py @@ -1,28 +1,30 @@ +from typing import Any, Optional, Tuple + from tabulate import tabulate from scapy_helper.main import get_hex, show_diff class Compare: - def __init__(self, first, second): + def __init__(self, first: Any, second: Any): self.first = first self.second = second - def equal(self): + def equal(self) -> bool: """ Return true if booth elements are equal :return: bool """ return not self.diff() - def hex(self): + def hex(self) -> Tuple[str, str]: """ Return tuple with hex elements :return: Tuple(str, str) """ return get_hex(self.first), get_hex(self.second) - def diff(self): + def diff(self) -> bool: """ Show differences between two packets :return: bool: Return True if packets are NOT EQUAL @@ -30,18 +32,20 @@ def diff(self): print("This is temporary -- will be changed in the future") return show_diff(self.first, self.second) - def tdiff(self): + def tdiff(self) -> bool: """[Shortcut] Wrapper for the table_diff""" - self.table_diff() + return self.table_diff() - def table_diff(self, index=False): + def table_diff(self, index: bool = False) -> bool: """ Print a difference and print table information about packets :param index: Default=False, If True show position under the differ position :return: bool: Return True if packets are NOT EQUAL """ - def prepare_data(first, second): + def prepare_data( + first, second + ) -> Tuple[Optional[str], Optional[str], Optional[str], Optional[str]]: if "=" in first and "=" in second: column_a = first.split("=") column_b = second.split("=") @@ -55,7 +59,7 @@ def prepare_data(first, second): self._print_table(prepare_data) return status - def _print_table(self, prepare_data): + def _print_table(self, prepare_data) -> None: """ Print table base on prepared data :param prepare_data: @@ -63,7 +67,10 @@ def _print_table(self, prepare_data): """ f_details = self.first.show(dump=True).split("\n") s_details = self.second.show(dump=True).split("\n") - data = [("Diff or header", "Element", "First", "Second")] - for r in range(len(f_details)): - data.append(prepare_data(f_details[r], s_details[r])) - print(tabulate(data, headers="firstrow", tablefmt="github")) + + data = ("Diff or header", "Element", "First", "Second") + details_data = [ + prepare_data(f_details[r], s_details[r]) for r in range(len(f_details)) + ] + + print(tabulate([data, *details_data], headers="firstrow", tablefmt="github")) diff --git a/scapy_helper/helpers/utils.py b/scapy_helper/helpers/utils.py index 5e6cba6..a28f44d 100644 --- a/scapy_helper/helpers/utils.py +++ b/scapy_helper/helpers/utils.py @@ -1,14 +1,12 @@ -__values = (int, float, str, bytes, bool, list, tuple, set, dict) - - def _layer2dict(frame): temp_dict = {} + __values = (int, float, str, bytes, bool, list, tuple, set, dict) if not getattr(frame, "fields_desc", None): return for _field in frame.fields_desc: value = getattr(frame, _field.name) - if isinstance(value, type(None)): + if isinstance(value, type(None)): # type: ignore value = None elif not isinstance(value, __values): value = _layer2dict(value) diff --git a/scapy_helper/hexdump.py b/scapy_helper/hexdump.py index fcf2ff0..4016e0b 100644 --- a/scapy_helper/hexdump.py +++ b/scapy_helper/hexdump.py @@ -1,25 +1,27 @@ +from typing import Any, List, Optional, Union + from scapy_helper.main import _prepare, get_hex -def hexdump(packet, dump=False, to_list=False): - def to_number(number): +def hexdump(packet, dump=False, to_list=False) -> Optional[Union[str, list[str]]]: + def to_number(number: Union[str, int]) -> int: return number if isinstance(number, int) else ord(number) - def to_char(string): + def to_char(string: Union[str, int]) -> str: j = to_number(string) if (j < 32) or (j >= 127): return "." else: return chr(j) - def split(obj, num): + def split(obj, num) -> List[Any]: return [ obj[start : start + num] for start in range(0, len(obj), num) if obj[start : start + num] ] - def __process_hexdump(ppacket): + def __process_hexdump(ppacket: Any) -> List[str]: row = [] for i, line in enumerate(split(ppacket, 16)): console_char = [to_char(int(x, 16)) for x in line] @@ -29,7 +31,7 @@ def __process_hexdump(ppacket): row.append("%03x0 %s %s" % (i, " ".join(line), "".join(console_char))) return row - def __alternative_process_hexdump(ppacket): + def __alternative_process_hexdump(ppacket: Any) -> List[str]: row = [] for i, line in enumerate(split(ppacket, 16)): console_char = [to_char(x) for x in line] @@ -39,7 +41,7 @@ def __alternative_process_hexdump(ppacket): row.append("%03x0 %s %s" % (i, " ".join(line), "".join(console_char))) return row - def __third_process_hexdump(ppacket): + def __third_process_hexdump(ppacket: Any) -> List[str]: row = [] for i, line in enumerate(split(ppacket, 16)): console_char = [to_char(x) for x in line] @@ -48,17 +50,18 @@ def __third_process_hexdump(ppacket): row.append("%03x0 %s %s" % (i, " ".join(line), "".join(console_char))) return row + processed_packet: Union[str, list[str]] try: processed_packet = _prepare(packet) if not processed_packet: - return + return None rows = __process_hexdump(processed_packet) except ValueError: - # It's not a ideal workaround for detecting a proper + # It's not an ideal workaround for detecting a proper # format of input, but works on Py2 & Py3 processed_packet = get_hex(packet) if not processed_packet: - return + return None try: rows = __alternative_process_hexdump(processed_packet) except TypeError: @@ -68,3 +71,4 @@ def __third_process_hexdump(ppacket): return rows return "\n".join(rows).rstrip("\n") print("\n".join(rows).rstrip("\n")) + return None diff --git a/scapy_helper/main.py b/scapy_helper/main.py index 574d020..faf806f 100644 --- a/scapy_helper/main.py +++ b/scapy_helper/main.py @@ -1,9 +1,10 @@ import sys +from typing import Any, List, Optional, Tuple from scapy_helper.helpers.depracated import deprecated -def diff(*args, **kwargs): +def diff(*args, **kwargs) -> Tuple[List[Any], List[Any], List[int]]: """ Return a diff between two hex list :param args: @@ -16,14 +17,11 @@ def diff(*args, **kwargs): if len(args) != 2: raise NotImplementedError("Only comparison of the two list are supported") - result_list = ([], []) - diff_indexes = [] - diff_list = [] - - for a in args: - diff_list.append(_prepare(a)) + diff_list = [_prepare(a) for a in args] _fill_empty_elements(*diff_list) + diff_indexes = [] + result_list: Tuple[List[Any], List[Any]] = ([], []) for e, _ in enumerate(diff_list[0]): if diff_list[0][e].lower() != diff_list[1][e].lower(): for i in range(2): @@ -39,7 +37,7 @@ def diff(*args, **kwargs): return result_list[0], result_list[1], diff_indexes -def _fill_empty_elements(first, second): +def _fill_empty_elements(first, second) -> None: if len(first) != len(second): print("WARN:: Frame len is not the same") @@ -47,67 +45,61 @@ def _fill_empty_elements(first, second): len_second = len(second) if len_first > len_second: print("WARN:: First row is longer by the %sB\n" % (len_first - len_second)) - for x in range(len_first - len_second): + for _ in range(len_first - len_second): second.append(" ") else: print("WARN:: Second row is longer by the %sB\n" % (len_second - len_first)) - for x in range(len_second - len_first): + for _ in range(len_second - len_first): first.append(" ") + return None -def _prepare(obj): - def isbytesarray(): +def _prepare(obj) -> List[str]: + def is_byte_array() -> List[str]: b = obj.split() if len(b) == 1: return get_hex(obj).split() return b - import sys - if sys.version_info > (3, 5): # TODO very naive hack, but should be ok as temporary fix if isinstance(obj, bytes): - return isbytesarray() + return is_byte_array() if hasattr(obj, "hex"): return obj.hex().split() if not isinstance(obj, str): obj = get_hex(obj) if isinstance(obj, str): - obj = isbytesarray() + obj = is_byte_array() return obj -def get_hex(frame, uppercase=False): +def get_hex(frame: Any, uppercase: bool = False) -> str: """ Return a string object of Hex representation of the Scapy's framework :param frame: Scapy's Packet Object :param uppercase: bool: If True letters be UPPERCASE :return: str: Hex """ - if sys.version_info.major == 2: - import binascii - - str_hex = binascii.b2a_hex(bytes(frame)) - else: - try: - str_hex = bytes(frame).hex() - except TypeError: - str_hex = bytes(frame, encoding="utf-8").hex() - j = [] - for e, i in enumerate(str_hex): - if e % 2: - j.append(str_hex[e - 1] + str_hex[e]) + try: + str_hex = bytes(frame).hex() + except TypeError: + str_hex = bytes(frame, encoding="utf-8").hex() + hex_list: List[str] = [ + str_hex[e - 1] + str_hex[e] for e, _ in enumerate(str_hex) if e % 2 + ] if uppercase: - return " ".join(j).upper() - return " ".join(j).lower() + return " ".join(hex_list).upper() + return " ".join(hex_list).lower() -def show_hex(frame, uppercase=False): +def show_hex(frame, uppercase: bool = False) -> None: """Print a hex representation of the Scapy's object""" print(get_hex(frame, uppercase=uppercase)) + return None -def _create_diff_indexes_list(indexes, max_len): +def _create_diff_indexes_list(indexes, max_len) -> str: new_list = [] for idx in range(max_len): @@ -125,14 +117,16 @@ def _create_diff_indexes_list(indexes, max_len): return " ".join(new_list) -def __process_char(char): +def __process_char(char: Optional[str] = "") -> str: multiplier = 2 if not char or not isinstance(char, str): return "X" * multiplier return char[0] * multiplier -def show_diff(first, second, index=False, extend=False, empty_char="XX"): +def show_diff( + first, second, index: bool = False, extend: bool = False, empty_char: str = "XX" +) -> bool: def get_name(module): if isinstance(module, str): return "hex" @@ -200,7 +194,7 @@ def get_name(module): return False -def show_diff_full(first, second, index=True, extend=False, empty_char="XX"): +def show_diff_full(first, second, index=True, extend=False, empty_char="XX") -> bool: first_row, second_row, indexes_of_diff = diff(first, second, skip_if_same=False) first_row_len_bytes = count_bytes(first_row) second_row_len_bytes = count_bytes(second_row) @@ -248,11 +242,11 @@ def show_diff_full(first, second, index=True, extend=False, empty_char="XX"): return False -def count_bytes(packet_hex_list): +def count_bytes(packet_hex_list) -> int: return len([x for x in packet_hex_list if x != " "]) -def hex_equal(first, second, show_inequalities=True, **kwargs): +def hex_equal(first, second, show_inequalities=True, **kwargs) -> bool: """ Compare a two hex or hex-able Scapy objects. Return a True if objects are equal :param first: str hex or Scapy object @@ -263,27 +257,27 @@ def hex_equal(first, second, show_inequalities=True, **kwargs): """ _, _, status = diff(first, second) if show_inequalities and status: - show_diff(first, second, **kwargs) + _ = show_diff(first, second, **kwargs) # diff returns a list of position on which the object is different - # if status is a empty Tuple, there's no difference between objects + # if status is empty Tuple, there's no difference between objects if status: return False return True -def get_diff(first, second): +def get_diff(first, second) -> Tuple[List[Any], List[Any], List[int]]: """This function is the wrapper for the diff. For backward compatibility""" return diff(first, second) -def get_diff_status(first, second): +def get_diff_status(first, second) -> List[int]: _, _, status = diff(first, second) return status @deprecated -def table(first, second): - f, s, status = diff(first, second) +def table(first, second) -> List[int]: + _, _, status = diff(first, second) show_diff(first, second) f_details = first.show(dump=True).split("\n") s_details = second.show(dump=True).split("\n") diff --git a/scapy_helper/test_case_extensions/assert_print.py b/scapy_helper/test_case_extensions/assert_print.py new file mode 100644 index 0000000..f2ea92d --- /dev/null +++ b/scapy_helper/test_case_extensions/assert_print.py @@ -0,0 +1,21 @@ +from functools import wraps + + +def assert_print(results): + def wrapper(func): + @wraps(func) + def wrapped(self, *func_args, **func_kwargs): + import sys + from io import StringIO + + old_stdout = sys.stdout + sys.stdout = buffer = StringIO() + + func(self, *func_args, **func_kwargs) + assert results == buffer.getvalue() + + sys.stdout = old_stdout + + return wrapped + + return wrapper diff --git a/scapy_helper/utils/hhstrip.py b/scapy_helper/utils/hhstrip.py index 14ab069..3302c50 100644 --- a/scapy_helper/utils/hhstrip.py +++ b/scapy_helper/utils/hhstrip.py @@ -1,7 +1,7 @@ from scapy_helper.utils.hstrip import hstrip -def hhstrip(*args, **kwargs): +def hhstrip(*args, **kwargs) -> None: try: one_line_hex = hstrip(raw=False) if one_line_hex: diff --git a/scapy_helper/utils/hstrip.py b/scapy_helper/utils/hstrip.py index 4203cd8..a20c2d7 100644 --- a/scapy_helper/utils/hstrip.py +++ b/scapy_helper/utils/hstrip.py @@ -1,7 +1,8 @@ from os import getenv +from typing import Optional try: - import pyperclip + import pyperclip # type: ignore except ImportError: print("Missing pyperclip: pip install pyperclip") @@ -13,7 +14,7 @@ """ -def hstrip(raw=True, hexdump=None): +def hstrip(raw: bool = True, hexdump=None) -> Optional[str]: """ Clean a Scapies hexdump into more friendly "oneliner" :param raw: If False, return oneliner @@ -36,16 +37,11 @@ def hstrip(raw=True, hexdump=None): index_to_start = int(striped_value[0].index("0000")) except ValueError: print("Something went wrong. Do you copy entire fragment of hexdump?") - return + return None - _list = [] - for x in striped_value: - _list.append(x[index_to_start + 1 : index_to_start + 17]) - else: - if len(_list[-1]) < 18: - _list[-1] = _list[-1][ - :-1 - ] # remove last element if the list is shorter than 18 + _list = [x[index_to_start + 1 : index_to_start + 17] for x in striped_value] + if len(_list[-1]) < 18: + _list[-1] = _list[-1][:-1] # remove last element if the list is shorter than 18 oneliner = " ".join([" ".join(x) for x in _list]) if not hexdump: diff --git a/setup.py b/setup.py index 7efadb2..5d1edc9 100644 --- a/setup.py +++ b/setup.py @@ -24,16 +24,13 @@ "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX :: Linux", "Operating System :: Microsoft :: Windows", - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Topic :: Software Development :: Libraries :: Python Modules", ], license="MIT", diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/_utils.py b/test/_utils.py new file mode 100644 index 0000000..82fa459 --- /dev/null +++ b/test/_utils.py @@ -0,0 +1,14 @@ +from typing import List + + +def normalize(text: str, lowercase: bool = False) -> str: + """Remove whitespaces and new line characters from text""" + for char in (" ", "\n"): + text = text.replace(char, "") + if lowercase: + text = text.lower() + return text + + +def normalize_and_split(text: str, split_char: str = "\n") -> List[str]: + return text.replace(" ", "").split(split_char) diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..b4135b6 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,12 @@ +import pytest +from scapy.all import Ether, Packet # type: ignore + + +@pytest.fixture +def default_ether() -> Packet: + return Ether() + + +@pytest.fixture +def ether() -> Packet: + return Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:00:00:00:00") diff --git a/test/helpers/test_ip.py b/test/helpers/test_ip.py index 79b0bc0..394a5b8 100644 --- a/test/helpers/test_ip.py +++ b/test/helpers/test_ip.py @@ -1,18 +1,16 @@ -from unittest import TestCase +import pytest from scapy_helper.helpers.ip import int2ip, ip2int +INTEGER = 3232235521 +IP = "192.168.0.1" -class TestIP(TestCase): - def setUp(self): - self.integer = 3232235521 - self.ip = "192.168.0.1" - def test_ip2int(self): - self.assertEqual(ip2int(self.ip), self.integer, "Values should be equal") +@pytest.mark.parametrize("value, expected", ((IP, INTEGER), ("0.0.0.0", 0))) +def test_ip2int(value: str, expected: int) -> None: + assert ip2int(value) == expected, "Values should be equal" - def test_int2ip(self): - self.assertEqual(int2ip(self.integer), self.ip, "Values should be equal") - def test_int2ip_for_zero(self): - self.assertEqual(int2ip(0), "0.0.0.0", "Values should be equal") +@pytest.mark.parametrize("value, expected_string_ip", ((INTEGER, IP), (0, "0.0.0.0"))) +def test_int2ip(value: int, expected_string_ip: str) -> None: + assert int2ip(value) == expected_string_ip, "Values should be equal" diff --git a/test/test_case_extensions/test_pytest_assert.py b/test/test_case_extensions/test_pytest_assert.py index 900a3dc..23ceff5 100644 --- a/test/test_case_extensions/test_pytest_assert.py +++ b/test/test_case_extensions/test_pytest_assert.py @@ -46,8 +46,10 @@ def test_assert_hex_len_not_equal(self): with self.assertRaises(AssertionError): assert_hex_len_not_equal(DEFAULT_HEX, DEFAULT_HEX) + # TODO add # def test_assert_bytes_equal(self): # self.fail() - # + + # TODO add # def test_assert_bytes_not_equal(self): # self.fail() diff --git a/test/test_chexdump.py b/test/test_chexdump.py index 5518aa1..cc2ad82 100644 --- a/test/test_chexdump.py +++ b/test/test_chexdump.py @@ -1,27 +1,22 @@ -import unittest - -from scapy.layers.l2 import Ether +from scapy.all import Ether, Packet # type: ignore from scapy_helper.chexdump import chexdump -class TestcHexdump(unittest.TestCase): - def setUp(self): - self.packet = Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:00:00:00:00") - - def test_chexdump(self): +class TestCHexdump: + def test_chexdump(self) -> None: # TODO Add a verification of the output packet = "\x00\x01".encode() - chexdump(packet) + assert not chexdump(packet) - def test_chexdump_dump_true(self): + def test_chexdump_dump_true(self, ether: Packet) -> None: expected_result = "0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00" - result = chexdump(self.packet, dump=True) + result = chexdump(ether, dump=True) - self.assertIsInstance(result, str, "Dump should be a string") - self.assertEqual(expected_result, result, "Dump of the hex should be the same") + assert isinstance(result, str), "Dump should be a string" + assert expected_result == result, "Dump of the hex should be the same" - def test_chexdump_dump_true_as_list(self): + def test_chexdump_dump_true_as_list(self, ether: Packet) -> None: expected_result = [ "0xff", "0xff", @@ -38,7 +33,7 @@ def test_chexdump_dump_true_as_list(self): "0x90", "0x00", ] - result = chexdump(self.packet, dump=True, to_list=True) + result = chexdump(ether, dump=True, to_list=True) - self.assertIsInstance(result, list, "Dump should be a string") - self.assertEqual(result, expected_result, "Dump of the hex should be the same") + assert isinstance(result, list), "Dump should be a string" + assert result == expected_result, "Dump of the hex should be the same" diff --git a/test/test_compare.py b/test/test_compare.py index 3acbe81..388a379 100644 --- a/test/test_compare.py +++ b/test/test_compare.py @@ -1,13 +1,12 @@ import unittest -from scapy.layers.inet import IP, UDP -from scapy.layers.l2 import Ether +from scapy.all import IP, UDP, Ether # type: ignore from scapy_helper.compare import Compare class TestCompare(unittest.TestCase): - def setUp(self): + def setUp(self) -> None: self.ether = ( Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:00:00:00:00") / IP(src="192.168.1.1", dst="192.168.1.20", ttl=15) @@ -21,10 +20,10 @@ def setUp(self): self.compare = Compare(self.ether, self.c_ether) - def test_table_diff(self): + def test_table_diff(self) -> None: self.compare.table_diff() - def test_hex(self): + def test_hex(self) -> None: self.assertEqual( self.compare.hex(), ( @@ -37,8 +36,10 @@ def test_hex(self): ), ) - def test_diff(self): + def test_diff(self) -> None: + # FIXME use a pytest and check that function was called self.assertTrue(self.compare.diff(), "diff() should return a difference") - def test_equal(self): + def test_equal(self) -> None: + # FIXME use a pytest and check that function was called self.assertFalse(self.compare.equal(), "equal() should return false") diff --git a/test/test_hexdump.py b/test/test_hexdump.py index 4357267..892e4a5 100644 --- a/test/test_hexdump.py +++ b/test/test_hexdump.py @@ -1,30 +1,34 @@ -import unittest +from test._utils import normalize, normalize_and_split -from scapy.layers.inet import IP -from scapy.layers.l2 import Ether -from scapy.utils import hexdump as scap_hexdump +import pytest +from scapy.all import IP, Ether, Packet # type: ignore +from scapy.utils import hexdump as scapy_hexdump # type: ignore from scapy_helper.hexdump import hexdump +packet_hex = ( + "ff ff ff ff ff ff 00 00 00 00 00 00 08 00 " + "45 00 00 14 00 01 00 00 40 00 fb e8 00 00 " + "00 00 7f 00 00 01" +) + + +@pytest.fixture +def ether_ip() -> Packet: + # FIXME: Provide non-default values + return Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:00:00:00:00") / IP() -class TestHexdump(unittest.TestCase): - def setUp(self): - self.packet = Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:00:00:00:00") / IP() - self.packet_hex = ( - "ff ff ff ff ff ff 00 00 00 00 00 00 08 00 " - "45 00 00 14 00 01 00 00 40 00 fb e8 00 00 " - "00 00 7f 00 00 01" - ) +class TestHexdump: def test_hexdump_dump_from_string(self): expected_result = ( "0000ffffffffffff00000000000008004500..............E." "00100014000100004000fbe8000000007f00......@........." "00200001.." ) - result = hexdump(self.packet_hex, dump=True).replace(" ", "").replace("\n", "") - hexdump(self.packet_hex) - self.assertEqual(expected_result, result, "String should be the same") + result = normalize(hexdump(packet_hex, dump=True)) + hexdump(packet_hex) + assert expected_result == result, "String should be the same" def test_hexdump_dump_from_string_as_list(self): expected_result = [ @@ -32,50 +36,34 @@ def test_hexdump_dump_from_string_as_list(self): "00100014000100004000fbe8000000007f00......@.........", "00200001..", ] - result = [ - x.replace(" ", "").replace("\n", "") - for x in hexdump(self.packet_hex, dump=True, to_list=True) - ] + result = [normalize(x) for x in hexdump(packet_hex, dump=True, to_list=True)] expected_result = [x.lower() for x in expected_result] result = [x.lower() for x in result] - self.assertIsInstance(result, list, "Dump should be a list") - self.assertEqual(expected_result, result, "Dump of the hex should be the same") + assert isinstance(result, list), "Dump should be a list" + assert expected_result == result, "Dump of the hex should be the same" def test_hexdump_can_convert_string_on_input(self): string = "\x00\x01".encode() expected_result = "00000001.." - result = hexdump(string, dump=True) - self.assertEqual( - result.replace(" ", ""), expected_result, "Dump of hex should be the same" - ) + result = normalize(hexdump(string, dump=True)) + assert result == expected_result, "Dump of hex should be the same" - def test_hexdump_dump_true(self): - expected_result = ( - scap_hexdump(self.packet, dump=True).replace(" ", "").replace("\n", "") - ) - result = hexdump(self.packet, dump=True).replace(" ", "").replace("\n", "") + def test_hexdump_dump_true(self, ether_ip: Packet): + expected_result = normalize(scapy_hexdump(ether_ip, dump=True), lowercase=True) + result = normalize(hexdump(ether_ip, dump=True), lowercase=True) - self.assertIsInstance(result, str, "Dump should be a string") - self.assertEqual( - expected_result.lower(), - result.lower(), - "Dump of the hex should be the same", - ) + assert isinstance(result, str), "Dump should be a string" + assert expected_result == result, "Dump of the hex should be the same" - def test_hexdump_dump_true_as_list(self): - expected_result = ( - scap_hexdump(self.packet, dump=True).replace(" ", "").split("\n") - ) - result = [ - x.replace(" ", "").replace("\n", "") - for x in hexdump(self.packet, dump=True, to_list=True) - ] + def test_hexdump_dump_true_as_list(self, ether_ip: Packet): + expected_result = scapy_hexdump(ether_ip, dump=True) + result = [normalize(x) for x in hexdump(ether_ip, dump=True, to_list=True)] - expected_result = [x.lower() for x in expected_result] + expected_result = [x.lower() for x in normalize_and_split(expected_result)] result = [x.lower() for x in result] - self.assertIsInstance(result, list, "Dump should be a list") - self.assertEqual(expected_result, result, "Dump of the hex should be the same") + assert isinstance(result, list), "Dump should be a list" + assert expected_result == result, "Dump of the hex should be the same" diff --git a/test/test_main.py b/test/test_main.py index 0c5bda2..fc5ba15 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -1,216 +1,194 @@ -from unittest import TestCase - +import pytest from mock import MagicMock -from scapy.layers.l2 import Ether -from scapy.utils import chexdump +from scapy.all import Ether, Packet, chexdump # type: ignore from scapy_helper import get_hex, show_diff from scapy_helper.main import _prepare, diff, get_diff, hex_equal -class TestScapyHelper(TestCase): - def setUp(self): - self.ether = Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:00:00:00:00") - self.ether2 = Ether(dst="00:00:00:00:00:00", src="00:00:00:00:00:00") +def remove_byte_char(frame) -> str: + return " ".join( + [ + x.replace("0x", "").replace(",", "") + for x in chexdump(frame, dump=True).split() + ] + ) - def test_get_diff_should_be_the_same_as_diff(self): - fun_diff = diff(self.ether, self.ether2) - fun_get_diff = get_diff(self.ether, self.ether2) - self.assertEqual( - fun_diff, fun_get_diff, "The result of get_diff and diff should be the same" - ) + +# FIXME change the name of fixtures +class TestScapyHelper: + def test_get_diff_should_be_the_same_as_diff(self, ether: Packet): + ether_2 = Ether(dst="00:00:00:00:00:00", src="00:00:00:00:00:00") + fun_diff = diff(ether, ether_2) + fun_get_diff = get_diff(ether, ether_2) + assert ( + fun_diff == fun_get_diff + ), "The result of get_diff and diff should be the same" def test_diff_list_equal(self): result = diff( "ff ff ff ff ff ff 00 00 00 00 00 00 90 00", "ff ff ff ff ff ff 00 00 00 00 00 00 90 00", ) - self.assertEqual( - result, - ( - [ - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - ], - [ - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - ], - [], - ), - "Results should be equal", - ) + assert result == ( + [ + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + ], + [ + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + ], + [], + ), "Results should be equal" def test_diff_list_should_be_diff_at_6_position(self): result = diff( "ff ff ff ff ff ff 11 00 00 00 00 00 90 00", "ff ff ff ff ff ff 00 00 00 00 00 00 90 00", ) - self.assertEqual( - result, - ( - [ - "__", - "__", - "__", - "__", - "__", - "__", - "11", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - ], - [ - "__", - "__", - "__", - "__", - "__", - "__", - "00", - "__", - "__", - "__", - "__", - "__", - "__", - "__", - ], - [6], - ), - "Results should be equal", - ) - - def test_diff_refactor(self): - with self.assertRaises(NotImplementedError): - diff("00 11", "00 11", "00 00") + assert result == ( + [ + "__", + "__", + "__", + "__", + "__", + "__", + "11", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + ], + [ + "__", + "__", + "__", + "__", + "__", + "__", + "00", + "__", + "__", + "__", + "__", + "__", + "__", + "__", + ], + [6], + ), "Results should be equal" - with self.assertRaises(NotImplementedError): - diff("00 11") + @pytest.mark.parametrize("value", (("00 11", "00 11", "00 00"), ("00 11",))) + def test_diff_refactor(self, value): + with pytest.raises(NotImplementedError): + diff(*value) - def test_get_hex(self): - assert get_hex(self.ether) == "ff ff ff ff ff ff 00 00 00 00 00 00 90 00" + def test_get_hex(self, ether: Packet): + assert get_hex(ether) == "ff ff ff ff ff ff 00 00 00 00 00 00 90 00" - def test_get_hex_uppercase(self): - self.assertEqual( - get_hex(self.ether, uppercase=True), - "FF FF FF FF FF FF 00 00 00 00 00 00 90 00", + def test_get_hex_uppercase(self, ether: Packet): + assert ( + get_hex(ether, uppercase=True) + == "FF FF FF FF FF FF 00 00 00 00 00 00 90 00" ) - def test_show_diff(self): + def test_show_diff(self, ether: Packet): ether_wrong = "ff ff ff ff ff ff 00 00 00 00 aa 00 90 00" - first_row, second_row, status = diff(self.ether, ether_wrong) + first_row, second_row, status = diff(ether, ether_wrong) - self.assertEqual(first_row, "__ __ __ __ __ __ __ __ __ __ 00 __ __ __".split()) - self.assertEqual( - second_row, "__ __ __ __ __ __ __ __ __ __ aa __ __ __".split() - ) - self.assertTrue(status) - self.assertEqual( - status, - [ - 10, - ], - ) + assert first_row == "__ __ __ __ __ __ __ __ __ __ 00 __ __ __".split() + assert second_row == "__ __ __ __ __ __ __ __ __ __ aa __ __ __".split() + assert status + assert status == [ + 10, + ] - def test_show_diff_for_same_elements(self): - first_row, second_row, status = diff(self.ether, self.ether) + def test_show_diff_for_same_elements(self, ether: Packet): + first_row, second_row, status = diff(ether, ether) - self.assertEqual(first_row, "__ __ __ __ __ __ __ __ __ __ __ __ __ __".split()) - self.assertEqual( - second_row, "__ __ __ __ __ __ __ __ __ __ __ __ __ __".split() - ) - self.assertFalse(status) + assert first_row == "__ __ __ __ __ __ __ __ __ __ __ __ __ __".split() + assert second_row == "__ __ __ __ __ __ __ __ __ __ __ __ __ __".split() + assert not status - def test_show_more_diff(self): - show_diff(self.ether, "ff ff ff ff ff ff 00 00 00 00 00 00 90 00") + def test_show_more_diff(self, ether: Packet): + show_diff(ether, "ff ff ff ff ff ff 00 00 00 00 00 00 90 00") - def test_diff_should_return_3_elements(self): + def test_diff_should_return_3_elements(self, ether: Packet): too_long_ether = get_hex(Ether() / "additional string") - diff_tuple = diff(self.ether, too_long_ether) - self.assertEqual(len(diff_tuple), 3, "_diff should return only a 3 elements") + diff_tuple = diff(ether, too_long_ether) + assert len(diff_tuple) == 3, "_diff should return only a 3 elements" - def test_diff_should_return_same_len_list_if_one_of_them_was_shorter(self): + def test_diff_should_return_same_len_list_if_one_of_them_was_shorter( + self, ether: Packet + ): too_long_ether = get_hex(Ether() / "additional string") - diff_tuple = diff(self.ether, too_long_ether) - self.assertEqual( - len(diff_tuple[0]), len(diff_tuple[1]), "List should be the same length" - ) + diff_tuple = diff(ether, too_long_ether) + assert len(diff_tuple[0]) == len( + diff_tuple[1] + ), "List should be the same length" - self.assertNotEqual(len(get_hex(self.ether).split()), len(diff_tuple[0])) - self.assertEqual(len(too_long_ether.split()), len(diff_tuple[1])) + assert len(get_hex(ether).split()) != len(diff_tuple[0]) + assert len(too_long_ether.split()) == len(diff_tuple[1]) - def test_hex_equal(self): - self.assertTrue( - hex_equal(self.ether, "ff ff ff ff ff ff 00 00 00 00 00 00 90 00") - ) + def test_hex_equal(self, ether: Packet): + assert hex_equal(ether, "ff ff ff ff ff ff 00 00 00 00 00 00 90 00") - def test_hex_equal_with_show_diff_options(self): + def test_hex_equal_with_show_diff_options(self, ether: Packet): options = {"index": True} - self.assertFalse( - hex_equal( - self.ether, - "ff ff ff ff ff ff 0a 00 00 00 00 00 90 00", - show_inequalities=False, - **options - ) + assert not hex_equal( + ether, + "ff ff ff ff ff ff 0a 00 00 00 00 00 90 00", + show_inequalities=False, + **options, ) def test__prepare(self): obj = MagicMock() obj.hex = MagicMock(return_value="ff ff ff") - self.assertEqual( - _prepare(obj), - ["ff", "ff", "ff"], - "Object method hex, should return str of hex", - ) + assert _prepare(obj) == [ + "ff", + "ff", + "ff", + ], "Object method hex, should return str of hex" def test__prepare_encoded(self): obj = "\x00\x01".encode() - self.assertEqual( - _prepare(obj), ["00", "01"], "Object method hex, should return str of hex" - ) - - def test_chexdump_native_support(self): - frame_chexdump = TestScapyHelper.old_method(self.ether) - self.assertEqual(frame_chexdump, get_hex(self.ether)) - - @staticmethod - def old_method(frame): - return " ".join( - [ - x.replace("0x", "").replace(",", "") - for x in chexdump(frame, dump=True).split() - ] - ) + assert _prepare(obj) == [ + "00", + "01", + ], "Object method hex, should return str of hex" + + def test_chexdump_native_support(self, ether: Packet): + frame_chexdump = remove_byte_char(ether) + assert frame_chexdump == get_hex(ether) diff --git a/test/test_packet_assert.py b/test/test_packet_assert.py index 595c865..3591b14 100644 --- a/test/test_packet_assert.py +++ b/test/test_packet_assert.py @@ -1,8 +1,7 @@ from unittest import TestCase from mock import MagicMock -from scapy.layers.inet import IP -from scapy.layers.l2 import Ether +from scapy.all import IP, Ether # type: ignore from scapy_helper.test_case_extensions.packet_assert import PacketAssert diff --git a/test/test_show_diff.py b/test/test_show_diff.py index cacf4d7..7657e72 100644 --- a/test/test_show_diff.py +++ b/test/test_show_diff.py @@ -1,47 +1,51 @@ -from unittest import TestCase - -from scapy.layers.l2 import Ether +from scapy.all import Ether, Packet # type: ignore from scapy_helper.main import show_diff +from scapy_helper.test_case_extensions.assert_print import assert_print -class TestShowDiff(TestCase): - def setUp(self): - self.ether_1 = Ether() - - def test_show_diff(self): +class TestShowDiff: + @assert_print( + results=( + "scapy | ff ff ff ff ff ff __ __ __ __ __ __ __ __ | len: 14B\n" + "scapy | 00 11 00 00 00 00 __ __ __ __ __ __ __ __ | len: 14B\n" + "\n" + "Not equal at 6B's\n" + ) + ) + def test_show_diff(self, ether: Packet): ether_2 = Ether(dst="00:11:00:00:00:00") - show_diff(self.ether_1, ether_2) + show_diff(ether, ether_2) - def test_show_diff__w_additional_str(self): + def test_show_diff__w_additional_str(self, ether: Packet): ether_2 = Ether(dst="00:11:00:00:00:00") / "additional str" - show_diff(self.ether_1, ether_2) + show_diff(ether, ether_2) - def test_show_diff_between_ether_obj_v_str(self): + def test_show_diff_between_ether_obj_v_str(self, ether: Packet): ether_2 = "ff ff fc ff ff fa 00 11 11 11 11 11 90 00 11 11 00 22" - show_diff(self.ether_1, ether_2) + show_diff(ether, ether_2) - def test_show_diff_with_indexes(self): + def test_show_diff_with_indexes(self, ether: Packet): ether_2 = "ff ff fc ff ff fa 00 11 11 11 11 11 90 00 11 11 00 22" - show_diff(self.ether_1, ether_2, index=True) + show_diff(ether, ether_2, index=True) - def test_show_diff_with_indexes_and_custom_empty_char(self): + def test_show_diff_with_indexes_and_custom_empty_char(self, ether: Packet): ether_2 = "ff ff fc ff ff fa 00 11 11 11 11 11 90 00 11 11 00 22" - show_diff(self.ether_1, ether_2, index=True, empty_char="++") + show_diff(ether, ether_2, index=True, empty_char="++") - def test_show_diff_with_indexes_and_custom_too_long_empty_char(self): + def test_show_diff_with_indexes_and_custom_too_long_empty_char(self, ether: Packet): ether_2 = "ff ff fc ff ff fa 00 11 11 11 11 11 90 00 11 11 00 22" show_diff( - self.ether_1, + ether, ether_2, index=True, empty_char="+Only Plus Char should be used", ) - def test_show_diff_with_indexes_and_custom_char_none(self): + def test_show_diff_with_indexes_and_custom_char_none(self, ether: Packet): ether_2 = "ff ff fc ff ff fa 00 11 11 11 11 11 90 00 11 11 00 22" - show_diff(self.ether_1, ether_2, index=True, empty_char=None) + show_diff(ether, ether_2, index=True, empty_char="") - def test_show_diff_with_indexes_proper_len_of_last_position(self): + def test_show_diff_with_indexes_proper_len_of_last_position(self, ether: Packet): ether_2 = "ff ff fc ff ff fa 00 11 11 11 11 11 90 00" - show_diff(self.ether_1, ether_2, index=True, empty_char=None) + show_diff(ether, ether_2, index=True, empty_char="") diff --git a/test/test_to_dict.py b/test/test_to_dict.py index e366cfe..e083745 100644 --- a/test/test_to_dict.py +++ b/test/test_to_dict.py @@ -1,13 +1,10 @@ -from unittest import TestCase - -from scapy.layers.inet import IP, TCP -from scapy.layers.l2 import Ether +from scapy.all import IP, TCP, Ether # type: ignore from scapy_helper.helpers.to_dict import to_dict -class TestToDict(TestCase): - def test_simple_dict(self): +class TestToDict: + def test_simple_dict(self) -> None: packet = ( Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:00:00:00:00") / IP(src="0.0.0.0", dst="127.0.0.1") @@ -23,8 +20,8 @@ def test_simple_dict(self): to_dict_result = to_dict(packet) - self.assertTrue(isinstance(to_dict_result, dict)) - self.assertEqual(to_dict_result, packet_result) + assert isinstance(to_dict_result, dict) + assert to_dict_result == packet_result def test_simple_dict_get_second_element(self): packet = ( @@ -52,5 +49,5 @@ def test_simple_dict_get_second_element(self): to_dict_result = to_dict(packet, layer=1) # layer 1 is IP - self.assertTrue(isinstance(to_dict_result, dict)) - self.assertEqual(to_dict_result, packet_result) + assert isinstance(to_dict_result, dict) + assert to_dict_result == packet_result diff --git a/test/test_to_list.py b/test/test_to_list.py index 000b968..25ea808 100644 --- a/test/test_to_list.py +++ b/test/test_to_list.py @@ -1,13 +1,10 @@ -from unittest import TestCase - -from scapy.layers.inet import IP, TCP -from scapy.layers.l2 import Ether +from scapy.all import IP, TCP, Ether # type: ignore from scapy_helper import to_list -class TestToList(TestCase): - def test_simple_list(self): +class TestToList: + def test_simple_list(self) -> None: packet = ( Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:00:00:00:00") / IP(src="0.0.0.0", dst="127.0.0.1") @@ -57,5 +54,5 @@ def test_simple_list(self): to_list_result = to_list(packet) - self.assertTrue(isinstance(to_list_result, list)) - self.assertEqual(to_list_result, packet_result) + assert isinstance(to_list_result, list) + assert to_list_result == packet_result diff --git a/test/utils/test_hstrip.py b/test/utils/test_hstrip.py index b2a79e6..b9ad30a 100644 --- a/test/utils/test_hstrip.py +++ b/test/utils/test_hstrip.py @@ -1,6 +1,4 @@ -from unittest import TestCase - -from pyperclip import PyperclipException +import pyperclip # type: ignore from scapy_helper.utils.hstrip import hstrip @@ -19,16 +17,15 @@ ) -class TestHStrip(TestCase): - def test_hstrip(self): - import pyperclip +def test_hstrip(): + try: + pyperclip.copy(HEXDUMP_VALUE) + except pyperclip.PyperclipException: + # some CI's block access to the clipboard + return True - try: - pyperclip.copy(HEXDUMP_VALUE) - except PyperclipException: - return True + assert hstrip(raw=False) == HSTRIP_RESULT - self.assertEqual(hstrip(raw=False), HSTRIP_RESULT) - def test_hstrip_from_hexdump(self): - self.assertEqual(hstrip(raw=False, hexdump=HEXDUMP_VALUE), HSTRIP_RESULT) +def test_hstrip_from_hexdump(): + assert hstrip(raw=False, hexdump=HEXDUMP_VALUE) == HSTRIP_RESULT diff --git a/tools/version_checker.py b/tools/version_checker.py index 7a25ff1..62ed9bb 100644 --- a/tools/version_checker.py +++ b/tools/version_checker.py @@ -1,18 +1,18 @@ -from typing import Dict, Any +from typing import Any, Dict import requests # noqa -def local_version(): +def local_version() -> str: with open("./VERSION") as vr: - version = vr.read() # type: str + version = vr.read() return version.lstrip().rstrip() -def latest_version_on_pypi(): - pypi_info = requests.request( +def latest_version_on_pypi() -> str: + pypi_info: Dict[str, Any] = requests.request( "get", "https://pypi.org/pypi/scapy-helper/json" - ).json() # type: Dict[str, Any] + ).json() return pypi_info.get("info").get("version")