Files
divide-and-conquer-analysis/tests/test_sorts.py
Carlos Gutierrez 10570af981 Initial commit: Divide-and-conquer sorting algorithms benchmark
- Implement Merge Sort and Quick Sort algorithms with instrumentation
- Add Quick Sort pivot strategies: first, last, median_of_three, random
- Create dataset generators for 5 dataset types (sorted, reverse, random, nearly_sorted, duplicates_heavy)
- Build comprehensive benchmarking CLI with metrics collection
- Add performance measurement (time, memory, comparisons, swaps)
- Configure logging with rotating file handlers
- Generate plots for time and memory vs size
- Include comprehensive test suite with pytest
- Add full documentation in README.md
2025-10-30 21:14:37 -04:00

162 lines
5.9 KiB
Python

"""Tests for sorting algorithms."""
import pytest
from typing import List
import random
from src.algorithms.merge_sort import merge_sort
from src.algorithms.quick_sort import quick_sort, PivotStrategy
class TestMergeSort:
"""Tests for merge sort algorithm."""
def test_empty_array(self) -> None:
"""Test sorting empty array."""
assert merge_sort([]) == []
def test_single_element(self) -> None:
"""Test sorting array with single element."""
assert merge_sort([42]) == [42]
def test_already_sorted(self) -> None:
"""Test sorting already sorted array."""
arr = [1, 2, 3, 4, 5]
assert merge_sort(arr) == [1, 2, 3, 4, 5]
# Original should not be modified
assert arr == [1, 2, 3, 4, 5]
def test_reverse_sorted(self) -> None:
"""Test sorting reverse sorted array."""
arr = [5, 4, 3, 2, 1]
assert merge_sort(arr) == [1, 2, 3, 4, 5]
def test_random_array(self) -> None:
"""Test sorting random array."""
arr = [3, 1, 4, 1, 5, 9, 2, 6, 5]
assert merge_sort(arr) == [1, 1, 2, 3, 4, 5, 5, 6, 9]
def test_duplicates(self) -> None:
"""Test sorting array with duplicates."""
arr = [5, 5, 5, 3, 3, 1]
assert merge_sort(arr) == [1, 3, 3, 5, 5, 5]
def test_large_array(self) -> None:
"""Test sorting large array."""
arr = list(range(1000, 0, -1))
result = merge_sort(arr)
assert result == list(range(1, 1001))
def test_instrumentation(self) -> None:
"""Test instrumentation callback."""
counters: dict = {"comparison": 0, "swap": 0}
def instrument(op: str) -> None:
if op in counters:
counters[op] += 1
arr = [3, 1, 4, 1, 5]
result = merge_sort(arr, instrument=instrument)
assert result == [1, 1, 3, 4, 5]
assert counters["comparison"] > 0
# Merge sort doesn't do swaps in traditional sense
assert counters["swap"] == 0
class TestQuickSort:
"""Tests for quick sort algorithm."""
@pytest.mark.parametrize("pivot", ["first", "last", "median_of_three", "random"])
def test_empty_array(self, pivot: PivotStrategy) -> None:
"""Test sorting empty array."""
assert quick_sort([], pivot_strategy=pivot) == []
@pytest.mark.parametrize("pivot", ["first", "last", "median_of_three", "random"])
def test_single_element(self, pivot: PivotStrategy) -> None:
"""Test sorting array with single element."""
assert quick_sort([42], pivot_strategy=pivot) == [42]
@pytest.mark.parametrize("pivot", ["first", "last", "median_of_three", "random"])
def test_already_sorted(self, pivot: PivotStrategy) -> None:
"""Test sorting already sorted array."""
arr = [1, 2, 3, 4, 5]
result = quick_sort(arr, pivot_strategy=pivot, seed=42)
assert result == [1, 2, 3, 4, 5]
# Original should not be modified
assert arr == [1, 2, 3, 4, 5]
@pytest.mark.parametrize("pivot", ["first", "last", "median_of_three", "random"])
def test_reverse_sorted(self, pivot: PivotStrategy) -> None:
"""Test sorting reverse sorted array."""
arr = [5, 4, 3, 2, 1]
result = quick_sort(arr, pivot_strategy=pivot, seed=42)
assert result == [1, 2, 3, 4, 5]
@pytest.mark.parametrize("pivot", ["first", "last", "median_of_three", "random"])
def test_random_array(self, pivot: PivotStrategy) -> None:
"""Test sorting random array."""
arr = [3, 1, 4, 1, 5, 9, 2, 6, 5]
result = quick_sort(arr, pivot_strategy=pivot, seed=42)
assert result == [1, 1, 2, 3, 4, 5, 5, 6, 9]
@pytest.mark.parametrize("pivot", ["first", "last", "median_of_three", "random"])
def test_duplicates(self, pivot: PivotStrategy) -> None:
"""Test sorting array with duplicates."""
arr = [5, 5, 5, 3, 3, 1]
result = quick_sort(arr, pivot_strategy=pivot, seed=42)
assert result == [1, 3, 3, 5, 5, 5]
@pytest.mark.parametrize("pivot", ["first", "last", "median_of_three", "random"])
def test_large_array(self, pivot: PivotStrategy) -> None:
"""Test sorting large array."""
arr = list(range(1000, 0, -1))
result = quick_sort(arr, pivot_strategy=pivot, seed=42)
assert result == list(range(1, 1001))
def test_instrumentation(self) -> None:
"""Test instrumentation callback."""
counters: dict = {"comparison": 0, "swap": 0}
def instrument(op: str) -> None:
if op in counters:
counters[op] += 1
arr = [3, 1, 4, 1, 5]
result = quick_sort(arr, pivot_strategy="first", instrument=instrument, seed=42)
assert result == [1, 1, 3, 4, 5]
assert counters["comparison"] > 0
assert counters["swap"] > 0
class TestPropertyTests:
"""Property-based tests comparing to Python's sorted()."""
@pytest.mark.parametrize("size", [10, 100, 1000])
def test_merge_sort_property(self, size: int) -> None:
"""Property test: merge_sort should match sorted() for random arrays."""
random.seed(42)
arr = [random.randint(-1000, 1000) for _ in range(size)]
result = merge_sort(arr)
expected = sorted(arr)
assert result == expected
@pytest.mark.parametrize("pivot", ["first", "last", "median_of_three", "random"])
@pytest.mark.parametrize("size", [10, 100, 1000])
def test_quick_sort_property(self, pivot: PivotStrategy, size: int) -> None:
"""Property test: quick_sort should match sorted() for random arrays."""
random.seed(42)
arr = [random.randint(-1000, 1000) for _ in range(size)]
result = quick_sort(arr, pivot_strategy=pivot, seed=42)
expected = sorted(arr)
assert result == expected
if __name__ == "__main__":
pytest.main([__file__, "-v"])