1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
|
import inspect
from unittest import mock
from unittest.mock import MagicMock
from faker.config import DEFAULT_LOCALE
from faker.sphinx.docstring import DEFAULT_SAMPLE_SIZE, DEFAULT_SEED, ProviderMethodDocstring, Sample
class TestProviderMethodDocstring:
def test_what_is_not_method(self):
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="not_a_method",
name="name",
obj=MagicMock,
options=MagicMock(),
lines=MagicMock(),
)
assert docstring.skipped
def test_name_is_not_dotted_path_to_provider_method(self):
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name="faker.sphinx.docstring.ProviderMethodDocString._parse",
obj=MagicMock,
options=MagicMock(),
lines=MagicMock(),
)
assert docstring.skipped
def test_name_is_dotted_path_to_base_provider_method(self):
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name="faker.providers.BaseProvider.bothify",
obj=MagicMock,
options=MagicMock(),
lines=MagicMock(),
)
assert not docstring.skipped
assert docstring._method == "bothify"
assert docstring._locale == DEFAULT_LOCALE
def test_name_is_dotted_path_to_standard_provider_method(self):
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name="faker.providers.barcode.Provider.upc_a",
obj=MagicMock,
options=MagicMock(),
lines=MagicMock(),
)
assert not docstring.skipped
assert docstring._method == "upc_a"
assert docstring._locale == DEFAULT_LOCALE
def test_name_is_dotted_path_to_localized_provider_method(self):
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name="faker.providers.automotive.en_PH.Provider.protocol_license_plate",
obj=MagicMock,
options=MagicMock(),
lines=MagicMock(),
)
assert not docstring.skipped
assert docstring._method == "protocol_license_plate"
assert docstring._locale == "en_PH"
@mock.patch("faker.sphinx.docstring.logger.warning")
def test_log_warning(self, mock_logger_warning):
path = inspect.getfile(MagicMock)
name = "faker.providers.color.Provider"
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name=name,
obj=MagicMock,
options=MagicMock(),
lines=MagicMock(),
)
docstring._log_warning("Test Warning 1")
docstring._log_warning("Test Warning 2")
assert docstring._log_prefix == f"{path}:docstring of {name}: WARNING:"
calls = mock_logger_warning.call_args_list
assert len(calls) == 2
# 1st call to logger.warning
args, kwargs = calls[0]
assert len(args) == 1
assert not kwargs
assert args[0] == f"{path}:docstring of {name}: WARNING: Test Warning 1"
# 2nd call to logger.warning
args, kwargs = calls[1]
assert len(args) == 1
assert not kwargs
assert args[0] == f"{path}:docstring of {name}: WARNING: Test Warning 2"
def test_stringify_results(self, faker):
class TestObject:
def __repr__(self):
return "abcdefg"
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name="faker.providers.BaseProvider.bothify",
obj=MagicMock,
options=MagicMock(),
lines=[],
)
results = [
"", # Empty string
"'", # Single quote literal (escaped)
"'", # Single quote literal (unescaped)
'"', # Double quote literal (unescaped)
'"', # Double quote literal (escaped)
"aa\taaaaa\r\n", # String containing \t, \r, \n
b"abcdef", # Bytes object
True, # Booleans
False,
None, # None types
[1, 2, 3, 4, 5], # Other non-primitives
(1, 2, 3, 4, 5),
{1: 2, 2: 3, 3: 4, 4: 5},
faker.uuid4(cast_to=None),
TestObject(),
]
output = [docstring._stringify_result(result) for result in results]
assert output == [
"''", # Ends up as '' when printed
'"\'"', # Ends up as "'" when printed
'"\'"', # Ends up as "'" when printed
"'\"'", # Ends up as '"' when printed
"'\"'", # Ends up as '"' when printed
"'aa\\taaaaa\\r\\n'", # Ends up as 'aa\\taaaaa\\r\\n' when printed
"b'abcdef'", # Ends up as b'abcdef' when printed
"True", # Ends up as True when printed
"False", # Ends up as False when printed
"None", # Ends up as None when printed
"[1, 2, 3, 4, 5]", # Ends up using object's __repr__
"(1, 2, 3, 4, 5)",
"{1: 2, 2: 3, 3: 4, 4: 5}",
"UUID('e3e70682-c209-4cac-a29f-6fbed82c07cd')",
"abcdefg",
]
@mock.patch.object(ProviderMethodDocstring, "_log_warning")
def test_parsing_empty_lines(self, mock_log_warning):
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name="faker.providers.BaseProvider.bothify",
obj=MagicMock,
options=MagicMock(),
lines=[],
)
assert not docstring.skipped
assert len(docstring._samples) == 1
assert docstring._samples[0] == Sample(DEFAULT_SAMPLE_SIZE, DEFAULT_SEED, "")
@mock.patch.object(ProviderMethodDocstring, "_log_warning")
def test_parsing_single_line_non_sample(self, mock_log_warning):
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name="faker.providers.BaseProvider.bothify",
obj=MagicMock,
options=MagicMock(),
lines=["lorem"],
)
assert not docstring.skipped
assert len(docstring._samples) == 1
assert docstring._samples[0] == Sample(DEFAULT_SAMPLE_SIZE, DEFAULT_SEED, "")
@mock.patch.object(ProviderMethodDocstring, "_log_warning")
def test_parsing_single_line_valid_sample(self, mock_log_warning):
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name="faker.providers.BaseProvider.bothify",
obj=MagicMock,
options=MagicMock(),
lines=[":sample: a=1"],
)
assert not docstring.skipped
assert docstring._samples == [Sample(5, 0, "a=1")]
@mock.patch.object(ProviderMethodDocstring, "_log_warning")
def test_parsing_multiple_lines(self, mock_log_warning):
lines = [
"lorem", # No-op, not a sample line
":sample:", # Valid, default sample count, default seed, empty kwargs, 1st in expected
":sample 10 2000:", # Invalid, size and seed must be specified as "keyword arguments"
":sample 10 seed=1000:", # Invalid, size and seed must be specified as "keyword arguments"
":sample size=10 1000:", # Invalid, size and seed must be specified as "keyword arguments"
":sample size=0:", # Invalid, sample count cannot be zero
":sample size=100:", # Valid, 100 samples, default seed, empty kwargs, 2nd in expected
":sample size=0100:", # Invalid, leading zeroes are not allowed
":sampler", # Invalid, starts with ":sample" but will not pass validation
":sample :", # No-op, must be ":sample:" verbatim
":sample seed=4761:", # Valid, default sample count, seed value of 4761
"", # but line break was detected, so sample parsing stops here
"ipsum", # No-op, not a sample line
":sample sede=123", # Invalid, seed misspelled
":sample size=4 seed=100:", # Valid, will reset to 5 samples, seed value of 100, empty kwargs, the 4th
":sample seed=103 size=104:", # Invalid, "seed" kwarg must come after "size" kwarg
":sample: a=1, b=2", # Valid, default count and seed with kwargs, the 5th
":sample size=2222: a=2, b=1", # Valid, 2222 samples, default seed, and with kwargs, the 6th
":sample 11 12:", # Invalid, seed value must be set with "seed=" prefix
":sample seed=3333: d=3", # Valid, default count, seed value of 3333, with kwargs, the 7th
":sample size=3333 seed=2222: c=1", # Valid, 3333 samples, seed value of 2222, with kwargs, the 8th
":sample size=10 seed=10:", # Valid 9th, 10 samples, seed value of 10, with kwargs
" arg1=1,", # and will continue reading the next few lines
' arg2="val2",arg3="val3",', # and will prettify (missing whitespace after comma)
" arg4=4 , arg5=5,", # and will remove excess whitespaces here
' arg6="ar g6",', # but not if whitespaces are within double quotes
" arg7=' ar g 7',", # or within single quotes
' arg8="aaa,aaa"', # and will not prettify commas within quotes
":sample size=20 seed=3456:", # Valid 10th, 20 samples, seed value of 3456, with kwargs
'arg1="val1,val1,val1",', # and this is very similar to previous sample
'arg2="val2",', # and it is ok not to have leading whitespaces in continuation lines
'arg3="val3 val3",', # and it is ok to have a trailing comma after the last kwarg
]
expected_output = [
Sample(DEFAULT_SAMPLE_SIZE, DEFAULT_SEED, ""), # 1st sample parsed
Sample(100, DEFAULT_SEED, ""), # 2nd sample parsed
Sample(DEFAULT_SAMPLE_SIZE, 4761, ""), # 3rd sample parsed
Sample(5, 100, ""), # 4th sample parsed
Sample(DEFAULT_SAMPLE_SIZE, DEFAULT_SEED, "a=1, b=2"), # 5th sample parsed
Sample(2222, DEFAULT_SEED, "a=2, b=1"), # 6th sample parsed
Sample(DEFAULT_SAMPLE_SIZE, 3333, "d=3"), # 7th sample parsed
Sample(3333, 2222, "c=1"), # 8th sample parsed
Sample( # 9th sample parsed
10,
10,
'arg1=1, arg2="val2", arg3="val3", arg4=4, arg5=5, arg6="ar g6", arg7=\' ar g 7\', arg8="aaa,aaa"',
),
Sample( # 10th sample parsed
20,
3456,
'arg1="val1,val1,val1", arg2="val2", arg3="val3 val3",',
),
]
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name="faker.providers.BaseProvider.bothify",
obj=MagicMock,
options=MagicMock(),
lines=lines,
)
assert not docstring.skipped
assert docstring._samples == expected_output
@mock.patch.object(ProviderMethodDocstring, "_log_warning")
def test_end_to_end_sample_generation(self, mock_warning, faker):
non_sample_lines = ["lorem", "ipsum", "dolor", "sit", "amet"]
valid_sample_lines = [
":sample 1234jdbvhjdbygdvbhxjhx", # Will fail during sample section processing, 1st log warning
":sample: invalid_arg='value'", # Will fail during sample generation, 2nd log warning
":sample size=3 seed=1000: text='???###'", # 1st sample generation
":sample: number=100**100**100", # Will fail SampleCodeValidator validation, 3rd log warning
":sample seed=3210: letters='abcde'", # 2nd sample generation
":sample size=10 seed=1: abcd='abcd'", # Will fail during sample generation, 4th log warning
":sample size=20 seed=1234: text='???###', ", # 3rd sample generation
" letters='abcde'",
]
lines = non_sample_lines + valid_sample_lines
docstring = ProviderMethodDocstring(
app=MagicMock(),
what="method",
name="faker.providers.BaseProvider.bothify",
obj=MagicMock,
options=MagicMock(),
lines=lines,
)
output = docstring.lines[len(non_sample_lines) :]
assert output[0] == ":examples:"
# 1st sample generation
faker.seed_instance(1000)
assert output[1] == ""
assert output[2] == ">>> Faker.seed(1000)"
assert output[3] == ">>> for _ in range(5):"
assert output[4] == "... fake.bothify(text='???###')"
assert output[5] == "..."
for i in range(6, 11):
assert output[i] == docstring._stringify_result(faker.bothify(text="???###"))
# 2nd sample generation
faker.seed_instance(3210)
assert output[11] == ""
assert output[12] == ">>> Faker.seed(3210)"
assert output[13] == ">>> for _ in range(5):"
assert output[14] == "... fake.bothify(letters='abcde')"
assert output[15] == "..."
for i in range(16, 21):
assert output[i] == docstring._stringify_result(faker.bothify(letters="abcde"))
# 3rd sample generation
faker.seed_instance(1234)
assert output[21] == ""
assert output[22] == ">>> Faker.seed(1234)"
assert output[23] == ">>> for _ in range(20):"
assert output[24] == "... fake.bothify(text='???###', letters='abcde')"
assert output[25] == "..."
for i in range(26, 46):
assert output[i] == docstring._stringify_result(faker.bothify(text="???###", letters="abcde"))
calls = mock_warning.call_args_list
assert len(calls) == 4
# 1st call to _log_warning
args, kwargs = calls[0]
assert len(args) == 1
assert not kwargs
assert args[0] == "The section `:sample 1234jdbvhjdbygdvbhxjhx` is malformed and will be discarded."
# 2nd call to _log_warning
args, kwargs = calls[1]
assert len(args) == 1
assert not kwargs
assert args[0] == "Sample generation failed for method `bothify` with arguments `invalid_arg='value'`."
# 3rd call to _log_warning
args, kwargs = calls[2]
assert len(args) == 1
assert not kwargs
assert args[0] == (
"Invalid code elements detected. Sample generation will be skipped for "
"method `bothify` with arguments `number=100**100**100`."
)
# 4th call to _log_warning
args, kwargs = calls[3]
assert len(args) == 1
assert not kwargs
assert args[0] == "Sample generation failed for method `bothify` with arguments `abcd='abcd'`."
|