creating tests
This commit is contained in:
168
run_tests.py
Normal file
168
run_tests.py
Normal file
@@ -0,0 +1,168 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple test runner for insertion sort tests.
|
||||
|
||||
This script provides an easy way to run different types of tests
|
||||
for the insertion sort implementation.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from test_insertion_sort import (
|
||||
TestInsertionSortDecreasing,
|
||||
TestInsertionSortDecreasingInplace,
|
||||
TestPerformance,
|
||||
TestEdgeCases,
|
||||
TestNegativeCases,
|
||||
TestHelperFunctions,
|
||||
run_performance_benchmark,
|
||||
run_stress_test
|
||||
)
|
||||
import unittest
|
||||
|
||||
|
||||
def run_unit_tests(verbose=False):
|
||||
"""Run all unit tests."""
|
||||
print("Running Unit Tests...")
|
||||
print("=" * 50)
|
||||
|
||||
# Create test suite
|
||||
loader = unittest.TestLoader()
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
# Add test classes
|
||||
test_classes = [
|
||||
TestInsertionSortDecreasing,
|
||||
TestInsertionSortDecreasingInplace,
|
||||
TestPerformance,
|
||||
TestEdgeCases,
|
||||
TestNegativeCases,
|
||||
TestHelperFunctions
|
||||
]
|
||||
|
||||
for test_class in test_classes:
|
||||
tests = loader.loadTestsFromTestCase(test_class)
|
||||
suite.addTests(tests)
|
||||
|
||||
# Run tests
|
||||
runner = unittest.TextTestRunner(verbosity=2 if verbose else 1)
|
||||
result = runner.run(suite)
|
||||
|
||||
return result.wasSuccessful()
|
||||
|
||||
|
||||
def run_quick_tests():
|
||||
"""Run a quick subset of essential tests."""
|
||||
print("Running Quick Tests...")
|
||||
print("=" * 50)
|
||||
|
||||
# Essential test cases
|
||||
essential_tests = [
|
||||
'test_insertion_sort.TestInsertionSortDecreasing.test_basic_sorting',
|
||||
'test_insertion_sort.TestInsertionSortDecreasing.test_empty_array',
|
||||
'test_insertion_sort.TestInsertionSortDecreasing.test_single_element',
|
||||
'test_insertion_sort.TestInsertionSortDecreasingInplace.test_basic_sorting',
|
||||
'test_insertion_sort.TestInsertionSortDecreasing.test_negative_numbers',
|
||||
'test_insertion_sort.TestInsertionSortDecreasing.test_duplicate_elements',
|
||||
]
|
||||
|
||||
suite = unittest.TestSuite()
|
||||
loader = unittest.TestLoader()
|
||||
|
||||
for test_name in essential_tests:
|
||||
try:
|
||||
test = loader.loadTestsFromName(test_name)
|
||||
suite.addTest(test)
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not load test {test_name}: {e}")
|
||||
|
||||
runner = unittest.TextTestRunner(verbosity=2)
|
||||
result = runner.run(suite)
|
||||
|
||||
return result.wasSuccessful()
|
||||
|
||||
|
||||
def run_negative_tests(verbose=False):
|
||||
"""Run only negative test cases."""
|
||||
print("Running Negative Test Cases...")
|
||||
print("=" * 50)
|
||||
|
||||
# Create test suite with only negative tests
|
||||
loader = unittest.TestLoader()
|
||||
suite = unittest.TestSuite()
|
||||
|
||||
# Add only negative test classes
|
||||
negative_test_classes = [
|
||||
TestNegativeCases,
|
||||
TestHelperFunctions # Includes negative tests for helper functions
|
||||
]
|
||||
|
||||
for test_class in negative_test_classes:
|
||||
tests = loader.loadTestsFromTestCase(test_class)
|
||||
suite.addTests(tests)
|
||||
|
||||
# Run tests
|
||||
runner = unittest.TextTestRunner(verbosity=2 if verbose else 1)
|
||||
result = runner.run(suite)
|
||||
|
||||
return result.wasSuccessful()
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to handle command line arguments."""
|
||||
parser = argparse.ArgumentParser(description='Test runner for insertion sort implementation')
|
||||
parser.add_argument('--quick', action='store_true',
|
||||
help='Run only essential tests')
|
||||
parser.add_argument('--unit-only', action='store_true',
|
||||
help='Run only unit tests (no benchmarks)')
|
||||
parser.add_argument('--benchmark', action='store_true',
|
||||
help='Run only performance benchmarks')
|
||||
parser.add_argument('--stress', action='store_true',
|
||||
help='Run only stress tests')
|
||||
parser.add_argument('--negative', action='store_true',
|
||||
help='Run only negative test cases')
|
||||
parser.add_argument('--verbose', '-v', action='store_true',
|
||||
help='Verbose output')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("Insertion Sort Test Runner")
|
||||
print("=" * 50)
|
||||
|
||||
success = True
|
||||
|
||||
if args.quick:
|
||||
success = run_quick_tests()
|
||||
elif args.benchmark:
|
||||
print("Running Performance Benchmarks...")
|
||||
run_performance_benchmark()
|
||||
elif args.stress:
|
||||
print("Running Stress Tests...")
|
||||
run_stress_test()
|
||||
elif args.negative:
|
||||
print("Running Negative Test Cases...")
|
||||
success = run_negative_tests(args.verbose)
|
||||
elif args.unit_only:
|
||||
success = run_unit_tests(args.verbose)
|
||||
else:
|
||||
# Run everything
|
||||
success = run_unit_tests(args.verbose)
|
||||
|
||||
if success:
|
||||
print("\n" + "=" * 50)
|
||||
run_performance_benchmark()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
run_stress_test()
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
if success:
|
||||
print("✅ All tests passed successfully!")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("❌ Some tests failed!")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
658
test_insertion_sort.py
Normal file
658
test_insertion_sort.py
Normal file
@@ -0,0 +1,658 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Comprehensive Test Suite for Insertion Sort - Decreasing Order
|
||||
|
||||
This module contains extensive test cases for the insertion sort implementation,
|
||||
including unit tests, edge cases, performance tests, and stress tests.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import random
|
||||
import time
|
||||
import sys
|
||||
from typing import List, Tuple
|
||||
|
||||
# Import the functions to test
|
||||
from insertion_sort_decreasing import insertion_sort_decreasing, insertion_sort_decreasing_inplace
|
||||
|
||||
|
||||
class TestInsertionSortDecreasing(unittest.TestCase):
|
||||
"""Test cases for insertion_sort_decreasing function."""
|
||||
|
||||
def test_basic_sorting(self):
|
||||
"""Test basic sorting functionality."""
|
||||
test_cases = [
|
||||
([5, 2, 8, 1, 9, 3], [9, 8, 5, 3, 2, 1]),
|
||||
([64, 34, 25, 12, 22, 11, 90], [90, 64, 34, 25, 22, 12, 11]),
|
||||
([3, 1, 4, 1, 5, 9, 2, 6], [9, 6, 5, 4, 3, 2, 1, 1]),
|
||||
]
|
||||
|
||||
for input_arr, expected in test_cases:
|
||||
with self.subTest(input_arr=input_arr):
|
||||
result = insertion_sort_decreasing(input_arr)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_already_sorted_ascending(self):
|
||||
"""Test arrays already sorted in ascending order."""
|
||||
test_cases = [
|
||||
([1, 2, 3, 4, 5], [5, 4, 3, 2, 1]),
|
||||
([1, 2, 3], [3, 2, 1]),
|
||||
([1], [1]),
|
||||
]
|
||||
|
||||
for input_arr, expected in test_cases:
|
||||
with self.subTest(input_arr=input_arr):
|
||||
result = insertion_sort_decreasing(input_arr)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_already_sorted_descending(self):
|
||||
"""Test arrays already sorted in descending order."""
|
||||
test_cases = [
|
||||
([5, 4, 3, 2, 1], [5, 4, 3, 2, 1]),
|
||||
([10, 8, 6, 4, 2], [10, 8, 6, 4, 2]),
|
||||
([3, 2, 1], [3, 2, 1]),
|
||||
]
|
||||
|
||||
for input_arr, expected in test_cases:
|
||||
with self.subTest(input_arr=input_arr):
|
||||
result = insertion_sort_decreasing(input_arr)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_empty_array(self):
|
||||
"""Test empty array."""
|
||||
result = insertion_sort_decreasing([])
|
||||
self.assertEqual(result, [])
|
||||
|
||||
def test_single_element(self):
|
||||
"""Test arrays with single element."""
|
||||
test_cases = [
|
||||
([1], [1]),
|
||||
([42], [42]),
|
||||
([0], [0]),
|
||||
([-5], [-5]),
|
||||
]
|
||||
|
||||
for input_arr, expected in test_cases:
|
||||
with self.subTest(input_arr=input_arr):
|
||||
result = insertion_sort_decreasing(input_arr)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_duplicate_elements(self):
|
||||
"""Test arrays with duplicate elements."""
|
||||
test_cases = [
|
||||
([3, 3, 3, 3], [3, 3, 3, 3]),
|
||||
([5, 2, 5, 2, 5], [5, 5, 5, 2, 2]),
|
||||
([1, 1, 2, 2, 3, 3], [3, 3, 2, 2, 1, 1]),
|
||||
]
|
||||
|
||||
for input_arr, expected in test_cases:
|
||||
with self.subTest(input_arr=input_arr):
|
||||
result = insertion_sort_decreasing(input_arr)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_negative_numbers(self):
|
||||
"""Test arrays with negative numbers."""
|
||||
test_cases = [
|
||||
([-1, -2, -3, -4, -5], [-1, -2, -3, -4, -5]),
|
||||
([5, -2, 8, -1, 9, -3], [9, 8, 5, -1, -2, -3]),
|
||||
([-10, 5, -3, 0, 2], [5, 2, 0, -3, -10]),
|
||||
]
|
||||
|
||||
for input_arr, expected in test_cases:
|
||||
with self.subTest(input_arr=input_arr):
|
||||
result = insertion_sort_decreasing(input_arr)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_zero_values(self):
|
||||
"""Test arrays containing zero values."""
|
||||
test_cases = [
|
||||
([0, 1, 2, 3], [3, 2, 1, 0]),
|
||||
([5, 0, -2, 0, 3], [5, 3, 0, 0, -2]),
|
||||
([0, 0, 0], [0, 0, 0]),
|
||||
]
|
||||
|
||||
for input_arr, expected in test_cases:
|
||||
with self.subTest(input_arr=input_arr):
|
||||
result = insertion_sort_decreasing(input_arr)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_large_numbers(self):
|
||||
"""Test arrays with large numbers."""
|
||||
test_cases = [
|
||||
([1000000, 999999, 1000001], [1000001, 1000000, 999999]),
|
||||
([2**31 - 1, 2**31 - 2, 2**31 - 3], [2**31 - 1, 2**31 - 2, 2**31 - 3]),
|
||||
]
|
||||
|
||||
for input_arr, expected in test_cases:
|
||||
with self.subTest(input_arr=input_arr):
|
||||
result = insertion_sort_decreasing(input_arr)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_original_array_unchanged(self):
|
||||
"""Test that original array is not modified."""
|
||||
original = [5, 2, 8, 1, 9, 3]
|
||||
original_copy = original.copy()
|
||||
result = insertion_sort_decreasing(original)
|
||||
|
||||
self.assertEqual(original, original_copy)
|
||||
self.assertNotEqual(result, original)
|
||||
|
||||
def test_sorting_stability(self):
|
||||
"""Test that sorting is stable (equal elements maintain relative order)."""
|
||||
# For insertion sort, stability means equal elements keep their original order
|
||||
input_arr = [(5, 'a'), (3, 'b'), (5, 'c'), (3, 'd')]
|
||||
result = insertion_sort_decreasing(input_arr)
|
||||
|
||||
# Check that elements with same first value maintain their order
|
||||
# Since we're sorting by the first element in decreasing order,
|
||||
# all 5s should come before all 3s, and within each group,
|
||||
# the original order should be preserved
|
||||
self.assertEqual(result[0], (5, 'c')) # First 5 should be 'c' (stable)
|
||||
self.assertEqual(result[1], (5, 'a')) # Second 5 should be 'a' (stable)
|
||||
self.assertEqual(result[2], (3, 'd')) # First 3 should be 'd' (stable)
|
||||
self.assertEqual(result[3], (3, 'b')) # Second 3 should be 'b' (stable)
|
||||
|
||||
|
||||
class TestInsertionSortDecreasingInplace(unittest.TestCase):
|
||||
"""Test cases for insertion_sort_decreasing_inplace function."""
|
||||
|
||||
def test_basic_sorting(self):
|
||||
"""Test basic sorting functionality."""
|
||||
test_cases = [
|
||||
([5, 2, 8, 1, 9, 3], [9, 8, 5, 3, 2, 1]),
|
||||
([64, 34, 25, 12, 22, 11, 90], [90, 64, 34, 25, 22, 12, 11]),
|
||||
([3, 1, 4, 1, 5, 9, 2, 6], [9, 6, 5, 4, 3, 2, 1, 1]),
|
||||
]
|
||||
|
||||
for input_arr, expected in test_cases:
|
||||
with self.subTest(input_arr=input_arr):
|
||||
result = insertion_sort_decreasing_inplace(input_arr)
|
||||
self.assertEqual(result, expected)
|
||||
self.assertEqual(input_arr, expected) # Original should be modified
|
||||
|
||||
def test_empty_array(self):
|
||||
"""Test empty array."""
|
||||
arr = []
|
||||
result = insertion_sort_decreasing_inplace(arr)
|
||||
self.assertEqual(result, [])
|
||||
self.assertEqual(arr, [])
|
||||
|
||||
def test_single_element(self):
|
||||
"""Test arrays with single element."""
|
||||
arr = [42]
|
||||
result = insertion_sort_decreasing_inplace(arr)
|
||||
self.assertEqual(result, [42])
|
||||
self.assertEqual(arr, [42])
|
||||
|
||||
def test_original_array_modified(self):
|
||||
"""Test that original array is modified."""
|
||||
original = [5, 2, 8, 1, 9, 3]
|
||||
result = insertion_sort_decreasing_inplace(original)
|
||||
|
||||
self.assertEqual(original, result)
|
||||
self.assertEqual(original, [9, 8, 5, 3, 2, 1])
|
||||
|
||||
|
||||
class TestPerformance(unittest.TestCase):
|
||||
"""Performance and stress tests."""
|
||||
|
||||
def test_performance_small_arrays(self):
|
||||
"""Test performance with small arrays."""
|
||||
sizes = [10, 50, 100]
|
||||
|
||||
for size in sizes:
|
||||
with self.subTest(size=size):
|
||||
arr = list(range(size))
|
||||
random.shuffle(arr)
|
||||
|
||||
start_time = time.time()
|
||||
result = insertion_sort_decreasing(arr)
|
||||
end_time = time.time()
|
||||
|
||||
# Verify correctness
|
||||
expected = list(range(size - 1, -1, -1))
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
# Performance should be reasonable for small arrays
|
||||
execution_time = end_time - start_time
|
||||
self.assertLess(execution_time, 1.0) # Should complete within 1 second
|
||||
|
||||
def test_worst_case_performance(self):
|
||||
"""Test performance with worst-case input (ascending order)."""
|
||||
sizes = [10, 50, 100]
|
||||
|
||||
for size in sizes:
|
||||
with self.subTest(size=size):
|
||||
arr = list(range(size)) # Already sorted ascending (worst case)
|
||||
|
||||
start_time = time.time()
|
||||
result = insertion_sort_decreasing(arr)
|
||||
end_time = time.time()
|
||||
|
||||
# Verify correctness
|
||||
expected = list(range(size - 1, -1, -1))
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
execution_time = end_time - start_time
|
||||
self.assertLess(execution_time, 1.0)
|
||||
|
||||
def test_best_case_performance(self):
|
||||
"""Test performance with best-case input (descending order)."""
|
||||
sizes = [10, 50, 100]
|
||||
|
||||
for size in sizes:
|
||||
with self.subTest(size=size):
|
||||
arr = list(range(size - 1, -1, -1)) # Already sorted descending
|
||||
|
||||
start_time = time.time()
|
||||
result = insertion_sort_decreasing(arr)
|
||||
end_time = time.time()
|
||||
|
||||
# Verify correctness
|
||||
expected = list(range(size - 1, -1, -1))
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
execution_time = end_time - start_time
|
||||
self.assertLess(execution_time, 1.0)
|
||||
|
||||
|
||||
class TestEdgeCases(unittest.TestCase):
|
||||
"""Edge cases and boundary conditions."""
|
||||
|
||||
def test_very_large_numbers(self):
|
||||
"""Test with very large numbers."""
|
||||
arr = [sys.maxsize, sys.maxsize - 1, sys.maxsize - 2]
|
||||
result = insertion_sort_decreasing(arr)
|
||||
expected = [sys.maxsize, sys.maxsize - 1, sys.maxsize - 2]
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_very_small_numbers(self):
|
||||
"""Test with very small numbers."""
|
||||
arr = [-sys.maxsize, -sys.maxsize + 1, -sys.maxsize + 2]
|
||||
result = insertion_sort_decreasing(arr)
|
||||
expected = [-sys.maxsize + 2, -sys.maxsize + 1, -sys.maxsize]
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_mixed_types_comparable(self):
|
||||
"""Test with mixed types that are comparable."""
|
||||
# Note: This will work with Python's comparison rules
|
||||
arr = [1, 1.5, 2, 2.5]
|
||||
result = insertion_sort_decreasing(arr)
|
||||
expected = [2.5, 2, 1.5, 1]
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_random_large_array(self):
|
||||
"""Test with a larger random array."""
|
||||
random.seed(42) # For reproducible tests
|
||||
arr = [random.randint(-1000, 1000) for _ in range(100)]
|
||||
result = insertion_sort_decreasing(arr)
|
||||
|
||||
# Verify it's sorted in decreasing order
|
||||
for i in range(len(result) - 1):
|
||||
self.assertGreaterEqual(result[i], result[i + 1])
|
||||
|
||||
|
||||
class TestNegativeCases(unittest.TestCase):
|
||||
"""Negative test cases - testing invalid inputs and error conditions."""
|
||||
|
||||
def test_none_input(self):
|
||||
"""Test that None input raises appropriate error."""
|
||||
with self.assertRaises((TypeError, AttributeError)):
|
||||
insertion_sort_decreasing(None)
|
||||
|
||||
with self.assertRaises((TypeError, AttributeError)):
|
||||
insertion_sort_decreasing_inplace(None)
|
||||
|
||||
def test_non_list_input(self):
|
||||
"""Test that non-list inputs behave unexpectedly or raise errors."""
|
||||
# Test inputs that should raise errors
|
||||
error_inputs = [
|
||||
"string", # String doesn't support item assignment
|
||||
123, # Int doesn't support indexing
|
||||
45.67, # Float doesn't support indexing
|
||||
]
|
||||
|
||||
for invalid_input in error_inputs:
|
||||
with self.subTest(input_type=type(invalid_input).__name__, input_value=invalid_input):
|
||||
with self.assertRaises((TypeError, AttributeError)):
|
||||
insertion_sort_decreasing(invalid_input)
|
||||
|
||||
with self.assertRaises((TypeError, AttributeError)):
|
||||
insertion_sort_decreasing_inplace(invalid_input)
|
||||
|
||||
# Test inputs that might work but shouldn't be used
|
||||
problematic_inputs = [
|
||||
{"key": "value"}, # Dict - might work but unpredictable
|
||||
(1, 2, 3), # Tuple - immutable, will cause errors on modification
|
||||
set([1, 2, 3]), # Set - unordered, unpredictable
|
||||
]
|
||||
|
||||
for invalid_input in problematic_inputs:
|
||||
with self.subTest(input_type=type(invalid_input).__name__, input_value=invalid_input):
|
||||
# These might not raise errors immediately but are problematic
|
||||
try:
|
||||
result = insertion_sort_decreasing(invalid_input)
|
||||
# If it doesn't raise an error, verify it's problematic
|
||||
self.assertIsInstance(result, type(invalid_input))
|
||||
except (TypeError, AttributeError):
|
||||
# Expected for some types
|
||||
pass
|
||||
|
||||
def test_non_comparable_elements(self):
|
||||
"""Test arrays with non-comparable elements."""
|
||||
# Mixed types that can't be compared
|
||||
non_comparable_arrays = [
|
||||
[1, "string", 3],
|
||||
[1, [2, 3], 4],
|
||||
[1, {"key": "value"}, 3],
|
||||
[1, None, 3],
|
||||
]
|
||||
|
||||
for arr in non_comparable_arrays:
|
||||
with self.subTest(array=arr):
|
||||
with self.assertRaises(TypeError):
|
||||
insertion_sort_decreasing(arr)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
insertion_sort_decreasing_inplace(arr)
|
||||
|
||||
def test_nested_lists(self):
|
||||
"""Test arrays with nested lists (should work with lexicographic comparison)."""
|
||||
nested_arrays = [
|
||||
[[1, 2], [3, 4]],
|
||||
[[1], [2], [3]],
|
||||
[[1, 2, 3], [4, 5, 6]],
|
||||
]
|
||||
|
||||
for arr in nested_arrays:
|
||||
with self.subTest(array=arr):
|
||||
result = insertion_sort_decreasing(arr)
|
||||
# Verify it's sorted correctly (lexicographically)
|
||||
for i in range(len(result) - 1):
|
||||
self.assertGreaterEqual(result[i], result[i + 1])
|
||||
|
||||
# Test in-place version
|
||||
arr_copy = arr.copy()
|
||||
insertion_sort_decreasing_inplace(arr_copy)
|
||||
self.assertEqual(arr_copy, result)
|
||||
|
||||
def test_objects_without_comparison(self):
|
||||
"""Test arrays with objects that don't support comparison."""
|
||||
class NonComparable:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
obj1 = NonComparable(1)
|
||||
obj2 = NonComparable(2)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
insertion_sort_decreasing([obj1, obj2])
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
insertion_sort_decreasing_inplace([obj1, obj2])
|
||||
|
||||
def test_custom_objects_with_comparison(self):
|
||||
"""Test arrays with custom objects that do support comparison."""
|
||||
class Comparable:
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.value < other.value
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.value > other.value
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.value == other.value
|
||||
|
||||
obj1 = Comparable(5)
|
||||
obj2 = Comparable(2)
|
||||
obj3 = Comparable(8)
|
||||
|
||||
# This should work since the objects support comparison
|
||||
result = insertion_sort_decreasing([obj1, obj2, obj3])
|
||||
self.assertEqual([obj.value for obj in result], [8, 5, 2])
|
||||
|
||||
def test_very_large_arrays(self):
|
||||
"""Test with very large arrays (should still work but may be slow)."""
|
||||
# Test with a moderately large array
|
||||
large_array = list(range(1000))
|
||||
random.shuffle(large_array)
|
||||
|
||||
# This should work but might be slow
|
||||
result = insertion_sort_decreasing(large_array)
|
||||
expected = list(range(999, -1, -1))
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_infinity_values(self):
|
||||
"""Test arrays containing infinity values."""
|
||||
import math
|
||||
|
||||
inf_arrays = [
|
||||
[1, float('inf'), 3],
|
||||
[1, float('-inf'), 3],
|
||||
[float('inf'), float('-inf'), 0],
|
||||
]
|
||||
|
||||
for arr in inf_arrays:
|
||||
with self.subTest(array=arr):
|
||||
result = insertion_sort_decreasing(arr)
|
||||
# Verify it's sorted correctly
|
||||
for i in range(len(result) - 1):
|
||||
self.assertGreaterEqual(result[i], result[i + 1])
|
||||
|
||||
def test_nan_values(self):
|
||||
"""Test arrays containing NaN values (NaN comparisons are unpredictable)."""
|
||||
import math
|
||||
|
||||
nan_arrays = [
|
||||
[1, float('nan'), 3],
|
||||
[float('nan'), 1, 3],
|
||||
[1, 3, float('nan')],
|
||||
]
|
||||
|
||||
for arr in nan_arrays:
|
||||
with self.subTest(array=arr):
|
||||
# NaN comparisons don't raise errors but produce unpredictable results
|
||||
# We just verify the function doesn't crash
|
||||
result = insertion_sort_decreasing(arr)
|
||||
self.assertEqual(len(result), len(arr))
|
||||
|
||||
# Test in-place version
|
||||
arr_copy = arr.copy()
|
||||
insertion_sort_decreasing_inplace(arr_copy)
|
||||
self.assertEqual(len(arr_copy), len(arr))
|
||||
|
||||
def test_mixed_numeric_types(self):
|
||||
"""Test arrays with mixed numeric types (should work)."""
|
||||
mixed_arrays = [
|
||||
[1, 2.5, 3],
|
||||
[1.0, 2, 3.14],
|
||||
[1, 2.0, 3],
|
||||
]
|
||||
|
||||
for arr in mixed_arrays:
|
||||
with self.subTest(array=arr):
|
||||
result = insertion_sort_decreasing(arr)
|
||||
# Verify it's sorted correctly
|
||||
for i in range(len(result) - 1):
|
||||
self.assertGreaterEqual(result[i], result[i + 1])
|
||||
|
||||
def test_empty_strings_and_numbers(self):
|
||||
"""Test arrays with empty strings and numbers."""
|
||||
mixed_arrays = [
|
||||
[1, "", 3],
|
||||
["", 1, 3],
|
||||
[1, 3, ""],
|
||||
]
|
||||
|
||||
for arr in mixed_arrays:
|
||||
with self.subTest(array=arr):
|
||||
with self.assertRaises(TypeError):
|
||||
insertion_sort_decreasing(arr)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
insertion_sort_decreasing_inplace(arr)
|
||||
|
||||
def test_boolean_values(self):
|
||||
"""Test arrays with boolean values (should work)."""
|
||||
bool_arrays = [
|
||||
[True, False, True],
|
||||
[False, True, False],
|
||||
[True, True, False],
|
||||
]
|
||||
|
||||
for arr in bool_arrays:
|
||||
with self.subTest(array=arr):
|
||||
result = insertion_sort_decreasing(arr)
|
||||
# Verify it's sorted correctly (True > False)
|
||||
for i in range(len(result) - 1):
|
||||
self.assertGreaterEqual(result[i], result[i + 1])
|
||||
|
||||
def test_very_deep_recursion_simulation(self):
|
||||
"""Test with arrays that might cause issues in very deep recursion."""
|
||||
# Create an array that's already sorted in descending order
|
||||
# This should be the best case for insertion sort
|
||||
descending_array = list(range(100, 0, -1))
|
||||
|
||||
result = insertion_sort_decreasing(descending_array)
|
||||
expected = list(range(100, 0, -1))
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_unicode_strings(self):
|
||||
"""Test arrays with unicode strings."""
|
||||
unicode_arrays = [
|
||||
["hello", "world", "test"],
|
||||
["α", "β", "γ"],
|
||||
["🚀", "🌟", "⭐"],
|
||||
]
|
||||
|
||||
for arr in unicode_arrays:
|
||||
with self.subTest(array=arr):
|
||||
result = insertion_sort_decreasing(arr)
|
||||
# Verify it's sorted correctly (lexicographically)
|
||||
for i in range(len(result) - 1):
|
||||
self.assertGreaterEqual(result[i], result[i + 1])
|
||||
|
||||
|
||||
class TestHelperFunctions(unittest.TestCase):
|
||||
"""Test helper functions if any."""
|
||||
|
||||
def test_print_array_function(self):
|
||||
"""Test the print_array helper function."""
|
||||
# This is more of a smoke test since print_array doesn't return anything
|
||||
from insertion_sort_decreasing import print_array
|
||||
|
||||
# Should not raise any exceptions
|
||||
print_array([1, 2, 3], "Test Array")
|
||||
print_array([], "Empty Array")
|
||||
|
||||
def test_print_array_with_invalid_input(self):
|
||||
"""Test print_array with invalid input."""
|
||||
from insertion_sort_decreasing import print_array
|
||||
|
||||
# Test with None - should handle gracefully or raise error
|
||||
try:
|
||||
print_array(None, "None Array")
|
||||
except (TypeError, AttributeError):
|
||||
pass # Expected behavior
|
||||
|
||||
# Test with non-list input - should handle gracefully or raise error
|
||||
try:
|
||||
print_array("not a list", "String Array")
|
||||
except (TypeError, AttributeError):
|
||||
pass # Expected behavior
|
||||
|
||||
|
||||
def run_performance_benchmark():
|
||||
"""Run a performance benchmark comparing different scenarios."""
|
||||
print("\n" + "="*60)
|
||||
print("PERFORMANCE BENCHMARK")
|
||||
print("="*60)
|
||||
|
||||
scenarios = [
|
||||
("Random Array", lambda n: [random.randint(1, 1000) for _ in range(n)]),
|
||||
("Ascending Order", lambda n: list(range(n))),
|
||||
("Descending Order", lambda n: list(range(n-1, -1, -1))),
|
||||
("All Same", lambda n: [42] * n),
|
||||
]
|
||||
|
||||
sizes = [10, 50, 100, 500]
|
||||
|
||||
for scenario_name, generator in scenarios:
|
||||
print(f"\n{scenario_name}:")
|
||||
print("-" * 40)
|
||||
|
||||
for size in sizes:
|
||||
arr = generator(size)
|
||||
|
||||
# Test non-inplace version
|
||||
start_time = time.time()
|
||||
result1 = insertion_sort_decreasing(arr)
|
||||
time1 = time.time() - start_time
|
||||
|
||||
# Test inplace version
|
||||
arr_copy = arr.copy()
|
||||
start_time = time.time()
|
||||
result2 = insertion_sort_decreasing_inplace(arr_copy)
|
||||
time2 = time.time() - start_time
|
||||
|
||||
print(f"Size {size:3d}: Non-inplace: {time1:.6f}s, In-place: {time2:.6f}s")
|
||||
|
||||
|
||||
def run_stress_test():
|
||||
"""Run stress tests with various edge cases."""
|
||||
print("\n" + "="*60)
|
||||
print("STRESS TEST")
|
||||
print("="*60)
|
||||
|
||||
stress_cases = [
|
||||
("Empty array", []),
|
||||
("Single element", [42]),
|
||||
("Two elements", [2, 1]),
|
||||
("All zeros", [0, 0, 0, 0]),
|
||||
("Negative numbers", [-5, -2, -8, -1]),
|
||||
("Mixed positive/negative", [5, -2, 8, -1, 0]),
|
||||
("Large numbers", [1000000, 999999, 1000001]),
|
||||
("Duplicate elements", [3, 3, 1, 1, 2, 2]),
|
||||
]
|
||||
|
||||
for case_name, test_arr in stress_cases:
|
||||
print(f"\n{case_name}: {test_arr}")
|
||||
|
||||
# Test both functions
|
||||
result1 = insertion_sort_decreasing(test_arr)
|
||||
test_arr_copy = test_arr.copy()
|
||||
result2 = insertion_sort_decreasing_inplace(test_arr_copy)
|
||||
|
||||
# Verify results are identical
|
||||
assert result1 == result2, f"Results differ for {case_name}"
|
||||
|
||||
# Verify sorting is correct
|
||||
for i in range(len(result1) - 1):
|
||||
assert result1[i] >= result1[i + 1], f"Not sorted correctly at index {i}"
|
||||
|
||||
print(f"✓ Passed: {result1}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Set random seed for reproducible tests
|
||||
random.seed(42)
|
||||
|
||||
print("Insertion Sort - Decreasing Order Test Suite")
|
||||
print("=" * 60)
|
||||
|
||||
# Run unit tests
|
||||
print("\nRunning Unit Tests...")
|
||||
unittest.main(argv=[''], exit=False, verbosity=2)
|
||||
|
||||
# Run performance benchmark
|
||||
run_performance_benchmark()
|
||||
|
||||
# Run stress tests
|
||||
run_stress_test()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("All tests completed!")
|
||||
print("="*60)
|
||||
Reference in New Issue
Block a user