diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7b6c5d7..79f4893 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,5 +19,6 @@ jobs: name: fzakaria authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - run: nix develop --command make lint + - run: nix develop --command make test - run: nix build --print-build-logs - run: nix run . -- --help \ No newline at end of file diff --git a/Makefile b/Makefile index 701c143..32d3292 100644 --- a/Makefile +++ b/Makefile @@ -26,4 +26,8 @@ lint: ## Run pep8, black, mypy linters. # TODO(fzakaria): without pythonpath it picks up the wrong python # and then does not find the venv for the imports pyright --pythonpath $(shell which python) - nixpkgs-fmt --check . \ No newline at end of file + nixpkgs-fmt --check . + +.PHONY: test +test: ## Run pytest primarily. + pytest \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 7831931..169ae17 100644 --- a/poetry.lock +++ b/poetry.lock @@ -167,6 +167,21 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "exceptiongroup" +version = "1.1.3" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, + {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, +] + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "flake8" version = "6.1.0" @@ -184,6 +199,18 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.11.0,<2.12.0" pyflakes = ">=3.1.0,<3.2.0" +[[package]] +name = "iniconfig" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] + [[package]] name = "isort" version = "5.12.0" @@ -314,6 +341,22 @@ files = [ docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +[[package]] +name = "pluggy" +version = "1.3.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + [[package]] name = "pycodestyle" version = "2.11.0" @@ -357,6 +400,29 @@ nodeenv = ">=1.6.0" all = ["twine (>=3.4.1)"] dev = ["twine (>=3.4.1)"] +[[package]] +name = "pytest" +version = "7.4.0" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} + +[package.extras] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] + [[package]] name = "setuptools" version = "68.1.2" @@ -389,4 +455,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = ">=3.10" -content-hash = "838653258e74177629af577ca8ec4cebaa7b06ab0fac57c0cce39c2319db4bbd" +content-hash = "89fac12455f97e1a220b2f47c76549829e90082685010ad7e8af9e33f052a2ea" diff --git a/pyproject.toml b/pyproject.toml index b4ab9ab..135166a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ black = "^23.7.0" isort = "^5.12.0" flake8 = "^6.1.0" pyright = "^1.1.325" +pytest = "^7.4.0" [build-system] requires = ["poetry-core"] diff --git a/sqlelf/cli.py b/sqlelf/cli.py index 55409f5..8ca9e19 100644 --- a/sqlelf/cli.py +++ b/sqlelf/cli.py @@ -1,5 +1,6 @@ import argparse import os +import sys from functools import reduce import apsw @@ -10,7 +11,14 @@ from .elf import dynamic, header, instruction, section, strings, symbol -def start(): +def start(args=sys.argv[1:], stdin=sys.stdin): + """ + Start the main CLI + + Args: + args: the command line arguments to parse + stdin: the stdin to use if invoking the shell + """ parser = argparse.ArgumentParser( prog="sqlelf", description="Analyze ELF files with the power of SQL", @@ -23,7 +31,7 @@ def start(): "-s", "--sql", help="Potential SQL to execute. Omitting this enters the REPL." ) - args = parser.parse_args() + args = parser.parse_args(args) # Iterate through our arguments and if one of them is a directory explode it out filenames: list[str] = reduce( @@ -52,7 +60,7 @@ def start(): strings.register(connection, binaries) instruction.register(connection, binaries) - shell = apsw.shell.Shell(db=connection) + shell = apsw.shell.Shell(db=connection, stdin=stdin) if args.sql: shell.process_complete_line(args.sql) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..1a08a21 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,21 @@ +from sqlelf import cli +import pytest +from io import StringIO + +def test_cli_bad_arguments(): + with pytest.raises(SystemExit): + cli.start(["--does-not-exist"]) + +def test_cli_no_arguments(): + with pytest.raises(SystemExit): + cli.start([]) + +def test_cli_single_file_arguments(): + stdin = StringIO("") + cli.start(["/bin/ls"], stdin) + +def test_cli_prompt_single_file_arguments(): + stdin = StringIO(".exit 56\n") + with pytest.raises(SystemExit) as err: + cli.start(["/bin/ls"], stdin) + assert err.value.code == 56 \ No newline at end of file