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