diff --git a/.editorconfig b/.editorconfig index eafade25..111e989c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,6 @@ trim_trailing_whitespace = true indent_style = tab indent_size = 4 -[*.yml,*.yaml] +[*.{yml,yaml}] indent_style = space indent_size = 2 diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..accc53c1 --- /dev/null +++ b/.flake8 @@ -0,0 +1,9 @@ +[flake8] +doctests = True +ignore = + # W503 and W504 are mutually exclusive, so one or the other must be ignored. + # PEP 8 recommends line break before, so we keep W504. + W503 +max-complexity = 10 +max-line-length = 120 +select = E,W,F,C,N diff --git a/.github/workflows/lint-python.yml b/.github/workflows/lint-python.yml new file mode 100644 index 00000000..2310d07a --- /dev/null +++ b/.github/workflows/lint-python.yml @@ -0,0 +1,40 @@ +name: Lint Python code + +on: + push: + paths: + - "**.py" + - ".flake8" + - "pyproject.toml" + - "Taskfile.yml" + pull_request: + paths: + - "**.py" + - ".flake8" + - "pyproject.toml" + - "Taskfile.yml" + +jobs: + lint-python: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Taskfile + uses: arduino/actions/setup-taskfile@master + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + version: 3.x + + - name: Install Python + uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: Install Poetry + run: pip install poetry + + - name: Lint Python files + run: task python:check diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a447ef77..4344a9ff 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,6 +10,9 @@ on: - "**/*.go" - "**/testdata/**" - "etc/schemas/**/*.json" + - "pyproject.toml" + - "test/**" + - "Taskfile.yml" pull_request: paths: - ".github/workflows/test.yml" @@ -19,6 +22,9 @@ on: - "**/*.go" - "**/testdata/**" - "etc/schemas/**/*.json" + - "pyproject.toml" + - "test/**" + - "Taskfile.yml" jobs: test-go: @@ -57,3 +63,14 @@ jobs: - name: Run unit tests run: task go:test-unit + + - name: Install Python + uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: Install Poetry + run: pip install poetry + + - name: Run integration tests + run: task test-integration diff --git a/.gitignore b/.gitignore index b492b071..5e90cb94 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Build artifacts arduino-lint arduino-lint.exe +__pycache__/ # Test artifacts coverage_unit.txt diff --git a/Taskfile.yml b/Taskfile.yml index 90415e6f..c57bf9be 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -10,6 +10,7 @@ tasks: desc: Run tests cmds: - task: go:test-unit + - task: test-integration - task: schema:compile go:generate: @@ -27,6 +28,12 @@ tasks: cmds: - go test -short -run '{{ default ".*" .TEST_REGEX }}' {{ default "-v" .GOFLAGS }} -coverprofile=coverage_unit.txt {{ default .DEFAULT_PACKAGES .PACKAGES }} + test-integration: + desc: Run integration tests + cmds: + - poetry install --no-root + - poetry run pytest test + schema:compile: desc: Compile JSON schema cmds: @@ -36,6 +43,7 @@ tasks: desc: Lint and check formatting of all files cmds: - task: go:check + - task: python:check - task: docs:check - task: config:check - task: check-spelling @@ -44,6 +52,7 @@ tasks: desc: Lint all files cmds: - task: go:lint + - task: python:lint - task: docs:lint - task: config:lint @@ -58,6 +67,7 @@ tasks: desc: Format all files cmds: - task: go:format + - task: python:format - task: docs:format - task: config:format @@ -88,6 +98,22 @@ tasks: cmds: - gofmt -l -w {{ default .DEFAULT_PATHS .PATHS }} + python:check: + cmds: + - task: python:lint + + python:lint: + desc: Lint Python code + cmds: + - poetry install --no-root + - poetry run flake8 + + python:format: + desc: Automatically formats Python files + cmds: + - poetry install --no-root + - poetry run black . + docs:check: desc: Lint and check formatting of documentation files cmds: diff --git a/poetry.lock b/poetry.lock index 77ec89e4..49137bbe 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,18 +1,482 @@ +[[package]] +category = "dev" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +name = "appdirs" +optional = false +python-versions = "*" +version = "1.4.4" + +[[package]] +category = "main" +description = "Atomic file writes." +marker = "sys_platform == \"win32\"" +name = "atomicwrites" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.4.0" + +[[package]] +category = "main" +description = "Classes Without Boilerplate" +name = "attrs" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.3.0" + +[package.extras] +dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] + +[[package]] +category = "dev" +description = "The uncompromising code formatter." +name = "black" +optional = false +python-versions = ">=3.6" +version = "20.8b1" + +[package.dependencies] +appdirs = "*" +click = ">=7.1.2" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.6,<1" +regex = ">=2020.1.8" +toml = ">=0.10.1" +typed-ast = ">=1.4.0" +typing-extensions = ">=3.7.4" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] + +[[package]] +category = "dev" +description = "Composable command line interface toolkit" +name = "click" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "7.1.2" + [[package]] category = "dev" description = "Codespell" name = "codespell" optional = false +python-versions = ">=3.5" +version = "2.0.0" + +[package.extras] +dev = ["check-manifest", "flake8", "pytest", "pytest-cov", "pytest-dependency"] +hard-encoding-detection = ["chardet"] + +[[package]] +category = "main" +description = "Cross-platform colored terminal text." +marker = "sys_platform == \"win32\"" +name = "colorama" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.4.4" + +[[package]] +category = "dev" +description = "the modular source code checker: pep8 pyflakes and co" +name = "flake8" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +version = "3.8.4" + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.6.0a1,<2.7.0" +pyflakes = ">=2.2.0,<2.3.0" + +[[package]] +category = "dev" +description = "Polyfill package for Flake8 plugins" +name = "flake8-polyfill" +optional = false +python-versions = "*" +version = "1.0.2" + +[package.dependencies] +flake8 = "*" + +[[package]] +category = "main" +description = "iniconfig: brain-dead simple config-ini parsing" +name = "iniconfig" +optional = false +python-versions = "*" +version = "1.1.1" + +[[package]] +category = "main" +description = "Pythonic task execution" +name = "invoke" +optional = false python-versions = "*" -version = "1.17.1" +version = "1.4.1" + +[[package]] +category = "dev" +description = "McCabe checker, plugin for flake8" +name = "mccabe" +optional = false +python-versions = "*" +version = "0.6.1" + +[[package]] +category = "dev" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +name = "mypy-extensions" +optional = false +python-versions = "*" +version = "0.4.3" + +[[package]] +category = "main" +description = "Core utilities for Python packages" +name = "packaging" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "20.7" + +[package.dependencies] +pyparsing = ">=2.0.2" + +[[package]] +category = "dev" +description = "Utility library for gitignore style pattern matching of file paths." +name = "pathspec" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +version = "0.8.1" + +[[package]] +category = "dev" +description = "Check PEP-8 naming conventions, plugin for flake8" +name = "pep8-naming" +optional = false +python-versions = "*" +version = "0.11.1" + +[package.dependencies] +flake8-polyfill = ">=1.0.2,<2" + +[[package]] +category = "main" +description = "plugin and hook calling mechanisms for python" +name = "pluggy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.13.1" + +[package.extras] +dev = ["pre-commit", "tox"] + +[[package]] +category = "main" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "py" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.9.0" + +[[package]] +category = "dev" +description = "Python style guide checker" +name = "pycodestyle" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.6.0" + +[[package]] +category = "dev" +description = "passive checker of Python programs" +name = "pyflakes" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.2.0" + +[[package]] +category = "main" +description = "Python parsing module" +name = "pyparsing" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "2.4.7" + +[[package]] +category = "main" +description = "pytest: simple powerful testing with Python" +name = "pytest" +optional = false +python-versions = ">=3.5" +version = "6.1.2" + +[package.dependencies] +atomicwrites = ">=1.0" +attrs = ">=17.4.0" +colorama = "*" +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<1.0" +py = ">=1.8.2" +toml = "*" + +[package.extras] +checkqa_mypy = ["mypy (0.780)"] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +category = "main" +description = "Extensions to the standard Python datetime module" +name = "python-dateutil" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +version = "2.8.1" + +[package.dependencies] +six = ">=1.5" + +[[package]] +category = "dev" +description = "Alternative regular expression module, to replace re." +name = "regex" +optional = false +python-versions = "*" +version = "2020.11.13" + +[[package]] +category = "main" +description = "Python helper for Semantic Versioning (http://semver.org/)" +name = "semver" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.13.0" + +[[package]] +category = "main" +description = "Python 2 and 3 compatibility utilities" +name = "six" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +version = "1.15.0" + +[[package]] +category = "main" +description = "Python Library for Tom's Obvious, Minimal Language" +name = "toml" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +version = "0.10.2" + +[[package]] +category = "dev" +description = "a fork of Python 2 and 3 ast modules with type comment support" +name = "typed-ast" +optional = false +python-versions = "*" +version = "1.4.1" + +[[package]] +category = "dev" +description = "Backported and Experimental Type Hints for Python 3.5+" +name = "typing-extensions" +optional = false +python-versions = "*" +version = "3.7.4.3" [metadata] -content-hash = "0de193fa2f0268b58dac39b8ef306448c38f414041d05c8b81f3bb7d6fcf7738" +content-hash = "a780e54598223d8e692fd0c0349f945d5a84852d4cccba583bc3cd18b9723cfe" lock-version = "1.0" python-versions = "^3.8" [metadata.files] +appdirs = [ + {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, + {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, +] +black = [ + {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, +] +click = [ + {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, + {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, +] codespell = [ - {file = "codespell-1.17.1-py3-none-any.whl", hash = "sha256:0ba864d8b25c4dc1b2a3fb067377ec11edb84c1a967a33d9e9cac73f386f3a0a"}, - {file = "codespell-1.17.1.tar.gz", hash = "sha256:25a2ecd86b9cdc111dc40a30d0ed28c578e13a0ce158d1c383f9d47811bfcd23"}, + {file = "codespell-2.0.0-py3-none-any.whl", hash = "sha256:a10b8bbb9f678e4edff7877af1f654fdc9e27c205f952c3ddee2981ad02ec5f2"}, + {file = "codespell-2.0.0.tar.gz", hash = "sha256:dd9983e096b9f7ba89dd2d2466d1fc37231d060f19066331b9571341363c77b8"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +flake8 = [ + {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, + {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, +] +flake8-polyfill = [ + {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, + {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +invoke = [ + {file = "invoke-1.4.1-py2-none-any.whl", hash = "sha256:93e12876d88130c8e0d7fd6618dd5387d6b36da55ad541481dfa5e001656f134"}, + {file = "invoke-1.4.1-py3-none-any.whl", hash = "sha256:87b3ef9d72a1667e104f89b159eaf8a514dbf2f3576885b2bbdefe74c3fb2132"}, + {file = "invoke-1.4.1.tar.gz", hash = "sha256:de3f23bfe669e3db1085789fd859eb8ca8e0c5d9c20811e2407fa042e8a5e15d"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +packaging = [ + {file = "packaging-20.7-py2.py3-none-any.whl", hash = "sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376"}, + {file = "packaging-20.7.tar.gz", hash = "sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236"}, +] +pathspec = [ + {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, + {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, +] +pep8-naming = [ + {file = "pep8-naming-0.11.1.tar.gz", hash = "sha256:a1dd47dd243adfe8a83616e27cf03164960b507530f155db94e10b36a6cd6724"}, + {file = "pep8_naming-0.11.1-py2.py3-none-any.whl", hash = "sha256:f43bfe3eea7e0d73e8b5d07d6407ab47f2476ccaeff6937c84275cd30b016738"}, +] +pluggy = [ + {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, + {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, +] +py = [ + {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"}, + {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"}, +] +pycodestyle = [ + {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, + {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, +] +pyflakes = [ + {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, + {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, +] +pyparsing = [ + {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, + {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, +] +pytest = [ + {file = "pytest-6.1.2-py3-none-any.whl", hash = "sha256:4288fed0d9153d9646bfcdf0c0428197dba1ecb27a33bb6e031d002fa88653fe"}, + {file = "pytest-6.1.2.tar.gz", hash = "sha256:c0a7e94a8cdbc5422a51ccdad8e6f1024795939cc89159a0ae7f0b316ad3823e"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, + {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, +] +regex = [ + {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, + {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, + {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, + {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, + {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, + {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, + {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, + {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, + {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, + {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, + {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, + {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, + {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, +] +semver = [ + {file = "semver-2.13.0-py2.py3-none-any.whl", hash = "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4"}, + {file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"}, +] +six = [ + {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, + {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] +typed-ast = [ + {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, + {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, + {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, + {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, + {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, + {file = "typed_ast-1.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f"}, + {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, + {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, + {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, + {file = "typed_ast-1.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298"}, + {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, + {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, + {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, + {file = "typed_ast-1.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d"}, + {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, + {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, + {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, + {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c"}, + {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072"}, + {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91"}, + {file = "typed_ast-1.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d"}, + {file = "typed_ast-1.4.1-cp39-cp39-win32.whl", hash = "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395"}, + {file = "typed_ast-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c"}, + {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, +] +typing-extensions = [ + {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, + {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, + {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, ] diff --git a/pyproject.toml b/pyproject.toml index 3c389ea7..678914de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,17 @@ authors = ["Arduino "] [tool.poetry.dependencies] python = "^3.8" +invoke = "1.4.1" +pytest = "6.1.2" +python-dateutil = "^2.8.1" +semver = "^2.13.0" [tool.poetry.dev-dependencies] +black = { version = "^20.8b1", allow-prereleases = true } codespell = ">=1.17.1" +flake8 = "^3.8.4" +pep8-naming = "^0.11.1" + +[tool.black] +line-length = 120 +target-version = ["py38"] diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 00000000..716032ff --- /dev/null +++ b/test/__init__.py @@ -0,0 +1,14 @@ +# This file is part of arduino-lint. +# +# Copyright 2020 ARDUINO SA(http: // www.arduino.cc/) +# +# This software is released under the GNU General Public License version 3, +# which covers the main part of arduino-lint. +# The terms of this license can be found at: +# https: // www.gnu.org/licenses/gpl-3.0.en.html +# +# You can be released from the requirements of the above licenses by purchasing +# a commercial license. Buying such a license is mandatory if you want to +# modify or otherwise use the software for commercial activities involving the +# Arduino software without disclosing the source code of your own applications. +# To purchase a commercial license, send an email to license@arduino.cc. diff --git a/test/pytest.ini b/test/pytest.ini new file mode 100644 index 00000000..d3f2009f --- /dev/null +++ b/test/pytest.ini @@ -0,0 +1,9 @@ +[pytest] +filterwarnings = + error + ignore::DeprecationWarning + ignore::ResourceWarning + +# --capture=no - disable per-test capture +# --tb=long sets the length of the traceback in case of failures +addopts = --capture=no --tb=long --verbose diff --git a/test/test_all.py b/test/test_all.py new file mode 100644 index 00000000..4a901104 --- /dev/null +++ b/test/test_all.py @@ -0,0 +1,227 @@ +# This file is part of arduino-lint. +# +# Copyright 2020 ARDUINO SA(http: // www.arduino.cc/) +# +# This software is released under the GNU General Public License version 3, +# which covers the main part of arduino-lint. +# The terms of this license can be found at: +# https: // www.gnu.org/licenses/gpl-3.0.en.html +# +# You can be released from the requirements of the above licenses by purchasing +# a commercial license. Buying such a license is mandatory if you want to +# modify or otherwise use the software for commercial activities involving the +# Arduino software without disclosing the source code of your own applications. +# To purchase a commercial license, send an email to license@arduino.cc. +import json +import pathlib +import platform +import typing + +import dateutil.parser +import invoke.context +import pytest +import semver + +test_data_path = pathlib.Path(__file__).resolve().parent.joinpath("testdata") + + +def test_defaults(run_command): + result = run_command(cmd=[], custom_working_dir=test_data_path.joinpath("recursive")) + assert result.ok + + +@pytest.mark.parametrize( + "project_folder, compliance_level", + [("Strict", "strict"), ("Specification", "specification"), ("Permissive", "permissive"), ("Invalid", None)], +) +def test_compliance(run_command, project_folder, compliance_level): + project_path = test_data_path.joinpath("compliance", project_folder) + expected_ok = False + for compliance_setting in ["strict", "specification", "permissive"]: + if compliance_setting == compliance_level: + expected_ok = True + + result = run_command(cmd=["--compliance", compliance_setting, project_path]) + assert result.ok == expected_ok + + +def test_compliance_invalid(run_command): + result = run_command(cmd=["--compliance", "foo", test_data_path.joinpath("ValidSketch")]) + assert not result.ok + + +def test_format(run_command): + project_path = test_data_path.joinpath("ValidSketch") + result = run_command(cmd=["--format", "text", project_path]) + assert result.ok + with pytest.raises(json.JSONDecodeError): + json.loads(result.stdout) + + result = run_command(cmd=["--format", "json", project_path]) + assert result.ok + json.loads(result.stdout) + + result = run_command(cmd=["--format", "foo", project_path]) + assert not result.ok + + +def test_help(run_command): + result = run_command(cmd=["--help"]) + assert result.ok + assert "Usage:" in result.stdout + + +@pytest.mark.parametrize( + "project_folder, expected_exit_statuses", + [ + ("Submit", {"submit": 0, "update": 1, "false": 0}), + ("Update", {"submit": 1, "update": 0, "false": 0}), + ("False", {"submit": 1, "update": 1, "false": 0}), + ("Invalid", {"submit": 1, "update": 1, "false": 1}), + ], +) +def test_library_manager(run_command, project_folder, expected_exit_statuses): + project_path = test_data_path.joinpath("library-manager", project_folder) + for library_manager_setting, expected_exit_status in expected_exit_statuses.items(): + result = run_command(cmd=["--library-manager", library_manager_setting, project_path]) + assert result.exited == expected_exit_status + + +def test_library_manager_invalid(run_command): + result = run_command(cmd=["--library-manager", "foo", test_data_path.joinpath("ValidSketch")]) + assert not result.ok + + +@pytest.mark.parametrize( + "project_folder, expected_exit_statuses", + [ + ("Sketch", {"sketch": 0, "library": 1, "platform": 1, "package-index": 1, "all": 0}), + ("Library", {"sketch": 1, "library": 0, "platform": 1, "package-index": 1, "all": 0}), + ("Platform", {"sketch": 1, "library": 1, "platform": 0, "package-index": 1, "all": 0}), + ("PackageIndex", {"sketch": 1, "library": 1, "platform": 1, "package-index": 0, "all": 0}), + ], +) +def test_project_type(run_command, project_folder, expected_exit_statuses): + project_path = test_data_path.joinpath("project-type", project_folder) + for project_type, expected_exit_status in expected_exit_statuses.items(): + result = run_command(cmd=["--project-type", project_type, project_path]) + assert result.exited == expected_exit_status + + +def test_project_type_invalid(run_command): + result = run_command(cmd=["--project-type", "foo", test_data_path.joinpath("ValidSketch")]) + assert not result.ok + + +def test_recursive(run_command): + valid_projects_path = test_data_path.joinpath("recursive") + result = run_command(cmd=["--recursive", "true", valid_projects_path]) + assert result.ok + + result = run_command(cmd=["--recursive", "false", valid_projects_path]) + assert not result.ok + + +def test_recursive_invalid(run_command): + result = run_command(cmd=["--recursive", "foo", test_data_path.joinpath("ValidSketch")]) + assert not result.ok + + +def test_report_file(run_command, working_dir): + project_path = test_data_path.joinpath("ValidSketch") + report_file_name = "report.json" + result = run_command(cmd=["--report-file", report_file_name, project_path]) + assert result.ok + with pathlib.Path(working_dir, report_file_name).open() as report_file: + report = json.load(report_file) + + assert pathlib.PurePath(report["configuration"]["paths"][0]) == project_path + assert report["configuration"]["projectType"] == "all" + assert report["configuration"]["recursive"] + assert pathlib.PurePath(report["projects"][0]["path"]) == project_path + assert report["projects"][0]["projectType"] == "sketch" + assert report["projects"][0]["summary"]["pass"] + assert report["projects"][0]["summary"]["errorCount"] == 0 + assert report["summary"]["pass"] + assert report["summary"]["errorCount"] == 0 + + +def test_verbose(run_command): + project_path = test_data_path.joinpath("verbose", "HasWarnings") + result = run_command(cmd=["--format", "text", project_path]) + assert result.ok + assert "result: pass" not in result.stdout + assert "result: fail" in result.stdout + + result = run_command(cmd=["--format", "text", "--verbose", project_path]) + assert result.ok + assert "result: pass" in result.stdout + + result = run_command(cmd=["--format", "json", project_path]) + assert result.ok + report = json.loads(result.stdout) + assert True not in [check.get("result") == "pass" for check in report["projects"][0]["checks"]] + assert True in [check.get("result") == "fail" for check in report["projects"][0]["checks"]] + + result = run_command(cmd=["--format", "json", "--verbose", project_path]) + assert result.ok + report = json.loads(result.stdout) + assert True in [check.get("result") == "pass" for check in report["projects"][0]["checks"]] + assert True in [check.get("result") == "fail" for check in report["projects"][0]["checks"]] + + +def test_version(run_command): + result = run_command(cmd=["--version"]) + assert result.ok + output_list = result.stdout.strip().split(sep=" ") + assert semver.VersionInfo.isvalid(version=output_list[0]) + dateutil.parser.isoparse(output_list[1]) + + +@pytest.fixture(scope="function") +def run_command(pytestconfig, working_dir) -> typing.Callable[..., invoke.runners.Result]: + """Provide a wrapper around invoke's `run` API so that every test will work in the same temporary folder. + + Useful reference: + http://docs.pyinvoke.org/en/1.4/api/runners.html#invoke.runners.Result + """ + + arduino_lint_path = pathlib.Path(pytestconfig.rootdir).parent / "arduino-lint" + + def _run( + cmd: list, + custom_working_dir: typing.Optional[str] = None, + custom_env: typing.Optional[dict] = None + ) -> invoke.runners.Result: + if cmd is None: + cmd = [] + if not custom_working_dir: + custom_working_dir = working_dir + quoted_cmd = [] + for token in cmd: + quoted_cmd.append(f'"{token}"') + cli_full_line = '"{}" {}'.format(arduino_lint_path, " ".join(quoted_cmd)) + run_context = invoke.context.Context() + # It might happen that we need to change directories between drives on Windows, + # in that case the "/d" flag must be used otherwise directory wouldn't change + cd_command = "cd" + if platform.system() == "Windows": + cd_command += " /d" + # Context.cd() is not used since it doesn't work correctly on Windows. + # It escapes spaces in the path using "\ " but it doesn't always work, + # wrapping the path in quotation marks is the safest approach + with run_context.prefix(f'{cd_command} "{custom_working_dir}"'): + return run_context.run( + command=cli_full_line, echo=False, hide=True, warn=True, env=custom_env, encoding="utf-8" + ) + + return _run + + +@pytest.fixture(scope="function") +def working_dir(tmpdir_factory) -> str: + """Create a temporary folder for the test to run in. It will be created before running each test and deleted at the + end. This way all the tests work in isolation. + """ + work_dir = tmpdir_factory.mktemp(basename="ArduinoLintTestWork") + yield str(work_dir) diff --git a/test/testdata/InvalidSketch/Invalid Sketch.ino b/test/testdata/InvalidSketch/Invalid Sketch.ino new file mode 100644 index 00000000..e51f794f --- /dev/null +++ b/test/testdata/InvalidSketch/Invalid Sketch.ino @@ -0,0 +1,2 @@ +void setup() {] +void loop() {} diff --git a/test/testdata/ValidSketch/LICENSE b/test/testdata/ValidSketch/LICENSE new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/ValidSketch/README.md b/test/testdata/ValidSketch/README.md new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/ValidSketch/ValidSketch.ino b/test/testdata/ValidSketch/ValidSketch.ino new file mode 100644 index 00000000..e51f794f --- /dev/null +++ b/test/testdata/ValidSketch/ValidSketch.ino @@ -0,0 +1,2 @@ +void setup() {] +void loop() {} diff --git a/test/testdata/compliance/Invalid/Invalid Sketch.ino b/test/testdata/compliance/Invalid/Invalid Sketch.ino new file mode 100644 index 00000000..e51f794f --- /dev/null +++ b/test/testdata/compliance/Invalid/Invalid Sketch.ino @@ -0,0 +1,2 @@ +void setup() {] +void loop() {} diff --git a/test/testdata/compliance/Permissive/MismatchedFilename.ino b/test/testdata/compliance/Permissive/MismatchedFilename.ino new file mode 100644 index 00000000..e51f794f --- /dev/null +++ b/test/testdata/compliance/Permissive/MismatchedFilename.ino @@ -0,0 +1,2 @@ +void setup() {] +void loop() {} diff --git a/test/testdata/compliance/Specification/Specification.ino b/test/testdata/compliance/Specification/Specification.ino new file mode 100644 index 00000000..e51f794f --- /dev/null +++ b/test/testdata/compliance/Specification/Specification.ino @@ -0,0 +1,2 @@ +void setup() {] +void loop() {} diff --git a/test/testdata/compliance/Strict/LICENSE b/test/testdata/compliance/Strict/LICENSE new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/compliance/Strict/README.md b/test/testdata/compliance/Strict/README.md new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/compliance/Strict/Strict.ino b/test/testdata/compliance/Strict/Strict.ino new file mode 100644 index 00000000..e51f794f --- /dev/null +++ b/test/testdata/compliance/Strict/Strict.ino @@ -0,0 +1,2 @@ +void setup() {] +void loop() {} diff --git a/test/testdata/library-manager/False/False.h b/test/testdata/library-manager/False/False.h new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/library-manager/Invalid/.gitkeep b/test/testdata/library-manager/Invalid/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/library-manager/Submit/Submit.h b/test/testdata/library-manager/Submit/Submit.h new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/library-manager/Submit/library.properties b/test/testdata/library-manager/Submit/library.properties new file mode 100644 index 00000000..d131eff3 --- /dev/null +++ b/test/testdata/library-manager/Submit/library.properties @@ -0,0 +1,9 @@ +name=SomeUniqueLibraryName +version=1.0.0 +author=Cristian Maglie , Pippo Pluto +maintainer=Cristian Maglie +sentence=A library that makes coding a Webserver a breeze. +paragraph=Supports HTTP1.1 and you can do GET and POST. +category=Communication +url=http://example.com/ +architectures=avr diff --git a/test/testdata/library-manager/Update/Update.h b/test/testdata/library-manager/Update/Update.h new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/library-manager/Update/library.properties b/test/testdata/library-manager/Update/library.properties new file mode 100644 index 00000000..e78129d8 --- /dev/null +++ b/test/testdata/library-manager/Update/library.properties @@ -0,0 +1,9 @@ +name=Servo +version=1.1.7 +author=Michael Margolis, Arduino +maintainer=Arduino +sentence=Allows Arduino/Genuino boards to control a variety of servo motors. +paragraph=This library can control a great number of servos.
It makes careful use of timers: the library can control 12 servos using only 1 timer.
On the Arduino Due you can control up to 60 servos.
+category=Device Control +url=http://www.arduino.cc/en/Reference/Servo +architectures=avr,megaavr,sam,samd,nrf52,stm32f4,mbed diff --git a/test/testdata/project-type/Library/Library.h b/test/testdata/project-type/Library/Library.h new file mode 100644 index 00000000..e69de29b diff --git a/test/testdata/project-type/Library/library.properties b/test/testdata/project-type/Library/library.properties new file mode 100644 index 00000000..d131eff3 --- /dev/null +++ b/test/testdata/project-type/Library/library.properties @@ -0,0 +1,9 @@ +name=SomeUniqueLibraryName +version=1.0.0 +author=Cristian Maglie , Pippo Pluto +maintainer=Cristian Maglie +sentence=A library that makes coding a Webserver a breeze. +paragraph=Supports HTTP1.1 and you can do GET and POST. +category=Communication +url=http://example.com/ +architectures=avr diff --git a/test/testdata/project-type/PackageIndex/package_valid_index.json b/test/testdata/project-type/PackageIndex/package_valid_index.json new file mode 100644 index 00000000..22e5b452 --- /dev/null +++ b/test/testdata/project-type/PackageIndex/package_valid_index.json @@ -0,0 +1,68 @@ +{ + "packages": [ + { + "name": "myboard", + "maintainer": "Jane Developer", + "websiteURL": "https://github.com/janedeveloper/myboard", + "email": "jane@example.com", + "help": { + "online": "http://example.com/forum/myboard" + }, + "platforms": [ + { + "name": "My Board", + "architecture": "avr", + "version": "1.0.0", + "category": "Contributed", + "help": { + "online": "http://example.com/forum/myboard" + }, + "url": "https://janedeveloper.github.io/myboard/myboard-1.0.0.zip", + "archiveFileName": "myboard-1.0.0.zip", + "checksum": "SHA-256:ec3ff8a1dc96d3ba6f432b9b837a35fd4174a34b3d2927de1d51010e8b94f9f1", + "size": "15005", + "boards": [{ "name": "My Board" }, { "name": "My Board Pro" }], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "avr-gcc", + "version": "4.8.1-arduino5" + }, + { + "packager": "arduino", + "name": "avrdude", + "version": "6.0.1-arduino5" + } + ] + }, + { + "name": "My Board", + "architecture": "avr", + "version": "1.0.1", + "category": "Contributed", + "help": { + "online": "http://example.com/forum/myboard" + }, + "url": "https://janedeveloper.github.io/myboard/myboard-1.0.1.zip", + "archiveFileName": "myboard-1.0.1.zip", + "checksum": "SHA-256:9c86ee28a7ce9fe33e8b07ec643316131e0031b0d22e63bb398902a5fdadbca9", + "size": "15125", + "boards": [{ "name": "My Board" }, { "name": "My Board Pro" }], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "avr-gcc", + "version": "4.8.1-arduino5" + }, + { + "packager": "arduino", + "name": "avrdude", + "version": "6.0.1-arduino5" + } + ] + } + ], + "tools": [] + } + ] +} diff --git a/test/testdata/project-type/Platform/boards.txt b/test/testdata/project-type/Platform/boards.txt new file mode 100644 index 00000000..28dddb07 --- /dev/null +++ b/test/testdata/project-type/Platform/boards.txt @@ -0,0 +1,8 @@ +uno.name=Arduino Uno + +uno.build.core=arduino +uno.build.variant=standard + +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 diff --git a/test/testdata/project-type/Sketch/Sketch.ino b/test/testdata/project-type/Sketch/Sketch.ino new file mode 100644 index 00000000..660bdbcc --- /dev/null +++ b/test/testdata/project-type/Sketch/Sketch.ino @@ -0,0 +1,2 @@ +void setup() {} +void loop() {} diff --git a/test/testdata/recursive/SpecificationSketch/SpecificationSketch.ino b/test/testdata/recursive/SpecificationSketch/SpecificationSketch.ino new file mode 100644 index 00000000..e51f794f --- /dev/null +++ b/test/testdata/recursive/SpecificationSketch/SpecificationSketch.ino @@ -0,0 +1,2 @@ +void setup() {] +void loop() {} diff --git a/test/testdata/verbose/HasWarnings/HasWarnings.ino b/test/testdata/verbose/HasWarnings/HasWarnings.ino new file mode 100644 index 00000000..660bdbcc --- /dev/null +++ b/test/testdata/verbose/HasWarnings/HasWarnings.ino @@ -0,0 +1,2 @@ +void setup() {} +void loop() {}