diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..870e654 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + # Configure check for outdated GitHub Actions actions in workflows. + # See: https://docs.github.com/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot + - package-ecosystem: "github-actions" + directory: "/" # Check the repository's workflows under /.github/workflows/ + schedule: + interval: "weekly" diff --git a/.github/workflows/run-tox.yml b/.github/workflows/run-tox.yml new file mode 100644 index 0000000..89d8928 --- /dev/null +++ b/.github/workflows/run-tox.yml @@ -0,0 +1,30 @@ +name: Run all tox jobs using Python3 + +on: + pull_request: + push: + branches: + - main + workflow_dispatch: + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["2.7", "3.3", "3.8", "3.10"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools + pip install tox-gh-actions + - name: Run tox + run: | + tox diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4a689e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +**/__pycache__ +*.egg-info +.coverage* +.tox +reports diff --git a/tests/test_types.py b/tests/test_types.py new file mode 100644 index 0000000..cb925d7 --- /dev/null +++ b/tests/test_types.py @@ -0,0 +1,496 @@ +import pytest + +from dicttoxml import default_item_func +from dicttoxml import dicttoxml + + +parametized_root = pytest.mark.parametrize( + "root", + [ + pytest.param(True, id="With root"), + pytest.param(False, id="Without root"), + ], +) + +parametized_custom_root = pytest.mark.parametrize( + "custom_root", + [ + pytest.param("root", id="Default root"), + pytest.param("a custom root", id="Custom root"), + ], +) + +parametized_xml_decl = pytest.mark.parametrize( + "xml_declaration", + [ + pytest.param(True, id="With XML declaration"), + pytest.param(False, id="Without XML declaration"), + ], +) + +parametized_ids = pytest.mark.parametrize( + "ids", + [ + pytest.param(True, id="With unique IDs"), + pytest.param(False, id="Without unique IDs"), + ], +) + +parametized_attr_type = pytest.mark.parametrize( + "attr_type", + [ + pytest.param(True, id="With attribute types"), + pytest.param(False, id="Without attribute types"), + ], +) + +def custom_item_func(parent): + """This function is the same as dicttoxml.default_item_func.""" + return 'item' + +parametized_item_func = pytest.mark.parametrize( + "item_func", + [ + pytest.param(default_item_func, id="With default item func"), + pytest.param(custom_item_func, id="Without custom item func"), + ], +) + +parametized_cdata = pytest.mark.parametrize( + "cdata", + [ + pytest.param(True, id="With CDATA sections"), + pytest.param(False, id="Without CDATA sections"), + ], +) + +parametized_include_encoding = pytest.mark.parametrize( + "include_encoding", + [ + pytest.param(True, id="With include encoding"), + pytest.param(False, id="Without include encoding"), + ], +) + +parametized_encoding = pytest.mark.parametrize( + "encoding", + [ + pytest.param("UTF-8", id="UTF-8 encoding"), + pytest.param("Latin-1", id="Latin1 encoding"), + ], +) + +parametized_return_bytes = pytest.mark.parametrize( + "return_bytes", + [ + pytest.param(True, id="With return bytes"), + pytest.param(False, id="Without return bytes"), + ], +) + + +def _check_dicttoxml( + data, + type_str, + value_str, + root, + custom_root, + xml_declaration, + ids, + attr_type, + item_func, + cdata, + include_encoding, + encoding, + return_bytes, +): + # Call the tested function + res = dicttoxml( + data, + root=root, + custom_root=custom_root, + xml_declaration=xml_declaration, + ids=False, + attr_type=attr_type, + item_func=item_func, + cdata=cdata, + include_encoding=include_encoding, + encoding=encoding, + return_bytes=return_bytes, + ) + + # Build expected result + if cdata == True: + a_xml = "" % value_str + else: + a_xml = "%s" % value_str + + div = "a" + + if attr_type == True: + a_xml = '<%s type="%s">%s' % (div, type_str, a_xml, div) + else: + a_xml = "<%s>%s" % (div, a_xml, div) + + expected = "" + if root == True: + if xml_declaration == True: + if include_encoding == False: + expected += '' + else: + expected += '' % (encoding, ) + expected += "<%s>%s" % (custom_root, a_xml, custom_root) + else: + expected += a_xml + + if return_bytes: + assert res == expected.encode("UTF-8") + else: + assert res == expected + + +@parametized_root +@parametized_custom_root +@parametized_xml_decl +@parametized_attr_type +@parametized_item_func +@parametized_cdata +@parametized_include_encoding +@parametized_encoding +@parametized_return_bytes +def test_str( + root, + custom_root, + xml_declaration, + attr_type, + item_func, + cdata, + include_encoding, + encoding, + return_bytes, +): + a = {"a": "string"} + + _check_dicttoxml( + a, + "str", + "string", + root=root, + custom_root=custom_root, + xml_declaration=xml_declaration, + ids=False, # Not relevant for str + attr_type=attr_type, + item_func=item_func, + cdata=cdata, + include_encoding=include_encoding, + encoding=encoding, + return_bytes=return_bytes, + ) + + +@parametized_root +@parametized_custom_root +@parametized_xml_decl +@parametized_attr_type +@parametized_item_func +@parametized_cdata +@parametized_include_encoding +@parametized_encoding +@parametized_return_bytes +def test_int( + root, + custom_root, + xml_declaration, + attr_type, + item_func, + cdata, + include_encoding, + encoding, + return_bytes, +): + a = {"a": 1} + + _check_dicttoxml( + a, + "int", + "1", + root=root, + custom_root=custom_root, + xml_declaration=xml_declaration, + ids=False, # Not relevant for str + attr_type=attr_type, + item_func=item_func, + cdata=cdata, + include_encoding=include_encoding, + encoding=encoding, + return_bytes=return_bytes, + ) + + +@parametized_root +@parametized_custom_root +@parametized_xml_decl +@parametized_attr_type +@parametized_item_func +@parametized_cdata +@parametized_include_encoding +@parametized_encoding +@parametized_return_bytes +def test_float( + root, + custom_root, + xml_declaration, + attr_type, + item_func, + cdata, + include_encoding, + encoding, + return_bytes, +): + a = {"a": 1.2} + + _check_dicttoxml( + a, + "float", + "1.2", + root=root, + custom_root=custom_root, + xml_declaration=xml_declaration, + ids=False, # Not relevant for str + attr_type=attr_type, + item_func=item_func, + cdata=cdata, + include_encoding=include_encoding, + encoding=encoding, + return_bytes=return_bytes, + ) + + +@parametized_root +@parametized_custom_root +@parametized_xml_decl +# @parametized_ids +@parametized_attr_type +@parametized_item_func +# @parametized_cdata +@parametized_include_encoding +@parametized_encoding +@parametized_return_bytes +def test_list( + root, + custom_root, + xml_declaration, + # ids, + attr_type, + item_func, + # cdata, + include_encoding, + encoding, + return_bytes, +): + a = {"a": [1, 2]} + + if attr_type == True: + expected = '12' + else: + expected = "12" + + _check_dicttoxml( + a, + "list", + expected, + root=root, + custom_root=custom_root, + xml_declaration=xml_declaration, + ids=False, # Not tested for now + attr_type=attr_type, + item_func=item_func, + cdata=False, # Not tested for now + include_encoding=include_encoding, + encoding=encoding, + return_bytes=return_bytes, + ) + + +@parametized_root +@parametized_custom_root +@parametized_xml_decl +# @parametized_ids +@parametized_attr_type +@parametized_item_func +# @parametized_cdata +@parametized_include_encoding +@parametized_encoding +@parametized_return_bytes +def test_tuple( + root, + custom_root, + xml_declaration, + # ids, + attr_type, + item_func, + # cdata, + include_encoding, + encoding, + return_bytes, +): + a = {"a": (1, 2)} + + if attr_type == True: + expected = '12' + else: + expected = "12" + + _check_dicttoxml( + a, + "list", + expected, + root=root, + custom_root=custom_root, + xml_declaration=xml_declaration, + ids=False, # Not tested for now + attr_type=attr_type, + item_func=item_func, + cdata=False, # Not tested for now + include_encoding=include_encoding, + encoding=encoding, + return_bytes=return_bytes, + ) + + +@parametized_root +@parametized_custom_root +@parametized_xml_decl +# @parametized_ids +@parametized_attr_type +@parametized_item_func +# @parametized_cdata +@parametized_include_encoding +@parametized_encoding +@parametized_return_bytes +def test_dict( + root, + custom_root, + xml_declaration, + # ids, + attr_type, + item_func, + # cdata, + include_encoding, + encoding, + return_bytes, +): + a = {"a": {"b": 1}} + + if attr_type == True: + expected = '1' + else: + expected = "1" + + _check_dicttoxml( + a, + "dict", + expected, + root=root, + custom_root=custom_root, + xml_declaration=xml_declaration, + ids=False, # Not tested for now + attr_type=attr_type, + item_func=item_func, + cdata=False, # Not tested for now + include_encoding=include_encoding, + encoding=encoding, + return_bytes=return_bytes, + ) + + +@parametized_root +@parametized_custom_root +@parametized_xml_decl +# @parametized_ids +@parametized_attr_type +@parametized_item_func +# @parametized_cdata +@parametized_include_encoding +@parametized_encoding +@parametized_return_bytes +def test_nested_dict_tuple( + root, + custom_root, + xml_declaration, + # ids, + attr_type, + item_func, + # cdata, + include_encoding, + encoding, + return_bytes, +): + a = {"a": {"b": (1, 2)}} + + if attr_type == True: + expected = '12' + else: + expected = "12" + + _check_dicttoxml( + a, + "dict", + expected, + root=root, + custom_root=custom_root, + xml_declaration=xml_declaration, + ids=False, # Not tested for now + attr_type=attr_type, + item_func=item_func, + cdata=False, # Not tested for now + include_encoding=include_encoding, + encoding=encoding, + return_bytes=return_bytes, + ) + + +@parametized_root +@parametized_custom_root +@parametized_xml_decl +# @parametized_ids +@parametized_attr_type +@parametized_item_func +# @parametized_cdata +@parametized_include_encoding +@parametized_encoding +@parametized_return_bytes +def test_nested_tuple_tuple( + root, + custom_root, + xml_declaration, + # ids, + attr_type, + item_func, + # cdata, + include_encoding, + encoding, + return_bytes, +): + a = {"a": (1, 2, (3, 4))} + + if attr_type == True: + expected = '1234' + else: + expected = "1234" + + _check_dicttoxml( + a, + "list", + expected, + root=root, + custom_root=custom_root, + xml_declaration=xml_declaration, + ids=False, # Not tested for now + attr_type=attr_type, + item_func=item_func, + cdata=False, # Not tested for now + include_encoding=include_encoding, + encoding=encoding, + return_bytes=return_bytes, + ) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..3b490d7 --- /dev/null +++ b/tox.ini @@ -0,0 +1,47 @@ +[base] +name = dir_content_diff +files = {[base]name} tests docs/source/conf.py setup.py + +[tox] +envlist = + check-packaging + py{27,33,38,310} + +minversion = 3.18 + +[testenv] +deps = + pytest + pytest-cov + pytest-html +setenv = + COVERAGE_FILE = {env:COVERAGE_FILE:.coverage-{envname}} +commands = pytest \ + --basetemp={envtmpdir} \ + --cov=dicttoxml \ + --cov-branch \ + --cov-fail-under=48 \ + --no-cov-on-fail \ + --cov-report term-missing \ + --cov-report html:reports/coverage-{envname} \ + --cov-report xml:reports/coverage-{envname}.xml \ + --html reports/pytest-{envname}.html \ + --junit-xml=reports/pytest-{envname}.xml \ + --self-contained-html \ + {posargs} + +[testenv:check-packaging] +skip_install = true +deps = + build + twine +commands = + python -m build -o {envtmpdir}/dist + twine check {envtmpdir}/dist/* + +[gh-actions] +python = + 2.7: py27 + 3.3: py33 + 3.8: py38, check-packaging + 3.10: py310