diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34289b3..a7856c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,9 @@ jobs: - name: flake8 run: pipenv run flake8 - name: pylint - run: pipenv run pylint src + run: | + pipenv run pylint src test + pipenv run pylint -d duplicate-code,invalid-name test/templates/*.py Coverage: needs: [Lint, Baseline] runs-on: ubuntu-latest diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..cd7cabf --- /dev/null +++ b/.pylintrc @@ -0,0 +1,6 @@ +[MESSAGES CONTROL] + +disable = fixme, # TODO/FIXME + line-too-long, # Assembly patterns do not fit + missing-function-docstring, + import-error, # import gdb diff --git a/test/templates/common.py b/test/templates/common.py index 5b5704c..520ca0e 100644 --- a/test/templates/common.py +++ b/test/templates/common.py @@ -1,31 +1,34 @@ +""" +Common code shared by all generators +""" + import subprocess import sys import re import os -_me = os.path.basename(__file__) -verbose = 0 +_ME = os.path.basename(__file__) def set_basename(file): - global _me - _me = file + global _ME # pylint: disable=global-statement + _ME = file def error(msg): """ Print nicely-formatted error message and exit. """ - sys.stderr.write(f'{_me}: error: {msg}\n') + sys.stderr.write(f'{_ME}: error: {msg}\n') sys.exit(1) -def _run(cmd, stdin=None): +def _run(cmd, stdin=None, verbose=0): """ Run process and abort on error. """ if verbose: - print(f"{_me}: running command: {' '.join(cmd)}") + print(f"{_ME}: running command: {' '.join(cmd)}") with subprocess.Popen(cmd, stdin=stdin, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as process: out, err = process.communicate() @@ -65,8 +68,8 @@ def strip_binary(file): _run(['strip', '-s', file]) -def grep(s, regex): - lines = s.split('\n') +def grep(text, regex): + lines = text.split('\n') return list(filter(lambda s: re.search(regex, s), lines)) diff --git a/test/test_parser.py b/test/test_parser.py index 6159b1d..7670ed2 100644 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -1,3 +1,7 @@ +""" +Unittests of asm2cfg's assembly parser +""" + import unittest from src.asm2cfg import asm2cfg @@ -9,6 +13,8 @@ def _get_the_source_block(blocks): class ParseLineTestCase(unittest.TestCase): + "Tests of parse_line function" + @unittest.expectedFailure def test_linear_sequence(self): lines = '''\ @@ -79,10 +85,10 @@ def test_conditional(self): self.assertEqual(len(dst_block.instructions), 2) @unittest.expectedFailure - def test_jumptables(): + def test_jumptables(self): lines = '''\ Dump of assembler code for function bar: - 0x0000000000001070 <+0>: endbr64 + 0x0000000000001070 <+0>: endbr64 0x0000000000001074 <+4>: cmp $0x9,%edi 0x0000000000001077 <+7>: ja 0x1120 0x000000000000107d <+13>: lea 0xf80(%rip),%rdx # 0x2004 @@ -96,44 +102,42 @@ def test_jumptables(): 0x000000000000109a <+42>: callq 0x1040 0x000000000000109f <+47>: mov $0xc,%eax 0x00000000000010a4 <+52>: add $0x8,%rsp - 0x00000000000010a8 <+56>: retq + 0x00000000000010a8 <+56>: retq 0x00000000000010a9 <+57>: nopl 0x0(%rax) 0x00000000000010b0 <+64>: lea 0xf49(%rip),%rdi # 0x2000 0x00000000000010b7 <+71>: xor %eax,%eax 0x00000000000010b9 <+73>: callq 0x1030 0x00000000000010be <+78>: mov $0xffffffe7,%eax 0x00000000000010c3 <+83>: add $0x8,%rsp - 0x00000000000010c7 <+87>: retq + 0x00000000000010c7 <+87>: retq 0x00000000000010c8 <+88>: nopl 0x0(%rax,%rax,1) 0x00000000000010d0 <+96>: mov $0xffffffe5,%eax 0x00000000000010d5 <+101>: add $0x8,%rsp - 0x00000000000010d9 <+105>: retq + 0x00000000000010d9 <+105>: retq 0x00000000000010da <+106>: nopw 0x0(%rax,%rax,1) 0x00000000000010e0 <+112>: mov $0x21,%eax 0x00000000000010e5 <+117>: add $0x8,%rsp - 0x00000000000010e9 <+121>: retq + 0x00000000000010e9 <+121>: retq 0x00000000000010ea <+122>: nopw 0x0(%rax,%rax,1) 0x00000000000010f0 <+128>: mov $0x23,%eax 0x00000000000010f5 <+133>: add $0x8,%rsp - 0x00000000000010f9 <+137>: retq + 0x00000000000010f9 <+137>: retq 0x00000000000010fa <+138>: nopw 0x0(%rax,%rax,1) 0x0000000000001100 <+144>: mov $0x63,%eax 0x0000000000001105 <+149>: add $0x8,%rsp - 0x0000000000001109 <+153>: retq + 0x0000000000001109 <+153>: retq 0x000000000000110a <+154>: nopw 0x0(%rax,%rax,1) 0x0000000000001110 <+160>: mov $0xa,%eax 0x0000000000001115 <+165>: add $0x8,%rsp - 0x0000000000001119 <+169>: retq + 0x0000000000001119 <+169>: retq 0x000000000000111a <+170>: nopw 0x0(%rax,%rax,1) - 0x0000000000001120 <+176>: retq + 0x0000000000001120 <+176>: retq ''' _, blocks = asm2cfg.parse_lines(lines, False) # TODO: special block for indirect jumps self.assertEqual(len(blocks), 2) - # TODO: # - functions (with and w/o calls) - # - jumptables # - skip calls diff --git a/test/test_regex.py b/test/test_regex.py index a267047..8caefbc 100644 --- a/test/test_regex.py +++ b/test/test_regex.py @@ -1,9 +1,15 @@ +""" +Unittests of asm2cfg's regexes +""" + import unittest from src.asm2cfg import asm2cfg class FunctionHeaderTestCase(unittest.TestCase): + "Tests of function header regex" + def test_gdb_unstripped(self): line = 'Dump of assembler code for function test_function:' strip, fun = asm2cfg.get_stripped_and_function_name(line) @@ -28,6 +34,8 @@ def test_objdump(self): class CallPatternTestCase(unittest.TestCase): + "Tests of call instruction regex" + def setUp(self): self.strip_regex = asm2cfg.get_call_pattern(True) self.unstrip_regex = asm2cfg.get_call_pattern(False) @@ -144,6 +152,8 @@ def test_objdump_indirect_call(self): class JumpPatternTestCase(unittest.TestCase): + "Tests of jump instruction regex" + def test_gdb_stripped(self): line = '0x000055555555600f: jmp 0x55555555603d' pattern = asm2cfg.get_jump_pattern(True, 'does_not_matter')