diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..7a2df47 --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max-line-length = 120 +exclude = venv, code.py +ignore = C812, D103, TAE001, D100, D101 diff --git a/.gitignore b/.gitignore index 307e001..2b35e36 100644 --- a/.gitignore +++ b/.gitignore @@ -348,3 +348,4 @@ make.exe *.json *sqlite3 +practice.py diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0c2c9c4 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: python +python: + - "3.9" +install: + - pip install -r requirements.txt +script: + - flake8 . + - coverage run -m pytest +after_success: + - bash <(curl -s https://codecov.io/bash) + diff --git a/README.md b/README.md index aedb07d..1adc609 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Testing sandbox level 1 +[![Build Status](https://travis-ci.com/DmitryTokyo/level_1.svg?branch=main)](https://travis-ci.com/DmitryTokyo/level_1) +[![codecov](https://codecov.io/gh/DmitryTokyo/level_1/branch/main/graph/badge.svg?token=25YKHAMA72)](https://codecov.io/gh/DmitryTokyo/level_1) + Welcome to the testing sandbox: practice-driven testing instrument. It can help you to get more comfortable and skilful at writing different types of tests. diff --git a/code.py b/code.py index 784807b..e2e2d37 100644 --- a/code.py +++ b/code.py @@ -2,7 +2,7 @@ import datetime import io import re -from typing import Iterable, Any, Optional, Union, Generator +from typing import Iterable, Any, Optional, Generator from PIL import Image from requests import get @@ -107,4 +107,3 @@ def max_with_default(items: Iterable, default: Optional = None): def is_python_class_name(name: str) -> bool: return name[0] == name[0].upper() and name[1:] == name[1:].lower() - diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..8d0476a --- /dev/null +++ b/conftest.py @@ -0,0 +1,12 @@ +import pytest +from PIL import Image + + +@pytest.fixture +def get_fixture_image_object(): + im = Image.new('1', (100, 100)) + return im + + +class SomeClass: + pass diff --git a/requirements.txt b/requirements.txt index f045283..4f841be 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,40 @@ Pillow==8.1.2 requests==2.25.1 +pytest==6.2.4 +coverage==5.5 +pytest-cov==2.12.0 +requests-mock==1.9.3 +pytest-mock==3.6.1 + +flake8==3.9.2 +flake8-2020==1.6.0 +flake8-blind-except==0.2.0 +flake8-bugbear==20.11.1 +flake8-builtins==1.5.3 +flake8-commas==2.0.0 +flake8-comprehensions==3.3.1 +flake8-debugger==4.0.0 +flake8-docstrings==1.5.0 +flake8-eradicate==1.0.0 +flake8-polyfill==1.0.2 +flake8-print==4.0.0 +flake8-quotes==3.2.0 +flake8-string-format==0.3.0 +flake8-fixme==1.1.1 +flake8-annotations-complexity==0.0.6 +flake8-variables-names==0.0.4 +flake8-class-attributes-order==0.1.2 +flake8-broken-line==0.3.0 +flake8-tidy-imports==4.2.1 +flake8-typing-imports==1.10.1 +flake8-if-statements==0.1.0 +flake8-functions==0.0.5 +flake8-annotations-coverage==0.0.5 +flake8-expression-complexity==0.0.9 +flake8-printf-formatting==1.1.2 +flake8-multiline-containers==0.0.17 +flake8-absolute-import==1.0 +flake8-simplify==0.13.0 +flake8-functions-names==0.0.5 +mypy-extensions==0.4.3 + diff --git a/test_code.py b/test_code.py new file mode 100644 index 0000000..cf6efbe --- /dev/null +++ b/test_code.py @@ -0,0 +1,171 @@ +import datetime +import pytest +import ast + +from code import (chunks, flat, has_recursive_calls, parse_iso_datetime, get_image_height_in_pixels, + if_logs_has_any_of_commands, extract_all_constants_from_ast, is_camel_case_word, + split_camel_case_words, is_path_in_exclude_list, get_full_class_name, max_with_default, + is_python_class_name) +from conftest import SomeClass + + +@pytest.mark.parametrize( + 'somelist, chunk_size, expected', + [ + ([1, 2, 3, 4, 5], 2, [[1, 2], [3, 4], [5]]), + ([], 3, []), + (['a', 'b'], 3, [['a', 'b']]), + ] +) +def test_chunk_iteration(somelist, chunk_size, expected): + some_chunks = chunks(somelist, chunk_size) + assert list(some_chunks) == expected + + +@pytest.mark.parametrize( + 'some_list, expected', + [ + ([['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'j']], ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j']), + ([], []), + ([{1: 'a', 2: 'b'}, {3: 'q', 4: 'w'}], [1, 2, 3, 4]), + ] +) +def test_flat(some_list, expected): + assert flat(some_list) == expected + + +@pytest.mark.parametrize( + 'expression, expected', + [ + ('5/3+8', False), + ('def foo(): return foo()', True), + + ] +) +def test_has_recursive_calls(expression, expected): + funcdef = ast.parse(expression).body[0] + assert has_recursive_calls(funcdef) == expected + + +@pytest.mark.parametrize( + 'some_datetime, expected', + [ + ('2021-05-24T10:34:25.518993Z', datetime.datetime(2021, 5, 24, 10, 34, 25, 518993)), + ('2021.05.28', None), + ('', None), + ('2021-05-24T10:34:25.518993', datetime.datetime(2021, 5, 24, 10, 34, 25, 518993)), + + ] +) +def test_parse_iso_datetime(some_datetime, expected): + assert parse_iso_datetime(some_datetime) == expected + + +@pytest.mark.parametrize( + 'log, commands, expected', + [ + (['move forward'], ['move'], True), + (['variable set on'], ['set'], True), + (['put'], ['put'], True), + (['some text'], ['erase'], False), + ] +) +def test_if_logs_has_any_of_commands(log, commands, expected): + assert if_logs_has_any_of_commands(log, commands) == expected + + +@pytest.mark.parametrize( + 'expression, expected', + [ + ('def foo(): return foo()', []), + ('def foo(): return "text"', ['text']), + ] +) +def test_extract_all_constants_from_ast(expression, expected): + tree = ast.parse(expression).body[0] + assert extract_all_constants_from_ast(tree) == expected + + +@pytest.mark.parametrize( + 'some_word, expected', + [ + ('SuperClass', True), + ('nonameclass', False), + ('email_address', False), + ('EmailVerification', True), + ('oldEmail', True), + ] +) +def test_is_camel_case_word(some_word, expected): + assert is_camel_case_word(some_word) == expected + + +@pytest.mark.parametrize( + 'camel_cased_word, expected', + [ + ('SuperMegaClass', ['super', 'mega', 'class']), + ('userName', ['user', 'name']), + ('Sometext', ['sometext']), + ] +) +def test_split_camel_case_words(camel_cased_word, expected): + assert split_camel_case_words(camel_cased_word) == expected + + +@pytest.mark.parametrize( + 'path, exclude, expected', + [ + ('user/project/template', ['project', 'projects'], True), + ('lib/bin/', ['project', 'projects'], False), + ('C:\\User\\Documents', ['User'], True), + ('D:\\Game\\Civilization', ['Program', 'User'], False), + ] +) +def test_is_path_in_exclude_list(path, exclude, expected): + assert is_path_in_exclude_list(path, exclude) == expected + + +@pytest.mark.parametrize( + 'items, default, expected', + [ + ([], 1, 1), + ([1, 2], None, 2), + ] +) +def test_max_with_default(items, default, expected): + assert max_with_default(items, default) == expected + + +@pytest.mark.parametrize( + 'classname, expected', + [ + ('List', True), + ('car', False), + ] +) +def test_is_python_class_name(classname, expected): + assert is_python_class_name(classname) == expected + + +@pytest.mark.parametrize( + 'image_url, expected', + [ + ('some.url', None), + ('https://fake.url', 100), + ] +) +def test_get_image_height_in_pixels(requests_mock, mocker, get_fixture_image_object, image_url, expected): + requests_mock.get(image_url, content=b'fakeurl') + mocker.patch('PIL.Image.open', return_value=get_fixture_image_object) + assert get_image_height_in_pixels(image_url) == expected + + +@pytest.mark.parametrize( + 'obj, expected', + [ + ('Hello World', 'str'), + (SomeClass(), 'conftest.SomeClass') + ] +) +def test_get_full_class_name(obj, expected): + assert get_full_class_name(obj) == expected