From e094f88cb61bb170d289ed9260d79cd9737a1eab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D1=80=D1=8C=D1=8F=20=D0=A2=D0=BE=D1=80=D0=B3?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0?= Date: Sun, 7 Dec 2025 22:18:10 +0300 Subject: [PATCH 1/2] Added implementations of heap, merge, quich sorts. Built tests for heap sort. --- pyproject.toml | 4 ++ src/tests_pytest_6/heap_sort.py | 27 +++++++ src/tests_pytest_6/merge_sort.py | 30 ++++++++ src/tests_pytest_6/quick_sort.py | 65 +++++++++++++++++ tests/test_heap_sort.py | 118 +++++++++++++++++++++++++++++++ 5 files changed, 244 insertions(+) create mode 100644 src/tests_pytest_6/heap_sort.py create mode 100644 src/tests_pytest_6/merge_sort.py create mode 100644 src/tests_pytest_6/quick_sort.py create mode 100644 tests/test_heap_sort.py diff --git a/pyproject.toml b/pyproject.toml index d268a5e..e88c832 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,3 +16,7 @@ target-version = "py312" "RET", # Хорошие практики возврата "SIM", # Общие правила упрощения ] + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["src"] diff --git a/src/tests_pytest_6/heap_sort.py b/src/tests_pytest_6/heap_sort.py new file mode 100644 index 0000000..a229154 --- /dev/null +++ b/src/tests_pytest_6/heap_sort.py @@ -0,0 +1,27 @@ +def heapify(arr, n, i): + largest = i + left = 2 * i + 1 + right = 2 * i + 2 + + if left < n and arr[i] < arr[left]: + largest = left + + if right < n and arr[largest] < arr[right]: + largest = right + + if largest != i: + arr[i], arr[largest] = arr[largest], arr[i] + heapify(arr, n, largest) + + +def heap_sort(arr): + n = len(arr) + + for i in range(n // 2 - 1, -1, -1): + heapify(arr, n, i) + + for i in range(n - 1, 0, -1): + arr[i], arr[0] = arr[0], arr[i] + + heapify(arr, i, 0) + return arr diff --git a/src/tests_pytest_6/merge_sort.py b/src/tests_pytest_6/merge_sort.py new file mode 100644 index 0000000..aad5ee2 --- /dev/null +++ b/src/tests_pytest_6/merge_sort.py @@ -0,0 +1,30 @@ +def merge_sort(arr): + if len(arr) > 1: + mid = len(arr) // 2 + left = arr[:mid] + right = arr[mid:] + + merge_sort(left) + merge_sort(right) + + i = j = k = 0 + + while i < len(left) and j < len(right): + if left[i] < right[j]: + arr[k] = left[i] + i += 1 + else: + arr[k] = right[j] + j += 1 + k += 1 + + while i < len(left): + arr[k] = left[i] + i += 1 + k += 1 + + while j < len(right): + arr[k] = right[j] + j += 1 + k += 1 + return arr diff --git a/src/tests_pytest_6/quick_sort.py b/src/tests_pytest_6/quick_sort.py new file mode 100644 index 0000000..1ebd246 --- /dev/null +++ b/src/tests_pytest_6/quick_sort.py @@ -0,0 +1,65 @@ +def partition(array, start, end): + pivot = array[start] + low = start + 1 + high = end + + while True: + while low <= high and array[high] >= pivot: + high = high - 1 + + while low <= high and array[low] <= pivot: + low = low + 1 + + if low <= high: + array[low], array[high] = array[high], array[low] + else: + break + + array[start], array[high] = array[high], array[start] + + return high + + +def quick_sort(array, start=0, end=None): + if end is None: + end = len(array) - 1 + + if start >= end: + return array + + p = partition(array, start, end) + quick_sort(array, start, p - 1) + quick_sort(array, p + 1, end) + return array + + +def merge_sort(arr): + if len(arr) > 1: + mid = len(arr) // 2 + left = arr[:mid] + right = arr[mid:] + + merge_sort(left) + merge_sort(right) + + i = j = k = 0 + + while i < len(left) and j < len(right): + if left[i] < right[j]: + arr[k] = left[i] + i += 1 + else: + arr[k] = right[j] + j += 1 + k += 1 + + while i < len(left): + arr[k] = left[i] + i += 1 + k += 1 + + while j < len(right): + arr[k] = right[j] + j += 1 + k += 1 + return arr diff --git a/tests/test_heap_sort.py b/tests/test_heap_sort.py new file mode 100644 index 0000000..ee3fdc3 --- /dev/null +++ b/tests/test_heap_sort.py @@ -0,0 +1,118 @@ +import random + +import pytest +from tests_pytest_6.heap_sort import heap_sort, heapify +from tests_pytest_6.merge_sort import merge_sort +from tests_pytest_6.quick_sort import quick_sort + +# unit тесты + + +def test_heapify_work(): + """Тест функции heapify на корректность""" + arr = [1, 3, 2] + heapify(arr, 3, 0) + assert arr == [3, 1, 2] + + +def test_duplicate_elements(): + """Тест с дубликатами""" + assert heap_sort([3, 1, 2, 3, 1]) == [1, 1, 2, 3, 3] + + +def test_negative_numbers(): + """Тест с отрицательными числами""" + assert heap_sort([-5, 3, -1, 0, 2]) == [-5, -1, 0, 2, 3] + + +# крайние случаи + + +def test_empty_list(): + """Тест пустого списка""" + assert heap_sort([]) == [] + + +def test_single_element(): + """Тест одного элемента""" + assert heap_sort([5]) == [5] + + +def test_sorted_list(): + """Тест уже отсортированного списка""" + assert heap_sort([1, 2, 3, 4, 5]) == [1, 2, 3, 4, 5] + + +def test_reverse_sorted(): + """Тест обратно отсортированного списка""" + assert heap_sort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5] + + +def test_large_list(): + """Тест большого списка""" + lst = list(range(1000, 0, -1)) + expected = list(range(1, 1001)) + assert heap_sort(lst) == expected + + +# параметризованный тест + + +@pytest.mark.parametrize( + "arr, expected", + [([], []), ([5], [5]), ([3, 1], [1, 3]), ([3, 1, 5, 7], [1, 3, 5, 7])], +) +def test_heap_sort_main(arr, expected): + assert heap_sort(arr) == expected + + +# property-based тесты + + +def test_property_idempotence(): + """Повторная сортировка не меняет результат""" + for _ in range(100): + lst = [random.randint(-1000, 1000) for _ in range(random.randint(0, 100))] + sorted_once = heap_sort(lst.copy()) + sorted_twice = heap_sort(sorted_once.copy()) + assert sorted_once == sorted_twice + + +def test_property_length_preservation(): + """Длина списка сохраняется""" + for _ in range(100): + lst = [random.randint(-1000, 1000) for _ in range(random.randint(0, 100))] + assert len(heap_sort(lst)) == len(lst) + + +def test_property_sorted_order(): + """Результат должен быть отсортирован""" + for _ in range(100): + lst = [random.randint(-1000, 1000) for _ in range(random.randint(1, 100))] + sorted_lst = heap_sort(lst) + for i in range(1, len(sorted_lst)): + assert sorted_lst[i - 1] <= sorted_lst[i] + + +def test_against_builtin_sort(): + """Сравнение со встроенной сортировкой Python""" + for _ in range(100): + lst = [random.randint(-1000, 1000) for _ in range(random.randint(0, 100))] + assert heap_sort(lst.copy()) == sorted(lst) + + +# Сравнение с другими сортировками + + +def test_against_quick_sort(): + """Сравнение с quick_sort""" + for _ in range(50): + lst = [random.randint(-100, 100) for _ in range(random.randint(0, 50))] + assert heap_sort(lst.copy()) == quick_sort(lst.copy()) + + +def test_against_merge_sort(): + """Сравнение с merge_sort""" + for _ in range(50): + lst = [random.randint(-100, 100) for _ in range(random.randint(0, 50))] + assert heap_sort(lst.copy()) == merge_sort(lst.copy()) From f06b0f52518be8c6af4682b2c8fce55942d09225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D1=80=D1=8C=D1=8F=20=D0=A2=D0=BE=D1=80=D0=B3?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0?= Date: Sun, 7 Dec 2025 23:24:03 +0300 Subject: [PATCH 2/2] Run tests in CI --- .github/workflows/tests.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..64c840a --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,32 @@ +name: Tests + +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: ["3.12"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install pytest + run: | + python -m pip install --upgrade pip + pip install pytest + + - name: Run tests + run: | + python -m pytest -v