Compare commits

..

23 Commits

Author SHA1 Message Date
Adrien Vergé
ba9d86d645 yamllint version 0.7.0 2016-02-01 22:43:42 +01:00
Adrien Vergé
3f4f13e848 Doc: Update screenshot to include 'key-duplicates' 2016-02-01 22:41:56 +01:00
Adrien Vergé
f6bab05e8a Rules: Add the 'key-duplicates' rule 2016-02-01 22:26:18 +01:00
Adrien Vergé
c16eec4681 Style: Fix indentation not multiple of four 2016-02-01 21:36:35 +01:00
Adrien Vergé
68618be4cc Rules: indentation: Handle sets
Sets are like mappings, that do not contain values. Example:

    set:
      ? key one
      ? key two
      ? [non, scalar, key]
2016-02-01 17:52:46 +01:00
Adrien Vergé
431a379c81 Tests: Add tests from YAML 1.2 specification
Write all examples from http://www.yaml.org/spec/1.2/spec.html in
independent files in tests/yaml-1.2-spec-examples; and test them with
yamllint.
2016-02-01 17:05:22 +01:00
Adrien Vergé
6b5948c06b Tests: Reorganize common and global tests 2016-02-01 16:56:32 +01:00
Adrien Vergé
dd163ed551 Rules: indentation: Fix flow sequences with multi-line scalars
Typically sequences like this:

    ["multi
      line 1", "multi
                line 2"]
2016-02-01 16:56:32 +01:00
Adrien Vergé
14c99da2bb Tests: Add test cases for empty flows 2016-02-01 16:56:32 +01:00
Adrien Vergé
cae100071a Rules: indentation: Add support for cleared sequence entries
The following construction is valid YAML, and its indentation should be
correctly handled:

    - this is
    -
      a
    -
      sequence:
        with cleared entries
2016-02-01 14:59:52 +01:00
Adrien Vergé
7cb7b4f669 Rules: commas: Add 'min-spaces-after'
Since such constructions are allowed and valid YAML:

    - [one,two, three,four]

this commit adds a `min-spaces-after` option that defaults to 1.
2016-02-01 12:13:10 +01:00
Adrien Vergé
d2b5f69309 Doc: Update Vim integration documentation
Since it has been merged into Syntastic:
https://github.com/scrooloose/syntastic/commit/8c4dadc
https://github.com/scrooloose/syntastic/pull/1675
2016-01-26 17:20:22 +01:00
Adrien Vergé
a7d39b5492 yamllint version 0.6.0 2016-01-25 11:03:00 +01:00
Adrien Vergé
4410bc3e23 Rules: indentation: Fix check-multi-line-strings
For strings that continue on next line at a lower indentation level:

    Blaise Pascal: Je vous écris une longue lettre parce que
      je n'ai pas le temps d'en écrire une courte.
2016-01-25 11:01:42 +01:00
Adrien Vergé
97c446907c Rules: line-length: Add option allow-non-breakable-words 2016-01-24 22:46:10 +01:00
Adrien Vergé
376a6ed484 Doc: Enhance short description 2016-01-24 18:40:48 +01:00
Adrien Vergé
a1eb9d7d2f yamllint version 0.5.2 2016-01-24 18:07:36 +01:00
Adrien Vergé
45538fb08a Doc: Explicit installation by adding sudo in README 2016-01-24 18:05:27 +01:00
Adrien Vergé
be998593dd Distribution: Create script with setup.py 2016-01-24 18:02:42 +01:00
Adrien Vergé
5ed496f471 Distribution: Remove unneeded setup_requires
With the new project layout, `pyyaml` is not needed anymore for parsing
setup.py.
2016-01-24 17:57:11 +01:00
Adrien Vergé
dbbecb5875 Refactor project layout to import yamllint alone
Currently importing yamllint recursively imports its submodules, which
finally requires having pyyaml installed. This is a problem when you
just want to import APP_VERSION from yamllint. For instance, setup.py
imports yamllint to know the version, but doesn't know yet that pyyaml
is to be installed, because it is stated in setup.py itself.

To solve this, yamllint/__init__.py will only contain constants. The
linting functions will be in yamllint/linter.py.
2016-01-24 17:48:20 +01:00
Adrien Vergé
7b147cb411 Tests: Remove Python 2.6 from CI tests
Because:

1. It is old. VERY old.

2. Some useful methods (`assertRaisesRegexp`, `assertIsInstance`) are
   only available from Python 2.7.
2016-01-24 17:39:36 +01:00
Adrien Vergé
fc108e7cee Config: Refactor to use YamlLintConfig objects 2016-01-24 17:39:27 +01:00
183 changed files with 2125 additions and 454 deletions

View File

@@ -1,7 +1,6 @@
--- ---
language: python language: python
python: python:
- 2.6
- 2.7 - 2.7
- 3.3 - 3.3
- 3.4 - 3.4

View File

@@ -3,6 +3,9 @@ yamllint
A linter for YAML files. A linter for YAML files.
yamllint does not only check for syntax validity, but for common cosmetic
conventions such as lines length, trailing spaces, indentation, etc.
.. image:: .. image::
https://travis-ci.org/adrienverge/yamllint.svg?branch=master https://travis-ci.org/adrienverge/yamllint.svg?branch=master
:target: https://travis-ci.org/adrienverge/yamllint :target: https://travis-ci.org/adrienverge/yamllint
@@ -36,7 +39,7 @@ Installation
.. code:: bash .. code:: bash
pip install yamllint sudo pip install yamllint
Usage Usage
^^^^^ ^^^^^

View File

@@ -1,24 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
from yamllint import cli
if __name__ == '__main__':
cli.run(sys.argv[1:])

View File

@@ -4,8 +4,5 @@ Development
yamllint provides both a script and a Python module. The latter can be used to yamllint provides both a script and a Python module. The latter can be used to
write your own linting tools: write your own linting tools:
.. autoclass:: yamllint.errors.LintProblem .. automodule:: yamllint.linter
:members:
.. automodule:: yamllint
:members: :members:

View File

@@ -3,6 +3,9 @@ yamllint documentation
A linter for YAML files. A linter for YAML files.
yamllint does not only check for syntax validity, but for common cosmetic
conventions such as lines length, trailing spaces, indentation, etc.
Screenshot Screenshot
---------- ----------

View File

@@ -38,15 +38,18 @@ The output will look like (colors are not displayed here):
:: ::
file.yml file.yml
6:2 warning missing starting space in comment (comments) 1:4 error trailing spaces (trailing-spaces)
57:1 error trailing spaces (trailing-spaces) 4:4 error wrong indentation: expected 4 but found 3 (indentation)
60:3 error wrong indentation: expected 4 but found 2 (indentation) 5:4 error duplication of key "id-00042" in mapping (key-duplicates)
6:6 warning comment not indented like content (comments-indentation)
12:6 error too many spaces after hyphen (hyphens)
15:12 error too many spaces before comma (commas)
other-file.yml other-file.yaml
1:1 warning missing document start "---" (document-start) 1:1 warning missing document start "---" (document-start)
9:81 error line too long (84 > 80 characters) (line-length) 6:81 error line too long (87 > 80 characters) (line-length)
31:1 error too many blank lines (4 > 2) (empty-lines) 10:1 error too many blank lines (4 > 2) (empty-lines)
37:12 error too many spaces inside braces (braces) 11:4 error too many spaces inside braces (braces)
Add the ``-f parsable`` arguments if you need an output format parsable by a Add the ``-f parsable`` arguments if you need an output format parsable by a
machine (for instance for :doc:`syntax highlighting in text editors machine (for instance for :doc:`syntax highlighting in text editors

View File

@@ -69,6 +69,11 @@ indentation
.. automodule:: yamllint.rules.indentation .. automodule:: yamllint.rules.indentation
key-duplicates
--------------
.. automodule:: yamllint.rules.key_duplicates
line-length line-length
----------- -----------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -14,7 +14,7 @@ is installed, add to your ``.vimrc``:
:: ::
TODO let g:syntastic_yaml_checkers = ['yamllint']
Neovim Neovim
------ ------

View File

@@ -24,7 +24,8 @@ setup(
name=APP_NAME, name=APP_NAME,
version=APP_VERSION, version=APP_VERSION,
author=__author__, author=__author__,
description=APP_DESCRIPTION, description=APP_DESCRIPTION.split('\n')[0],
long_description=APP_DESCRIPTION,
license=__license__, license=__license__,
keywords=['yaml', 'lint', 'linter', 'syntax', 'checker'], keywords=['yaml', 'lint', 'linter', 'syntax', 'checker'],
url='https://github.com/adrienverge/yamllint', url='https://github.com/adrienverge/yamllint',
@@ -42,11 +43,9 @@ setup(
], ],
packages=find_packages(), packages=find_packages(),
scripts=['bin/yamllint'], entry_points={'console_scripts': ['yamllint=yamllint.cli:run']},
package_data={'yamllint': ['conf/*.yml']}, package_data={'yamllint': ['conf/*.yml']},
install_requires=['pyyaml'], install_requires=['pyyaml'],
setup_requires=['pyyaml'], # importing `yamllint` (for APP_NAME etc.)
# requires importing `yaml`
tests_require=['nose'], tests_require=['nose'],
test_suite='nose.collector', test_suite='nose.collector',
) )

View File

@@ -18,9 +18,8 @@ import unittest
import yaml import yaml
from yamllint.config import parse_config from yamllint.config import YamlLintConfig
from yamllint.errors import LintProblem from yamllint import linter
from yamllint import lint
class RuleTestCase(unittest.TestCase): class RuleTestCase(unittest.TestCase):
@@ -31,7 +30,7 @@ class RuleTestCase(unittest.TestCase):
conf = yaml.safe_load(conf) conf = yaml.safe_load(conf)
conf = {'extends': 'default', conf = {'extends': 'default',
'rules': conf} 'rules': conf}
return parse_config(yaml.safe_dump(conf)) return YamlLintConfig(yaml.safe_dump(conf))
def check(self, source, conf, **kwargs): def check(self, source, conf, **kwargs):
expected_problems = [] expected_problems = []
@@ -44,9 +43,9 @@ class RuleTestCase(unittest.TestCase):
rule_id = kwargs[key][2] rule_id = kwargs[key][2]
else: else:
rule_id = self.rule_id rule_id = self.rule_id
expected_problems.append( expected_problems.append(linter.LintProblem(
LintProblem(kwargs[key][0], kwargs[key][1], rule=rule_id)) kwargs[key][0], kwargs[key][1], rule=rule_id))
expected_problems.sort() expected_problems.sort()
real_problems = list(lint(source, self.build_fake_config(conf))) real_problems = list(linter.run(source, self.build_fake_config(conf)))
self.assertEqual(real_problems, expected_problems) self.assertEqual(real_problems, expected_problems)

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class ColonTestCase(RuleTestCase): class ColonTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class ColonTestCase(RuleTestCase): class ColonTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class ColonTestCase(RuleTestCase): class ColonTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class CommaTestCase(RuleTestCase): class CommaTestCase(RuleTestCase):
@@ -33,9 +33,25 @@ class CommaTestCase(RuleTestCase):
' key2: val2,\n' ' key2: val2,\n'
'}\n' '}\n'
'...\n', conf) '...\n', conf)
self.check('---\n'
'- [one, two , three,four]\n'
'- {five,six , seven, eight}\n'
'- [\n'
' nine, ten\n'
' , eleven\n'
' ,twelve\n'
']\n'
'- {\n'
' thirteen: 13, fourteen\n'
' , fifteen: 15\n'
' ,sixteen: 16\n'
'}\n', conf)
def test_before_enabled(self): def test_before_max(self):
conf = 'commas: {max-spaces-before: 0, max-spaces-after: -1}' conf = ('commas:\n'
' max-spaces-before: 0\n'
' min-spaces-after: 0\n'
' max-spaces-after: -1\n')
self.check('---\n' self.check('---\n'
'array: [1, 2, 3, 4]\n' 'array: [1, 2, 3, 4]\n'
'...\n', conf) '...\n', conf)
@@ -75,8 +91,51 @@ class CommaTestCase(RuleTestCase):
' key2: val2 ,\n' ' key2: val2 ,\n'
'}\n', conf, problem=(4, 13)) '}\n', conf, problem=(4, 13))
def test_before_max(self): def test_before_max_with_comma_on_new_line(self):
conf = 'commas: {max-spaces-before: 3, max-spaces-after: -1}' conf = ('commas:\n'
' max-spaces-before: 0\n'
' min-spaces-after: 0\n'
' max-spaces-after: -1\n')
self.check('---\n'
'flow-seq: [1, 2, 3\n'
' , 4, 5, 6]\n'
'...\n', conf, problem=(3, 11))
self.check('---\n'
'flow-map: {a: 1, b: 2\n'
' , c: 3}\n'
'...\n', conf, problem=(3, 11))
conf = ('commas:\n'
' max-spaces-before: 0\n'
' min-spaces-after: 0\n'
' max-spaces-after: -1\n'
'indentation: disable\n')
self.check('---\n'
'flow-seq: [1, 2, 3\n'
' , 4, 5, 6]\n'
'...\n', conf, problem=(3, 9))
self.check('---\n'
'flow-map: {a: 1, b: 2\n'
' , c: 3}\n'
'...\n', conf, problem=(3, 9))
self.check('---\n'
'[\n'
'1,\n'
'2\n'
', 3\n'
']\n', conf, problem=(5, 1))
self.check('---\n'
'{\n'
'a: 1,\n'
'b: 2\n'
', c: 3\n'
'}\n', conf, problem=(5, 1))
def test_before_max_3(self):
conf = ('commas:\n'
' max-spaces-before: 3\n'
' min-spaces-after: 0\n'
' max-spaces-after: -1\n')
self.check('---\n' self.check('---\n'
'array: [1 , 2, 3 , 4]\n' 'array: [1 , 2, 3 , 4]\n'
'...\n', conf) '...\n', conf)
@@ -90,8 +149,32 @@ class CommaTestCase(RuleTestCase):
' key: val,\n' ' key: val,\n'
']\n', conf, problem=(4, 11)) ']\n', conf, problem=(4, 11))
def test_after_enabled(self): def test_after_min(self):
conf = 'commas: {max-spaces-before: -1, max-spaces-after: 1}' conf = ('commas:\n'
' max-spaces-before: -1\n'
' min-spaces-after: 1\n'
' max-spaces-after: -1\n')
self.check('---\n'
'- [one, two , three,four]\n'
'- {five,six , seven, eight}\n'
'- [\n'
' nine, ten\n'
' , eleven\n'
' ,twelve\n'
']\n'
'- {\n'
' thirteen: 13, fourteen\n'
' , fifteen: 15\n'
' ,sixteen: 16\n'
'}\n', conf,
problem1=(2, 21), problem2=(3, 9),
problem3=(7, 4), problem4=(12, 4))
def test_after_max(self):
conf = ('commas:\n'
' max-spaces-before: -1\n'
' min-spaces-after: 0\n'
' max-spaces-after: 1\n')
self.check('---\n' self.check('---\n'
'array: [1, 2, 3, 4]\n' 'array: [1, 2, 3, 4]\n'
'...\n', conf) '...\n', conf)
@@ -124,8 +207,11 @@ class CommaTestCase(RuleTestCase):
' key1: val1, key2: [val2, val3]\n' ' key1: val1, key2: [val2, val3]\n'
'}\n', conf, problem1=(3, 16), problem2=(3, 30)) '}\n', conf, problem1=(3, 16), problem2=(3, 30))
def test_after_max(self): def test_after_max_3(self):
conf = 'commas: {max-spaces-before: -1, max-spaces-after: 3}' conf = ('commas:\n'
' max-spaces-before: -1\n'
' min-spaces-after: 1\n'
' max-spaces-after: 3\n')
self.check('---\n' self.check('---\n'
'array: [1, 2, 3, 4]\n' 'array: [1, 2, 3, 4]\n'
'...\n', conf) '...\n', conf)
@@ -137,7 +223,10 @@ class CommaTestCase(RuleTestCase):
'...\n', conf, problem1=(2, 31), problem2=(2, 49)) '...\n', conf, problem1=(2, 31), problem2=(2, 49))
def test_both_before_and_after(self): def test_both_before_and_after(self):
conf = 'commas: {max-spaces-before: 0, max-spaces-after: 1}' conf = ('commas:\n'
' max-spaces-before: 0\n'
' min-spaces-after: 1\n'
' max-spaces-after: 1\n')
self.check('---\n' self.check('---\n'
'dict: {a: b , c: "1 2 3", d: e , f: [g, h]}\n' 'dict: {a: b , c: "1 2 3", d: e , f: [g, h]}\n'
'array: [\n' 'array: [\n'
@@ -152,36 +241,25 @@ class CommaTestCase(RuleTestCase):
problem1=(2, 12), problem2=(2, 16), problem3=(2, 31), problem1=(2, 12), problem2=(2, 16), problem3=(2, 31),
problem4=(2, 36), problem5=(2, 50), problem6=(4, 8), problem4=(2, 36), problem5=(2, 50), problem6=(4, 8),
problem7=(5, 11), problem8=(8, 13)) problem7=(5, 11), problem8=(8, 13))
conf = ('commas:\n'
def test_comma_on_new_line(self): ' max-spaces-before: 0\n'
conf = 'commas: {max-spaces-before: 0, max-spaces-after: 1}' ' min-spaces-after: 1\n'
self.check('---\n' ' max-spaces-after: 1\n'
'flow-seq: [1, 2, 3\n'
' , 4, 5, 6]\n'
'...\n', conf, problem=(3, 11))
self.check('---\n'
'flow-map: {a: 1, b: 2\n'
' , c: 3}\n'
'...\n', conf, problem=(3, 11))
conf = ('commas: {max-spaces-before: 0, max-spaces-after: 1}\n'
'indentation: disable\n') 'indentation: disable\n')
self.check('---\n' self.check('---\n'
'flow-seq: [1, 2, 3\n' '- [one, two , three,four]\n'
' , 4, 5, 6]\n' '- {five,six , seven, eight}\n'
'...\n', conf, problem=(3, 9)) '- [\n'
self.check('---\n' ' nine, ten\n'
'flow-map: {a: 1, b: 2\n' ' , eleven\n'
' , c: 3}\n' ' ,twelve\n'
'...\n', conf, problem=(3, 9)) ']\n'
self.check('---\n' '- {\n'
'[\n' ' thirteen: 13, fourteen\n'
'1,\n' ' , fifteen: 15\n'
'2\n' ' ,sixteen: 16\n'
', 3\n' '}\n', conf,
']\n', conf, problem=(5, 1)) problem1=(2, 12), problem2=(2, 21), problem3=(3, 9),
self.check('---\n' problem4=(3, 12), problem5=(5, 9), problem6=(6, 2),
'{\n' problem7=(7, 2), problem8=(7, 4), problem9=(10, 17),
'a: 1,\n' problem10=(11, 2), problem11=(12, 2), problem12=(12, 4))
'b: 2\n'
', c: 3\n'
'}\n', conf, problem=(5, 1))

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class CommentsTestCase(RuleTestCase): class CommentsTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class CommentsIndentationTestCase(RuleTestCase): class CommentsIndentationTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class DocumentEndTestCase(RuleTestCase): class DocumentEndTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class DocumentStartTestCase(RuleTestCase): class DocumentStartTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class EmptyLinesTestCase(RuleTestCase): class EmptyLinesTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class HyphenTestCase(RuleTestCase): class HyphenTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class IndentationTestCase(RuleTestCase): class IndentationTestCase(RuleTestCase):
@@ -489,6 +489,51 @@ class IndentationTestCase(RuleTestCase):
' value\n' ' value\n'
'...\n', conf, problem1=(4, 10), problem2=(6, 8)) '...\n', conf, problem1=(4, 10), problem2=(6, 8))
def test_clear_sequence_item(self):
conf = 'indentation: {spaces: 2}'
self.check('---\n'
'-\n'
' string\n'
'-\n'
' map: ping\n'
'-\n'
' - sequence\n'
' -\n'
' nested\n'
' -\n'
' >\n'
' multi\n'
' line\n'
'...\n', conf)
self.check('---\n'
'-\n'
' string\n'
'-\n'
' string\n', conf, problem1=(3, 2), problem2=(5, 4))
self.check('---\n'
'-\n'
' map: ping\n'
'-\n'
' map: ping\n', conf, problem1=(3, 2), problem2=(5, 4))
self.check('---\n'
'-\n'
' - sequence\n'
'-\n'
' - sequence\n', conf, problem1=(3, 2), problem2=(5, 4))
self.check('---\n'
'-\n'
' -\n'
' nested\n'
' -\n'
' nested\n', conf, problem1=(4, 4), problem2=(6, 6))
self.check('---\n'
'-\n'
' -\n'
' >\n'
' multi\n'
' line\n'
'...\n', conf, problem=(4, 6))
class ScalarIndentationTestCase(RuleTestCase): class ScalarIndentationTestCase(RuleTestCase):
rule_id = 'indentation' rule_id = 'indentation'
@@ -507,7 +552,7 @@ class ScalarIndentationTestCase(RuleTestCase):
self.check('a key: multi\n' self.check('a key: multi\n'
' line\n', conf) ' line\n', conf)
self.check('a key: multi\n' self.check('a key: multi\n'
' line\n', conf, problem=(2, 3)) ' line\n', conf)
self.check('a key: multi\n' self.check('a key: multi\n'
' line\n', conf) ' line\n', conf)
self.check('a key:\n' self.check('a key:\n'
@@ -528,6 +573,8 @@ class ScalarIndentationTestCase(RuleTestCase):
' line\n', conf, problem=(2, 2)) ' line\n', conf, problem=(2, 2))
self.check('- multi\n' self.check('- multi\n'
' line\n', conf, problem=(2, 4)) ' line\n', conf, problem=(2, 4))
self.check('a key: multi\n'
' line\n', conf, problem=(2, 3))
self.check('a key: multi\n' self.check('a key: multi\n'
' line\n', conf, problem=(2, 9)) ' line\n', conf, problem=(2, 9))
self.check('a key:\n' self.check('a key:\n'
@@ -546,24 +593,13 @@ class ScalarIndentationTestCase(RuleTestCase):
'document-start: disable\n') 'document-start: disable\n')
self.check('"multi\n' self.check('"multi\n'
' line"\n', conf) ' line"\n', conf)
self.check('"multi\n'
'line"\n', conf, problem=(2, 1))
self.check('- "multi\n' self.check('- "multi\n'
' line"\n', conf) ' line"\n', conf)
self.check('- "multi\n'
' line"\n', conf, problem=(2, 3))
self.check('a key: "multi\n' self.check('a key: "multi\n'
' line"\n', conf) ' line"\n', conf)
self.check('a key: "multi\n'
' line"\n', conf, problem=(2, 3))
self.check('a key: "multi\n'
' line"\n', conf, problem=(2, 8))
self.check('a key:\n' self.check('a key:\n'
' "multi\n' ' "multi\n'
' line"\n', conf) ' line"\n', conf)
self.check('a key:\n'
' "multi\n'
' line"\n', conf, problem=(3, 3))
self.check('- jinja2: "{% if ansible is defined %}\n' self.check('- jinja2: "{% if ansible is defined %}\n'
' {{ ansible }}\n' ' {{ ansible }}\n'
' {% else %}\n' ' {% else %}\n'
@@ -575,16 +611,33 @@ class ScalarIndentationTestCase(RuleTestCase):
' {% else %}\n' ' {% else %}\n'
' {{ chef }}\n' ' {{ chef }}\n'
' {% endif %}"\n', conf) ' {% endif %}"\n', conf)
self.check('["this is a very long line\n'
' that needs to be split",\n'
' "other line"]\n', conf)
self.check('["multi\n'
' line 1", "multi\n'
' line 2"]\n', conf)
def test_check_multi_line_quoted(self): def test_check_multi_line_quoted(self):
conf = ('indentation: {spaces: 2, check-multi-line-strings: yes}\n' conf = ('indentation: {spaces: 2, check-multi-line-strings: yes}\n'
'document-start: disable\n') 'document-start: disable\n')
self.check('"multi\n' self.check('"multi\n'
'line"\n', conf, problem=(2, 1))
self.check('"multi\n'
' line"\n', conf, problem=(2, 3))
self.check('- "multi\n'
' line"\n', conf, problem=(2, 3)) ' line"\n', conf, problem=(2, 3))
self.check('- "multi\n' self.check('- "multi\n'
' line"\n', conf, problem=(2, 5)) ' line"\n', conf, problem=(2, 5))
self.check('a key: "multi\n'
' line"\n', conf, problem=(2, 3))
self.check('a key: "multi\n'
' line"\n', conf, problem=(2, 8))
self.check('a key: "multi\n' self.check('a key: "multi\n'
' line"\n', conf, problem=(2, 10)) ' line"\n', conf, problem=(2, 10))
self.check('a key:\n'
' "multi\n'
' line"\n', conf, problem=(3, 3))
self.check('a key:\n' self.check('a key:\n'
' "multi\n' ' "multi\n'
' line"\n', conf, problem=(3, 5)) ' line"\n', conf, problem=(3, 5))
@@ -601,6 +654,24 @@ class ScalarIndentationTestCase(RuleTestCase):
' {{ chef }}\n' ' {{ chef }}\n'
' {% endif %}"\n', conf, ' {% endif %}"\n', conf,
problem1=(3, 8), problem2=(5, 8)) problem1=(3, 8), problem2=(5, 8))
self.check('["this is a very long line\n'
' that needs to be split",\n'
' "other line"]\n', conf)
self.check('["this is a very long line\n'
' that needs to be split",\n'
' "other line"]\n', conf, problem=(2, 2))
self.check('["this is a very long line\n'
' that needs to be split",\n'
' "other line"]\n', conf, problem=(2, 4))
self.check('["multi\n'
' line 1", "multi\n'
' line 2"]\n', conf)
self.check('["multi\n'
' line 1", "multi\n'
' line 2"]\n', conf, problem=(3, 12))
self.check('["multi\n'
' line 1", "multi\n'
' line 2"]\n', conf, problem=(3, 14))
def test_basics_folded_style(self): def test_basics_folded_style(self):
conf = ('indentation: {spaces: 2, check-multi-line-strings: no}\n' conf = ('indentation: {spaces: 2, check-multi-line-strings: no}\n'

View File

@@ -0,0 +1,156 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.common import RuleTestCase
class KeyDuplicatesTestCase(RuleTestCase):
rule_id = 'key-duplicates'
def test_disabled(self):
conf = 'key-duplicates: disable'
self.check('---\n'
'block mapping:\n'
' key: a\n'
' otherkey: b\n'
' key: c\n', conf)
self.check('---\n'
'flow mapping:\n'
' {key: a, otherkey: b, key: c}\n', conf)
self.check('---\n'
'duplicated twice:\n'
' - k: a\n'
' ok: b\n'
' k: c\n'
' k: d\n', conf)
self.check('---\n'
'duplicated twice:\n'
' - {k: a, ok: b, k: c, k: d}\n', conf)
self.check('---\n'
'multiple duplicates:\n'
' a: 1\n'
' b: 2\n'
' c: 3\n'
' d: 4\n'
' d: 5\n'
' b: 6\n', conf)
self.check('---\n'
'multiple duplicates:\n'
' {a: 1, b: 2, c: 3, d: 4, d: 5, b: 6}\n', conf)
self.check('---\n'
'at: root\n'
'multiple: times\n'
'at: root\n', conf)
self.check('---\n'
'nested but OK:\n'
' a: {a: {a: 1}}\n'
' b:\n'
' b: 2\n'
' c: 3\n', conf)
self.check('---\n'
'nested duplicates:\n'
' a: {a: 1, a: 1}\n'
' b:\n'
' c: 3\n'
' d: 4\n'
' d: 4\n'
' b: 2\n', conf)
self.check('---\n'
'duplicates with many styles: 1\n'
'"duplicates with many styles": 1\n'
'\'duplicates with many styles\': 1\n'
'? duplicates with many styles\n'
': 1\n'
'? >-\n'
' duplicates with\n'
' many styles\n'
': 1\n', conf)
def test_enabled(self):
conf = 'key-duplicates: {}'
self.check('---\n'
'block mapping:\n'
' key: a\n'
' otherkey: b\n'
' key: c\n', conf,
problem=(5, 3))
self.check('---\n'
'flow mapping:\n'
' {key: a, otherkey: b, key: c}\n', conf,
problem=(3, 25))
self.check('---\n'
'duplicated twice:\n'
' - k: a\n'
' ok: b\n'
' k: c\n'
' k: d\n', conf,
problem1=(5, 5), problem2=(6, 5))
self.check('---\n'
'duplicated twice:\n'
' - {k: a, ok: b, k: c, k: d}\n', conf,
problem1=(3, 19), problem2=(3, 25))
self.check('---\n'
'multiple duplicates:\n'
' a: 1\n'
' b: 2\n'
' c: 3\n'
' d: 4\n'
' d: 5\n'
' b: 6\n', conf,
problem1=(7, 3), problem2=(8, 3))
self.check('---\n'
'multiple duplicates:\n'
' {a: 1, b: 2, c: 3, d: 4, d: 5, b: 6}\n', conf,
problem1=(3, 28), problem2=(3, 34))
self.check('---\n'
'at: root\n'
'multiple: times\n'
'at: root\n', conf,
problem=(4, 1))
self.check('---\n'
'nested but OK:\n'
' a: {a: {a: 1}}\n'
' b:\n'
' b: 2\n'
' c: 3\n', conf)
self.check('---\n'
'nested duplicates:\n'
' a: {a: 1, a: 1}\n'
' b:\n'
' c: 3\n'
' d: 4\n'
' d: 4\n'
' b: 2\n', conf,
problem1=(3, 13), problem2=(7, 5), problem3=(8, 3))
self.check('---\n'
'duplicates with many styles: 1\n'
'"duplicates with many styles": 1\n'
'\'duplicates with many styles\': 1\n'
'? duplicates with many styles\n'
': 1\n'
'? >-\n'
' duplicates with\n'
' many styles\n'
': 1\n', conf,
problem1=(3, 1), problem2=(4, 1), problem3=(5, 3),
problem4=(7, 3))
def test_key_tokens_in_flow_sequences(self):
conf = 'key-duplicates: {}'
self.check('---\n'
'[\n'
' flow: sequence, with, key: value, mappings\n'
']\n', conf)

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class LineLengthTestCase(RuleTestCase): class LineLengthTestCase(RuleTestCase):
@@ -43,17 +43,17 @@ class LineLengthTestCase(RuleTestCase):
self.check('---\n', conf) self.check('---\n', conf)
self.check(80 * 'a', conf) self.check(80 * 'a', conf)
self.check('---\n' + 80 * 'a' + '\n', conf) self.check('---\n' + 80 * 'a' + '\n', conf)
self.check(81 * 'a', conf, problem=(1, 81)) self.check(16 * 'aaaa ' + 'z', conf, problem=(1, 81))
self.check('---\n' + 81 * 'a' + '\n', conf, problem=(2, 81)) self.check('---\n' + 16 * 'aaaa ' + 'z' + '\n', conf, problem=(2, 81))
self.check(1000 * 'b', conf, problem=(1, 81)) self.check(1000 * 'word ' + 'end', conf, problem=(1, 81))
self.check('---\n' + 1000 * 'b' + '\n', conf, problem=(2, 81)) self.check('---\n' + 1000 * 'word ' + 'end\n', conf, problem=(2, 81))
def test_max_length_10(self): def test_max_length_10(self):
conf = ('line-length: {max: 10}\n' conf = ('line-length: {max: 10}\n'
'new-line-at-end-of-file: disable\n') 'new-line-at-end-of-file: disable\n')
self.check('---\nABCDEFGHIJ', conf) self.check('---\nABCD EFGHI', conf)
self.check('---\nABCDEFGHIJK', conf, problem=(2, 11)) self.check('---\nABCD EFGHIJ', conf, problem=(2, 11))
self.check('---\nABCDEFGHIJK\n', conf, problem=(2, 11)) self.check('---\nABCD EFGHIJ\n', conf, problem=(2, 11))
def test_spaces(self): def test_spaces(self):
conf = ('line-length: {max: 80}\n' conf = ('line-length: {max: 80}\n'
@@ -61,3 +61,36 @@ class LineLengthTestCase(RuleTestCase):
'trailing-spaces: disable\n') 'trailing-spaces: disable\n')
self.check('---\n' + 81 * ' ', conf, problem=(2, 81)) self.check('---\n' + 81 * ' ', conf, problem=(2, 81))
self.check('---\n' + 81 * ' ' + '\n', conf, problem=(2, 81)) self.check('---\n' + 81 * ' ' + '\n', conf, problem=(2, 81))
def test_non_breakable_word(self):
conf = 'line-length: {max: 20, allow-non-breakable-words: yes}'
self.check('---\n' + 30 * 'A' + '\n', conf)
self.check('---\n'
'this:\n'
' is:\n'
' - a:\n'
' http://localhost/very/long/url\n'
'...\n', conf)
self.check('---\n'
'this:\n'
' is:\n'
' - a:\n'
' # http://localhost/very/long/url\n'
' comment\n'
'...\n', conf)
conf = 'line-length: {max: 20, allow-non-breakable-words: no}'
self.check('---\n' + 30 * 'A' + '\n', conf, problem=(2, 21))
self.check('---\n'
'this:\n'
' is:\n'
' - a:\n'
' http://localhost/very/long/url\n'
'...\n', conf, problem=(5, 21))
self.check('---\n'
'this:\n'
' is:\n'
' - a:\n'
' # http://localhost/very/long/url\n'
' comment\n'
'...\n', conf, problem=(5, 21))

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class NewLineAtEndOfFileTestCase(RuleTestCase): class NewLineAtEndOfFileTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class NewLinesTestCase(RuleTestCase): class NewLinesTestCase(RuleTestCase):

View File

@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class TrailingSpacesTestCase(RuleTestCase): class TrailingSpacesTestCase(RuleTestCase):

View File

@@ -14,56 +14,164 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import unittest import unittest
from yamllint import config from yamllint import config
class ConfigTestCase(unittest.TestCase): class SimpleConfigTestCase(unittest.TestCase):
def setUp(self): def test_parse_config(self):
self.base = config.parse_config_from_file(os.path.join( new = config.YamlLintConfig('rules:\n'
os.path.dirname(os.path.dirname(os.path.realpath(__file__))), ' colons:\n'
'yamllint', 'conf', 'default.yml')) ' max-spaces-before: 0\n'
' max-spaces-after: 1\n')
self.assertEqual(list(new.rules.keys()), ['colons'])
self.assertEqual(new.rules['colons']['max-spaces-before'], 0)
self.assertEqual(new.rules['colons']['max-spaces-after'], 1)
self.assertEqual(len(new.enabled_rules()), 1)
def test_unknown_rule(self):
with self.assertRaisesRegexp(
config.YamlLintConfigError,
'invalid config: no such rule: "this-one-does-not-exist"'):
config.YamlLintConfig('rules:\n'
' this-one-does-not-exist: {}\n')
def test_missing_option(self):
with self.assertRaisesRegexp(
config.YamlLintConfigError,
'invalid config: missing option "max-spaces-before" '
'for rule "colons"'):
config.YamlLintConfig('rules:\n'
' colons:\n'
' max-spaces-after: 1\n')
def test_unknown_option(self):
with self.assertRaisesRegexp(
config.YamlLintConfigError,
'invalid config: unknown option "abcdef" for rule "colons"'):
config.YamlLintConfig('rules:\n'
' colons:\n'
' max-spaces-before: 0\n'
' max-spaces-after: 1\n'
' abcdef: yes\n')
class ExtendedConfigTestCase(unittest.TestCase):
def test_extend_add_rule(self):
old = config.YamlLintConfig('rules:\n'
' colons:\n'
' max-spaces-before: 0\n'
' max-spaces-after: 1\n')
new = config.YamlLintConfig('rules:\n'
' hyphens:\n'
' max-spaces-after: 2\n')
new.extend(old)
self.assertEqual(sorted(new.rules.keys()), ['colons', 'hyphens'])
self.assertEqual(new.rules['colons']['max-spaces-before'], 0)
self.assertEqual(new.rules['colons']['max-spaces-after'], 1)
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
self.assertEqual(len(new.enabled_rules()), 2)
def test_extend_remove_rule(self):
old = config.YamlLintConfig('rules:\n'
' colons:\n'
' max-spaces-before: 0\n'
' max-spaces-after: 1\n'
' hyphens:\n'
' max-spaces-after: 2\n')
new = config.YamlLintConfig('rules:\n'
' colons: disable\n')
new.extend(old)
self.assertEqual(sorted(new.rules.keys()), ['colons', 'hyphens'])
self.assertEqual(new.rules['colons'], False)
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
self.assertEqual(len(new.enabled_rules()), 1)
def test_extend_edit_rule(self):
old = config.YamlLintConfig('rules:\n'
' colons:\n'
' max-spaces-before: 0\n'
' max-spaces-after: 1\n'
' hyphens:\n'
' max-spaces-after: 2\n')
new = config.YamlLintConfig('rules:\n'
' colons:\n'
' max-spaces-before: 3\n'
' max-spaces-after: 4\n')
new.extend(old)
self.assertEqual(sorted(new.rules.keys()), ['colons', 'hyphens'])
self.assertEqual(new.rules['colons']['max-spaces-before'], 3)
self.assertEqual(new.rules['colons']['max-spaces-after'], 4)
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
self.assertEqual(len(new.enabled_rules()), 2)
def test_extend_reenable_rule(self):
old = config.YamlLintConfig('rules:\n'
' colons:\n'
' max-spaces-before: 0\n'
' max-spaces-after: 1\n'
' hyphens: disable\n')
new = config.YamlLintConfig('rules:\n'
' hyphens:\n'
' max-spaces-after: 2\n')
new.extend(old)
self.assertEqual(sorted(new.rules.keys()), ['colons', 'hyphens'])
self.assertEqual(new.rules['colons']['max-spaces-before'], 0)
self.assertEqual(new.rules['colons']['max-spaces-after'], 1)
self.assertEqual(new.rules['hyphens']['max-spaces-after'], 2)
self.assertEqual(len(new.enabled_rules()), 2)
class ExtendedLibraryConfigTestCase(unittest.TestCase):
def test_extend_config_disable_rule(self): def test_extend_config_disable_rule(self):
new = config.parse_config('extends: default\n' old = config.YamlLintConfig('extends: default')
'rules:\n' new = config.YamlLintConfig('extends: default\n'
' trailing-spaces: disable\n') 'rules:\n'
' trailing-spaces: disable\n')
base = self.base.copy() old.rules['trailing-spaces'] = False
del base['trailing-spaces']
self.assertEqual(sorted(new.keys()), sorted(base.keys())) self.assertEqual(sorted(new.rules.keys()), sorted(old.rules.keys()))
for rule in new: for rule in new.rules:
self.assertEqual(new[rule], base[rule]) self.assertEqual(new.rules[rule], old.rules[rule])
def test_extend_config_override_whole_rule(self): def test_extend_config_override_whole_rule(self):
new = config.parse_config('extends: default\n' old = config.YamlLintConfig('extends: default')
'rules:\n' new = config.YamlLintConfig('extends: default\n'
' empty-lines:\n' 'rules:\n'
' max: 42\n' ' empty-lines:\n'
' max-start: 43\n' ' max: 42\n'
' max-end: 44\n') ' max-start: 43\n'
' max-end: 44\n')
base = self.base.copy() old.rules['empty-lines']['max'] = 42
base['empty-lines']['max'] = 42 old.rules['empty-lines']['max-start'] = 43
base['empty-lines']['max-start'] = 43 old.rules['empty-lines']['max-end'] = 44
base['empty-lines']['max-end'] = 44
self.assertEqual(sorted(new.keys()), sorted(base.keys())) self.assertEqual(sorted(new.rules.keys()), sorted(old.rules.keys()))
for rule in new: for rule in new.rules:
self.assertEqual(new[rule], base[rule]) self.assertEqual(new.rules[rule], old.rules[rule])
def test_extend_config_override_rule_partly(self): def test_extend_config_override_rule_partly(self):
new = config.parse_config('extends: default\n' old = config.YamlLintConfig('extends: default')
'rules:\n' new = config.YamlLintConfig('extends: default\n'
' empty-lines:\n' 'rules:\n'
' max-start: 42\n') ' empty-lines:\n'
' max-start: 42\n')
base = self.base.copy() old.rules['empty-lines']['max-start'] = 42
base['empty-lines']['max-start'] = 42
self.assertEqual(sorted(new.keys()), sorted(base.keys())) self.assertEqual(sorted(new.rules.keys()), sorted(old.rules.keys()))
for rule in new: for rule in new.rules:
self.assertEqual(new[rule], base[rule]) self.assertEqual(new.rules[rule], old.rules[rule])

View File

@@ -65,8 +65,8 @@ class ParserTestCase(unittest.TestCase):
e = list(token_generator('')) e = list(token_generator(''))
self.assertEqual(len(e), 2) self.assertEqual(len(e), 2)
self.assertEqual(e[0].prev, None) self.assertEqual(e[0].prev, None)
self.assertTrue(isinstance(e[0].curr, yaml.Token)) self.assertIsInstance(e[0].curr, yaml.Token)
self.assertTrue(isinstance(e[0].next, yaml.Token)) self.assertIsInstance(e[0].next, yaml.Token)
self.assertEqual(e[1].prev, e[0].curr) self.assertEqual(e[1].prev, e[0].curr)
self.assertEqual(e[1].curr, e[0].next) self.assertEqual(e[1].curr, e[0].next)
self.assertEqual(e[1].next, None) self.assertEqual(e[1].next, None)
@@ -74,20 +74,20 @@ class ParserTestCase(unittest.TestCase):
e = list(token_generator('---\n' e = list(token_generator('---\n'
'k: v\n')) 'k: v\n'))
self.assertEqual(len(e), 9) self.assertEqual(len(e), 9)
self.assertTrue(isinstance(e[3].curr, yaml.KeyToken)) self.assertIsInstance(e[3].curr, yaml.KeyToken)
self.assertTrue(isinstance(e[5].curr, yaml.ValueToken)) self.assertIsInstance(e[5].curr, yaml.ValueToken)
def test_token_or_line_generator(self): def test_token_or_line_generator(self):
e = list(token_or_line_generator('---\n' e = list(token_or_line_generator('---\n'
'k: v\n')) 'k: v\n'))
self.assertEqual(len(e), 12) self.assertEqual(len(e), 12)
self.assertTrue(isinstance(e[0], Token)) self.assertIsInstance(e[0], Token)
self.assertTrue(isinstance(e[0].curr, yaml.StreamStartToken)) self.assertIsInstance(e[0].curr, yaml.StreamStartToken)
self.assertTrue(isinstance(e[1], Token)) self.assertIsInstance(e[1], Token)
self.assertTrue(isinstance(e[1].curr, yaml.DocumentStartToken)) self.assertIsInstance(e[1].curr, yaml.DocumentStartToken)
self.assertTrue(isinstance(e[2], Line)) self.assertIsInstance(e[2], Line)
self.assertTrue(isinstance(e[3].curr, yaml.BlockMappingStartToken)) self.assertIsInstance(e[3].curr, yaml.BlockMappingStartToken)
self.assertTrue(isinstance(e[4].curr, yaml.KeyToken)) self.assertIsInstance(e[4].curr, yaml.KeyToken)
self.assertTrue(isinstance(e[6].curr, yaml.ValueToken)) self.assertIsInstance(e[6].curr, yaml.ValueToken)
self.assertTrue(isinstance(e[8], Line)) self.assertIsInstance(e[8], Line)
self.assertTrue(isinstance(e[11], Line)) self.assertIsInstance(e[11], Line)

186
tests/test_spec_examples.py Normal file
View File

@@ -0,0 +1,186 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Adrien Vergé
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from tests.common import RuleTestCase
# This file checks examples from YAML 1.2 specification [1] against yamllint.
#
# [1]: http://www.yaml.org/spec/1.2/spec.html
#
# Example files generated with:
#
# from bs4 import BeautifulSoup
# with open('spec.html', encoding='iso-8859-1') as f:
# soup = BeautifulSoup(f, 'lxml')
# for ex in soup.find_all('div', class_='example'):
# title = ex.find('p', class_='title').find('b').get_text()
# id = '-'.join(title.split('\xa0')[:2])[:-1].lower()
# span = ex.find('span', class_='database')
# for br in span.find_all("br"):
# br.replace_with("\n")
# text = text.replace('\u2193', '') # downwards arrow
# text = text.replace('\u00b7', ' ') # visible space
# text = text.replace('\u21d4', '') # byte order mark
# text = text.replace('\u2192', '\t') # right arrow
# text = text.replace('\u00b0', '') # empty scalar
# with open('tests/yaml-1.2-spec-examples/%s' % id, 'w',
# encoding='utf-8') as g:
# g.write(text)
class SpecificationTestCase(RuleTestCase):
rule_id = None
conf_general = ('document-start: disable\n'
'comments: {min-spaces-from-content: 1}\n'
'braces: {min-spaces-inside: 1, max-spaces-inside: 1}\n'
'brackets: {min-spaces-inside: 1, max-spaces-inside: 1}\n')
conf_overrides = {
'example-2.2': ('colons: {max-spaces-after: 2}\n'),
'example-2.4': ('colons: {max-spaces-after: 3}\n'),
'example-2.5': ('empty-lines: {max-end: 2}\n'
'brackets: {min-spaces-inside: 0, max-spaces-inside: 2}\n'
'commas: {max-spaces-before: -1}\n'),
'example-2.6': ('braces: {min-spaces-inside: 0, max-spaces-inside: 0}\n'
'indentation: disable\n'),
'example-2.12': ('empty-lines: {max-end: 1}\n'
'colons: {max-spaces-before: -1}\n'),
'example-2.16': ('empty-lines: {max-end: 1}\n'),
'example-2.18': ('empty-lines: {max-end: 1}\n'),
'example-2.19': ('empty-lines: {max-end: 1}\n'),
'example-2.28': ('empty-lines: {max-end: 3}\n'),
'example-5.3': ('indentation: {indent-sequences: no}\n'
'colons: {max-spaces-before: 1}\n'),
'example-6.4': ('trailing-spaces: disable\n'),
'example-6.5': ('trailing-spaces: disable\n'),
'example-6.6': ('trailing-spaces: disable\n'),
'example-6.7': ('trailing-spaces: disable\n'),
'example-6.8': ('trailing-spaces: disable\n'),
'example-6.10': ('empty-lines: {max-end: 2}\n'
'trailing-spaces: disable\n'
'comments-indentation: disable\n'),
'example-6.11': ('empty-lines: {max-end: 1}\n'
'comments-indentation: disable\n'),
'example-6.13': ('comments-indentation: disable\n'),
'example-6.14': ('comments-indentation: disable\n'),
'example-6.23': ('colons: {max-spaces-before: 1}\n'),
'example-7.4': ('colons: {max-spaces-before: 1}\n'
'indentation: disable\n'),
'example-7.5': ('trailing-spaces: disable\n'),
'example-7.6': ('trailing-spaces: disable\n'),
'example-7.7': ('indentation: disable\n'),
'example-7.8': ('colons: {max-spaces-before: 1}\n'
'indentation: disable\n'),
'example-7.9': ('trailing-spaces: disable\n'),
'example-7.11': ('colons: {max-spaces-before: 1}\n'
'indentation: disable\n'),
'example-7.13': ('brackets: {min-spaces-inside: 0, max-spaces-inside: 1}\n'
'commas: {max-spaces-before: 1, min-spaces-after: 0}\n'),
'example-7.14': ('indentation: disable\n'),
'example-7.15': ('braces: {min-spaces-inside: 0, max-spaces-inside: 1}\n'
'commas: {max-spaces-before: 1, min-spaces-after: 0}\n'
'colons: {max-spaces-before: 1}\n'),
'example-7.17': ('indentation: disable\n'),
'example-7.18': ('indentation: disable\n'),
'example-7.19': ('indentation: disable\n'),
'example-7.20': ('colons: {max-spaces-before: 1}\n'
'indentation: disable\n'),
'example-8.1': ('empty-lines: {max-end: 1}\n'),
'example-8.2': ('trailing-spaces: disable\n'),
'example-8.5': ('comments-indentation: disable\n'
'trailing-spaces: disable\n'),
'example-8.6': ('empty-lines: {max-end: 1}\n'),
'example-8.7': ('empty-lines: {max-end: 1}\n'),
'example-8.8': ('trailing-spaces: disable\n'),
'example-8.9': ('empty-lines: {max-end: 1}\n'),
'example-8.14': ('colons: {max-spaces-before: 1}\n'),
'example-8.16': ('indentation: {spaces: 1}\n'),
'example-8.17': ('indentation: disable\n'),
}
files = os.listdir('tests/yaml-1.2-spec-examples')
assert len(files) == 132
def _gen_test(buffer, conf):
def test(self):
self.check(buffer, conf)
return test
# TODO
# The following tests are blacklisted because they contain rarely-used formats
# that yamllint does not handle yet.
tmp_blacklist = (
'example-7.16',
'example-8.20',
'example-8.22',
'example-10.1',
)
# The following tests are blacklisted (i.e. will not be checked against
# yamllint), because pyyaml is currently not able to parse the contents
# (using yaml.parse()).
pyyaml_blacklist = (
'example-2.11',
'example-2.23',
'example-2.24',
'example-2.27',
'example-5.10',
'example-5.12',
'example-5.13',
'example-5.14',
'example-5.6',
'example-6.1',
'example-6.12',
'example-6.15',
'example-6.17',
'example-6.18',
'example-6.19',
'example-6.2',
'example-6.20',
'example-6.21',
'example-6.22',
'example-6.24',
'example-6.25',
'example-6.26',
'example-6.27',
'example-6.3',
'example-7.1',
'example-7.10',
'example-7.12',
'example-7.17',
'example-7.2',
'example-7.21',
'example-7.22',
'example-7.3',
'example-8.18',
'example-8.19',
'example-8.21',
'example-8.3',
'example-9.3',
'example-9.4',
'example-9.5',
)
for file in files:
if file in tmp_blacklist or file in pyyaml_blacklist:
continue
with open('tests/yaml-1.2-spec-examples/' + file) as f:
conf = conf_general + conf_overrides.get(file, '')
setattr(SpecificationTestCase, 'test_' + file,
_gen_test(f.read(), conf))

View File

@@ -14,13 +14,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from tests.rules.common import RuleTestCase from tests.common import RuleTestCase
class YamlLintTestCase(RuleTestCase): class YamlLintTestCase(RuleTestCase):
rule_id = None # syntax error rule_id = None # syntax error
def test_lint(self): def test_syntax_errors(self):
self.check('---\n' self.check('---\n'
'this is not: valid: YAML\n', None, problem=(2, 19)) 'this is not: valid: YAML\n', None, problem=(2, 19))
self.check('---\n' self.check('---\n'
@@ -29,13 +29,21 @@ class YamlLintTestCase(RuleTestCase):
'this is an error: [\n' 'this is an error: [\n'
'\n' '\n'
'...\n', None, problem=(6, 1)) '...\n', None, problem=(6, 1))
def test_directives(self):
self.check('%YAML 1.2\n' self.check('%YAML 1.2\n'
'%TAG ! tag:clarkevans.com,2002:\n' '%TAG ! tag:clarkevans.com,2002:\n'
'doc: ument\n' 'doc: ument\n'
'...\n', None, problem=(3, 1)) '...\n', None, problem=(3, 1))
def test_empty_flows(self):
self.check('---\n'
'- []\n'
'- {}\n'
'- [\n'
']\n'
'- {\n'
'}\n'
'...\n', None)
def test_explicit_mapping(self): def test_explicit_mapping(self):
self.check('---\n' self.check('---\n'
'? key\n' '? key\n'
@@ -67,3 +75,20 @@ class YamlLintTestCase(RuleTestCase):
' Atlanta Braves]\n' ' Atlanta Braves]\n'
': [2001-07-02, 2001-08-12,\n' ': [2001-07-02, 2001-08-12,\n'
' 2001-08-14]\n', None) ' 2001-08-14]\n', None)
def test_sets(self):
self.check('---\n'
'? key one\n'
'? key two\n'
'? [non, scalar, key]\n'
'? key with value\n'
': value\n'
'...\n', None)
self.check('---\n'
'? - multi\n'
' - line\n'
' - keys\n'
'? in:\n'
' a:\n'
' set\n'
'...\n', None)

View File

@@ -0,0 +1,6 @@
Block style: !!map
Clark : Evans
Ingy : döt Net
Oren : Ben-Kiki
Flow style: !!map { Clark: Evans, Ingy: döt Net, Oren: Ben-Kiki }

View File

@@ -0,0 +1,6 @@
Block style: !!seq
- Clark Evans
- Ingy döt Net
- Oren Ben-Kiki
Flow style: !!seq [ Clark Evans, Ingy döt Net, Oren Ben-Kiki ]

View File

@@ -0,0 +1,4 @@
Block style: !!str |-
String: just a theory.
Flow style: !!str "String: just a theory."

View File

@@ -0,0 +1,2 @@
!!null null: value for null key
key with null value: !!null null

View File

@@ -0,0 +1,2 @@
YAML is a superset of JSON: !!bool true
Pluto is a planet: !!bool false

View File

@@ -0,0 +1,3 @@
negative: !!int -12
zero: !!int 0
positive: !!int 34

View File

@@ -0,0 +1,5 @@
negative: !!float -1
zero: !!float 0
positive: !!float 2.3e4
infinity: !!float .inf
not a number: !!float .nan

View File

@@ -0,0 +1,5 @@
A null: null
Booleans: [ true, false ]
Integers: [ 0, -0, 3, -19 ]
Floats: [ 0., -0.0, 12e03, -2E+05 ]
Invalid: [ True, Null, 0o7, 0x3A, +12.3 ]

View File

@@ -0,0 +1,7 @@
A null: null
Also a null: # Empty
Not a null: ""
Booleans: [ true, True, false, FALSE ]
Integers: [ 0, 0o7, 0x3A, -19 ]
Floats: [ 0., -0.0, .5, +12e03, -2E+05 ]
Also floats: [ .inf, -.Inf, +.INF, .NAN ]

View File

@@ -0,0 +1,3 @@
- Mark McGwire
- Sammy Sosa
- Ken Griffey

View File

@@ -0,0 +1,8 @@
---
hr:
- Mark McGwire
# Following node labeled SS
- &SS Sammy Sosa
rbi:
- *SS # Subsequent occurrence
- Ken Griffey

View File

@@ -0,0 +1,9 @@
? - Detroit Tigers
- Chicago cubs
:
- 2001-07-23
? [ New York Yankees,
Atlanta Braves ]
: [ 2001-07-02, 2001-08-12,
2001-08-14 ]

View File

@@ -0,0 +1,9 @@
---
# Products purchased
- item : Super Hoop
quantity: 1
- item : Basketball
quantity: 4
- item : Big Shoes
quantity: 1

View File

@@ -0,0 +1,4 @@
# ASCII Art
--- |
\//||\/||
// || ||__

View File

@@ -0,0 +1,4 @@
--- >
Mark McGwire's
year was crippled
by a knee injury.

View File

@@ -0,0 +1,8 @@
>
Sammy Sosa completed another
fine season with great stats.
63 Home Runs
0.288 Batting Average
What a year!

View File

@@ -0,0 +1,8 @@
name: Mark McGwire
accomplishment: >
Mark set a major league
home run record in 1998.
stats: |
65 Home Runs
0.278 Batting Average

View File

@@ -0,0 +1,7 @@
unicode: "Sosa did fine.\u263A"
control: "\b1998\t1999\t2000\n"
hex esc: "\x0d\x0a is \r\n"
single: '"Howdy!" he cried.'
quoted: ' # Not a ''comment''.'
tie-fighter: '|\-*-/|'

View File

@@ -0,0 +1,7 @@
plain:
This unquoted scalar
spans many lines.
quoted: "So does this
quoted scalar.\n"

View File

@@ -0,0 +1,5 @@
canonical: 12345
decimal: +12345
octal: 0o14
hexadecimal: 0xC

View File

@@ -0,0 +1,3 @@
hr: 65 # Home runs
avg: 0.278 # Batting average
rbi: 147 # Runs Batted In

View File

@@ -0,0 +1,5 @@
canonical: 1.23015e+3
exponential: 12.3015e+02
fixed: 1230.15
negative infinity: -.inf
not a number: .NaN

View File

@@ -0,0 +1,3 @@
null:
booleans: [ true, false ]
string: '012345'

View File

@@ -0,0 +1,4 @@
canonical: 2001-12-15T02:59:43.1Z
iso8601: 2001-12-14t21:59:43.10-05:00
spaced: 2001-12-14 21:59:43.10 -5
date: 2002-12-14

View File

@@ -0,0 +1,14 @@
---
not-date: !!str 2002-04-28
picture: !!binary |
R0lGODlhDAAMAIQAAP//9/X
17unp5WZmZgAAAOfn515eXv
Pz7Y6OjuDg4J+fn5OTk6enp
56enmleECcgggoBADs=
application specific tag: !something |
The semantics of the tag
above may be different for
different documents.

View File

@@ -0,0 +1,14 @@
%TAG ! tag:clarkevans.com,2002:
--- !shape
# Use the ! handle for presenting
# tag:clarkevans.com,2002:circle
- !circle
center: &ORIGIN {x: 73, y: 129}
radius: 7
- !line
start: *ORIGIN
finish: { x: 89, y: 102 }
- !label
start: *ORIGIN
color: 0xFFEEBB
text: Pretty vector drawing.

View File

@@ -0,0 +1,7 @@
# Sets are represented as a
# Mapping where each key is
# associated with a null value
--- !!set
? Mark McGwire
? Sammy Sosa
? Ken Griff

View File

@@ -0,0 +1,7 @@
# Ordered maps are represented as
# A sequence of mappings, with
# each mapping having one key
--- !!omap
- Mark McGwire: 65
- Sammy Sosa: 63
- Ken Griffy: 58

View File

@@ -0,0 +1,29 @@
--- !<tag:clarkevans.com,2002:invoice>
invoice: 34843
date : 2001-01-23
bill-to: &id001
given : Chris
family : Dumars
address:
lines: |
458 Walkman Dr.
Suite #292
city : Royal Oak
state : MI
postal : 48046
ship-to: *id001
product:
- sku : BL394D
quantity : 4
description : Basketball
price : 450.00
- sku : BL4438H
quantity : 1
description : Super Hoop
price : 2392.00
tax : 251.42
total: 4443.52
comments:
Late afternoon is best.
Backup contact is Nancy
Billsmer @ 338-4338.

View File

@@ -0,0 +1,29 @@
---
Time: 2001-11-23 15:01:42 -5
User: ed
Warning:
This is an error message
for the log file
---
Time: 2001-11-23 15:02:31 -5
User: ed
Warning:
A slightly different error
message.
---
Date: 2001-11-23 15:03:17 -5
User: ed
Fatal:
Unknown variable "bar"
Stack:
- file: TopClass.py
line: 23
code: |
x = MoreObject("345\n")
- file: MoreClass.py
line: 58
code: |-
foo = bar

View File

@@ -0,0 +1,8 @@
american:
- Boston Red Sox
- Detroit Tigers
- New York Yankees
national:
- New York Mets
- Chicago Cubs
- Atlanta Braves

View File

@@ -0,0 +1,8 @@
-
name: Mark McGwire
hr: 65
avg: 0.278
-
name: Sammy Sosa
hr: 63
avg: 0.288

View File

@@ -0,0 +1,5 @@
- [name , hr, avg ]
- [Mark McGwire, 65, 0.278]
- [Sammy Sosa , 63, 0.288]

View File

@@ -0,0 +1,5 @@
Mark McGwire: {hr: 65, avg: 0.278}
Sammy Sosa: {
hr: 63,
avg: 0.288
}

View File

@@ -0,0 +1,10 @@
# Ranking of 1998 home runs
---
- Mark McGwire
- Sammy Sosa
- Ken Griffey
# Team ranking
---
- Chicago Cubs
- St Louis Cardinals

View File

@@ -0,0 +1,10 @@
---
time: 20:03:20
player: Sammy Sosa
action: strike (miss)
...
---
time: 20:03:47
player: Sammy Sosa
action: grand slam
...

View File

@@ -0,0 +1,8 @@
---
hr: # 1998 hr ranking
- Mark McGwire
- Sammy Sosa
rbi:
# 1998 rbi ranking
- Sammy Sosa
- Ken Griffey

View File

@@ -0,0 +1 @@
# Comment only.

View File

@@ -0,0 +1,2 @@
commercial-at: @text
grave-accent: `text

View File

@@ -0,0 +1,3 @@
|
Line break (no glyph)
Line break (glyphed)

View File

@@ -0,0 +1,6 @@
# Tabs and spaces
quoted: "Quoted "
block: |
void main() {
printf("Hello, world!\n");
}

View File

@@ -0,0 +1,5 @@
"Fun with \\
\" \a \b \e \f \
\n \r \t \v \0 \
\  \_ \N \L \P \
\x41 \u0041 \U00000041"

View File

@@ -0,0 +1,3 @@
Bad escapes:
"\c
\xq-"

View File

@@ -0,0 +1,3 @@
- Invalid use of BOM
- Inside a document.

View File

@@ -0,0 +1,7 @@
sequence:
- one
- two
mapping:
? sky
: blue
sea : green

View File

@@ -0,0 +1,2 @@
sequence: [ one, two, ]
mapping: { sky: blue, sea: green }

View File

@@ -0,0 +1 @@
# Comment only.

View File

@@ -0,0 +1,2 @@
anchored: !local &anchor value
alias: *anchor

View File

@@ -0,0 +1,6 @@
literal: |
some
text
folded: >
some
text

View File

@@ -0,0 +1,2 @@
single: 'text'
double: "text"

View File

@@ -0,0 +1,2 @@
%YAML 1.2
--- text

View File

@@ -0,0 +1,12 @@
# Leading comment line spaces are
# neither content nor indentation.
Not indented:
By one space: |
By four
spaces
Flow style: [ # Leading spaces
By two, # in flow style
Also by two, # are neither
Still by two # content nor
] # indentation.

View File

@@ -0,0 +1,3 @@
# Comment

View File

@@ -0,0 +1,4 @@
key: # Comment
# lines
value

View File

@@ -0,0 +1,6 @@
{ first: Sammy, last: Sosa }:
# Statistics:
hr: # Home runs
65
avg: # Average
0.278

View File

@@ -0,0 +1,3 @@
%FOO bar baz # Should be ignored
# with a warning.
--- "foo"

View File

@@ -0,0 +1,4 @@
%YAML 1.3 # Attempt parsing
# with a warning
---
"foo"

View File

@@ -0,0 +1,3 @@
%YAML 1.2
%YAML 1.1
foo

View File

@@ -0,0 +1,3 @@
%TAG !yaml! tag:yaml.org,2002:
---
!yaml!str "foo"

View File

@@ -0,0 +1,3 @@
%TAG ! !foo
%TAG ! !foo
bar

View File

@@ -0,0 +1,7 @@
# Private
!foo "bar"
...
# Global
%TAG ! tag:example.com,2000:app/
---
!foo "bar"

View File

@@ -0,0 +1,3 @@
%TAG !! tag:example.com,2000:app/
---
!!int 1 - 3 # Interval, not integer

View File

@@ -0,0 +1,4 @@
? a
: - b
- - c
- d

View File

@@ -0,0 +1,3 @@
%TAG !e! tag:example.com,2000:app/
---
!e!foo "bar"

View File

@@ -0,0 +1,7 @@
%TAG !m! !my-
--- # Bulb here
!m!light fluorescent
...
%TAG !m! !my-
--- # Color here
!m!light green

View File

@@ -0,0 +1,3 @@
%TAG !e! tag:example.com,2000:app/
---
- !e!foo "bar"

View File

@@ -0,0 +1,3 @@
!!str &a1 "foo":
!!str bar
&a2 baz : *a1

View File

@@ -0,0 +1,2 @@
!<tag:yaml.org,2002:str> foo :
!<!bar> baz

View File

@@ -0,0 +1,2 @@
- !<!> foo
- !<$:?> bar

Some files were not shown because too many files have changed in this diff Show More