Initial commit: Heapsort and Priority Queue Implementation
- Implemented complete Heapsort algorithm with max-heap operations - Implemented binary heap-based Priority Queue with all core operations - Created Task class for task scheduling applications - Implemented Task Scheduler simulation using priority queue - Added comprehensive test suite (70+ tests, all passing) - Implemented sorting algorithm comparison utilities (Heapsort vs Quicksort vs Merge Sort) - Added detailed README with comprehensive analysis and documentation - Included demonstration scripts for all features - Generated performance comparison plots - Modular, well-documented code following academic standards Author: Carlos Gutierrez Email: cgutierrez44833@ucumberlands.edu
This commit is contained in:
13
tests/__init__.py
Normal file
13
tests/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Test Suite for MSCS532 Assignment 4
|
||||
|
||||
This package contains comprehensive tests for:
|
||||
- Heapsort implementation
|
||||
- Priority Queue implementation
|
||||
- Task class
|
||||
- Sorting algorithm comparisons
|
||||
|
||||
Author: Carlos Gutierrez
|
||||
Email: cgutierrez44833@ucumberlands.edu
|
||||
"""
|
||||
|
||||
120
tests/test_comparison.py
Normal file
120
tests/test_comparison.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""
|
||||
Test Suite for Sorting Algorithm Comparison
|
||||
|
||||
This module contains tests for the comparison utilities and sorting algorithms.
|
||||
|
||||
Author: Carlos Gutierrez
|
||||
Email: cgutierrez44833@ucumberlands.edu
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path to import src modules
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
from src.comparison import (
|
||||
quicksort, merge_sort,
|
||||
generate_sorted_array, generate_reverse_sorted_array, generate_random_array
|
||||
)
|
||||
|
||||
|
||||
class TestSortingAlgorithms(unittest.TestCase):
|
||||
"""Test cases for sorting algorithm implementations."""
|
||||
|
||||
def test_quicksort_empty(self):
|
||||
"""Test quicksort on empty array."""
|
||||
arr = []
|
||||
result = quicksort(arr)
|
||||
self.assertEqual(result, [])
|
||||
|
||||
def test_quicksort_single(self):
|
||||
"""Test quicksort on single element."""
|
||||
arr = [42]
|
||||
result = quicksort(arr)
|
||||
self.assertEqual(result, [42])
|
||||
|
||||
def test_quicksort_sorted(self):
|
||||
"""Test quicksort on sorted array."""
|
||||
arr = [1, 2, 3, 4, 5]
|
||||
result = quicksort(arr)
|
||||
self.assertEqual(result, [1, 2, 3, 4, 5])
|
||||
|
||||
def test_quicksort_reverse(self):
|
||||
"""Test quicksort on reverse-sorted array."""
|
||||
arr = [5, 4, 3, 2, 1]
|
||||
result = quicksort(arr)
|
||||
self.assertEqual(result, [1, 2, 3, 4, 5])
|
||||
|
||||
def test_quicksort_random(self):
|
||||
"""Test quicksort on random array."""
|
||||
arr = [3, 1, 4, 1, 5, 9, 2, 6]
|
||||
result = quicksort(arr)
|
||||
self.assertEqual(result, [1, 1, 2, 3, 4, 5, 6, 9])
|
||||
|
||||
def test_merge_sort_empty(self):
|
||||
"""Test merge sort on empty array."""
|
||||
arr = []
|
||||
result = merge_sort(arr)
|
||||
self.assertEqual(result, [])
|
||||
|
||||
def test_merge_sort_single(self):
|
||||
"""Test merge sort on single element."""
|
||||
arr = [42]
|
||||
result = merge_sort(arr)
|
||||
self.assertEqual(result, [42])
|
||||
|
||||
def test_merge_sort_sorted(self):
|
||||
"""Test merge sort on sorted array."""
|
||||
arr = [1, 2, 3, 4, 5]
|
||||
result = merge_sort(arr)
|
||||
self.assertEqual(result, [1, 2, 3, 4, 5])
|
||||
|
||||
def test_merge_sort_reverse(self):
|
||||
"""Test merge sort on reverse-sorted array."""
|
||||
arr = [5, 4, 3, 2, 1]
|
||||
result = merge_sort(arr)
|
||||
self.assertEqual(result, [1, 2, 3, 4, 5])
|
||||
|
||||
def test_merge_sort_random(self):
|
||||
"""Test merge sort on random array."""
|
||||
arr = [3, 1, 4, 1, 5, 9, 2, 6]
|
||||
result = merge_sort(arr)
|
||||
self.assertEqual(result, [1, 1, 2, 3, 4, 5, 6, 9])
|
||||
|
||||
|
||||
class TestArrayGenerators(unittest.TestCase):
|
||||
"""Test cases for array generator functions."""
|
||||
|
||||
def test_generate_sorted_array(self):
|
||||
"""Test generating sorted array."""
|
||||
arr = generate_sorted_array(5)
|
||||
self.assertEqual(arr, [0, 1, 2, 3, 4])
|
||||
self.assertEqual(len(arr), 5)
|
||||
|
||||
def test_generate_reverse_sorted_array(self):
|
||||
"""Test generating reverse-sorted array."""
|
||||
arr = generate_reverse_sorted_array(5)
|
||||
self.assertEqual(arr, [5, 4, 3, 2, 1])
|
||||
self.assertEqual(len(arr), 5)
|
||||
|
||||
def test_generate_random_array(self):
|
||||
"""Test generating random array."""
|
||||
arr = generate_random_array(10, seed=42)
|
||||
self.assertEqual(len(arr), 10)
|
||||
# With same seed, should get same array
|
||||
arr2 = generate_random_array(10, seed=42)
|
||||
self.assertEqual(arr, arr2)
|
||||
|
||||
def test_generate_random_array_different_seeds(self):
|
||||
"""Test that different seeds produce different arrays."""
|
||||
arr1 = generate_random_array(100, seed=1)
|
||||
arr2 = generate_random_array(100, seed=2)
|
||||
# Very unlikely to be the same
|
||||
self.assertNotEqual(arr1, arr2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
156
tests/test_heapsort.py
Normal file
156
tests/test_heapsort.py
Normal file
@@ -0,0 +1,156 @@
|
||||
"""
|
||||
Test Suite for Heapsort Implementation
|
||||
|
||||
This module contains comprehensive tests for the heapsort algorithm,
|
||||
including edge cases, different data types, and correctness verification.
|
||||
|
||||
Author: Carlos Gutierrez
|
||||
Email: cgutierrez44833@ucumberlands.edu
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path to import src modules
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
from src.heapsort import heapsort, heap_extract_max, heap_insert, _build_max_heap, _heapify
|
||||
|
||||
|
||||
class TestHeapsort(unittest.TestCase):
|
||||
"""Test cases for heapsort function."""
|
||||
|
||||
def test_empty_array(self):
|
||||
"""Test sorting an empty array."""
|
||||
arr = []
|
||||
result = heapsort(arr)
|
||||
self.assertEqual(result, [])
|
||||
|
||||
def test_single_element(self):
|
||||
"""Test sorting an array with a single element."""
|
||||
arr = [42]
|
||||
result = heapsort(arr)
|
||||
self.assertEqual(result, [42])
|
||||
|
||||
def test_already_sorted(self):
|
||||
"""Test sorting an already sorted array."""
|
||||
arr = [1, 2, 3, 4, 5]
|
||||
result = heapsort(arr)
|
||||
self.assertEqual(result, [1, 2, 3, 4, 5])
|
||||
|
||||
def test_reverse_sorted(self):
|
||||
"""Test sorting a reverse-sorted array."""
|
||||
arr = [5, 4, 3, 2, 1]
|
||||
result = heapsort(arr)
|
||||
self.assertEqual(result, [1, 2, 3, 4, 5])
|
||||
|
||||
def test_random_array(self):
|
||||
"""Test sorting a random array."""
|
||||
arr = [12, 11, 13, 5, 6, 7]
|
||||
result = heapsort(arr)
|
||||
self.assertEqual(result, [5, 6, 7, 11, 12, 13])
|
||||
|
||||
def test_duplicate_elements(self):
|
||||
"""Test sorting an array with duplicate elements."""
|
||||
arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
|
||||
result = heapsort(arr)
|
||||
self.assertEqual(result, [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9])
|
||||
|
||||
def test_negative_numbers(self):
|
||||
"""Test sorting an array with negative numbers."""
|
||||
arr = [-5, -2, -8, 1, 3, -1]
|
||||
result = heapsort(arr)
|
||||
self.assertEqual(result, [-8, -5, -2, -1, 1, 3])
|
||||
|
||||
def test_large_array(self):
|
||||
"""Test sorting a large array."""
|
||||
arr = list(range(1000, 0, -1))
|
||||
result = heapsort(arr)
|
||||
self.assertEqual(result, list(range(1, 1001)))
|
||||
|
||||
def test_inplace_sorting(self):
|
||||
"""Test that inplace sorting modifies the original array."""
|
||||
arr = [3, 1, 4, 1, 5]
|
||||
original_id = id(arr)
|
||||
result = heapsort(arr, inplace=True)
|
||||
self.assertEqual(id(result), original_id)
|
||||
self.assertEqual(arr, [1, 1, 3, 4, 5])
|
||||
|
||||
def test_not_inplace_sorting(self):
|
||||
"""Test that non-inplace sorting doesn't modify the original array."""
|
||||
arr = [3, 1, 4, 1, 5]
|
||||
original_arr = arr.copy()
|
||||
result = heapsort(arr, inplace=False)
|
||||
self.assertNotEqual(id(result), id(arr))
|
||||
self.assertEqual(arr, original_arr)
|
||||
self.assertEqual(result, [1, 1, 3, 4, 5])
|
||||
|
||||
def test_custom_key_function(self):
|
||||
"""Test sorting with a custom key function."""
|
||||
arr = [{'value': 3}, {'value': 1}, {'value': 4}]
|
||||
result = heapsort(arr, key=lambda x: x['value'], inplace=False)
|
||||
self.assertEqual([x['value'] for x in result], [1, 3, 4])
|
||||
|
||||
|
||||
class TestHeapOperations(unittest.TestCase):
|
||||
"""Test cases for heap utility functions."""
|
||||
|
||||
def test_heapify(self):
|
||||
"""Test the heapify function."""
|
||||
arr = [4, 10, 3, 5, 1]
|
||||
_heapify(arr, 5, 0)
|
||||
# After heapify, root should be the maximum
|
||||
self.assertEqual(arr[0], 10)
|
||||
|
||||
def test_build_max_heap(self):
|
||||
"""Test building a max-heap from an array."""
|
||||
arr = [4, 10, 3, 5, 1]
|
||||
_build_max_heap(arr)
|
||||
# Root should be maximum
|
||||
self.assertEqual(arr[0], 10)
|
||||
# Verify heap property: parent >= children
|
||||
for i in range(len(arr)):
|
||||
left = 2 * i + 1
|
||||
right = 2 * i + 2
|
||||
if left < len(arr):
|
||||
self.assertGreaterEqual(arr[i], arr[left])
|
||||
if right < len(arr):
|
||||
self.assertGreaterEqual(arr[i], arr[right])
|
||||
|
||||
def test_heap_extract_max(self):
|
||||
"""Test extracting maximum from a heap."""
|
||||
heap = [10, 5, 3, 4, 1]
|
||||
_build_max_heap(heap)
|
||||
max_val = heap_extract_max(heap)
|
||||
self.assertEqual(max_val, 10)
|
||||
self.assertEqual(len(heap), 4)
|
||||
# Verify heap property is maintained
|
||||
self.assertEqual(heap[0], 5)
|
||||
|
||||
def test_heap_extract_max_empty(self):
|
||||
"""Test extracting from an empty heap raises error."""
|
||||
heap = []
|
||||
with self.assertRaises(IndexError):
|
||||
heap_extract_max(heap)
|
||||
|
||||
def test_heap_insert(self):
|
||||
"""Test inserting into a heap."""
|
||||
heap = [10, 5, 3, 4, 1]
|
||||
_build_max_heap(heap)
|
||||
heap_insert(heap, 15)
|
||||
# New maximum should be at root
|
||||
self.assertEqual(heap[0], 15)
|
||||
# Verify heap property
|
||||
for i in range(len(heap)):
|
||||
left = 2 * i + 1
|
||||
right = 2 * i + 2
|
||||
if left < len(heap):
|
||||
self.assertGreaterEqual(heap[i], heap[left])
|
||||
if right < len(heap):
|
||||
self.assertGreaterEqual(heap[i], heap[right])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
223
tests/test_priority_queue.py
Normal file
223
tests/test_priority_queue.py
Normal file
@@ -0,0 +1,223 @@
|
||||
"""
|
||||
Test Suite for Priority Queue Implementation
|
||||
|
||||
This module contains comprehensive tests for the Priority Queue data structure,
|
||||
including all core operations and edge cases.
|
||||
|
||||
Author: Carlos Gutierrez
|
||||
Email: cgutierrez44833@ucumberlands.edu
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path to import src modules
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
from src.priority_queue import PriorityQueue
|
||||
from src.task import Task
|
||||
|
||||
|
||||
class TestPriorityQueue(unittest.TestCase):
|
||||
"""Test cases for Priority Queue implementation."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.pq = PriorityQueue(is_max_heap=True)
|
||||
|
||||
def test_initialization(self):
|
||||
"""Test priority queue initialization."""
|
||||
pq = PriorityQueue()
|
||||
self.assertTrue(pq.is_empty())
|
||||
self.assertEqual(pq.size(), 0)
|
||||
self.assertTrue(pq.is_max_heap)
|
||||
|
||||
def test_initialization_min_heap(self):
|
||||
"""Test min-heap initialization."""
|
||||
pq = PriorityQueue(is_max_heap=False)
|
||||
self.assertTrue(pq.is_empty())
|
||||
self.assertFalse(pq.is_max_heap)
|
||||
|
||||
def test_insert_single_task(self):
|
||||
"""Test inserting a single task."""
|
||||
task = Task("T1", priority=10, arrival_time=0.0)
|
||||
self.pq.insert(task)
|
||||
self.assertFalse(self.pq.is_empty())
|
||||
self.assertEqual(self.pq.size(), 1)
|
||||
|
||||
def test_insert_multiple_tasks(self):
|
||||
"""Test inserting multiple tasks."""
|
||||
tasks = [
|
||||
Task("T1", priority=10, arrival_time=0.0),
|
||||
Task("T2", priority=5, arrival_time=1.0),
|
||||
Task("T3", priority=15, arrival_time=2.0)
|
||||
]
|
||||
for task in tasks:
|
||||
self.pq.insert(task)
|
||||
self.assertEqual(self.pq.size(), 3)
|
||||
|
||||
def test_extract_max_ordering(self):
|
||||
"""Test that extract_max returns tasks in priority order."""
|
||||
tasks = [
|
||||
Task("T1", priority=10, arrival_time=0.0),
|
||||
Task("T2", priority=5, arrival_time=1.0),
|
||||
Task("T3", priority=15, arrival_time=2.0),
|
||||
Task("T4", priority=20, arrival_time=3.0)
|
||||
]
|
||||
for task in tasks:
|
||||
self.pq.insert(task)
|
||||
|
||||
# Should extract in descending priority order
|
||||
self.assertEqual(self.pq.extract_max().priority, 20)
|
||||
self.assertEqual(self.pq.extract_max().priority, 15)
|
||||
self.assertEqual(self.pq.extract_max().priority, 10)
|
||||
self.assertEqual(self.pq.extract_max().priority, 5)
|
||||
self.assertTrue(self.pq.is_empty())
|
||||
|
||||
def test_extract_max_empty(self):
|
||||
"""Test extracting from empty queue raises error."""
|
||||
with self.assertRaises(IndexError):
|
||||
self.pq.extract_max()
|
||||
|
||||
def test_extract_min_ordering(self):
|
||||
"""Test that extract_min returns tasks in ascending priority order."""
|
||||
pq = PriorityQueue(is_max_heap=False)
|
||||
tasks = [
|
||||
Task("T1", priority=10, arrival_time=0.0),
|
||||
Task("T2", priority=5, arrival_time=1.0),
|
||||
Task("T3", priority=15, arrival_time=2.0),
|
||||
Task("T4", priority=20, arrival_time=3.0)
|
||||
]
|
||||
for task in tasks:
|
||||
pq.insert(task)
|
||||
|
||||
# Should extract in ascending priority order
|
||||
self.assertEqual(pq.extract_min().priority, 5)
|
||||
self.assertEqual(pq.extract_min().priority, 10)
|
||||
self.assertEqual(pq.extract_min().priority, 15)
|
||||
self.assertEqual(pq.extract_min().priority, 20)
|
||||
self.assertTrue(pq.is_empty())
|
||||
|
||||
def test_peek(self):
|
||||
"""Test peeking at the highest priority task."""
|
||||
tasks = [
|
||||
Task("T1", priority=10, arrival_time=0.0),
|
||||
Task("T2", priority=5, arrival_time=1.0),
|
||||
Task("T3", priority=15, arrival_time=2.0)
|
||||
]
|
||||
for task in tasks:
|
||||
self.pq.insert(task)
|
||||
|
||||
peeked = self.pq.peek()
|
||||
self.assertEqual(peeked.priority, 15)
|
||||
# Peek should not remove the element
|
||||
self.assertEqual(self.pq.size(), 3)
|
||||
|
||||
def test_peek_empty(self):
|
||||
"""Test peeking at empty queue returns None."""
|
||||
self.assertIsNone(self.pq.peek())
|
||||
|
||||
def test_increase_key(self):
|
||||
"""Test increasing the priority of a task."""
|
||||
task = Task("T1", priority=10, arrival_time=0.0)
|
||||
self.pq.insert(task)
|
||||
self.pq.insert(Task("T2", priority=20, arrival_time=1.0))
|
||||
|
||||
# Initially, T2 should be at root
|
||||
self.assertEqual(self.pq.peek().priority, 20)
|
||||
|
||||
# Increase T1's priority
|
||||
success = self.pq.increase_key(task, 25)
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(task.priority, 25)
|
||||
|
||||
# Now T1 should be at root
|
||||
self.assertEqual(self.pq.peek().priority, 25)
|
||||
self.assertEqual(self.pq.peek().task_id, "T1")
|
||||
|
||||
def test_increase_key_not_found(self):
|
||||
"""Test increasing key of non-existent task."""
|
||||
task = Task("T1", priority=10, arrival_time=0.0)
|
||||
success = self.pq.increase_key(task, 20)
|
||||
self.assertFalse(success)
|
||||
|
||||
def test_decrease_key(self):
|
||||
"""Test decreasing the priority of a task."""
|
||||
task = Task("T1", priority=20, arrival_time=0.0)
|
||||
self.pq.insert(task)
|
||||
self.pq.insert(Task("T2", priority=10, arrival_time=1.0))
|
||||
|
||||
# Initially, T1 should be at root
|
||||
self.assertEqual(self.pq.peek().priority, 20)
|
||||
|
||||
# Decrease T1's priority
|
||||
success = self.pq.decrease_key(task, 5)
|
||||
self.assertTrue(success)
|
||||
self.assertEqual(task.priority, 5)
|
||||
|
||||
# Now T2 should be at root
|
||||
self.assertEqual(self.pq.peek().priority, 10)
|
||||
self.assertEqual(self.pq.peek().task_id, "T2")
|
||||
|
||||
def test_decrease_key_not_found(self):
|
||||
"""Test decreasing key of non-existent task."""
|
||||
task = Task("T1", priority=10, arrival_time=0.0)
|
||||
success = self.pq.decrease_key(task, 5)
|
||||
self.assertFalse(success)
|
||||
|
||||
def test_is_empty(self):
|
||||
"""Test is_empty method."""
|
||||
self.assertTrue(self.pq.is_empty())
|
||||
self.pq.insert(Task("T1", priority=10, arrival_time=0.0))
|
||||
self.assertFalse(self.pq.is_empty())
|
||||
self.pq.extract_max()
|
||||
self.assertTrue(self.pq.is_empty())
|
||||
|
||||
def test_size(self):
|
||||
"""Test size method."""
|
||||
self.assertEqual(self.pq.size(), 0)
|
||||
for i in range(5):
|
||||
self.pq.insert(Task(f"T{i}", priority=i, arrival_time=float(i)))
|
||||
self.assertEqual(self.pq.size(), i + 1)
|
||||
|
||||
for i in range(5):
|
||||
self.pq.extract_max()
|
||||
self.assertEqual(self.pq.size(), 4 - i)
|
||||
|
||||
def test_large_queue(self):
|
||||
"""Test priority queue with many elements."""
|
||||
for i in range(1000):
|
||||
self.pq.insert(Task(f"T{i}", priority=i, arrival_time=float(i)))
|
||||
|
||||
self.assertEqual(self.pq.size(), 1000)
|
||||
|
||||
# Extract all and verify ordering
|
||||
prev_priority = float('inf')
|
||||
while not self.pq.is_empty():
|
||||
task = self.pq.extract_max()
|
||||
self.assertLessEqual(task.priority, prev_priority)
|
||||
prev_priority = task.priority
|
||||
|
||||
def test_duplicate_priorities(self):
|
||||
"""Test handling of tasks with duplicate priorities."""
|
||||
tasks = [
|
||||
Task("T1", priority=10, arrival_time=0.0),
|
||||
Task("T2", priority=10, arrival_time=1.0),
|
||||
Task("T3", priority=10, arrival_time=2.0)
|
||||
]
|
||||
for task in tasks:
|
||||
self.pq.insert(task)
|
||||
|
||||
# All should be extractable
|
||||
extracted = []
|
||||
while not self.pq.is_empty():
|
||||
extracted.append(self.pq.extract_max())
|
||||
|
||||
self.assertEqual(len(extracted), 3)
|
||||
self.assertTrue(all(task.priority == 10 for task in extracted))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
190
tests/test_scheduler.py
Normal file
190
tests/test_scheduler.py
Normal file
@@ -0,0 +1,190 @@
|
||||
"""
|
||||
Test Suite for Task Scheduler Implementation
|
||||
|
||||
This module contains comprehensive tests for the task scheduler,
|
||||
including scheduling algorithms, statistics, and edge cases.
|
||||
|
||||
Author: Carlos Gutierrez
|
||||
Email: cgutierrez44833@ucumberlands.edu
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path to import src modules
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
from src.scheduler import TaskScheduler, SchedulingResult, SchedulingStatistics, simulate_scheduler
|
||||
from src.task import Task
|
||||
|
||||
|
||||
class TestTaskScheduler(unittest.TestCase):
|
||||
"""Test cases for TaskScheduler class."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.scheduler = TaskScheduler()
|
||||
|
||||
def test_basic_scheduling(self):
|
||||
"""Test basic priority-based scheduling."""
|
||||
tasks = [
|
||||
Task("T1", priority=10, arrival_time=0.0, execution_time=5.0),
|
||||
Task("T2", priority=20, arrival_time=0.0, execution_time=3.0),
|
||||
Task("T3", priority=15, arrival_time=0.0, execution_time=4.0),
|
||||
]
|
||||
|
||||
results = self.scheduler.schedule_tasks(tasks)
|
||||
|
||||
self.assertEqual(len(results), 3)
|
||||
# Highest priority should execute first
|
||||
self.assertEqual(results[0].task_id, "T2")
|
||||
self.assertEqual(results[1].task_id, "T3")
|
||||
self.assertEqual(results[2].task_id, "T1")
|
||||
|
||||
def test_scheduling_order(self):
|
||||
"""Test that tasks are scheduled in priority order."""
|
||||
tasks = [
|
||||
Task("T1", priority=5, arrival_time=0.0, execution_time=1.0),
|
||||
Task("T2", priority=10, arrival_time=0.0, execution_time=1.0),
|
||||
Task("T3", priority=15, arrival_time=0.0, execution_time=1.0),
|
||||
]
|
||||
|
||||
results = self.scheduler.schedule_tasks(tasks)
|
||||
|
||||
# Should be in descending priority order
|
||||
priorities = [t.priority for t in tasks]
|
||||
priorities.sort(reverse=True)
|
||||
|
||||
for i, result in enumerate(results):
|
||||
# Find the task that matches this result
|
||||
task = next(t for t in tasks if t.task_id == result.task_id)
|
||||
self.assertEqual(task.priority, priorities[i])
|
||||
|
||||
def test_deadline_tracking(self):
|
||||
"""Test that deadlines are correctly tracked."""
|
||||
tasks = [
|
||||
Task("T1", priority=20, arrival_time=0.0, deadline=10.0, execution_time=5.0),
|
||||
Task("T2", priority=10, arrival_time=0.0, deadline=100.0, execution_time=20.0),
|
||||
]
|
||||
|
||||
results = self.scheduler.schedule_tasks(tasks)
|
||||
|
||||
# T1 should meet deadline (starts at 0, completes at 5, deadline 10)
|
||||
self.assertTrue(results[0].deadline_met)
|
||||
|
||||
# T2 should also meet deadline (starts at 5, completes at 25, deadline 100)
|
||||
self.assertTrue(results[1].deadline_met)
|
||||
|
||||
def test_deadline_missed(self):
|
||||
"""Test detection of missed deadlines."""
|
||||
tasks = [
|
||||
Task("T1", priority=20, arrival_time=0.0, deadline=3.0, execution_time=5.0),
|
||||
]
|
||||
|
||||
results = self.scheduler.schedule_tasks(tasks)
|
||||
|
||||
# Task should miss deadline (completes at 5, deadline is 3)
|
||||
self.assertFalse(results[0].deadline_met)
|
||||
|
||||
def test_wait_time_calculation(self):
|
||||
"""Test wait time calculation."""
|
||||
tasks = [
|
||||
Task("T1", priority=20, arrival_time=0.0, execution_time=5.0),
|
||||
Task("T2", priority=10, arrival_time=0.0, execution_time=3.0),
|
||||
]
|
||||
|
||||
results = self.scheduler.schedule_tasks(tasks)
|
||||
|
||||
# T1 should have no wait time (executes first)
|
||||
self.assertEqual(results[0].wait_time, 0.0)
|
||||
|
||||
# T2 should wait for T1 to complete
|
||||
self.assertEqual(results[1].wait_time, 5.0)
|
||||
|
||||
def test_empty_task_list(self):
|
||||
"""Test scheduling with empty task list."""
|
||||
results = self.scheduler.schedule_tasks([])
|
||||
self.assertEqual(len(results), 0)
|
||||
|
||||
def test_single_task(self):
|
||||
"""Test scheduling a single task."""
|
||||
tasks = [Task("T1", priority=10, arrival_time=0.0, execution_time=5.0)]
|
||||
results = self.scheduler.schedule_tasks(tasks)
|
||||
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0].task_id, "T1")
|
||||
self.assertEqual(results[0].start_time, 0.0)
|
||||
self.assertEqual(results[0].completion_time, 5.0)
|
||||
self.assertEqual(results[0].wait_time, 0.0)
|
||||
|
||||
|
||||
class TestSchedulingStatistics(unittest.TestCase):
|
||||
"""Test cases for scheduling statistics."""
|
||||
|
||||
def test_statistics_calculation(self):
|
||||
"""Test statistics calculation."""
|
||||
scheduler = TaskScheduler()
|
||||
|
||||
tasks = [
|
||||
Task("T1", priority=20, arrival_time=0.0, deadline=10.0, execution_time=5.0),
|
||||
Task("T2", priority=10, arrival_time=0.0, deadline=100.0, execution_time=3.0),
|
||||
]
|
||||
|
||||
results = scheduler.schedule_tasks(tasks)
|
||||
stats = scheduler.get_statistics(results)
|
||||
|
||||
self.assertEqual(stats.total_tasks, 2)
|
||||
self.assertEqual(stats.completed_tasks, 2)
|
||||
self.assertEqual(stats.deadline_met, 2)
|
||||
self.assertEqual(stats.deadline_missed, 0)
|
||||
self.assertEqual(stats.total_execution_time, 8.0) # 5 + 3
|
||||
self.assertGreater(stats.average_wait_time, 0)
|
||||
self.assertGreater(stats.throughput, 0)
|
||||
|
||||
def test_statistics_with_missed_deadlines(self):
|
||||
"""Test statistics with missed deadlines."""
|
||||
scheduler = TaskScheduler()
|
||||
|
||||
tasks = [
|
||||
Task("T1", priority=20, arrival_time=0.0, deadline=3.0, execution_time=5.0),
|
||||
Task("T2", priority=10, arrival_time=0.0, deadline=100.0, execution_time=3.0),
|
||||
]
|
||||
|
||||
results = scheduler.schedule_tasks(tasks)
|
||||
stats = scheduler.get_statistics(results)
|
||||
|
||||
self.assertEqual(stats.deadline_met, 1)
|
||||
self.assertEqual(stats.deadline_missed, 1)
|
||||
|
||||
def test_statistics_empty_results(self):
|
||||
"""Test statistics with empty results."""
|
||||
scheduler = TaskScheduler()
|
||||
stats = scheduler.get_statistics([])
|
||||
|
||||
self.assertEqual(stats.total_tasks, 0)
|
||||
self.assertEqual(stats.completed_tasks, 0)
|
||||
self.assertEqual(stats.total_execution_time, 0.0)
|
||||
self.assertEqual(stats.average_wait_time, 0.0)
|
||||
self.assertEqual(stats.throughput, 0.0)
|
||||
|
||||
|
||||
class TestSimulateScheduler(unittest.TestCase):
|
||||
"""Test cases for simulate_scheduler convenience function."""
|
||||
|
||||
def test_simulate_scheduler(self):
|
||||
"""Test the simulate_scheduler function."""
|
||||
tasks = [
|
||||
Task("T1", priority=20, arrival_time=0.0, execution_time=5.0),
|
||||
Task("T2", priority=10, arrival_time=0.0, execution_time=3.0),
|
||||
]
|
||||
|
||||
stats = simulate_scheduler(tasks, verbose=False)
|
||||
|
||||
self.assertEqual(stats.total_tasks, 2)
|
||||
self.assertEqual(stats.completed_tasks, 2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
125
tests/test_task.py
Normal file
125
tests/test_task.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""
|
||||
Test Suite for Task Class
|
||||
|
||||
This module contains tests for the Task class, including comparisons,
|
||||
priority updates, and deadline checking.
|
||||
|
||||
Author: Carlos Gutierrez
|
||||
Email: cgutierrez44833@ucumberlands.edu
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path to import src modules
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
|
||||
from src.task import Task
|
||||
|
||||
|
||||
class TestTask(unittest.TestCase):
|
||||
"""Test cases for Task class."""
|
||||
|
||||
def test_task_creation(self):
|
||||
"""Test creating a task with all parameters."""
|
||||
task = Task(
|
||||
task_id="T1",
|
||||
priority=10,
|
||||
arrival_time=0.0,
|
||||
deadline=100.0,
|
||||
execution_time=5.0,
|
||||
description="Test task"
|
||||
)
|
||||
self.assertEqual(task.task_id, "T1")
|
||||
self.assertEqual(task.priority, 10)
|
||||
self.assertEqual(task.arrival_time, 0.0)
|
||||
self.assertEqual(task.deadline, 100.0)
|
||||
self.assertEqual(task.execution_time, 5.0)
|
||||
self.assertEqual(task.description, "Test task")
|
||||
|
||||
def test_task_creation_minimal(self):
|
||||
"""Test creating a task with minimal parameters."""
|
||||
task = Task("T1", priority=10, arrival_time=0.0)
|
||||
self.assertEqual(task.task_id, "T1")
|
||||
self.assertEqual(task.priority, 10)
|
||||
self.assertEqual(task.arrival_time, 0.0)
|
||||
self.assertIsNone(task.deadline)
|
||||
self.assertEqual(task.execution_time, 1.0)
|
||||
self.assertEqual(task.description, "")
|
||||
|
||||
def test_task_comparison_lt(self):
|
||||
"""Test less than comparison."""
|
||||
task1 = Task("T1", priority=5, arrival_time=0.0)
|
||||
task2 = Task("T2", priority=10, arrival_time=1.0)
|
||||
self.assertTrue(task1 < task2)
|
||||
self.assertFalse(task2 < task1)
|
||||
|
||||
def test_task_comparison_gt(self):
|
||||
"""Test greater than comparison."""
|
||||
task1 = Task("T1", priority=10, arrival_time=0.0)
|
||||
task2 = Task("T2", priority=5, arrival_time=1.0)
|
||||
self.assertTrue(task1 > task2)
|
||||
self.assertFalse(task2 > task1)
|
||||
|
||||
def test_task_comparison_eq(self):
|
||||
"""Test equality comparison."""
|
||||
task1 = Task("T1", priority=10, arrival_time=0.0)
|
||||
task2 = Task("T2", priority=10, arrival_time=1.0)
|
||||
task3 = Task("T3", priority=5, arrival_time=2.0)
|
||||
self.assertTrue(task1 == task2)
|
||||
self.assertFalse(task1 == task3)
|
||||
|
||||
def test_task_comparison_le(self):
|
||||
"""Test less than or equal comparison."""
|
||||
task1 = Task("T1", priority=5, arrival_time=0.0)
|
||||
task2 = Task("T2", priority=10, arrival_time=1.0)
|
||||
task3 = Task("T3", priority=5, arrival_time=2.0)
|
||||
self.assertTrue(task1 <= task2)
|
||||
self.assertTrue(task1 <= task3)
|
||||
self.assertFalse(task2 <= task1)
|
||||
|
||||
def test_task_comparison_ge(self):
|
||||
"""Test greater than or equal comparison."""
|
||||
task1 = Task("T1", priority=10, arrival_time=0.0)
|
||||
task2 = Task("T2", priority=5, arrival_time=1.0)
|
||||
task3 = Task("T3", priority=10, arrival_time=2.0)
|
||||
self.assertTrue(task1 >= task2)
|
||||
self.assertTrue(task1 >= task3)
|
||||
self.assertFalse(task2 >= task1)
|
||||
|
||||
def test_update_priority(self):
|
||||
"""Test updating task priority."""
|
||||
task = Task("T1", priority=10, arrival_time=0.0)
|
||||
self.assertEqual(task.priority, 10)
|
||||
task.update_priority(20)
|
||||
self.assertEqual(task.priority, 20)
|
||||
|
||||
def test_is_overdue_with_deadline(self):
|
||||
"""Test checking if task is overdue."""
|
||||
task = Task("T1", priority=10, arrival_time=0.0, deadline=100.0)
|
||||
self.assertFalse(task.is_overdue(50.0))
|
||||
self.assertFalse(task.is_overdue(100.0))
|
||||
self.assertTrue(task.is_overdue(150.0))
|
||||
|
||||
def test_is_overdue_no_deadline(self):
|
||||
"""Test checking overdue status for task without deadline."""
|
||||
task = Task("T1", priority=10, arrival_time=0.0)
|
||||
self.assertFalse(task.is_overdue(1000.0))
|
||||
|
||||
def test_time_until_deadline(self):
|
||||
"""Test calculating time until deadline."""
|
||||
task = Task("T1", priority=10, arrival_time=0.0, deadline=100.0)
|
||||
self.assertEqual(task.time_until_deadline(50.0), 50.0)
|
||||
self.assertEqual(task.time_until_deadline(100.0), 0.0)
|
||||
self.assertEqual(task.time_until_deadline(150.0), 0.0)
|
||||
|
||||
def test_time_until_deadline_no_deadline(self):
|
||||
"""Test time until deadline for task without deadline."""
|
||||
task = Task("T1", priority=10, arrival_time=0.0)
|
||||
self.assertIsNone(task.time_until_deadline(100.0))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user