web-dev/hws/hw-2/test.py
2026-02-13 15:01:19 +03:00

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'