diff --git a/.github/workflows/assets/validate-registry/tests/__init__.py b/.github/workflows/assets/validate-registry/tests/__init__.py new file mode 100644 index 00000000..795888f6 --- /dev/null +++ b/.github/workflows/assets/validate-registry/tests/__init__.py @@ -0,0 +1,13 @@ +# Source: +# https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/test-python/__init__.py +# Copyright 2021 ARDUINO SA (http://www.arduino.cc/) +# +# This software is released under the GNU General Public License version 3, +# 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/.github/workflows/assets/validate-registry/tests/pytest.ini b/.github/workflows/assets/validate-registry/tests/pytest.ini new file mode 100644 index 00000000..b8beed3f --- /dev/null +++ b/.github/workflows/assets/validate-registry/tests/pytest.ini @@ -0,0 +1,10 @@ +# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/test-python/pytest.ini +[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/.github/workflows/assets/validate-registry/tests/test_all.py b/.github/workflows/assets/validate-registry/tests/test_all.py new file mode 100644 index 00000000..91a1bdec --- /dev/null +++ b/.github/workflows/assets/validate-registry/tests/test_all.py @@ -0,0 +1,94 @@ +# Source: +# https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/test-integration/test_all.py +# Copyright 2021 ARDUINO SA (http://www.arduino.cc/) +# +# This software is released under the GNU General Public License version 3, +# 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 os +import pathlib +import platform +import shutil +import typing + +import invoke.context +import pytest + +test_data_path = pathlib.Path(__file__).resolve().parent.joinpath("testdata") + + +@pytest.mark.parametrize( + "test_file, success_assertion", + [ + ("", False), + ("nonexistent.txt", False), + ("invalid-data-format.txt", False), + ("invalid-url-format.txt", False), + ("duplicate-url.txt", False), + ("valid.txt", True), + ], +) +def test_all(run_command, test_file, success_assertion): + result = run_command(cmd=[test_data_path.joinpath(test_file)]) + assert result.ok == success_assertion + + +@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 + """ + + executable_path = pathlib.Path(pytestconfig.rootdir).parent / "validate-registry" + + 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(executable_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="IntegrationTestWorkingDir") + yield os.path.realpath(work_dir) + shutil.rmtree(work_dir, ignore_errors=True) diff --git a/.github/workflows/assets/validate-registry/tests/testdata/duplicate-url.txt b/.github/workflows/assets/validate-registry/tests/testdata/duplicate-url.txt new file mode 100644 index 00000000..13727fc9 --- /dev/null +++ b/.github/workflows/assets/validate-registry/tests/testdata/duplicate-url.txt @@ -0,0 +1,4 @@ +https://github.com/arduino-libraries/Scheduler.git|Arduino|Scheduler +https://github.com/arduino-libraries/SD.git|Partner|SD +https://github.com/arduino-libraries/Servo.git|Recommended|Servo +https://github.com/arduino-libraries/SD.git|Contributed|Foo diff --git a/.github/workflows/assets/validate-registry/tests/testdata/invalid-data-format.txt b/.github/workflows/assets/validate-registry/tests/testdata/invalid-data-format.txt new file mode 100644 index 00000000..ba787b98 --- /dev/null +++ b/.github/workflows/assets/validate-registry/tests/testdata/invalid-data-format.txt @@ -0,0 +1,3 @@ +https://github.com/arduino-libraries/Scheduler.git|Arduino|Scheduler +https://github.com/arduino-libraries/SD.git|Partner;SD +https://github.com/arduino-libraries/Servo.git|Recommended|Servo diff --git a/.github/workflows/assets/validate-registry/tests/testdata/invalid-url-format.txt b/.github/workflows/assets/validate-registry/tests/testdata/invalid-url-format.txt new file mode 100644 index 00000000..9327b7ac --- /dev/null +++ b/.github/workflows/assets/validate-registry/tests/testdata/invalid-url-format.txt @@ -0,0 +1,3 @@ +https://github.com/arduino-libraries/Scheduler.git|Arduino|Scheduler +https://github.com/arduino-libraries/SD|Partner|SD +https://github.com/arduino-libraries/Servo.git|Recommended|Servo diff --git a/.github/workflows/assets/validate-registry/tests/testdata/valid.txt b/.github/workflows/assets/validate-registry/tests/testdata/valid.txt new file mode 100644 index 00000000..48f549c4 --- /dev/null +++ b/.github/workflows/assets/validate-registry/tests/testdata/valid.txt @@ -0,0 +1,3 @@ +https://github.com/arduino-libraries/Scheduler.git|Arduino|Scheduler +https://github.com/arduino-libraries/SD.git|Partner|SD +https://github.com/arduino-libraries/Servo.git|Recommended|Servo diff --git a/.github/workflows/test-go-integration-task.yml b/.github/workflows/test-go-integration-task.yml new file mode 100644 index 00000000..877d41a8 --- /dev/null +++ b/.github/workflows/test-go-integration-task.yml @@ -0,0 +1,63 @@ +# Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/test-go-integration-task.md +name: Test Integration + +env: + # See: https://github.com/actions/setup-go/tree/v2#readme + GO_VERSION: "1.14" + # See: https://github.com/actions/setup-python/tree/v2#available-versions-of-python + PYTHON_VERSION: "3.9" + +# See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows +on: + push: + paths: + - ".github/workflows/test-go-integration-task.ya?ml" + - "Taskfile.ya?ml" + - "**.go" + - "**/go.mod" + - "**/go.sum" + - "poetry.lock" + - "pyproject.toml" + - ".github/workflows/assets/validate-registry/tests/**" + pull_request: + paths: + - ".github/workflows/test-go-integration-task.ya?ml" + - "Taskfile.ya?ml" + - "**.go" + - "**/go.mod" + - "**/go.sum" + - "poetry.lock" + - "pyproject.toml" + - ".github/workflows/assets/validate-registry/tests/**" + workflow_dispatch: + repository_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Install Python + uses: actions/setup-python@v2 + with: + python-version: ${{ env.PYTHON_VERSION }} + + - name: Install Poetry + run: pip install poetry + + - name: Install Taskfile + uses: arduino/setup-task@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + version: 3.x + + - name: Run integration tests + run: task go:test-integration diff --git a/.gitignore b/.gitignore index 41a640a2..cfe84620 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +__pycache__/ /.github/workflows/assets/validate-registry/validate-registry /.github/workflows/assets/validate-registry/validate-registry.exe diff --git a/README.md b/README.md index 71a9272e..a972aaeb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Arduino Library Manager list [![Check Registry status](https://github.com/arduino/library-registry/actions/workflows/check-registry.yml/badge.svg)](https://github.com/arduino/library-registry/actions/workflows/check-registry.yml) +[![Test Integration status](https://github.com/arduino/library-registry/actions/workflows/test-go-integration-task.yml/badge.svg)](https://github.com/arduino/library-registry/actions/workflows/test-go-integration-task.yml) [![Check General Formatting status](https://github.com/arduino/library-registry/actions/workflows/check-general-formatting-task.yml/badge.svg)](https://github.com/arduino/library-registry/actions/workflows/check-general-formatting-task.yml) This branch contains the generated source file for the Arduino Library Manager index. If you want to add a library to diff --git a/Taskfile.yml b/Taskfile.yml index 58f00565..36c275da 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -24,6 +24,28 @@ tasks: cmds: - go build -v {{.LDFLAGS}} + # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/test-go-integration-task/Taskfile.yml + go:test-integration: + desc: Run integration tests + dir: "{{.GO_PROJECT_PATH}}" + deps: + - task: go:build + - task: poetry:install-deps + cmds: + - poetry run pytest tests + + # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/poetry-task/Taskfile.yml + poetry:install-deps: + desc: Install dependencies managed by Poetry + cmds: + - poetry install --no-root + + # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/poetry-task/Taskfile.yml + poetry:update-deps: + desc: Update all dependencies managed by Poetry to their newest versions + cmds: + - poetry update + registry:validate: desc: Validate registry data file deps: diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..6fd3520c --- /dev/null +++ b/poetry.lock @@ -0,0 +1,164 @@ +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.2.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "invoke" +version = "1.6.0" +description = "Pythonic task execution" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "packaging" +version = "21.0" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2" + +[[package]] +name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +dev = ["pre-commit", "tox"] + +[[package]] +name = "py" +version = "1.10.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "pytest" +version = "6.2.4" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<1.0.0a1" +py = ">=1.8.2" +toml = "*" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[metadata] +lock-version = "1.1" +python-versions = "^3.9" +content-hash = "457471fb00b0b51a49788bdbbd7819f437097ea238ae272f42f045c9fcb7d38a" + +[metadata.files] +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-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +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.6.0-py2-none-any.whl", hash = "sha256:e6c9917a1e3e73e7ea91fdf82d5f151ccfe85bf30cc65cdb892444c02dbb5f74"}, + {file = "invoke-1.6.0-py3-none-any.whl", hash = "sha256:769e90caeb1bd07d484821732f931f1ad8916a38e3f3e618644687fc09cb6317"}, + {file = "invoke-1.6.0.tar.gz", hash = "sha256:374d1e2ecf78981da94bfaf95366216aaec27c2d6a7b7d5818d92da55aa258d3"}, +] +packaging = [ + {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, + {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, +] +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.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, +] +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.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, + {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..732085a6 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "library-registry" +version = "0.0.0" +description = "" +authors = ["Your Name "] +license = "CC0-1.0" + +[tool.poetry.dependencies] +python = "^3.9" + +[tool.poetry.dev-dependencies] +pytest = "^6.2.4" +invoke = "^1.5.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api"