import unittest
import time
from typing import List, Callable, Any
from /mnt/e/genesis-system/core/rwl/optimization.parallel_executor import ParallelExecutor

class ParallelExecutorTest(unittest.TestCase):

    def test_execute_independent_tasks(self):
        def task1():
            time.sleep(0.1)
            return "Result 1"

        def task2():
            time.sleep(0.2)
            return "Result 2"

        def task3():
            time.sleep(0.05)
            return "Result 3"

        tasks = [task1, task2, task3]
        executor = ParallelExecutor(max_concurrency=2)
        start_time = time.time()
        results = executor.execute(tasks)
        end_time = time.time()

        self.assertEqual(results, ["Result 1", "Result 2", "Result 3"])
        self.assertLess(end_time - start_time, 0.3)  # Ensure tasks ran in parallel

    def test_execute_with_exception(self):
        def task1():
            return "Result 1"

        def task2():
            raise ValueError("Task 2 failed")

        def task3():
            return "Result 3"

        tasks = [task1, task2, task3]
        executor = ParallelExecutor(max_concurrency=2)
        results = executor.execute(tasks)

        self.assertEqual(len(results), 3)
        self.assertEqual(results[0], "Result 1")
        self.assertIsNone(results[1]) # Or assertRaises if desired
        self.assertEqual(results[2], "Result 3")

    def test_zero_tasks(self):
        tasks: List[Callable[[], Any]] = []
        executor = ParallelExecutor(max_concurrency=2)
        results = executor.execute(tasks)

        self.assertEqual(results, [])

    def test_configurable_concurrency(self):
        def task1():
            time.sleep(0.5)
            return "Result 1"

        def task2():
            time.sleep(0.5)
            return "Result 2"

        tasks = [task1, task2]

        # Test with concurrency 1
        executor1 = ParallelExecutor(max_concurrency=1)
        start_time1 = time.time()
        results1 = executor1.execute(tasks)
        end_time1 = time.time()

        self.assertEqual(results1, ["Result 1", "Result 2"])
        self.assertGreater(end_time1 - start_time1, 0.9) # Should take at least 1 second

        # Test with concurrency 2
        executor2 = ParallelExecutor(max_concurrency=2)
        start_time2 = time.time()
        results2 = executor2.execute(tasks)
        end_time2 = time.time()

        self.assertEqual(results2, ["Result 1", "Result 2"])
        self.assertLess(end_time2 - start_time2, 0.7) # Should take less than 0.7 seconds

if __name__ == '__main__':
    unittest.main()