mirror of
https://github.com/EDeev/web-dev.git
synced 2026-06-15 19:11:12 +03:00
523 lines
18 KiB
Python
523 lines
18 KiB
Python
import pytest
|
|
import math
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
from fact import fact_it, fact_rec
|
|
from show_employee import show_employee
|
|
from sum_and_sub import sum_and_sub
|
|
from process_list import process_list, process_list_lc, process_list_gen
|
|
from my_sum import my_sum
|
|
from email_validation import fun
|
|
from fibonacci import fibonacci
|
|
from average_scores import compute_average_scores
|
|
from plane_angle import Point, plane_angle
|
|
from complex_numbers import Complex
|
|
from circle_square_mk import circle_square_mk
|
|
from log_decorator import function_logger
|
|
from people_sort import name_format
|
|
from phone_number import sort_phone
|
|
|
|
# ==================== Тестовые данные ====================
|
|
|
|
test_data = {
|
|
'fact_it': [
|
|
(1, 1), # граница: минимум n=1
|
|
(2, 2),
|
|
(5, 120),
|
|
(7, 5040),
|
|
(10, 3628800),
|
|
],
|
|
'fact_rec': [
|
|
(1, 1), # граница: минимум n=1
|
|
(2, 2),
|
|
(5, 120),
|
|
(7, 5040),
|
|
(10, 3628800),
|
|
],
|
|
'show_employee': [
|
|
(("Иванов Иван Иванович", 30000), "Иванов Иван Иванович: 30000 ₽"),
|
|
(("Петров Петр Петрович",), "Петров Петр Петрович: 100000 ₽"),
|
|
(("Сидоров Сидор Сидорович", 0), "Сидоров Сидор Сидорович: 0 ₽"),
|
|
(("Test User", 1000000), "Test User: 1000000 ₽"),
|
|
(("", 50000), ": 50000 ₽"),
|
|
(("Test", -1000), "Test: -1000 ₽"),
|
|
],
|
|
'sum_and_sub': [
|
|
((5, 3), (8, 2)),
|
|
((-5, -3), (-8, -2)),
|
|
((5, -3), (2, 8)),
|
|
((5.5, 2.5), (8.0, 3.0)),
|
|
((0, 0), (0, 0)),
|
|
((1000.5, 500.5), (1501.0, 500.0)),
|
|
((-10, -5), (-15, -5)),
|
|
],
|
|
'process_list': [
|
|
([1, 2, 3, 4], [1, 4, 27, 16]),
|
|
([2, 4, 6], [4, 16, 36]),
|
|
([1, 3, 5], [1, 27, 125]),
|
|
([5], [125]), # граница: минимум len(arr)=1
|
|
],
|
|
'my_sum': [
|
|
((1, 2), 3),
|
|
((1, 2, 3, 4, 5), 15),
|
|
((), 0),
|
|
((1.5, 2.5, 3.0), 7.0),
|
|
((-1, -2, -3), -6),
|
|
((5,), 5),
|
|
((1000, 2000, 3000), 6000),
|
|
((10, -5, 3, -2), 6),
|
|
],
|
|
'email_validation': [
|
|
("test@example.com", True),
|
|
("test_user@example.com", True),
|
|
("test-user@example.com", True),
|
|
("testexample.com", False),
|
|
("test@exam!ple.com", False),
|
|
("test@example.comm", False), # extension > 3
|
|
("user123@test45.com", True),
|
|
("user123@test.com", True),
|
|
("test@site123.com", True),
|
|
("test@example", False),
|
|
("@example.com", False),
|
|
("test@.com", False),
|
|
("test@example.abc", True), # граница: максимум extension=3
|
|
],
|
|
'fibonacci': [
|
|
(1, [0]), # граница: минимум n=1
|
|
(2, [0, 1]),
|
|
(5, [0, 1, 1, 2, 3]),
|
|
(8, [0, 1, 1, 2, 3, 5, 8, 13]),
|
|
(10, [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]),
|
|
],
|
|
'complex_add': [
|
|
((2, 1, 5, 6), (7, 7)),
|
|
((100, 200, 50, 75), (150, 275)),
|
|
],
|
|
'complex_sub': [
|
|
((2, 1, 5, 6), (-3, -5)),
|
|
],
|
|
'complex_mul': [
|
|
((2, 1, 5, 6), (4, 17)),
|
|
],
|
|
'complex_str': [
|
|
((2, 1), "2.00+1.00i"),
|
|
((2, -1), "2.00-1.00i"),
|
|
((2, 0), "2.00+0.00i"),
|
|
((0, 5), "0.00+5.00i"),
|
|
((0, 0), "0.00+0.00i"),
|
|
],
|
|
'point_sub': [
|
|
((5, 3, 2, 1, 1, 1), (4, 2, 1)),
|
|
((-1, -2, -3, -4, -5, -6), (3, 3, 3)),
|
|
],
|
|
'people_sort': [
|
|
([["Mike", "Thomson", "20", "M"],
|
|
["Robert", "Downey", "30", "M"],
|
|
["Alice", "Wonder", "25", "F"]],
|
|
["Mr. Mike Thomson", "Ms. Alice Wonder", "Mr. Robert Downey"]),
|
|
([["Anna", "Smith", "18", "F"],
|
|
["John", "Doe", "18", "M"]],
|
|
["Ms. Anna Smith", "Mr. John Doe"]),
|
|
([["Bob", "Brown", "50", "M"]],
|
|
["Mr. Bob Brown"]),
|
|
],
|
|
'phone_number': [
|
|
(["89991234567"], ["+7 (999) 123-45-67"]),
|
|
(["79991234567"], ["+7 (999) 123-45-67"]),
|
|
(["9991234567"], ["+7 (999) 123-45-67"]),
|
|
(["+7(999)123-45-67"], ["+7 (999) 123-45-67"]),
|
|
(["89991234567", "84951112233"],
|
|
["+7 (495) 111-22-33", "+7 (999) 123-45-67"]), # проверка сортировки
|
|
],
|
|
'my_sum_argv': [
|
|
(['1', '2'], '3'),
|
|
(['1', '2', '3', '4', '5'], '15'),
|
|
(['10', '20', '30'], '60'),
|
|
],
|
|
}
|
|
|
|
# Тестовые данные для проверки обработки ошибок
|
|
error_test_data = {
|
|
'fact_it': [
|
|
(0, "Error"), # n < 1
|
|
(-1, "Error"), # n < 1
|
|
(1000, "Error"), # n >= 1000
|
|
],
|
|
'fact_rec': [
|
|
(0, "Error"), # n < 1
|
|
(-5, "Error"), # n < 1
|
|
(1000, "Error"), # n >= 1000
|
|
],
|
|
'process_list': [
|
|
([], "Error"), # len(arr) < 1
|
|
(list(range(1, 1002)), "Error"), # len(arr) > 1000
|
|
],
|
|
'process_list_lc': [
|
|
([], "Error"), # len(arr) < 1
|
|
(list(range(1, 1002)), "Error"), # len(arr) > 1000
|
|
],
|
|
'process_list_gen': [
|
|
([], ["Error"]), # len(arr) < 1
|
|
(list(range(1, 1002)), ["Error"]), # len(arr) > 1000
|
|
],
|
|
'fibonacci': [
|
|
(0, "Error"), # n < 1
|
|
(-1, "Error"), # n < 1
|
|
(16, "Error"), # n > 15
|
|
(100, "Error"), # n > 15
|
|
],
|
|
'average_scores': [
|
|
([], "Error"), # X <= 0
|
|
([tuple([50.0] * 5) for _ in range(101)], "Error"), # X > 100
|
|
([tuple([50.0] * 101)], "Error"), # N > 100
|
|
],
|
|
}
|
|
|
|
# ==================== Основные тесты ====================
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['fact_it'])
|
|
def test_fact_it(input_data, expected):
|
|
assert fact_it(input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['fact_rec'])
|
|
def test_fact_rec(input_data, expected):
|
|
assert fact_rec(input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['show_employee'])
|
|
def test_show_employee(input_data, expected):
|
|
assert show_employee(*input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['sum_and_sub'])
|
|
def test_sum_and_sub(input_data, expected):
|
|
assert sum_and_sub(*input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['process_list'])
|
|
def test_process_list(input_data, expected):
|
|
assert process_list(input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['process_list'])
|
|
def test_process_list_lc(input_data, expected):
|
|
assert process_list_lc(input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['process_list'])
|
|
def test_process_list_gen(input_data, expected):
|
|
assert list(process_list_gen(input_data)) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['my_sum'])
|
|
def test_my_sum(input_data, expected):
|
|
assert my_sum(*input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['email_validation'])
|
|
def test_email_validation(input_data, expected):
|
|
assert fun(input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['fibonacci'])
|
|
def test_fibonacci(input_data, expected):
|
|
assert fibonacci(input_data) == expected
|
|
|
|
# Тесты для average_scores.py
|
|
def test_average_scores():
|
|
scores = [(89, 90, 78, 93, 80), (90, 91, 85, 88, 86), (91, 92, 83, 89, 90.5)]
|
|
result = compute_average_scores(scores)
|
|
assert len(result) == 5
|
|
assert abs(result[0] - 90.0) < 0.1
|
|
|
|
def test_average_scores_boundary_max():
|
|
scores = [tuple([50.0] * 100) for _ in range(100)] # граница: максимум X=100, N=100
|
|
result = compute_average_scores(scores)
|
|
assert result != "Error" and len(result) == 100
|
|
|
|
# Тесты для plane_angle.py
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['point_sub'])
|
|
def test_point_sub(input_data, expected):
|
|
p1 = Point(input_data[0], input_data[1], input_data[2])
|
|
p2 = Point(input_data[3], input_data[4], input_data[5])
|
|
p3 = p1 - p2
|
|
assert (p3.x, p3.y, p3.z) == expected
|
|
|
|
def test_point_dot():
|
|
p1 = Point(1, 2, 3)
|
|
p2 = Point(4, 5, 6)
|
|
assert p1.dot(p2) == 32
|
|
|
|
def test_point_cross():
|
|
p1 = Point(1, 0, 0)
|
|
p2 = Point(0, 1, 0)
|
|
p3 = p1.cross(p2)
|
|
assert (p3.x, p3.y, p3.z) == (0, 0, 1)
|
|
|
|
def test_point_absolute():
|
|
p = Point(3, 4, 0)
|
|
assert p.absolute() == 5.0
|
|
|
|
def test_point_zero_vector():
|
|
p = Point(0, 0, 0)
|
|
assert p.absolute() == 0.0
|
|
|
|
# Тесты для complex_numbers.py
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['complex_add'])
|
|
def test_complex_add(input_data, expected):
|
|
c1 = Complex(input_data[0], input_data[1])
|
|
c2 = Complex(input_data[2], input_data[3])
|
|
c3 = c1 + c2
|
|
assert (c3.real, c3.imaginary) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['complex_sub'])
|
|
def test_complex_sub(input_data, expected):
|
|
c1 = Complex(input_data[0], input_data[1])
|
|
c2 = Complex(input_data[2], input_data[3])
|
|
c3 = c1 - c2
|
|
assert (c3.real, c3.imaginary) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['complex_mul'])
|
|
def test_complex_mul(input_data, expected):
|
|
c1 = Complex(input_data[0], input_data[1])
|
|
c2 = Complex(input_data[2], input_data[3])
|
|
c3 = c1 * c2
|
|
assert (c3.real, c3.imaginary) == expected
|
|
|
|
def test_complex_div():
|
|
c1 = Complex(2, 1)
|
|
c2 = Complex(5, 6)
|
|
c3 = c1 / c2
|
|
assert abs(c3.real - 0.26) < 0.01 and abs(c3.imaginary - (-0.11)) < 0.01
|
|
|
|
def test_complex_mod():
|
|
c = Complex(2, 1)
|
|
m = c.mod()
|
|
assert abs(m.real - 2.236) < 0.01
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['complex_str'])
|
|
def test_complex_str(input_data, expected):
|
|
c = Complex(input_data[0], input_data[1])
|
|
assert str(c) == expected
|
|
|
|
# Тесты для circle_square_mk.py
|
|
|
|
def test_circle_square_mk_small():
|
|
result = circle_square_mk(1, 1000)
|
|
expected = math.pi
|
|
assert abs(result - expected) < 1.0
|
|
|
|
def test_circle_square_mk_large():
|
|
result = circle_square_mk(5, 10000)
|
|
expected = math.pi * 25
|
|
assert abs(result - expected) < 5.0
|
|
|
|
def test_circle_square_mk_zero_radius():
|
|
result = circle_square_mk(0, 100)
|
|
assert result == 0
|
|
|
|
def test_circle_square_mk_large_experiments():
|
|
result = circle_square_mk(10, 100000)
|
|
expected = math.pi * 100
|
|
assert abs(result - expected) < 10.0
|
|
|
|
# Тесты для process_list.py: граничные значения
|
|
|
|
def test_process_list_boundary_max():
|
|
arr = list(range(1, 1001)) # граница: максимум len(arr)=1000
|
|
result = process_list(arr)
|
|
assert result != "Error" and len(result) == 1000
|
|
|
|
def test_process_list_lc_boundary_max():
|
|
arr = list(range(1, 1001)) # граница: максимум len(arr)=1000
|
|
result = process_list_lc(arr)
|
|
assert result != "Error" and len(result) == 1000
|
|
|
|
def test_fibonacci_boundary_max():
|
|
result = fibonacci(15) # граница: максимум n=15
|
|
assert result != "Error" and len(result) == 15 and result[14] == 377
|
|
|
|
def test_fact_it_boundary_max():
|
|
result = fact_it(999) # граница: максимум n=999
|
|
assert result != "Error"
|
|
|
|
# ==================== Тесты обработки ошибок ====================
|
|
|
|
@pytest.mark.parametrize("input_data, expected", error_test_data['fact_it'])
|
|
def test_fact_it_errors(input_data, expected):
|
|
assert fact_it(input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", error_test_data['fact_rec'])
|
|
def test_fact_rec_errors(input_data, expected):
|
|
assert fact_rec(input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", error_test_data['process_list'])
|
|
def test_process_list_errors(input_data, expected):
|
|
assert process_list(input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", error_test_data['process_list_lc'])
|
|
def test_process_list_lc_errors(input_data, expected):
|
|
assert process_list_lc(input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", error_test_data['process_list_gen'])
|
|
def test_process_list_gen_errors(input_data, expected):
|
|
assert list(process_list_gen(input_data)) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", error_test_data['fibonacci'])
|
|
def test_fibonacci_errors(input_data, expected):
|
|
assert fibonacci(input_data) == expected
|
|
|
|
@pytest.mark.parametrize("input_data, expected", error_test_data['average_scores'])
|
|
def test_average_scores_errors(input_data, expected):
|
|
assert compute_average_scores(input_data) == expected
|
|
|
|
# ==================== Тесты для people_sort.py ====================
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['people_sort'])
|
|
def test_people_sort(input_data, expected):
|
|
assert name_format(input_data) == expected
|
|
|
|
# ==================== Тесты для phone_number.py ====================
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['phone_number'])
|
|
def test_phone_number(input_data, expected):
|
|
assert sort_phone(input_data) == expected
|
|
|
|
# ==================== Тесты для log_decorator.py ====================
|
|
|
|
def test_log_decorator_writes_file(tmp_path):
|
|
log_file = str(tmp_path / "test.log")
|
|
|
|
@function_logger(log_file)
|
|
def add(a, b):
|
|
return a + b
|
|
|
|
result = add(2, 3)
|
|
assert result == 5
|
|
assert os.path.exists(log_file)
|
|
with open(log_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
assert 'add' in content
|
|
assert '(2, 3)' in content
|
|
assert '5' in content
|
|
|
|
def test_log_decorator_no_return(tmp_path):
|
|
log_file = str(tmp_path / "test.log")
|
|
|
|
@function_logger(log_file)
|
|
def noop():
|
|
pass
|
|
|
|
result = noop()
|
|
assert result is None
|
|
with open(log_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
assert 'noop' in content
|
|
assert '-' in content # None записывается как '-'
|
|
|
|
def test_log_decorator_kwargs(tmp_path):
|
|
log_file = str(tmp_path / "test.log")
|
|
|
|
@function_logger(log_file)
|
|
def greet(name="World"):
|
|
return f"Hello, {name}!"
|
|
|
|
result = greet(name="Test")
|
|
assert result == "Hello, Test!"
|
|
with open(log_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
assert 'greet' in content
|
|
assert "name" in content
|
|
|
|
def test_log_decorator_multiple_calls(tmp_path):
|
|
log_file = str(tmp_path / "test.log")
|
|
|
|
@function_logger(log_file)
|
|
def square(x):
|
|
return x * x
|
|
|
|
square(3)
|
|
square(5)
|
|
with open(log_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
assert content.count('square') == 2
|
|
|
|
# ==================== Тесты для my_sum_argv.py (subprocess) ====================
|
|
|
|
INTERPRETER = 'python'
|
|
|
|
def run_script(filename, args=None):
|
|
cmd = [INTERPRETER, filename] + (args if args else [])
|
|
proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
return proc.stdout.strip()
|
|
|
|
@pytest.mark.parametrize("input_data, expected", test_data['my_sum_argv'])
|
|
def test_my_sum_argv(input_data, expected):
|
|
assert run_script('my_sum_argv.py', input_data) == expected
|
|
|
|
# ==================== Тесты для file_search.py (subprocess) ====================
|
|
|
|
def test_file_search_found(tmp_path):
|
|
test_file = tmp_path / "hello.txt"
|
|
test_file.write_text("line1\nline2\nline3\n", encoding='utf-8')
|
|
result = subprocess.run(
|
|
[INTERPRETER, 'file_search.py', 'hello.txt'],
|
|
capture_output=True, text=True, check=False, cwd=str(tmp_path)
|
|
)
|
|
|
|
import shutil
|
|
shutil.copy('file_search.py', str(tmp_path / 'file_search.py'))
|
|
result = subprocess.run(
|
|
[INTERPRETER, 'file_search.py', 'hello.txt'],
|
|
capture_output=True, text=True, check=False, cwd=str(tmp_path)
|
|
)
|
|
assert 'line1' in result.stdout
|
|
|
|
def test_file_search_not_found(tmp_path):
|
|
import shutil
|
|
shutil.copy('file_search.py', str(tmp_path / 'file_search.py'))
|
|
result = subprocess.run(
|
|
[INTERPRETER, 'file_search.py', 'nonexistent.txt'],
|
|
capture_output=True, text=True, check=False, cwd=str(tmp_path)
|
|
)
|
|
assert 'не найден' in result.stdout
|
|
|
|
def test_file_search_max_5_lines(tmp_path):
|
|
import shutil
|
|
shutil.copy('file_search.py', str(tmp_path / 'file_search.py'))
|
|
test_file = tmp_path / "many_lines.txt"
|
|
test_file.write_text('\n'.join([f"line{i}" for i in range(10)]), encoding='utf-8')
|
|
result = subprocess.run(
|
|
[INTERPRETER, 'file_search.py', 'many_lines.txt'],
|
|
capture_output=True, text=True, check=False, cwd=str(tmp_path)
|
|
)
|
|
lines = result.stdout.strip().split('\n')
|
|
assert len(lines) == 5 # выводит только первые 5 строк
|
|
|
|
# ==================== Тесты для files_sort.py (subprocess) ====================
|
|
|
|
def test_files_sort(tmp_path):
|
|
import shutil
|
|
test_dir = tmp_path / "data"
|
|
test_dir.mkdir()
|
|
(test_dir / "b.txt").write_text("", encoding='utf-8')
|
|
(test_dir / "a.txt").write_text("", encoding='utf-8')
|
|
(test_dir / "c.py").write_text("", encoding='utf-8')
|
|
shutil.copy('files_sort.py', str(tmp_path / 'files_sort.py'))
|
|
result = subprocess.run(
|
|
[INTERPRETER, 'files_sort.py', str(test_dir)],
|
|
capture_output=True, text=True, check=False, cwd=str(tmp_path)
|
|
)
|
|
lines = result.stdout.strip().split('\n')
|
|
# Сортировка: сначала по расширению, потом по имени
|
|
assert lines == ['c.py', 'a.txt', 'b.txt']
|
|
|
|
def test_files_sort_no_extension(tmp_path):
|
|
import shutil
|
|
test_dir = tmp_path / "data"
|
|
test_dir.mkdir()
|
|
(test_dir / "Makefile").write_text("", encoding='utf-8')
|
|
(test_dir / "a.txt").write_text("", encoding='utf-8')
|
|
shutil.copy('files_sort.py', str(tmp_path / 'files_sort.py'))
|
|
result = subprocess.run(
|
|
[INTERPRETER, 'files_sort.py', str(test_dir)],
|
|
capture_output=True, text=True, check=False, cwd=str(tmp_path)
|
|
)
|
|
lines = result.stdout.strip().split('\n')
|
|
# Файлы без расширения (ext='') идут первыми по sorted
|
|
assert lines[0] == 'Makefile'
|