From 333953117c9e2ff62ebccdbcbb5947271b88ce73 Mon Sep 17 00:00:00 2001 From: Andrey Klychkov Date: Tue, 23 Jul 2019 15:56:56 +0300 Subject: [PATCH] Add human_to_bytes isbits arg unit tests&docstring PR #58623 Co-Authored-By: Sviatoslav Sydorenko Co-Authored-By: Sviatoslav Sydorenko --- .../module_utils/common/text/formatters.py | 16 +- lib/ansible/module_utils/common/validation.py | 6 +- .../text/formatters/test_human_to_bytes.py | 186 ++++++++++++++---- 3 files changed, 164 insertions(+), 44 deletions(-) diff --git a/lib/ansible/module_utils/common/text/formatters.py b/lib/ansible/module_utils/common/text/formatters.py index a71c1417258..971247a0bd6 100644 --- a/lib/ansible/module_utils/common/text/formatters.py +++ b/lib/ansible/module_utils/common/text/formatters.py @@ -38,7 +38,21 @@ def lenient_lowercase(lst): def human_to_bytes(number, default_unit=None, isbits=False): """Convert number in string format into bytes (ex: '2K' => 2048) or using unit argument. - example: human_to_bytes('10M') <=> human_to_bytes(10, 'M') + + example: human_to_bytes('10M') <=> human_to_bytes(10, 'M'). + + When isbits is False (default), converts bytes from a human-readable format to integer. + example: human_to_bytes('1MB') returns 1048576 (int). + The function expects 'B' (uppercase) as a byte identifier passed + as a part of 'name' param string or 'unit', e.g. 'MB'/'KB'/etc. + (except when the identifier is single 'b', it is perceived as a byte identifier too). + if 'Mb'/'Kb'/... is passed, the ValueError will be rased. + + When isbits is True, converts bits from a human-readable format to integer. + example: human_to_bytes('1Mb', isbits=True) returns 1048576 (int) - + string bits representation was passed and return as a number or bits. + The function expects 'b' (lowercase) as a bit identifier, e.g. 'Mb'/'Kb'/etc. + if 'MB'/'KB'/... is passed, the ValueError will be rased. """ m = re.search(r'^\s*(\d*\.?\d*)\s*([A-Za-z]+)?', str(number), flags=re.IGNORECASE) if m is None: diff --git a/lib/ansible/module_utils/common/validation.py b/lib/ansible/module_utils/common/validation.py index a2ce32d9962..9d247c70d10 100644 --- a/lib/ansible/module_utils/common/validation.py +++ b/lib/ansible/module_utils/common/validation.py @@ -521,9 +521,11 @@ def check_type_bytes(value): def check_type_bits(value): - """Convert a human-readable string value to bits + """Convert a human-readable string bits value to bits in integer. - Raises TypeError if unable to covert the value + Example: check_type_bits('1Mb') returns integer 1048576. + + Raises TypeError if unable to covert the value. """ try: return human_to_bytes(value, isbits=True) diff --git a/test/units/module_utils/common/text/formatters/test_human_to_bytes.py b/test/units/module_utils/common/text/formatters/test_human_to_bytes.py index 54dff56ce7d..d02699a656b 100644 --- a/test/units/module_utils/common/text/formatters/test_human_to_bytes.py +++ b/test/units/module_utils/common/text/formatters/test_human_to_bytes.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Copyright 2019, Andrew Klychkov @Andersson007 -# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) +# Copyright 2019, Sviatoslav Sydorenko +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type @@ -10,29 +11,41 @@ import pytest from ansible.module_utils.common.text.formatters import human_to_bytes +NUM_IN_METRIC = { + 'K': 2 ** 10, + 'M': 2 ** 20, + 'G': 2 ** 30, + 'T': 2 ** 40, + 'P': 2 ** 50, + 'E': 2 ** 60, + 'Z': 2 ** 70, + 'Y': 2 ** 80, +} + + @pytest.mark.parametrize( 'input_data,expected', [ (0, 0), - (1024, 1024), - (u'1024B', 1024), (u'0B', 0), - (u'1K', 1024), - (u'1KB', 1024), - (u'1MB', 1048576), - (u'1M', 1048576), - (u'1G', 1073741824), - (u'1GB', 1073741824), - (u'1T', 1099511627776), - (u'1TB', 1099511627776), - (u'1P', 1125899906842624), - (u'1PB', 1125899906842624), - (u'1E', 1152921504606846976), - (u'1EB', 1152921504606846976), - (u'1Z', 1180591620717411303424), - (u'1ZB', 1180591620717411303424), - (u'1Y', 1208925819614629174706176), - (u'1YB', 1208925819614629174706176), + (1024, NUM_IN_METRIC['K']), + (u'1024B', NUM_IN_METRIC['K']), + (u'1K', NUM_IN_METRIC['K']), + (u'1KB', NUM_IN_METRIC['K']), + (u'1M', NUM_IN_METRIC['M']), + (u'1MB', NUM_IN_METRIC['M']), + (u'1G', NUM_IN_METRIC['G']), + (u'1GB', NUM_IN_METRIC['G']), + (u'1T', NUM_IN_METRIC['T']), + (u'1TB', NUM_IN_METRIC['T']), + (u'1P', NUM_IN_METRIC['P']), + (u'1PB', NUM_IN_METRIC['P']), + (u'1E', NUM_IN_METRIC['E']), + (u'1EB', NUM_IN_METRIC['E']), + (u'1Z', NUM_IN_METRIC['Z']), + (u'1ZB', NUM_IN_METRIC['Z']), + (u'1Y', NUM_IN_METRIC['Y']), + (u'1YB', NUM_IN_METRIC['Y']), ] ) def test_human_to_bytes_number(input_data, expected): @@ -41,30 +54,30 @@ def test_human_to_bytes_number(input_data, expected): @pytest.mark.parametrize( - 'input_data,unit,expected', + 'input_data,unit', [ - (u'1024', u'B', 1024), - (1, u'K', 1024), - (1, u'KB', 1024), - (u'1', u'M', 1048576), - (u'1', u'MB', 1048576), - (1, u'G', 1073741824), - (1, u'GB', 1073741824), - (1, u'T', 1099511627776), - (1, u'TB', 1099511627776), - (u'1', u'P', 1125899906842624), - (u'1', u'PB', 1125899906842624), - (u'1', u'E', 1152921504606846976), - (u'1', u'EB', 1152921504606846976), - (u'1', u'Z', 1180591620717411303424), - (u'1', u'ZB', 1180591620717411303424), - (u'1', u'Y', 1208925819614629174706176), - (u'1', u'YB', 1208925819614629174706176), + (u'1024', 'B'), + (1, u'K'), + (1, u'KB'), + (u'1', u'M'), + (u'1', u'MB'), + (1, u'G'), + (1, u'GB'), + (1, u'T'), + (1, u'TB'), + (u'1', u'P'), + (u'1', u'PB'), + (u'1', u'E'), + (u'1', u'EB'), + (u'1', u'Z'), + (u'1', u'ZB'), + (u'1', u'Y'), + (u'1', u'YB'), ] ) -def test_human_to_bytes_number_unit(input_data, unit, expected): +def test_human_to_bytes_number_unit(input_data, unit): """Test of human_to_bytes function, number and default_unit args are passed.""" - assert human_to_bytes(input_data, default_unit=unit) == expected + assert human_to_bytes(input_data, default_unit=unit) == NUM_IN_METRIC.get(unit[0], 1024) @pytest.mark.parametrize('test_input', [u'1024s', u'1024w', ]) @@ -74,8 +87,99 @@ def test_human_to_bytes_wrong_unit(test_input): human_to_bytes(test_input) -@pytest.mark.parametrize('test_input', [u'b1bbb', u'm2mmm', u'', u' ', ]) +@pytest.mark.parametrize('test_input', [u'b1bbb', u'm2mmm', u'', u' ', -1]) def test_human_to_bytes_wrong_number(test_input): - """Test of human_to_bytes function, nubmer param is invalid string / number.""" + """Test of human_to_bytes function, number param is invalid string / number.""" with pytest.raises(ValueError, match="can't interpret"): human_to_bytes(test_input) + + +@pytest.mark.parametrize( + 'input_data,expected', + [ + (0, 0), + (u'0B', 0), + (u'1024b', 1024), + (u'1024B', 1024), + (u'1K', NUM_IN_METRIC['K']), + (u'1Kb', NUM_IN_METRIC['K']), + (u'1M', NUM_IN_METRIC['M']), + (u'1Mb', NUM_IN_METRIC['M']), + (u'1G', NUM_IN_METRIC['G']), + (u'1Gb', NUM_IN_METRIC['G']), + (u'1T', NUM_IN_METRIC['T']), + (u'1Tb', NUM_IN_METRIC['T']), + (u'1P', NUM_IN_METRIC['P']), + (u'1Pb', NUM_IN_METRIC['P']), + (u'1E', NUM_IN_METRIC['E']), + (u'1Eb', NUM_IN_METRIC['E']), + (u'1Z', NUM_IN_METRIC['Z']), + (u'1Zb', NUM_IN_METRIC['Z']), + (u'1Y', NUM_IN_METRIC['Y']), + (u'1Yb', NUM_IN_METRIC['Y']), + ] +) +def test_human_to_bytes_isbits(input_data, expected): + """Test of human_to_bytes function, isbits = True.""" + assert human_to_bytes(input_data, isbits=True) == expected + + +@pytest.mark.parametrize( + 'input_data,unit', + [ + (1024, 'b'), + (1024, 'B'), + (1, u'K'), + (1, u'Kb'), + (u'1', u'M'), + (u'1', u'Mb'), + (1, u'G'), + (1, u'Gb'), + (1, u'T'), + (1, u'Tb'), + (u'1', u'P'), + (u'1', u'Pb'), + (u'1', u'E'), + (u'1', u'Eb'), + (u'1', u'Z'), + (u'1', u'Zb'), + (u'1', u'Y'), + (u'1', u'Yb'), + ] +) +def test_human_to_bytes_isbits_default_unit(input_data, unit): + """Test of human_to_bytes function, isbits = True and default_unit args are passed.""" + assert human_to_bytes(input_data, default_unit=unit, isbits=True) == NUM_IN_METRIC.get(unit[0], 1024) + + +@pytest.mark.parametrize( + 'test_input,isbits', + [ + ('1024Kb', False), + ('10Mb', False), + ('1Gb', False), + ('10MB', True), + ('2KB', True), + ('4GB', True), + ] +) +def test_human_to_bytes_isbits_wrong_unit(test_input, isbits): + """Test of human_to_bytes function, unit identifier is in an invalid format for isbits value.""" + with pytest.raises(ValueError, match="Value is not a valid string"): + human_to_bytes(test_input, isbits=isbits) + + +@pytest.mark.parametrize( + 'test_input,unit,isbits', + [ + (1024, 'Kb', False), + ('10', 'Mb', False), + ('10', 'MB', True), + (2, 'KB', True), + ('4', 'GB', True), + ] +) +def test_human_to_bytes_isbits_wrong_default_unit(test_input, unit, isbits): + """Test of human_to_bytes function, default_unit is in an invalid format for isbits value.""" + with pytest.raises(ValueError, match="Value is not a valid string"): + human_to_bytes(test_input, default_unit=unit, isbits=isbits)