- Replace nose usage with pytest.
- Remove legacy Shippable integration.sh.
- Update Makefile to use pytest and ansible-test.
- Convert most yield unit tests to pytest parametrize.
... | ... |
@@ -98,22 +98,30 @@ RPMNVR = "$(NAME)-$(VERSION)-$(RPMRELEASE)$(RPMDIST)" |
98 | 98 |
MOCK_BIN ?= mock |
99 | 99 |
MOCK_CFG ?= |
100 | 100 |
|
101 |
-NOSETESTS ?= nosetests |
|
101 |
+# ansible-test parameters |
|
102 |
+ANSIBLE_TEST ?= test/runner/ansible-test |
|
103 |
+TEST_FLAGS ?= |
|
102 | 104 |
|
103 |
-NOSETESTS3 ?= nosetests-3.5 |
|
105 |
+# ansible-test units parameters (make test / make test-py3) |
|
106 |
+PYTHON_VERSION ?= $(shell python2 -c 'import sys; print("%s.%s" % sys.version_info[:2])') |
|
107 |
+PYTHON3_VERSION ?= $(shell python3 -c 'import sys; print("%s.%s" % sys.version_info[:2])') |
|
108 |
+ |
|
109 |
+# ansible-test integration parameters (make integration) |
|
110 |
+IMAGE ?= centos7 |
|
111 |
+TARGET ?= |
|
104 | 112 |
|
105 | 113 |
######################################################## |
106 | 114 |
|
107 | 115 |
all: clean python |
108 | 116 |
|
109 | 117 |
tests: |
110 |
- PYTHONPATH=./lib $(NOSETESTS) -d -w test/units -v --with-coverage --cover-package=ansible --cover-branches --cover-erase -e test_os_server |
|
118 |
+ $(ANSIBLE_TEST) units -v --python $(PYTHON_VERSION) $(TEST_FLAGS) |
|
111 | 119 |
|
112 | 120 |
tests-py3: |
113 |
- PYTHONPATH=./lib $(NOSETESTS3) -d -w test/units -v --with-coverage --cover-package=ansible --cover-branches --cover-erase -e test_os_server |
|
121 |
+ $(ANSIBLE_TEST) units -v --python $(PYTHON3_VERSION) $(TEST_FLAGS) |
|
114 | 122 |
|
115 | 123 |
integration: |
116 |
- test/utils/shippable/integration.sh |
|
124 |
+ $(ANSIBLE_TEST) integration -v --docker $(IMAGE) $(TARGET) $(TEST_FLAGS) |
|
117 | 125 |
|
118 | 126 |
authors: |
119 | 127 |
sh hacking/authors.sh |
... | ... |
@@ -17,7 +17,7 @@ and do not wish to install them from your operating system package manager, you |
17 | 17 |
can install them from pip |
18 | 18 |
|
19 | 19 |
$ easy_install pip # if pip is not already available |
20 |
- $ pip install pyyaml jinja2 nose passlib pycrypto |
|
20 |
+ $ pip install pyyaml jinja2 nose pytest passlib pycrypto |
|
21 | 21 |
|
22 | 22 |
From there, follow ansible instructions on docs.ansible.com as normal. |
23 | 23 |
|
... | ... |
@@ -12,7 +12,7 @@ mock interfaces rather than producing side effects. |
12 | 12 |
|
13 | 13 |
Playbook engine code is better suited for integration tests. |
14 | 14 |
|
15 |
-Requirements: `sudo pip install paramiko PyYAML jinja2 httplib2 passlib nose mock` |
|
15 |
+Requirements: `sudo pip install paramiko PyYAML jinja2 httplib2 passlib nose pytest mock` |
|
16 | 16 |
|
17 | 17 |
integration |
18 | 18 |
----------- |
... | ... |
@@ -1,121 +1,100 @@ |
1 |
-import collections |
|
2 |
-import os |
|
3 |
-import re |
|
4 |
- |
|
5 |
- |
|
6 |
-from nose.tools import eq_ |
|
7 |
-try: |
|
8 |
- from nose.tools import assert_raises_regexp |
|
9 |
-except ImportError: |
|
10 |
- from ansible.compat.six import string_types |
|
11 |
- # Python < 2.7 |
|
12 |
- def assert_raises_regexp(expected, regexp, callable, *a, **kw): |
|
13 |
- try: |
|
14 |
- callable(*a, **kw) |
|
15 |
- except expected as e: |
|
16 |
- if isinstance(regexp, string_types): |
|
17 |
- regexp = re.compile(regexp) |
|
18 |
- if not regexp.search(str(e)): |
|
19 |
- raise Exception('"%s" does not match "%s"' % |
|
20 |
- (regexp.pattern, str(e))) |
|
21 |
- else: |
|
22 |
- if hasattr(expected,'__name__'): excName = expected.__name__ |
|
23 |
- else: excName = str(expected) |
|
24 |
- raise AssertionError("%s not raised" % excName) |
|
25 |
- |
|
26 |
-from ansible.compat.tests import mock |
|
1 |
+import pytest |
|
27 | 2 |
|
28 | 3 |
from ansible.module_utils.database import ( |
29 | 4 |
pg_quote_identifier, |
30 | 5 |
SQLParseError, |
31 | 6 |
) |
32 | 7 |
|
8 |
+# These are all valid strings |
|
9 |
+# The results are based on interpreting the identifier as a table name |
|
10 |
+VALID = { |
|
11 |
+ # User quoted |
|
12 |
+ '"public.table"': '"public.table"', |
|
13 |
+ '"public"."table"': '"public"."table"', |
|
14 |
+ '"schema test"."table test"': '"schema test"."table test"', |
|
15 |
+ |
|
16 |
+ # We quote part |
|
17 |
+ 'public.table': '"public"."table"', |
|
18 |
+ '"public".table': '"public"."table"', |
|
19 |
+ 'public."table"': '"public"."table"', |
|
20 |
+ 'schema test.table test': '"schema test"."table test"', |
|
21 |
+ '"schema test".table test': '"schema test"."table test"', |
|
22 |
+ 'schema test."table test"': '"schema test"."table test"', |
|
23 |
+ |
|
24 |
+ # Embedded double quotes |
|
25 |
+ 'table "test"': '"table ""test"""', |
|
26 |
+ 'public."table ""test"""': '"public"."table ""test"""', |
|
27 |
+ 'public.table "test"': '"public"."table ""test"""', |
|
28 |
+ 'schema "test".table': '"schema ""test"""."table"', |
|
29 |
+ '"schema ""test""".table': '"schema ""test"""."table"', |
|
30 |
+ '"""wat"""."""test"""': '"""wat"""."""test"""', |
|
31 |
+ # Sigh, handle these as well: |
|
32 |
+ '"no end quote': '"""no end quote"', |
|
33 |
+ 'schema."table': '"schema"."""table"', |
|
34 |
+ '"schema.table': '"""schema"."table"', |
|
35 |
+ 'schema."table.something': '"schema"."""table"."something"', |
|
36 |
+ |
|
37 |
+ # Embedded dots |
|
38 |
+ '"schema.test"."table.test"': '"schema.test"."table.test"', |
|
39 |
+ '"schema.".table': '"schema."."table"', |
|
40 |
+ '"schema."."table"': '"schema."."table"', |
|
41 |
+ 'schema.".table"': '"schema".".table"', |
|
42 |
+ '"schema".".table"': '"schema".".table"', |
|
43 |
+ '"schema.".".table"': '"schema.".".table"', |
|
44 |
+ # These are valid but maybe not what the user intended |
|
45 |
+ '."table"': '".""table"""', |
|
46 |
+ 'table.': '"table."', |
|
47 |
+} |
|
48 |
+ |
|
49 |
+INVALID = { |
|
50 |
+ ('test.too.many.dots', 'table'): 'PostgreSQL does not support table with more than 3 dots', |
|
51 |
+ ('"test.too".many.dots', 'database'): 'PostgreSQL does not support database with more than 1 dots', |
|
52 |
+ ('test.too."many.dots"', 'database'): 'PostgreSQL does not support database with more than 1 dots', |
|
53 |
+ ('"test"."too"."many"."dots"', 'database'): "PostgreSQL does not support database with more than 1 dots", |
|
54 |
+ ('"test"."too"."many"."dots"', 'schema'): "PostgreSQL does not support schema with more than 2 dots", |
|
55 |
+ ('"test"."too"."many"."dots"', 'table'): "PostgreSQL does not support table with more than 3 dots", |
|
56 |
+ ('"test"."too"."many"."dots"."for"."column"', 'column'): "PostgreSQL does not support column with more than 4 dots", |
|
57 |
+ ('"table "invalid" double quote"', 'table'): 'User escaped identifiers must escape extra quotes', |
|
58 |
+ ('"schema "invalid"""."table "invalid"', 'table'): 'User escaped identifiers must escape extra quotes', |
|
59 |
+ ('"schema."table"', 'table'): 'User escaped identifiers must escape extra quotes', |
|
60 |
+ ('"schema".', 'table'): 'Identifier name unspecified or unquoted trailing dot', |
|
61 |
+} |
|
62 |
+ |
|
63 |
+HOW_MANY_DOTS = ( |
|
64 |
+ ('role', 'role', '"role"', |
|
65 |
+ 'PostgreSQL does not support role with more than 1 dots'), |
|
66 |
+ ('db', 'database', '"db"', |
|
67 |
+ 'PostgreSQL does not support database with more than 1 dots'), |
|
68 |
+ ('db.schema', 'schema', '"db"."schema"', |
|
69 |
+ 'PostgreSQL does not support schema with more than 2 dots'), |
|
70 |
+ ('db.schema.table', 'table', '"db"."schema"."table"', |
|
71 |
+ 'PostgreSQL does not support table with more than 3 dots'), |
|
72 |
+ ('db.schema.table.column', 'column', '"db"."schema"."table"."column"', |
|
73 |
+ 'PostgreSQL does not support column with more than 4 dots'), |
|
74 |
+) |
|
75 |
+ |
|
76 |
+VALID_QUOTES = ((test, VALID[test]) for test in VALID) |
|
77 |
+INVALID_QUOTES = ((test[0], test[1], INVALID[test]) for test in INVALID) |
|
78 |
+ |
|
79 |
+ |
|
80 |
+@pytest.mark.parametrize("identifier, quoted_identifier", VALID_QUOTES) |
|
81 |
+def test_valid_quotes(identifier, quoted_identifier): |
|
82 |
+ assert pg_quote_identifier(identifier, 'table') == quoted_identifier |
|
83 |
+ |
|
84 |
+ |
|
85 |
+@pytest.mark.parametrize("identifier, id_type, msg", INVALID_QUOTES) |
|
86 |
+def test_invalid_quotes(identifier, id_type, msg): |
|
87 |
+ with pytest.raises(SQLParseError) as ex: |
|
88 |
+ pg_quote_identifier(identifier, id_type) |
|
89 |
+ |
|
90 |
+ ex.match(msg) |
|
91 |
+ |
|
92 |
+ |
|
93 |
+@pytest.mark.parametrize("identifier, id_type, quoted_identifier, msg", HOW_MANY_DOTS) |
|
94 |
+def test_how_many_dots(identifier, id_type, quoted_identifier, msg): |
|
95 |
+ assert pg_quote_identifier(identifier, id_type) == quoted_identifier |
|
96 |
+ |
|
97 |
+ with pytest.raises(SQLParseError) as ex: |
|
98 |
+ pg_quote_identifier('%s.more' % identifier, id_type) |
|
33 | 99 |
|
34 |
-# Note: Using nose's generator test cases here so we can't inherit from |
|
35 |
-# unittest.TestCase |
|
36 |
-class TestQuotePgIdentifier(object): |
|
37 |
- |
|
38 |
- # These are all valid strings |
|
39 |
- # The results are based on interpreting the identifier as a table name |
|
40 |
- valid = { |
|
41 |
- # User quoted |
|
42 |
- '"public.table"': '"public.table"', |
|
43 |
- '"public"."table"': '"public"."table"', |
|
44 |
- '"schema test"."table test"': '"schema test"."table test"', |
|
45 |
- |
|
46 |
- # We quote part |
|
47 |
- 'public.table': '"public"."table"', |
|
48 |
- '"public".table': '"public"."table"', |
|
49 |
- 'public."table"': '"public"."table"', |
|
50 |
- 'schema test.table test': '"schema test"."table test"', |
|
51 |
- '"schema test".table test': '"schema test"."table test"', |
|
52 |
- 'schema test."table test"': '"schema test"."table test"', |
|
53 |
- |
|
54 |
- # Embedded double quotes |
|
55 |
- 'table "test"': '"table ""test"""', |
|
56 |
- 'public."table ""test"""': '"public"."table ""test"""', |
|
57 |
- 'public.table "test"': '"public"."table ""test"""', |
|
58 |
- 'schema "test".table': '"schema ""test"""."table"', |
|
59 |
- '"schema ""test""".table': '"schema ""test"""."table"', |
|
60 |
- '"""wat"""."""test"""': '"""wat"""."""test"""', |
|
61 |
- # Sigh, handle these as well: |
|
62 |
- '"no end quote': '"""no end quote"', |
|
63 |
- 'schema."table': '"schema"."""table"', |
|
64 |
- '"schema.table': '"""schema"."table"', |
|
65 |
- 'schema."table.something': '"schema"."""table"."something"', |
|
66 |
- |
|
67 |
- # Embedded dots |
|
68 |
- '"schema.test"."table.test"': '"schema.test"."table.test"', |
|
69 |
- '"schema.".table': '"schema."."table"', |
|
70 |
- '"schema."."table"': '"schema."."table"', |
|
71 |
- 'schema.".table"': '"schema".".table"', |
|
72 |
- '"schema".".table"': '"schema".".table"', |
|
73 |
- '"schema.".".table"': '"schema.".".table"', |
|
74 |
- # These are valid but maybe not what the user intended |
|
75 |
- '."table"': '".""table"""', |
|
76 |
- 'table.': '"table."', |
|
77 |
- } |
|
78 |
- |
|
79 |
- invalid = { |
|
80 |
- ('test.too.many.dots', 'table'): 'PostgreSQL does not support table with more than 3 dots', |
|
81 |
- ('"test.too".many.dots', 'database'): 'PostgreSQL does not support database with more than 1 dots', |
|
82 |
- ('test.too."many.dots"', 'database'): 'PostgreSQL does not support database with more than 1 dots', |
|
83 |
- ('"test"."too"."many"."dots"', 'database'): "PostgreSQL does not support database with more than 1 dots", |
|
84 |
- ('"test"."too"."many"."dots"', 'schema'): "PostgreSQL does not support schema with more than 2 dots", |
|
85 |
- ('"test"."too"."many"."dots"', 'table'): "PostgreSQL does not support table with more than 3 dots", |
|
86 |
- ('"test"."too"."many"."dots"."for"."column"', 'column'): "PostgreSQL does not support column with more than 4 dots", |
|
87 |
- ('"table "invalid" double quote"', 'table'): 'User escaped identifiers must escape extra quotes', |
|
88 |
- ('"schema "invalid"""."table "invalid"', 'table'): 'User escaped identifiers must escape extra quotes', |
|
89 |
- ('"schema."table"','table'): 'User escaped identifiers must escape extra quotes', |
|
90 |
- ('"schema".', 'table'): 'Identifier name unspecified or unquoted trailing dot', |
|
91 |
- } |
|
92 |
- |
|
93 |
- def check_valid_quotes(self, identifier, quoted_identifier): |
|
94 |
- eq_(pg_quote_identifier(identifier, 'table'), quoted_identifier) |
|
95 |
- |
|
96 |
- def test_valid_quotes(self): |
|
97 |
- for identifier in self.valid: |
|
98 |
- yield self.check_valid_quotes, identifier, self.valid[identifier] |
|
99 |
- |
|
100 |
- def check_invalid_quotes(self, identifier, id_type, msg): |
|
101 |
- assert_raises_regexp(SQLParseError, msg, pg_quote_identifier, *(identifier, id_type)) |
|
102 |
- |
|
103 |
- def test_invalid_quotes(self): |
|
104 |
- for test in self.invalid: |
|
105 |
- yield self.check_invalid_quotes, test[0], test[1], self.invalid[test] |
|
106 |
- |
|
107 |
- def test_how_many_dots(self): |
|
108 |
- eq_(pg_quote_identifier('role', 'role'), '"role"') |
|
109 |
- assert_raises_regexp(SQLParseError, "PostgreSQL does not support role with more than 1 dots", pg_quote_identifier, *('role.more', 'role')) |
|
110 |
- |
|
111 |
- eq_(pg_quote_identifier('db', 'database'), '"db"') |
|
112 |
- assert_raises_regexp(SQLParseError, "PostgreSQL does not support database with more than 1 dots", pg_quote_identifier, *('db.more', 'database')) |
|
113 |
- |
|
114 |
- eq_(pg_quote_identifier('db.schema', 'schema'), '"db"."schema"') |
|
115 |
- assert_raises_regexp(SQLParseError, "PostgreSQL does not support schema with more than 2 dots", pg_quote_identifier, *('db.schema.more', 'schema')) |
|
116 |
- |
|
117 |
- eq_(pg_quote_identifier('db.schema.table', 'table'), '"db"."schema"."table"') |
|
118 |
- assert_raises_regexp(SQLParseError, "PostgreSQL does not support table with more than 3 dots", pg_quote_identifier, *('db.schema.table.more', 'table')) |
|
119 |
- |
|
120 |
- eq_(pg_quote_identifier('db.schema.table.column', 'column'), '"db"."schema"."table"."column"') |
|
121 |
- assert_raises_regexp(SQLParseError, "PostgreSQL does not support column with more than 4 dots", pg_quote_identifier, *('db.schema.table.column.more', 'column')) |
|
100 |
+ ex.match(msg) |
... | ... |
@@ -20,96 +20,88 @@ |
20 | 20 |
from __future__ import (absolute_import, division, print_function) |
21 | 21 |
__metaclass__ = type |
22 | 22 |
|
23 |
-from nose import tools |
|
24 |
-from ansible.compat.tests import unittest |
|
25 |
- |
|
26 | 23 |
from ansible.parsing.splitter import split_args, parse_kv |
27 | 24 |
|
25 |
+import pytest |
|
26 |
+ |
|
27 |
+SPLIT_DATA = ( |
|
28 |
+ (u'a', |
|
29 |
+ [u'a'], |
|
30 |
+ {u'_raw_params': u'a'}), |
|
31 |
+ (u'a=b', |
|
32 |
+ [u'a=b'], |
|
33 |
+ {u'a': u'b'}), |
|
34 |
+ (u'a="foo bar"', |
|
35 |
+ [u'a="foo bar"'], |
|
36 |
+ {u'a': u'foo bar'}), |
|
37 |
+ (u'"foo bar baz"', |
|
38 |
+ [u'"foo bar baz"'], |
|
39 |
+ {u'_raw_params': '"foo bar baz"'}), |
|
40 |
+ (u'foo bar baz', |
|
41 |
+ [u'foo', u'bar', u'baz'], |
|
42 |
+ {u'_raw_params': u'foo bar baz'}), |
|
43 |
+ (u'a=b c="foo bar"', |
|
44 |
+ [u'a=b', u'c="foo bar"'], |
|
45 |
+ {u'a': u'b', u'c': u'foo bar'}), |
|
46 |
+ (u'a="echo \\"hello world\\"" b=bar', |
|
47 |
+ [u'a="echo \\"hello world\\""', u'b=bar'], |
|
48 |
+ {u'a': u'echo "hello world"', u'b': u'bar'}), |
|
49 |
+ (u'a="multi\nline"', |
|
50 |
+ [u'a="multi\nline"'], |
|
51 |
+ {u'a': u'multi\nline'}), |
|
52 |
+ (u'a="blank\n\nline"', |
|
53 |
+ [u'a="blank\n\nline"'], |
|
54 |
+ {u'a': u'blank\n\nline'}), |
|
55 |
+ (u'a="blank\n\n\nlines"', |
|
56 |
+ [u'a="blank\n\n\nlines"'], |
|
57 |
+ {u'a': u'blank\n\n\nlines'}), |
|
58 |
+ (u'a="a long\nmessage\\\nabout a thing\n"', |
|
59 |
+ [u'a="a long\nmessage\\\nabout a thing\n"'], |
|
60 |
+ {u'a': u'a long\nmessage\\\nabout a thing\n'}), |
|
61 |
+ (u'a="multiline\nmessage1\\\n" b="multiline\nmessage2\\\n"', |
|
62 |
+ [u'a="multiline\nmessage1\\\n"', u'b="multiline\nmessage2\\\n"'], |
|
63 |
+ {u'a': 'multiline\nmessage1\\\n', u'b': u'multiline\nmessage2\\\n'}), |
|
64 |
+ (u'a={{jinja}}', |
|
65 |
+ [u'a={{jinja}}'], |
|
66 |
+ {u'a': u'{{jinja}}'}), |
|
67 |
+ (u'a={{ jinja }}', |
|
68 |
+ [u'a={{ jinja }}'], |
|
69 |
+ {u'a': u'{{ jinja }}'}), |
|
70 |
+ (u'a="{{jinja}}"', |
|
71 |
+ [u'a="{{jinja}}"'], |
|
72 |
+ {u'a': u'{{jinja}}'}), |
|
73 |
+ (u'a={{ jinja }}{{jinja2}}', |
|
74 |
+ [u'a={{ jinja }}{{jinja2}}'], |
|
75 |
+ {u'a': u'{{ jinja }}{{jinja2}}'}), |
|
76 |
+ (u'a="{{ jinja }}{{jinja2}}"', |
|
77 |
+ [u'a="{{ jinja }}{{jinja2}}"'], |
|
78 |
+ {u'a': u'{{ jinja }}{{jinja2}}'}), |
|
79 |
+ (u'a={{jinja}} b={{jinja2}}', |
|
80 |
+ [u'a={{jinja}}', u'b={{jinja2}}'], |
|
81 |
+ {u'a': u'{{jinja}}', u'b': u'{{jinja2}}'}), |
|
82 |
+ (u'a="{{jinja}}\n" b="{{jinja2}}\n"', |
|
83 |
+ [u'a="{{jinja}}\n"', u'b="{{jinja2}}\n"'], |
|
84 |
+ {u'a': u'{{jinja}}\n', u'b': u'{{jinja2}}\n'}), |
|
85 |
+ (u'a="café eñyei"', |
|
86 |
+ [u'a="café eñyei"'], |
|
87 |
+ {u'a': u'café eñyei'}), |
|
88 |
+ (u'a=café b=eñyei', |
|
89 |
+ [u'a=café', u'b=eñyei'], |
|
90 |
+ {u'a': u'café', u'b': u'eñyei'}), |
|
91 |
+ (u'a={{ foo | some_filter(\' \', " ") }} b=bar', |
|
92 |
+ [u'a={{ foo | some_filter(\' \', " ") }}', u'b=bar'], |
|
93 |
+ {u'a': u'{{ foo | some_filter(\' \', " ") }}', u'b': u'bar'}), |
|
94 |
+) |
|
28 | 95 |
|
29 |
-# Tests using nose's test generators cannot use unittest base class. |
|
30 |
-# http://nose.readthedocs.org/en/latest/writing_tests.html#test-generators |
|
31 |
-class TestSplitter_Gen: |
|
32 |
- SPLIT_DATA = ( |
|
33 |
- (u'a', |
|
34 |
- [u'a'], |
|
35 |
- {u'_raw_params': u'a'}), |
|
36 |
- (u'a=b', |
|
37 |
- [u'a=b'], |
|
38 |
- {u'a': u'b'}), |
|
39 |
- (u'a="foo bar"', |
|
40 |
- [u'a="foo bar"'], |
|
41 |
- {u'a': u'foo bar'}), |
|
42 |
- (u'"foo bar baz"', |
|
43 |
- [u'"foo bar baz"'], |
|
44 |
- {u'_raw_params': '"foo bar baz"'}), |
|
45 |
- (u'foo bar baz', |
|
46 |
- [u'foo', u'bar', u'baz'], |
|
47 |
- {u'_raw_params': u'foo bar baz'}), |
|
48 |
- (u'a=b c="foo bar"', |
|
49 |
- [u'a=b', u'c="foo bar"'], |
|
50 |
- {u'a': u'b', u'c': u'foo bar'}), |
|
51 |
- (u'a="echo \\"hello world\\"" b=bar', |
|
52 |
- [u'a="echo \\"hello world\\""', u'b=bar'], |
|
53 |
- {u'a': u'echo "hello world"', u'b': u'bar'}), |
|
54 |
- (u'a="multi\nline"', |
|
55 |
- [u'a="multi\nline"'], |
|
56 |
- {u'a': u'multi\nline'}), |
|
57 |
- (u'a="blank\n\nline"', |
|
58 |
- [u'a="blank\n\nline"'], |
|
59 |
- {u'a': u'blank\n\nline'}), |
|
60 |
- (u'a="blank\n\n\nlines"', |
|
61 |
- [u'a="blank\n\n\nlines"'], |
|
62 |
- {u'a': u'blank\n\n\nlines'}), |
|
63 |
- (u'a="a long\nmessage\\\nabout a thing\n"', |
|
64 |
- [u'a="a long\nmessage\\\nabout a thing\n"'], |
|
65 |
- {u'a': u'a long\nmessage\\\nabout a thing\n'}), |
|
66 |
- (u'a="multiline\nmessage1\\\n" b="multiline\nmessage2\\\n"', |
|
67 |
- [u'a="multiline\nmessage1\\\n"', u'b="multiline\nmessage2\\\n"'], |
|
68 |
- {u'a': 'multiline\nmessage1\\\n', u'b': u'multiline\nmessage2\\\n'}), |
|
69 |
- (u'a={{jinja}}', |
|
70 |
- [u'a={{jinja}}'], |
|
71 |
- {u'a': u'{{jinja}}'}), |
|
72 |
- (u'a={{ jinja }}', |
|
73 |
- [u'a={{ jinja }}'], |
|
74 |
- {u'a': u'{{ jinja }}'}), |
|
75 |
- (u'a="{{jinja}}"', |
|
76 |
- [u'a="{{jinja}}"'], |
|
77 |
- {u'a': u'{{jinja}}'}), |
|
78 |
- (u'a={{ jinja }}{{jinja2}}', |
|
79 |
- [u'a={{ jinja }}{{jinja2}}'], |
|
80 |
- {u'a': u'{{ jinja }}{{jinja2}}'}), |
|
81 |
- (u'a="{{ jinja }}{{jinja2}}"', |
|
82 |
- [u'a="{{ jinja }}{{jinja2}}"'], |
|
83 |
- {u'a': u'{{ jinja }}{{jinja2}}'}), |
|
84 |
- (u'a={{jinja}} b={{jinja2}}', |
|
85 |
- [u'a={{jinja}}', u'b={{jinja2}}'], |
|
86 |
- {u'a': u'{{jinja}}', u'b': u'{{jinja2}}'}), |
|
87 |
- (u'a="{{jinja}}\n" b="{{jinja2}}\n"', |
|
88 |
- [u'a="{{jinja}}\n"', u'b="{{jinja2}}\n"'], |
|
89 |
- {u'a': u'{{jinja}}\n', u'b': u'{{jinja2}}\n'}), |
|
90 |
- (u'a="café eñyei"', |
|
91 |
- [u'a="café eñyei"'], |
|
92 |
- {u'a': u'café eñyei'}), |
|
93 |
- (u'a=café b=eñyei', |
|
94 |
- [u'a=café', u'b=eñyei'], |
|
95 |
- {u'a': u'café', u'b': u'eñyei'}), |
|
96 |
- (u'a={{ foo | some_filter(\' \', " ") }} b=bar', |
|
97 |
- [u'a={{ foo | some_filter(\' \', " ") }}', u'b=bar'], |
|
98 |
- {u'a': u'{{ foo | some_filter(\' \', " ") }}', u'b': u'bar'}), |
|
99 |
- ) |
|
96 |
+SPLIT_ARGS = ((test[0], test[1]) for test in SPLIT_DATA) |
|
97 |
+PARSE_KV = ((test[0], test[2]) for test in SPLIT_DATA) |
|
100 | 98 |
|
101 |
- def check_split_args(self, args, expected): |
|
102 |
- tools.eq_(split_args(args), expected) |
|
103 | 99 |
|
104 |
- def test_split_args(self): |
|
105 |
- for datapoint in self.SPLIT_DATA: |
|
106 |
- yield self.check_split_args, datapoint[0], datapoint[1] |
|
100 |
+@pytest.mark.parametrize("args, expected", SPLIT_ARGS) |
|
101 |
+def test_split_args(args, expected): |
|
102 |
+ assert split_args(args) == expected |
|
107 | 103 |
|
108 |
- def check_parse_kv(self, args, expected): |
|
109 |
- tools.eq_(parse_kv(args), expected) |
|
110 | 104 |
|
111 |
- def test_parse_kv(self): |
|
112 |
- for datapoint in self.SPLIT_DATA: |
|
113 |
- try: |
|
114 |
- yield self.check_parse_kv, datapoint[0], datapoint[2] |
|
115 |
- except: pass |
|
105 |
+@pytest.mark.parametrize("args, expected", PARSE_KV) |
|
106 |
+def test_parse_kv(args, expected): |
|
107 |
+ assert parse_kv(args) == expected |
... | ... |
@@ -20,39 +20,32 @@ |
20 | 20 |
from __future__ import (absolute_import, division, print_function) |
21 | 21 |
__metaclass__ = type |
22 | 22 |
|
23 |
-from nose import tools |
|
24 |
-from ansible.compat.tests import unittest |
|
25 |
- |
|
26 | 23 |
from ansible.parsing.quoting import unquote |
27 | 24 |
|
25 |
+import pytest |
|
28 | 26 |
|
29 |
-# Tests using nose's test generators cannot use unittest base class. |
|
30 |
-# http://nose.readthedocs.org/en/latest/writing_tests.html#test-generators |
|
31 |
-class TestUnquote: |
|
32 |
- UNQUOTE_DATA = ( |
|
33 |
- (u'1', u'1'), |
|
34 |
- (u'\'1\'', u'1'), |
|
35 |
- (u'"1"', u'1'), |
|
36 |
- (u'"1 \'2\'"', u'1 \'2\''), |
|
37 |
- (u'\'1 "2"\'', u'1 "2"'), |
|
38 |
- (u'\'1 \'2\'\'', u'1 \'2\''), |
|
39 |
- (u'"1\\"', u'"1\\"'), |
|
40 |
- (u'\'1\\\'', u'\'1\\\''), |
|
41 |
- (u'"1 \\"2\\" 3"', u'1 \\"2\\" 3'), |
|
42 |
- (u'\'1 \\\'2\\\' 3\'', u'1 \\\'2\\\' 3'), |
|
43 |
- (u'"', u'"'), |
|
44 |
- (u'\'', u'\''), |
|
45 |
- # Not entirely sure these are good but they match the current |
|
46 |
- # behaviour |
|
47 |
- (u'"1""2"', u'1""2'), |
|
48 |
- (u'\'1\'\'2\'', u'1\'\'2'), |
|
49 |
- (u'"1" 2 "3"', u'1" 2 "3'), |
|
50 |
- (u'"1"\'2\'"3"', u'1"\'2\'"3'), |
|
51 |
- ) |
|
27 |
+UNQUOTE_DATA = ( |
|
28 |
+ (u'1', u'1'), |
|
29 |
+ (u'\'1\'', u'1'), |
|
30 |
+ (u'"1"', u'1'), |
|
31 |
+ (u'"1 \'2\'"', u'1 \'2\''), |
|
32 |
+ (u'\'1 "2"\'', u'1 "2"'), |
|
33 |
+ (u'\'1 \'2\'\'', u'1 \'2\''), |
|
34 |
+ (u'"1\\"', u'"1\\"'), |
|
35 |
+ (u'\'1\\\'', u'\'1\\\''), |
|
36 |
+ (u'"1 \\"2\\" 3"', u'1 \\"2\\" 3'), |
|
37 |
+ (u'\'1 \\\'2\\\' 3\'', u'1 \\\'2\\\' 3'), |
|
38 |
+ (u'"', u'"'), |
|
39 |
+ (u'\'', u'\''), |
|
40 |
+ # Not entirely sure these are good but they match the current |
|
41 |
+ # behaviour |
|
42 |
+ (u'"1""2"', u'1""2'), |
|
43 |
+ (u'\'1\'\'2\'', u'1\'\'2'), |
|
44 |
+ (u'"1" 2 "3"', u'1" 2 "3'), |
|
45 |
+ (u'"1"\'2\'"3"', u'1"\'2\'"3'), |
|
46 |
+) |
|
52 | 47 |
|
53 |
- def check_unquote(self, quoted, expected): |
|
54 |
- tools.eq_(unquote(quoted), expected) |
|
55 | 48 |
|
56 |
- def test_unquote(self): |
|
57 |
- for datapoint in self.UNQUOTE_DATA: |
|
58 |
- yield self.check_unquote, datapoint[0], datapoint[1] |
|
49 |
+@pytest.mark.parametrize("quoted, expected", UNQUOTE_DATA) |
|
50 |
+def test_unquote(quoted, expected): |
|
51 |
+ assert unquote(quoted) == expected |
2 | 1 |
deleted file mode 100755 |
... | ... |
@@ -1,140 +0,0 @@ |
1 |
-#!/bin/bash -eux |
|
2 |
- |
|
3 |
-source_root=$(python -c "from os import path; print(path.abspath(path.join(path.dirname('$0'), '../../..')))") |
|
4 |
- |
|
5 |
-test_image="${IMAGE:-ansible/ansible:centos7}" |
|
6 |
-test_privileged="${PRIVILEGED:-false}" |
|
7 |
-test_flags="${TEST_FLAGS:-}" |
|
8 |
-test_target="${TARGET:-all}" |
|
9 |
-test_ansible_dir="${TEST_ANSIBLE_DIR:-/root/ansible}" |
|
10 |
-test_python3="${PYTHON3:-}" |
|
11 |
- |
|
12 |
-http_image="${HTTP_IMAGE:-ansible/ansible:httptester}" |
|
13 |
- |
|
14 |
-# Keep the docker containers after tests complete. |
|
15 |
-# The default behavior is to always remove the containers. |
|
16 |
-# Set to "onfailure" to keep the containers only on test failure. |
|
17 |
-# Any other non-empty value will always keep the containers. |
|
18 |
-keep_containers="${KEEP_CONTAINERS:-}" |
|
19 |
- |
|
20 |
-# Run the tests directly from the source directory shared with the container. |
|
21 |
-# The default behavior is to run the tests on a copy of the source. |
|
22 |
-# Copying the source isolates changes to the source between host and container. |
|
23 |
-# Set to any non-empty value to share the source. |
|
24 |
-share_source="${SHARE_SOURCE:-}" |
|
25 |
- |
|
26 |
-# Force ansible color output by default. |
|
27 |
-# To disable color force mode use FORCE_COLOR=0 |
|
28 |
-force_color="${FORCE_COLOR:-1}" |
|
29 |
- |
|
30 |
-if [ "${SHIPPABLE_BUILD_DIR:-}" ]; then |
|
31 |
- host_shared_dir="/home/shippable/cache/build-${BUILD_NUMBER}" |
|
32 |
- controller_shared_dir="/home/shippable/cache/build-${BUILD_NUMBER}" |
|
33 |
- share_source=1 |
|
34 |
- test_privileged=false # temporarily disabled to troubleshoot performance issues |
|
35 |
-else |
|
36 |
- host_shared_dir="${source_root}" |
|
37 |
- controller_shared_dir="" |
|
38 |
-fi |
|
39 |
- |
|
40 |
-if [ -z "${share_source}" ]; then |
|
41 |
- test_shared_dir="/shared" |
|
42 |
-else |
|
43 |
- test_shared_dir="${test_ansible_dir}" |
|
44 |
-fi |
|
45 |
- |
|
46 |
-container_id= |
|
47 |
-httptester_id= |
|
48 |
-tests_completed= |
|
49 |
- |
|
50 |
-function show_environment |
|
51 |
-{ |
|
52 |
- docker ps |
|
53 |
- |
|
54 |
- if [ -d /home/shippable/cache ]; then |
|
55 |
- ls -l /home/shippable/cache |
|
56 |
- fi |
|
57 |
-} |
|
58 |
- |
|
59 |
-function cleanup |
|
60 |
-{ |
|
61 |
- if [ "${controller_shared_dir}" ]; then |
|
62 |
- cp -av "${controller_shared_dir}/shippable" "${SHIPPABLE_BUILD_DIR}" |
|
63 |
- rm -rf "${controller_shared_dir}" |
|
64 |
- fi |
|
65 |
- |
|
66 |
- if [ "${keep_containers}" == "onfailure" ] && [ "${tests_completed}" != "" ]; then |
|
67 |
- keep_containers= |
|
68 |
- fi |
|
69 |
- |
|
70 |
- if [ "${keep_containers}" == "" ]; then |
|
71 |
- if [ "${container_id}" ]; then |
|
72 |
- docker rm -f "${container_id}" |
|
73 |
- fi |
|
74 |
- |
|
75 |
- if [ "${httptester_id}" ]; then |
|
76 |
- docker rm -f "${httptester_id}" |
|
77 |
- fi |
|
78 |
- fi |
|
79 |
- |
|
80 |
- show_environment |
|
81 |
-} |
|
82 |
- |
|
83 |
-trap cleanup EXIT INT TERM |
|
84 |
-docker images ansible/ansible |
|
85 |
-show_environment |
|
86 |
- |
|
87 |
-if [ "${controller_shared_dir}" ]; then |
|
88 |
- cp -a "${SHIPPABLE_BUILD_DIR}" "${controller_shared_dir}" |
|
89 |
-fi |
|
90 |
- |
|
91 |
-httptester_id=$(docker run -d "${http_image}") |
|
92 |
-container_id=$(docker run -d \ |
|
93 |
- --env "ANSIBLE_FORCE_COLOR=${force_color}" \ |
|
94 |
- -v "/sys/fs/cgroup:/sys/fs/cgroup:ro" \ |
|
95 |
- -v "${host_shared_dir}:${test_shared_dir}" \ |
|
96 |
- --link="${httptester_id}:ansible.http.tests" \ |
|
97 |
- --link="${httptester_id}:sni1.ansible.http.tests" \ |
|
98 |
- --link="${httptester_id}:sni2.ansible.http.tests" \ |
|
99 |
- --link="${httptester_id}:fail.ansible.http.tests" \ |
|
100 |
- --privileged="${test_privileged}" \ |
|
101 |
- "${test_image}") |
|
102 |
- |
|
103 |
-show_environment |
|
104 |
- |
|
105 |
-skip= |
|
106 |
- |
|
107 |
-if [ "${test_python3}" ]; then |
|
108 |
- docker exec "${container_id}" ln -s /usr/bin/python3 /usr/bin/python |
|
109 |
- docker exec "${container_id}" ln -s /usr/bin/pip3 /usr/bin/pip |
|
110 |
- |
|
111 |
- skip+=",$(tr '\n' ',' < "${source_root}/test/utils/shippable/python3-test-tag-blacklist.txt")" |
|
112 |
-fi |
|
113 |
- |
|
114 |
-if [ "${test_privileged}" = 'false' ]; then |
|
115 |
- skip+=",needs_privileged" |
|
116 |
-fi |
|
117 |
- |
|
118 |
-if [ "${skip}" ]; then |
|
119 |
- test_flags="--skip-tags ${skip} ${test_flags}" |
|
120 |
-fi |
|
121 |
- |
|
122 |
-if [ -z "${share_source}" ]; then |
|
123 |
- docker exec "${container_id}" cp -a "${test_shared_dir}" "${test_ansible_dir}" |
|
124 |
-fi |
|
125 |
- |
|
126 |
-docker exec "${container_id}" \ |
|
127 |
- pip install -r "${test_ansible_dir}/test/utils/shippable/integration-requirements.txt" --upgrade |
|
128 |
- |
|
129 |
-if [ "${test_python3}" ]; then |
|
130 |
- docker exec "${container_id}" sed -i -f \ |
|
131 |
- "${test_ansible_dir}/test/utils/shippable/python3-test-target-blacklist.txt" \ |
|
132 |
- "${test_ansible_dir}/test/integration/Makefile" |
|
133 |
-fi |
|
134 |
- |
|
135 |
-docker exec "${container_id}" mkdir -p "${test_shared_dir}/shippable/testresults" |
|
136 |
-docker exec "${container_id}" /bin/sh -c "cd '${test_ansible_dir}' && . hacking/env-setup && cd test/integration && \ |
|
137 |
- JUNIT_OUTPUT_DIR='${test_shared_dir}/shippable/testresults' ANSIBLE_CALLBACK_WHITELIST=junit \ |
|
138 |
- HTTPTESTER=1 TEST_FLAGS='${test_flags}' LC_ALL=en_US.utf-8 make ${test_target}" |
|
139 |
- |
|
140 |
-tests_completed=1 |