1# -*- coding: utf-8 -*-
2# (c) 2020, Alexei Znamensky <russoz@gmail.com>
3# Copyright (c) 2020 Ansible Project
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6from __future__ import (absolute_import, division, print_function)
7__metaclass__ = type
8
9import pytest
10
11from ansible_collections.community.general.plugins.module_utils.module_helper import (
12    ArgFormat, DependencyCtxMgr, VarMeta, VarDict, cause_changes
13)
14
15
16def single_lambda_2star(x, y, z):
17    return ["piggies=[{0},{1},{2}]".format(x, y, z)]
18
19
20ARG_FORMATS = dict(
21    simple_boolean_true=("--superflag", ArgFormat.BOOLEAN, 0,
22                         True, ["--superflag"]),
23    simple_boolean_false=("--superflag", ArgFormat.BOOLEAN, 0,
24                          False, []),
25    simple_boolean_none=("--superflag", ArgFormat.BOOLEAN, 0,
26                         None, []),
27    simple_boolean_not_true=("--superflag", ArgFormat.BOOLEAN_NOT, 0,
28                             True, []),
29    simple_boolean_not_false=("--superflag", ArgFormat.BOOLEAN_NOT, 0,
30                              False, ["--superflag"]),
31    simple_boolean_not_none=("--superflag", ArgFormat.BOOLEAN_NOT, 0,
32                             None, ["--superflag"]),
33    single_printf=("--param=%s", ArgFormat.PRINTF, 0,
34                   "potatoes", ["--param=potatoes"]),
35    single_printf_no_substitution=("--param", ArgFormat.PRINTF, 0,
36                                   "potatoes", ["--param"]),
37    single_printf_none=("--param=%s", ArgFormat.PRINTF, 0,
38                        None, []),
39    multiple_printf=(["--param", "free-%s"], ArgFormat.PRINTF, 0,
40                     "potatoes", ["--param", "free-potatoes"]),
41    single_format=("--param={0}", ArgFormat.FORMAT, 0,
42                   "potatoes", ["--param=potatoes"]),
43    single_format_none=("--param={0}", ArgFormat.FORMAT, 0,
44                        None, []),
45    single_format_no_substitution=("--param", ArgFormat.FORMAT, 0,
46                                   "potatoes", ["--param"]),
47    multiple_format=(["--param", "free-{0}"], ArgFormat.FORMAT, 0,
48                     "potatoes", ["--param", "free-potatoes"]),
49    multiple_format_none=(["--param", "free-{0}"], ArgFormat.FORMAT, 0,
50                          None, []),
51    single_lambda_0star=((lambda v: ["piggies=[{0},{1},{2}]".format(v[0], v[1], v[2])]), None, 0,
52                         ['a', 'b', 'c'], ["piggies=[a,b,c]"]),
53    single_lambda_0star_none=((lambda v: ["piggies=[{0},{1},{2}]".format(v[0], v[1], v[2])]), None, 0,
54                              None, []),
55    single_lambda_1star=((lambda a, b, c: ["piggies=[{0},{1},{2}]".format(a, b, c)]), None, 1,
56                         ['a', 'b', 'c'], ["piggies=[a,b,c]"]),
57    single_lambda_1star_none=((lambda a, b, c: ["piggies=[{0},{1},{2}]".format(a, b, c)]), None, 1,
58                              None, []),
59    single_lambda_2star=(single_lambda_2star, None, 2,
60                         dict(z='c', x='a', y='b'), ["piggies=[a,b,c]"]),
61    single_lambda_2star_none=(single_lambda_2star, None, 2,
62                              None, []),
63)
64ARG_FORMATS_IDS = sorted(ARG_FORMATS.keys())
65
66
67@pytest.mark.parametrize('fmt, style, stars, value, expected',
68                         (ARG_FORMATS[tc] for tc in ARG_FORMATS_IDS),
69                         ids=ARG_FORMATS_IDS)
70def test_arg_format(fmt, style, stars, value, expected):
71    af = ArgFormat('name', fmt, style, stars)
72    actual = af.to_text(value)
73    print("formatted string = {0}".format(actual))
74    assert actual == expected, "actual = {0}".format(actual)
75
76
77ARG_FORMATS_FAIL = dict(
78    int_fmt=(3, None, 0, "", [""]),
79    bool_fmt=(True, None, 0, "", [""]),
80)
81ARG_FORMATS_FAIL_IDS = sorted(ARG_FORMATS_FAIL.keys())
82
83
84@pytest.mark.parametrize('fmt, style, stars, value, expected',
85                         (ARG_FORMATS_FAIL[tc] for tc in ARG_FORMATS_FAIL_IDS),
86                         ids=ARG_FORMATS_FAIL_IDS)
87def test_arg_format_fail(fmt, style, stars, value, expected):
88    with pytest.raises(TypeError):
89        af = ArgFormat('name', fmt, style, stars)
90        actual = af.to_text(value)
91        print("formatted string = {0}".format(actual))
92
93
94def test_dependency_ctxmgr():
95    ctx = DependencyCtxMgr("POTATOES", "Potatoes must be installed")
96    with ctx:
97        import potatoes_that_will_never_be_there
98    print("POTATOES: ctx.text={0}".format(ctx.text))
99    assert ctx.text == "Potatoes must be installed"
100    assert not ctx.has_it
101
102    ctx = DependencyCtxMgr("POTATOES2")
103    with ctx:
104        import potatoes_that_will_never_be_there_again
105    assert not ctx.has_it
106    print("POTATOES2: ctx.text={0}".format(ctx.text))
107    assert ctx.text.startswith("No module named")
108    assert "potatoes_that_will_never_be_there_again" in ctx.text
109
110    ctx = DependencyCtxMgr("TYPING")
111    with ctx:
112        import sys
113    assert ctx.has_it
114
115
116def test_variable_meta():
117    meta = VarMeta()
118    assert meta.output is True
119    assert meta.diff is False
120    assert meta.value is None
121    meta.set_value("abc")
122    assert meta.initial_value == "abc"
123    assert meta.value == "abc"
124    assert meta.diff_result is None
125    meta.set_value("def")
126    assert meta.initial_value == "abc"
127    assert meta.value == "def"
128    assert meta.diff_result is None
129
130
131def test_variable_meta_diff():
132    meta = VarMeta(diff=True)
133    assert meta.output is True
134    assert meta.diff is True
135    assert meta.value is None
136    meta.set_value("abc")
137    assert meta.initial_value == "abc"
138    assert meta.value == "abc"
139    assert meta.diff_result is None
140    meta.set_value("def")
141    assert meta.initial_value == "abc"
142    assert meta.value == "def"
143    assert meta.diff_result == {"before": "abc", "after": "def"}
144    meta.set_value("ghi")
145    assert meta.initial_value == "abc"
146    assert meta.value == "ghi"
147    assert meta.diff_result == {"before": "abc", "after": "ghi"}
148
149
150def test_vardict():
151    vd = VarDict()
152    vd.set('a', 123)
153    assert vd['a'] == 123
154    assert vd.a == 123
155    assert 'a' in vd._meta
156    assert vd.meta('a').output is True
157    assert vd.meta('a').diff is False
158    assert vd.meta('a').change is False
159    vd['b'] = 456
160    assert vd.meta('b').output is True
161    assert vd.meta('b').diff is False
162    assert vd.meta('b').change is False
163    vd.set_meta('a', diff=True, change=True)
164    vd.set_meta('b', diff=True, output=False)
165    vd['c'] = 789
166    assert vd.has_changed('c') is False
167    vd['a'] = 'new_a'
168    assert vd.has_changed('a') is True
169    vd['c'] = 'new_c'
170    assert vd.has_changed('c') is False
171    vd['b'] = 'new_b'
172    assert vd.has_changed('b') is False
173    assert vd.a == 'new_a'
174    assert vd.c == 'new_c'
175    assert vd.output() == {'a': 'new_a', 'c': 'new_c'}
176    assert vd.diff() == {'before': {'a': 123}, 'after': {'a': 'new_a'}}, "diff={0}".format(vd.diff())
177
178
179def test_variable_meta_change():
180    vd = VarDict()
181    vd.set('a', 123, change=True)
182    vd.set('b', [4, 5, 6], change=True)
183    vd.set('c', {'m': 7, 'n': 8, 'o': 9}, change=True)
184    vd.set('d', {'a1': {'a11': 33, 'a12': 34}}, change=True)
185
186    vd.a = 1234
187    assert vd.has_changed('a') is True
188    vd.b.append(7)
189    assert vd.b == [4, 5, 6, 7]
190    assert vd.has_changed('b')
191    vd.c.update({'p': 10})
192    assert vd.c == {'m': 7, 'n': 8, 'o': 9, 'p': 10}
193    assert vd.has_changed('c')
194    vd.d['a1'].update({'a13': 35})
195    assert vd.d == {'a1': {'a11': 33, 'a12': 34, 'a13': 35}}
196    assert vd.has_changed('d')
197
198
199class MockMH(object):
200    changed = None
201
202    def _div(self, x, y):
203        return x / y
204
205    func_none = cause_changes()(_div)
206    func_onsucc = cause_changes(on_success=True)(_div)
207    func_onfail = cause_changes(on_failure=True)(_div)
208    func_onboth = cause_changes(on_success=True, on_failure=True)(_div)
209
210
211CAUSE_CHG_DECO_PARAMS = ['method', 'expect_exception', 'expect_changed']
212CAUSE_CHG_DECO = dict(
213    none_succ=dict(method='func_none', expect_exception=False, expect_changed=None),
214    none_fail=dict(method='func_none', expect_exception=True, expect_changed=None),
215    onsucc_succ=dict(method='func_onsucc', expect_exception=False, expect_changed=True),
216    onsucc_fail=dict(method='func_onsucc', expect_exception=True, expect_changed=None),
217    onfail_succ=dict(method='func_onfail', expect_exception=False, expect_changed=None),
218    onfail_fail=dict(method='func_onfail', expect_exception=True, expect_changed=True),
219    onboth_succ=dict(method='func_onboth', expect_exception=False, expect_changed=True),
220    onboth_fail=dict(method='func_onboth', expect_exception=True, expect_changed=True),
221)
222CAUSE_CHG_DECO_IDS = sorted(CAUSE_CHG_DECO.keys())
223
224
225@pytest.mark.parametrize(CAUSE_CHG_DECO_PARAMS,
226                         [[CAUSE_CHG_DECO[tc][param]
227                          for param in CAUSE_CHG_DECO_PARAMS]
228                          for tc in CAUSE_CHG_DECO_IDS],
229                         ids=CAUSE_CHG_DECO_IDS)
230def test_cause_changes_deco(method, expect_exception, expect_changed):
231    mh = MockMH()
232    if expect_exception:
233        with pytest.raises(Exception):
234            getattr(mh, method)(1, 0)
235    else:
236        getattr(mh, method)(9, 3)
237
238    assert mh.changed == expect_changed
239