diff --git a/testing_hw/issue-01/README.md b/testing_hw/issue-01/README.md new file mode 100644 index 0000000..4bf1248 --- /dev/null +++ b/testing_hw/issue-01/README.md @@ -0,0 +1,8 @@ +# issue-01 +При запуске в PyСharm все тесты проходят, но не выводится отчет. + +Для вывода отчета о пройденных тестах переходим в директории с файлом morse.py и в терминале выполнияем команду: + +```python -m doctest -o NORMALIZE_WHITESPACE -v morse.py``` + +(Добавляем ```-o NORMALIZE_WHITESPACE```, так как этот флаг позволяет не различать последовательности пробелов, что необходимо для одного из тестов) diff --git a/testing_hw/issue-01/morse.py b/testing_hw/issue-01/morse.py new file mode 100644 index 0000000..1e37d72 --- /dev/null +++ b/testing_hw/issue-01/morse.py @@ -0,0 +1,55 @@ +"""Encoding text according to Morse code table""" + +import doctest + +LETTER_TO_MORSE = {'A': '.-', 'B': '-...', + 'C': '-.-.', 'D': '-..', 'E': '.', + 'F': '..-.', 'G': '--.', 'H': '....', + 'I': '..', 'J': '.---', 'K': '-.-', + 'L': '.-..', 'M': '--', 'N': '-.', + 'O': '---', 'P': '.--.', 'Q': '--.-', + 'R': '.-.', 'S': '...', 'T': '-', + 'U': '..-', 'V': '...-', 'W': '.--', + 'X': '-..-', 'Y': '-.--', 'Z': '--..', + '1': '.----', '2': '..---', '3': '...--', + '4': '....-', '5': '.....', '6': '-....', + '7': '--...', '8': '---..', '9': '----.', + '0': '-----', ', ': '--..--', '.': '.-.-.-', + '?': '..--..', '/': '-..-.', '-': '-....-', + '(': '-.--.', ')': '-.--.-'} + + +def encode(message: str) -> str: + """ + Кодирует строку в соответсвии с таблицей азбуки Морзе + + >>> encode('SOS') # doctest: +NORMALIZE_WHITESPACE + '... --- ...' + >>> encode('SOS') + '... --- ...' + >>> encode(".)") + '.-.-.- -.--.-' + >>> encode('') + '' + >>> encode(911) + Traceback (most recent call last): + ... + TypeError: 'int' object is not iterable + >>> encode('sos') + Traceback (most recent call last): + ... + KeyError: 's' + >>> encode(';)') + Traceback (most recent call last): + ... + KeyError: ';' + """ + encoded_signs = [ + LETTER_TO_MORSE[letter] for letter in message + ] + + return ' '.join(encoded_signs) + + +if __name__ == "__main__": + doctest.testmod() diff --git a/testing_hw/issue-01/result.txt b/testing_hw/issue-01/result.txt new file mode 100644 index 0000000..5e455dc --- /dev/null +++ b/testing_hw/issue-01/result.txt @@ -0,0 +1,49 @@ +PS D:\python> python -m doctest -o NORMALIZE_WHITESPACE -v morse.py +Trying: + encode('SOS') # doctest: +NORMALIZE_WHITESPACE +Expecting: + '... --- ...' +ok +Trying: + encode('SOS') +Expecting: + '... --- ...' +ok +Trying: + encode(".)") +Expecting: + '.-.-.- -.--.-' +ok +Trying: + encode('') +Expecting: + '' +ok +Trying: + encode(911) +Expecting: + Traceback (most recent call last): + ... + TypeError: 'int' object is not iterable +ok +Trying: + encode('sos') +Expecting: + Traceback (most recent call last): + ... + KeyError: 's' +ok +Trying: + encode(';)') +Expecting: + Traceback (most recent call last): + ... + KeyError: ';' +ok +1 items had no tests: + morse +1 items passed all tests: + 7 tests in morse.encode +7 tests in 2 items. +7 passed and 0 failed. +Test passed. diff --git a/testing_hw/issue-02/README.md b/testing_hw/issue-02/README.md new file mode 100644 index 0000000..94665f8 --- /dev/null +++ b/testing_hw/issue-02/README.md @@ -0,0 +1,6 @@ +# issue-02 +Для начала нужно установить пакет **pytest** с помощью команды: ```pip install -U pytest``` + +Затем ```import pytest``` в рабочий файл. + +Для вывода отчета о пройденных тестах в терминал и выполнияем команду: ```python -m pytest -v morse_2.py``` diff --git a/testing_hw/issue-02/morse_2.py b/testing_hw/issue-02/morse_2.py new file mode 100644 index 0000000..0ac69d4 --- /dev/null +++ b/testing_hw/issue-02/morse_2.py @@ -0,0 +1,48 @@ +"""Decoding text according to Morse code table""" + +import pytest + + +LETTER_TO_MORSE = {'A': '.-', 'B': '-...', + 'C': '-.-.', 'D': '-..', 'E': '.', + 'F': '..-.', 'G': '--.', 'H': '....', + 'I': '..', 'J': '.---', 'K': '-.-', + 'L': '.-..', 'M': '--', 'N': '-.', + 'O': '---', 'P': '.--.', 'Q': '--.-', + 'R': '.-.', 'S': '...', 'T': '-', + 'U': '..-', 'V': '...-', 'W': '.--', + 'X': '-..-', 'Y': '-.--', 'Z': '--..', + '1': '.----', '2': '..---', '3': '...--', + '4': '....-', '5': '.....', '6': '-....', + '7': '--...', '8': '---..', '9': '----.', + '0': '-----', ', ': '--..--', '.': '.-.-.-', + '?': '..--..', '/': '-..-.', '-': '-....-', + '(': '-.--.', ')': '-.--.-'} + +MORSE_TO_LETTER = { + morse: symbol for symbol, morse in LETTER_TO_MORSE +} + + +def decode(morse_message: str) -> str: + """ + Декодирует строку из азбуки Морзе в английский + """ + decoded_letters = [ + MORSE_TO_LETTER[letter] for letter in morse_message.split() + ] + + return ''.join(decoded_letters) + + +@pytest.mark.parametrize( + "source_string,result", + [ + ('SOS', '... --- ...'), + ('', ''), + ('.)', '.-.-.- -.--.-'), + pytest.param(123, '', marks=pytest.mark.xfail), + ], +) +def test_decode(source_string, result): + assert decode(source_string) == result diff --git a/testing_hw/issue-02/result_2.txt b/testing_hw/issue-02/result_2.txt new file mode 100644 index 0000000..9fd1d3e --- /dev/null +++ b/testing_hw/issue-02/result_2.txt @@ -0,0 +1,13 @@ +PS D:\python> python -m pytest -v morse_2.py +================================================================= test session starts ================================================================= +platform win32 -- Python 3.10.7, pytest-7.4.3, pluggy-1.3.0 -- D:\python\python.exe +cachedir: .pytest_cache +rootdir: D:\python +collected 4 items + +morse.py::test_encode[SOS-... --- ...] PASSED [ 25%] +morse.py::test_encode[-] PASSED [ 50%] +morse.py::test_encode[.)-.-.-.- -.--.-] PASSED [ 75%] +morse.py::test_encode[123-] XFAIL [100%] + +============================================================ 3 passed, 1 xfailed in 0.09s ============================================================= diff --git a/testing_hw/issue-03/README.md b/testing_hw/issue-03/README.md new file mode 100644 index 0000000..f0e9482 --- /dev/null +++ b/testing_hw/issue-03/README.md @@ -0,0 +1,5 @@ +# issue-03 + +Для начала нужно испортировать пакет **unittest** с помощью команды: ```import unittest``` + +Для вывода отчета о пройденных тестах в терминале выполнияем команду: ```python -m unittest -v morse.TestOneHotEncoder``` diff --git a/testing_hw/issue-03/onehotencoding.py b/testing_hw/issue-03/onehotencoding.py new file mode 100644 index 0000000..f23bfe0 --- /dev/null +++ b/testing_hw/issue-03/onehotencoding.py @@ -0,0 +1,69 @@ +""" +Encoding a value into a binary representation +based on the ordinal number of the first element encountered +""" +from typing import List, Tuple +import unittest + + +def fit_transform(*args: str) -> List[Tuple[str, List[int]]]: + """ + fit_transform(iterable) + fit_transform(arg1, arg2, *args) + """ + if len(args) == 0: + raise TypeError('expected at least 1 arguments, got 0') + + categories = args if isinstance(args[0], str) else list(args[0]) + uniq_categories = set(categories) + bin_format = f'{{0:0{len(uniq_categories)}b}}' + + seen_categories = dict() + transformed_rows = [] + + for cat in categories: + bin_view_cat = (int(b) for b in bin_format.format(1 << len(seen_categories))) + seen_categories.setdefault(cat, list(bin_view_cat)) + transformed_rows.append((cat, seen_categories[cat])) + + return transformed_rows + + +class TestOneHotEncoder(unittest.TestCase): + def test_symbols(self): + """test transformation from list of strings""" + actual = ['.', ',', '.'] + expected = [('.', [0, 1]), (',', [1, 0]), ('.', [0, 1])] + self.assertEqual(fit_transform(actual), expected) + + def test_string(self): + """test transformation from string out of list""" + actual = '^' + expected = [('^', [1])] + self.assertEqual(fit_transform(actual), expected) + + def test_nums(self): + """test transformation from list of digits""" + actual = [3] + expected = [(3, [1])] + self.assertEqual(fit_transform(actual), expected) + + def test_num(self): + """test transformation from digit out of list""" + actual = 3 + self.assertRaises(TypeError, fit_transform, actual) + + def test_none(self): + """test transformation from none object""" + actual = None + self.assertIsNone(actual) + + def test_empty(self): + """test transformation from empty list""" + actual = [] + expected = [] + self.assertTrue(fit_transform(actual) == expected) + + +if __name__ == '__main__': + unittest.main() diff --git a/testing_hw/issue-03/result_3.txt b/testing_hw/issue-03/result_3.txt new file mode 100644 index 0000000..46dd4e3 --- /dev/null +++ b/testing_hw/issue-03/result_3.txt @@ -0,0 +1,12 @@ +PS D:\python> python -m unittest -v morse.TestOneHotEncoder +test_empty (morse.TestOneHotEncoder) ... ok +test_none (morse.TestOneHotEncoder) ... ok +test_num (morse.TestOneHotEncoder) ... ok +test_nums (morse.TestOneHotEncoder) ... ok +test_string (morse.TestOneHotEncoder) ... ok +test_symbols (morse.TestOneHotEncoder) ... ok + +---------------------------------------------------------------------- +Ran 6 tests in 0.004s + +OK diff --git a/testing_hw/issue-04/README.md b/testing_hw/issue-04/README.md new file mode 100644 index 0000000..5d61211 --- /dev/null +++ b/testing_hw/issue-04/README.md @@ -0,0 +1,7 @@ +# issue-04 + +Для начала нужно установить пакет **pytest** с помощью команды: ```pip install -U pytest``` + +Затем ```import pytest``` в рабочий файл. + +Для вывода отчета о пройденных тестах в терминале выполнияем команду: ```python -m pytest -v onehotencoding_pytest.py``` diff --git a/testing_hw/issue-04/onehotencoding_pytest.py b/testing_hw/issue-04/onehotencoding_pytest.py new file mode 100644 index 0000000..2604bcd --- /dev/null +++ b/testing_hw/issue-04/onehotencoding_pytest.py @@ -0,0 +1,56 @@ +""" +Encoding a value into a binary representation +based on the ordinal number of the first element encountered +""" +from typing import List, Tuple +import pytest + + +def fit_transform(*args: str) -> List[Tuple[str, List[int]]]: + """ + fit_transform(iterable) + fit_transform(arg1, arg2, *args) + """ + if len(args) == 0: + raise TypeError('expected at least 1 arguments, got 0') + + categories = args if isinstance(args[0], str) else list(args[0]) + uniq_categories = set(categories) + bin_format = f'{{0:0{len(uniq_categories)}b}}' + + seen_categories = dict() + transformed_rows = [] + + for cat in categories: + bin_view_cat = (int(b) for b in bin_format.format(1 << len(seen_categories))) + seen_categories.setdefault(cat, list(bin_view_cat)) + transformed_rows.append((cat, seen_categories[cat])) + + return transformed_rows + + +def test_int_raises_type_error(): + """test transformation from digit""" + with pytest.raises(TypeError): + fit_transform(3) + + +def test_none_raises_type_error(): + """test transformation from none object""" + with pytest.raises(TypeError): + fit_transform(None) + + +def test_string(): + """test transformation from string""" + assert fit_transform('^') == [('^', [1])] + + +def test_empty_list(): + """test transformation from empty list""" + assert fit_transform([]) == [] + + +def test_empty_list(): + """test transformation from list of strings""" + assert fit_transform(['.', ',', '.']) == [('.', [0, 1]), (',', [1, 0]), ('.', [0, 1])] diff --git a/testing_hw/issue-04/result_4.txt b/testing_hw/issue-04/result_4.txt new file mode 100644 index 0000000..4281273 --- /dev/null +++ b/testing_hw/issue-04/result_4.txt @@ -0,0 +1,9 @@ +PS D:\python> python -m pytest onehotencoding_pytest.py +================================================================= test session starts ================================================================= +platform win32 -- Python 3.10.7, pytest-7.4.3, pluggy-1.3.0 +rootdir: D:\python +collected 4 items + +onehotencoding_pytest.py .... [100%] + +================================================================== 4 passed in 0.09s ================================================================== diff --git a/testing_hw/issue-05/README.md b/testing_hw/issue-05/README.md new file mode 100644 index 0000000..3dfe9c4 --- /dev/null +++ b/testing_hw/issue-05/README.md @@ -0,0 +1,8 @@ +# issue-05 +Сначала устанавливаем плагин под названием *pytest-cov*, который позволит вам вызывать coverage.py от **pytest** с некоторыми дополнительными опциями pytest. +Поскольку coverage является одной из зависимостей *pytest-cov*, достаточно установить *pytest-cov* и он притянет за собой coverage.py: +```pip install pytest-cov```. + +Далее хотим вывести отчет о базовом покрытии в терминале нам поможет это сделать команда: ```pytest --cov=what_is_year_now```. + +Если вы снова запустите coverage.py с параметром --cov-report=html, будет создан отчет в формате HTML: ```pytest --cov=what_is_year_now --cov-report=html```. diff --git a/testing_hw/issue-05/index.html b/testing_hw/issue-05/index.html new file mode 100644 index 0000000..ba700c5 --- /dev/null +++ b/testing_hw/issue-05/index.html @@ -0,0 +1,109 @@ + + + + + Coverage report + + + + + +
+
+

Coverage report: + 100% +

+ +
+ +
+

+ coverage.py v7.3.2, + created at 2023-11-16 01:34 +0300 +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Modulestatementsmissingexcludedcoverage
issue05\test_what_is_year_now.py2700100%
issue05\what_is_year_now.py1900100%
Total4600100%
+

+ No items found using the specified filter. +

+
+ + + diff --git a/testing_hw/issue-05/result_5.txt b/testing_hw/issue-05/result_5.txt new file mode 100644 index 0000000..5401778 --- /dev/null +++ b/testing_hw/issue-05/result_5.txt @@ -0,0 +1,33 @@ +PS D:\pythonProject1> pytest --cov=what_is_year_now +================================================================= test session starts ================================================================= +platform win32 -- Python 3.10.7, pytest-7.4.3, pluggy-1.3.0 +rootdir: D:\pythonProject1 +plugins: cov-4.1.0 +collected 4 items + +test_what_is_year_now.py .... [100%] + +---------- coverage: platform win32, python 3.10.7-final-0 ----------- +Name Stmts Miss Cover +----------------------------------------- +what_is_year_now.py 19 0 100% +----------------------------------------- +TOTAL 19 0 100% + + +================================================================== 4 passed in 0.34s ================================================================== +PS D:\pythonProject1> python -m pytest --cov . --cov-report html +================================================================= test session starts ================================================================= +platform win32 -- Python 3.10.7, pytest-7.4.3, pluggy-1.3.0 +rootdir: D:\pythonProject1 +plugins: cov-4.1.0 +collected 4 items + +test_what_is_year_now.py .... [100%] + +---------- coverage: platform win32, python 3.10.7-final-0 ----------- +Coverage HTML written to dir htmlcov + + +================================================================== 4 passed in 0.37s ================================================================== + diff --git a/testing_hw/issue-05/test_what_is_year_day_now.py b/testing_hw/issue-05/test_what_is_year_day_now.py new file mode 100644 index 0000000..68c912c --- /dev/null +++ b/testing_hw/issue-05/test_what_is_year_day_now.py @@ -0,0 +1,47 @@ +import pytest +import urllib.request +from what_is_year_now import what_is_year_now +from unittest.mock import patch +from io import StringIO + + +def test_format_yyyy_mm_dd(): + """ + Test checks date format: YYYY-MM-DD + """ + date = StringIO('{"currentDateTime": "1980-01-01"}') + expected = 1980 + with patch.object(urllib.request, 'urlopen', return_value=date): + actual = what_is_year_now() + assert expected == actual + + +def test_format_dd_mm_yyyy(): + """ + Test checks date format: DD-MM-YYYY + """ + date = StringIO('{"currentDateTime": "01.01.1980"}') + expected = 1980 + with patch.object(urllib.request, 'urlopen', return_value=date): + actual = what_is_year_now() + assert expected == actual + + +def test_invalid_data(): + """ + Test checks date is not valid + """ + date = StringIO('{"currentDateTime": "ast$@%ro098123"}') + with pytest.raises(ValueError): + with patch.object(urllib.request, 'urlopen', return_value=date): + what_is_year_now() + + +def test_no_date_at_all(): + """ + Test checks json format with no date at all + """ + date = StringIO('{"Russia": "Moscow"}') + with pytest.raises(KeyError): + with patch.object(urllib.request, 'urlopen', return_value=date): + what_is_year_now() diff --git a/testing_hw/issue-05/test_what_is_year_now.html b/testing_hw/issue-05/test_what_is_year_now.html new file mode 100644 index 0000000..cc1d48f --- /dev/null +++ b/testing_hw/issue-05/test_what_is_year_now.html @@ -0,0 +1,145 @@ + + + + + Coverage for issue05\test_what_is_year_now.py: 100% + + + + + +
+
+

+ Coverage for issue05\test_what_is_year_now.py: + 100% +

+ +

+ 27 statements   + + + +

+

+ « prev     + ^ index     + » next +       + coverage.py v7.3.2, + created at 2023-11-16 01:33 +0300 +

+ +
+
+
+

1import pytest 

+

2import urllib.request 

+

3from what_is_year_now import what_is_year_now 

+

4from unittest.mock import patch 

+

5from io import StringIO 

+

6 

+

7 

+

8def test_format_yyyy_mm_dd(): 

+

9 """ 

+

10 Test checks date format: YYYY-MM-DD 

+

11 """ 

+

12 date = StringIO('{"currentDateTime": "1980-01-01"}') 

+

13 expected = 1980 

+

14 with patch.object(urllib.request, 'urlopen', return_value=date): 

+

15 actual = what_is_year_now() 

+

16 assert expected == actual 

+

17 

+

18 

+

19def test_format_dd_mm_yyyy(): 

+

20 """ 

+

21 Test checks date format: DD-MM-YYYY 

+

22 """ 

+

23 date = StringIO('{"currentDateTime": "01.01.1980"}') 

+

24 expected = 1980 

+

25 with patch.object(urllib.request, 'urlopen', return_value=date): 

+

26 actual = what_is_year_now() 

+

27 assert expected == actual 

+

28 

+

29 

+

30def test_invalid_data(): 

+

31 """ 

+

32 Test checks date is not valid 

+

33 """ 

+

34 date = StringIO('{"currentDateTime": "ast$@%ro098123"}') 

+

35 with pytest.raises(ValueError): 

+

36 with patch.object(urllib.request, 'urlopen', return_value=date): 

+

37 what_is_year_now() 

+

38 

+

39 

+

40def test_no_date_at_all(): 

+

41 """ 

+

42 Test checks json format with no date at all 

+

43 """ 

+

44 date = StringIO('{"Russia": "Moscow"}') 

+

45 with pytest.raises(KeyError): 

+

46 with patch.object(urllib.request, 'urlopen', return_value=date): 

+

47 what_is_year_now() 

+

48 

+
+ + + diff --git a/testing_hw/issue-05/what_is_year_now.html b/testing_hw/issue-05/what_is_year_now.html new file mode 100644 index 0000000..e7a7dec --- /dev/null +++ b/testing_hw/issue-05/what_is_year_now.html @@ -0,0 +1,132 @@ + + + + + Coverage for issue05\what_is_year_now.py: 100% + + + + + +
+
+

+ Coverage for issue05\what_is_year_now.py: + 100% +

+ +

+ 19 statements   + + + +

+

+ « prev     + ^ index     + » next +       + coverage.py v7.3.2, + created at 2023-11-16 01:33 +0300 +

+ +
+
+
+

1import urllib.request 

+

2import json 

+

3 

+

4 

+

5API_URL = 'http://worldclockapi.com/api/json/utc/now' 

+

6 

+

7YMD_SEP = '-' 

+

8YMD_SEP_INDEX = 4 

+

9YMD_YEAR_SLICE = slice(None, YMD_SEP_INDEX) 

+

10 

+

11DMY_SEP = '.' 

+

12DMY_SEP_INDEX = 5 

+

13DMY_YEAR_SLICE = slice(DMY_SEP_INDEX + 1, DMY_SEP_INDEX + 5) 

+

14 

+

15 

+

16def what_is_year_now() -> int: 

+

17 """ 

+

18 Получает текущее время из API-worldclock и извлекает из поля 'currentDateTime' год 

+

19 Предположим, что currentDateTime может быть в двух форматах: 

+

20 * YYYY-MM-DD - 2019-03-01 

+

21 * DD.MM.YYYY - 01.03.2019 

+

22 """ 

+

23 with urllib.request.urlopen(API_URL) as resp: 

+

24 resp_json = json.load(resp) 

+

25 

+

26 datetime_str = resp_json['currentDateTime'] 

+

27 if datetime_str[YMD_SEP_INDEX] == YMD_SEP: 

+

28 year_str = datetime_str[YMD_YEAR_SLICE] 

+

29 elif datetime_str[DMY_SEP_INDEX] == DMY_SEP: 

+

30 year_str = datetime_str[DMY_YEAR_SLICE] 

+

31 else: 

+

32 raise ValueError('Invalid format') 

+

33 

+

34 return int(year_str) 

+

35 

+
+ + + diff --git a/testing_hw/issue-05/what_is_year_now.py b/testing_hw/issue-05/what_is_year_now.py new file mode 100644 index 0000000..ec8d141 --- /dev/null +++ b/testing_hw/issue-05/what_is_year_now.py @@ -0,0 +1,34 @@ +import urllib.request +import json + +API_URL = 'http://worldclockapi.com/api/json/utc/now' + +YMD_SEP = '-' +YMD_SEP_INDEX = 4 +YMD_YEAR_SLICE = slice(None, YMD_SEP_INDEX) + +DMY_SEP = '.' +DMY_SEP_INDEX = 5 +DMY_YEAR_SLICE = slice(DMY_SEP_INDEX + 1, DMY_SEP_INDEX + 5) + + +def what_is_year_now() -> int: + """ + Получает текущее время из API-worldclock и извлекает из поля 'currentDateTime' год + Предположим, что currentDateTime может быть в двух форматах: + * YYYY-MM-DD - 2019-03-01 + * DD.MM.YYYY - 01.03.2019 + """ + with urllib.request.urlopen(API_URL) as resp: + resp_json = json.load(resp) + + datetime_str = resp_json['currentDateTime'] + if datetime_str[YMD_SEP_INDEX] == YMD_SEP: + year_str = datetime_str[YMD_YEAR_SLICE] + elif datetime_str[DMY_SEP_INDEX] == DMY_SEP: + year_str = datetime_str[DMY_YEAR_SLICE] + else: + raise ValueError('Invalid format') + + return int(year_str) +