From 1ac4b22cc0e76ee36e88c8eee838d47c6f1dbb9e Mon Sep 17 00:00:00 2001 From: Carlos Gutierrez Date: Wed, 22 Oct 2025 22:08:01 -0400 Subject: [PATCH] creating tests --- run_tests.py | 168 +++++++++++ test_insertion_sort.py | 658 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 826 insertions(+) create mode 100644 run_tests.py create mode 100644 test_insertion_sort.py diff --git a/run_tests.py b/run_tests.py new file mode 100644 index 0000000..48aa856 --- /dev/null +++ b/run_tests.py @@ -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() diff --git a/test_insertion_sort.py b/test_insertion_sort.py new file mode 100644 index 0000000..eeacf3d --- /dev/null +++ b/test_insertion_sort.py @@ -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)