import logging
import os
from typing import Any, Dict, List
from pydantic import BaseModel, Field
from carvana_enzo_worker.enums.gpt_enums import GptModels, VertextAIModels
from carvana_enzo_worker.providers.vertexai_claude_provider import VertexAIClaudeProvider
from carvana_enzo_worker.providers.vertexai_gemini_provider import VertexAIGeminiProvider
from carvana_enzo_worker.providers.azure_o1_provider import AzureOpenAIo1Provider
from carvana_enzo_worker.providers.azure_gpt_provider import AzureOpenAIChatProvider

# pylint: disable=W1203, C0415 [Use %s formatting in logging function, import-outside-toplevel]

class LLMArena(BaseModel):
    """
    A tool to generate chats using multiple LLM's for a given prompt
    """

    prompt: str = Field(..., description="The input prompt for the LLMs.")
    models: List[str] = Field(..., description="A list of model names to use for generating chats.")
    responses: List[str] = Field([], description="A list of generated chat responses.")
    kwargs: Dict[str, Any] = Field({}, description="Additional keyword arguments for the LLMs.")

    @staticmethod
    async def generate_responses_for_models(prompt: str, models: List[str], **kwargs: Any) -> List[str]:
        """
        Generate responses from multiple models for a given prompt.

        :param prompt: The input prompt for the LLMs.
        :param models: A list of model names to use for generating responses.
        :return: A list of generated responses.
        """
        responses = []
        providers = []
        for model in models:
            provider_for_model = LLMArena._get_provider_for_model(model, **kwargs)
            providers.append(provider_for_model)

for provider in providers:
            try:
                response = await provider.generate_chat_response(prompt)
                responses.append(response)
            except Exception as e:
                logging.error(f"Error generating response from {provider}: {e}")
                responses.append(f"Error generating response from {provider}: {e}")

        return responses

    @staticmethod
    def _get_provider_for_model(model: str, **kwargs: Any) -> Any:
        event_id = event_id = kwargs.get("event_id", "")

        if model == VertextAIModels.CLAUDE_3_5_SONNET_V2.name:
            return  VertexAIClaudeProvider(event_id=event_id, location=str(os.getenv("VERTEXAI_CLAUDE_REGION")), deployment_id=model)

        if model == VertextAIModels.GEMINI_2_0_FLASH_EXP.name:
            return VertexAIGeminiProvider(event_id=event_id, location=str(os.getenv("VERTEXAI_GEMINI_REGION")), deployment_id=model)

        if model == GptModels.o1.value:
            return AzureOpenAIo1Provider(event_id=event_id, deployment_id=model)

        return AzureOpenAIChatProvider(event_id=event_id, deployment_id=model)

Instruction: run these in parallel

import pytest
import asyncio
import inspect
import sys
import os
import importlib
import logging
import time
from unittest.mock import AsyncMock, patch, MagicMock
from typing import Tuple, Any, List, Dict

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Create mock classes for the external dependencies
class MockVertexAIClaudeProvider:
    def __init__(self, event_id=None, location=None, deployment_id=None):
        self.event_id = event_id
        self.location = location
        self.deployment_id = deployment_id

    async def generate_chat_response(self, prompt):
        return f"Claude response for {prompt}"

class MockVertexAIGeminiProvider:
    def __init__(self, event_id=None, location=None, deployment_id=None):
        self.event_id = event_id
        self.location = location
        self.deployment_id = deployment_id

    async def generate_chat_response(self, prompt):
        return f"Gemini response for {prompt}"

class MockAzureOpenAIo1Provider:
    def __init__(self, event_id=None, deployment_id=None):
        self.event_id = event_id
        self.deployment_id = deployment_id

    async def generate_chat_response(self, prompt):
        return f"o1 response for {prompt}"

class MockAzureOpenAIChatProvider:
    def __init__(self, event_id=None, deployment_id=None):
        self.event_id = event_id
        self.deployment_id = deployment_id

    async def generate_chat_response(self, prompt):
        return f"GPT response for {prompt}"

# Set up module mocks
sys.modules['carvana_enzo_worker.providers.vertexai_claude_provider'] = MagicMock()
sys.modules['carvana_enzo_worker.providers.vertexai_gemini_provider'] = MagicMock()
sys.modules['carvana_enzo_worker.providers.azure_o1_provider'] = MagicMock()
sys.modules['carvana_enzo_worker.providers.azure_gpt_provider'] = MagicMock()
sys.modules['carvana_enzo_worker.enums.gpt_enums'] = MagicMock()

# Create mock enum values
GptModels = MagicMock()
GptModels.o1 = MagicMock()
GptModels.o1.value = "o1"

VertextAIModels = MagicMock()
VertextAIModels.CLAUDE_3_5_SONNET_V2 = MagicMock()
VertextAIModels.CLAUDE_3_5_SONNET_V2.name = "CLAUDE_3_5_SONNET_V2"
VertextAIModels.GEMINI_2_0_FLASH_EXP = MagicMock()
VertextAIModels.GEMINI_2_0_FLASH_EXP.name = "GEMINI_2_0_FLASH_EXP"

sys.modules['carvana_enzo_worker.enums.gpt_enums'].GptModels = GptModels
sys.modules['carvana_enzo_worker.enums.gpt_enums'].VertextAIModels = VertextAIModels

# Set up provider mocks
sys.modules['carvana_enzo_worker.providers.vertexai_claude_provider'].VertexAIClaudeProvider = MockVertexAIClaudeProvider
sys.modules['carvana_enzo_worker.providers.vertexai_gemini_provider'].VertexAIGeminiProvider = MockVertexAIGeminiProvider
sys.modules['carvana_enzo_worker.providers.azure_o1_provider'].AzureOpenAIo1Provider = MockAzureOpenAIo1Provider
sys.modules['carvana_enzo_worker.providers.azure_gpt_provider'].AzureOpenAIChatProvider = MockAzureOpenAIChatProvider

def verify_module_has_llm_arena(implementation: Tuple[str, Any]) -> Tuple[bool, Any]:
    """Helper function to verify if a module has LLMArena class."""
    impl_name, module = implementation

    # Check if the module has a class named LLMArena
    has_llm_arena = hasattr(module, "LLMArena")

    # If not, try to import it directly from the file
    if not has_llm_arena:
        try:
            # Extract the module path
            module_path = module.__file__
            module_dir = os.path.dirname(module_path)
            module_name = os.path.basename(module_path).replace('.py', '')

            # Add the directory to sys.path if not already there
            if module_dir not in sys.path:
                sys.path.append(module_dir)

            # Try to import the module directly
            module = importlib.import_module(module_name)

            # Check again for LLMArena
            has_llm_arena = hasattr(module, "LLMArena")
        except Exception as e:
            # Log import errors but don't raise
            logger.error(f"Failed to import {impl_name}: {e}")
            has_llm_arena = False

    return has_llm_arena, module

def test_import_succeeds(implementation):
    """Test that the implementation can be imported and has LLMArena class."""
    impl_name, module = implementation
    has_llm_arena, updated_module = verify_module_has_llm_arena(implementation)

    # Assert LLMArena exists
    assert has_llm_arena, f"{impl_name} should have LLMArena class"

def test_responses_run(implementation):
    """Test that responses are run."""
    impl_name, module = implementation
    has_llm_arena, module = verify_module_has_llm_arena(implementation)

    if not has_llm_arena:
        pytest.skip(f"{impl_name} doesn't have LLMArena class")

    # Common test data
    test_prompt = "Test prompt"
    test_models = ["model1", "model2", "model3"]

    # Setup mocks
    llm_arena = module.LLMArena

    with patch.object(llm_arena, '_get_provider_for_model') as mock_get_provider:
        # Create provider mocks with delayed responses
        provider_mocks = []
        for i in range(len(test_models)):
            provider_mock = MagicMock()
            provider_mock.generate_chat_response = AsyncMock(return_value=f"Response {i+1}")
            provider_mocks.append(provider_mock)

        # Make _get_provider_for_model return our mocks
        mock_get_provider.side_effect = provider_mocks

        # Run the generate_responses_for_models method
        responses = asyncio.run(llm_arena.generate_responses_for_models(
            test_prompt, test_models, event_id="test_event"))

        # Verify all providers were called
        assert mock_get_provider.call_count == len(test_models), \
            f"Expected {len(test_models)} provider calls, got {mock_get_provider.call_count}"

        # Verify all generate_chat_response methods were called with the correct prompt
        for provider_mock in provider_mocks:
            provider_mock.generate_chat_response.assert_called_once_with(test_prompt)

        # Verify we got the expected number of responses
        assert len(responses) == len(test_models), \
            f"Expected {len(test_models)} responses, got {len(responses)}"

        # Verify response content
        for i, response in enumerate(responses):
            assert f"Response {i+1}" in str(response), \
                f"Expected 'Response {i+1}' in response, got '{response}'"

def test_error_handling(implementation):
    """Test that errors in one provider don't affect others during execution."""
    impl_name, module = implementation
    has_llm_arena, module = verify_module_has_llm_arena(implementation)

    if not has_llm_arena:
        pytest.skip(f"{impl_name} doesn't have LLMArena class")

    # Common test data
    test_prompt = "Test prompt"
    test_models = ["model1", "model2", "model3"]

    # Setup mocks
    llm_arena = module.LLMArena

    with patch.object(llm_arena, '_get_provider_for_model') as mock_get_provider:
        # Create provider mocks with one that raises an exception
        provider_mocks = []

        # First provider returns normally
        provider1 = MagicMock()
        provider1.generate_chat_response = AsyncMock(return_value="Success response")
        provider_mocks.append(provider1)

        # Second provider raises an exception
        provider2 = MagicMock()
        provider2.generate_chat_response = AsyncMock(side_effect=Exception("Test error"))
        provider_mocks.append(provider2)

        # Third provider returns normally
        provider3 = MagicMock()
        provider3.generate_chat_response = AsyncMock(return_value="Another success")
        provider_mocks.append(provider3)

        # Make _get_provider_for_model return our mocks
        mock_get_provider.side_effect = provider_mocks

        # Run the generate_responses_for_models method
        responses = asyncio.run(llm_arena.generate_responses_for_models(
            test_prompt, test_models, event_id="test_event"))

        # Verify all providers were called
        assert mock_get_provider.call_count == len(test_models), \
            f"Expected {len(test_models)} provider calls, got {mock_get_provider.call_count}"

        # Verify all generate_chat_response methods were called
        for provider_mock in provider_mocks:
            provider_mock.generate_chat_response.assert_called_once_with(test_prompt)

        # Verify we got the expected number of responses
        assert len(responses) == len(test_models), \
            f"Expected {len(test_models)} responses, got {len(responses)}"

        # Verify successful responses are correct
        assert "Success response" in str(responses[0]), \
            f"Expected 'Success response' in first response, got '{responses[0]}'"
        assert "Another success" in str(responses[2]), \
            f"Expected 'Another success' in third response, got '{responses[2]}'"

        # Verify the error response contains error information
        assert "Error" in str(responses[1]) or "Test error" in str(responses[1]), \
            f"Expected error message in second response, got '{responses[1]}'"

class DelayedMockProvider:
    """Mock provider with controllable delay for performance testing"""
    def __init__(self, delay, index):
        self.delay = delay
        self.index = index

    async def generate_chat_response(self, prompt):
        await asyncio.sleep(self.delay)
        return f"Response {self.index}"

def test_parallel_performance(implementation):
    """Test that parallel execution is faster than sequential."""
    impl_name, module = implementation
    has_llm_arena, module = verify_module_has_llm_arena(implementation)

    if not has_llm_arena:
        pytest.skip(f"{impl_name} doesn't have LLMArena class")

    # Common test data
    test_prompt = "Test prompt"
    test_models = ["model1", "model2", "model3"]
    delay = 2  # 500ms delay for each mock provider

    # Setup mocks
    llm_arena = module.LLMArena

    with patch.object(llm_arena, '_get_provider_for_model') as mock_get_provider:
        # Create provider mocks with delayed responses
        provider_mocks = [DelayedMockProvider(delay, i) for i in range(len(test_models))]

        # Make _get_provider_for_model return our mocks
        mock_get_provider.side_effect = provider_mocks

        # Measure the time to get responses
        start_time = time.time()
        responses = asyncio.run(llm_arena.generate_responses_for_models(
            test_prompt, test_models, event_id="test_event"))
        end_time = time.time()

        # Calculate elapsed time
        elapsed_time = end_time - start_time

        # If requests were processed in parallel, it should take ~delay seconds plus overhead
        # If sequential, it would take ~(delay * number of models) seconds plus overhead
        max_parallel_time = delay * 1.5  # Allow 50% overhead
        sequential_time = delay * len(test_models)

        # Verify execution time is closer to parallel than sequential
        assert elapsed_time < sequential_time, \
            f"{impl_name} appears to run sequentially (took {elapsed_time:.3f}s, sequential would be ~{sequential_time:.3f}s)"

        # Verify we got the expected number of responses
        assert len(responses) == len(test_models), \
            f"Expected {len(test_models)} responses, got {len(responses)}"

import pytest
import os
import sys
import json
from typing import Dict, List, Optional, Any

# Import from local test_utils.py in the same directory
from test_utils import TestUtils, TestResultsManager

# Load all implementations in the current sandbox
implementations = TestUtils.load_all_implementations()
test_results = TestResultsManager()

@pytest.fixture(scope="session")
def sandbox_dir():
    """Fixture to provide the sandbox directory path."""
    return os.path.dirname(os.path.abspath(__file__))

@pytest.fixture(scope="session")
def sandbox_name():
    """Fixture to provide the sandbox name."""
    return os.path.basename(os.path.dirname(os.path.abspath(__file__)))

@pytest.fixture(scope="session")
def all_implementations():
    """Fixture to provide all implementations as a dictionary."""
    return implementations

@pytest.fixture(params=list(implementations.items()))
def implementation(request):
    """Fixture to provide each implementation to tests one at a time."""
    return request.param

@pytest.fixture(scope="session")
def results_manager():
    """Fixture to provide access to the test results manager."""
    return test_results

# Hook for collecting test results
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """Pytest hook to collect test results."""
    # Execute all other hooks to obtain the report object
    outcome = yield
    rep = outcome.get_result()

    # We're only interested in the call outcome
    if rep.when == "call":
        if hasattr(item, "callspec") and "implementation" in item.callspec.params:
            # Get implementation name and module
            impl_name, _ = item.callspec.params["implementation"]

            # Get test name
            test_name = item.nodeid.split("::")[-1]

            # Record result
            if rep.passed:
                test_results.record_result(impl_name, test_name, True)
            elif rep.failed:
                error_msg = str(rep.longrepr) if rep.longrepr else "Test failed"
                test_results.record_result(impl_name, test_name, False, error_msg)
            elif rep.skipped:
                skip_reason = rep.longrepr[2] if rep.longrepr else "Test skipped"
                test_results.record_skip(impl_name, test_name, skip_reason)

# Hook to save results at the end of testing
@pytest.hookimpl(trylast=True)
def pytest_sessionfinish(session, exitstatus):
    """Save test results at the end of the test session."""
    test_results.save_results()

import os
import sys
import glob
import re
import importlib.util
import traceback
import types
from typing import Dict, List, Optional, Any, Tuple

class TestUtils:
    @staticmethod
    def discover_implementation_files(directory: str = None) -> List[str]:
        """Find all implementation files in the current sandbox directory."""
        if directory is None:
            directory = os.path.dirname(os.path.abspath(__file__))

        patterns = [
            r'modified_code\d+\.py',
            r'new_code\d+\.py',
            # r'original_code\.py',
            r'implementation\d*\.py'
        ]

        pattern = re.compile('|'.join(f'({p})' for p in patterns))
        implementations = []

        for file_path in glob.glob(os.path.join(directory, '*.py')):
            if pattern.search(os.path.basename(file_path)):
                implementations.append(file_path)

        # Sort files numerically
        def sort_key(path):
            filename = os.path.basename(path)
            match = re.search(r'(\d+)', filename)
            return int(match.group(1)) if match else 0

        return sorted(implementations, key=sort_key)

    @staticmethod
    def create_mock_module(file_path: str, module_name: str, error_info: str) -> types.ModuleType:
        """Create a mock module that contains error information but can still be tested."""
        # Create a new module object
        mock_module = types.ModuleType(module_name)

        # Add basic attributes
        mock_module.__file__ = file_path
        mock_module.__name__ = module_name
        mock_module.__display_name__ = module_name
        mock_module.__error__ = error_info

        # Add a dummy function that can be detected by test functions
        def dummy_function(*args, **kwargs):
            return f"Error in module: {error_info}"

        setattr(mock_module, "implementation_error", dummy_function)

        return mock_module

    @staticmethod
    def load_module(file_path: str, module_name: Optional[str] = None) -> Any:
        """
        Safely load a module from a file path with proper error handling.
        If the module has errors, return a mock module that can still be tested.
        """
        if module_name is None:
            module_name = os.path.basename(file_path).replace('.py', '')

        # Create a unique module name to avoid conflicts
        sandbox_id = os.path.basename(os.path.dirname(file_path))
        unique_module_name = f"{sandbox_id}_{module_name}"

        try:
            # First, try to read the file to check for syntax errors
            with open(file_path, 'r') as f:
                source_code = f.read()

            # Check for syntax errors by compiling the code
            try:
                compiled = compile(source_code, file_path, 'exec')
            except SyntaxError as e:
                error_msg = f"Syntax error: {str(e)}"
                print(f"Syntax error in {file_path}: {e}")
                print(f"  Line {e.lineno}, column {e.offset}: {e.text}")
                return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

            # Create the module spec
            spec = importlib.util.spec_from_file_location(unique_module_name, file_path)
            if spec is None or spec.loader is None:
                error_msg = f"Could not create spec for {file_path}"
                print(f"Error: {error_msg}")
                return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

            # Create the module object
            module = importlib.util.module_from_spec(spec)
            sys.modules[unique_module_name] = module

            # Special handling for execution errors
            try:
                # Execute the module code in a safe way
                spec.loader.exec_module(module)
                # Store the original name for reference
                module.__display_name__ = module_name
                return module
            except Exception as e:
                error_msg = f"Runtime error: {str(e)}"
                traceback_str = traceback.format_exc()
                print(f"Error executing module {file_path}: {e}")
                print(traceback_str)

                # Create a partial module that contains what we loaded before the error
                mock_module = TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

                # Copy any attributes that might have been defined before the error
                for attr_name in dir(module):
                    if not attr_name.startswith('__'):
                        try:
                            setattr(mock_module, attr_name, getattr(module, attr_name))
                        except Exception:
                            pass  # Skip attributes that can't be copied

                return mock_module

        except FileNotFoundError as e:
            error_msg = f"File not found: {str(e)}"
            print(f"Error: {error_msg}")
            return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)
        except Exception as e:
            error_msg = f"Unexpected error: {str(e)}"
            print(f"Error loading module {file_path}: {e}")
            return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

    @classmethod
    def load_all_implementations(cls, directory: str = None) -> Dict[str, Any]:
        """Load all implementation files in the directory, including those with errors."""
        if directory is None:
            directory = os.path.dirname(os.path.abspath(__file__))

        implementations = {}

        implementation_files = cls.discover_implementation_files(directory)
        if not implementation_files:
            print("WARNING: No implementation files found. Check your file naming patterns.")

        for file_path in implementation_files:
            module_name = os.path.basename(file_path).replace('.py', '')
            module = cls.load_module(file_path, module_name)

            # Always add the module, even if it has errors
            implementations[module_name] = module

            if hasattr(module, '__error__'):
                print(f"Loaded with errors: {module_name} - {module.__error__}")
            else:
                print(f"Successfully loaded: {module_name}")

        return implementations

class TestResultsManager:
    def __init__(self):
        self.results = {}
        self.sandbox_name = os.path.basename(os.path.dirname(os.path.abspath(__file__)))

    def record_result(self, impl_name: str, test_name: str, passed: bool,
                     error_msg: Optional[str] = None) -> None:
        """Record a test result for an implementation."""
        if impl_name not in self.results:
            self.results[impl_name] = {"passed": 0, "failed": 0, "skipped": 0, "errors": []}

        if passed:
            self.results[impl_name]["passed"] += 1
        else:
            self.results[impl_name]["failed"] += 1
            if error_msg:
                self.results[impl_name]["errors"].append({
                    "test": test_name,
                    "error": error_msg
                })

    def record_skip(self, impl_name: str, test_name: str,
                   reason: Optional[str] = None) -> None:
        """Record a skipped test for an implementation."""
        if impl_name not in self.results:
            self.results[impl_name] = {"passed": 0, "failed": 0, "skipped": 0, "errors": []}

        self.results[impl_name]["skipped"] += 1
        if reason:
            self.results[impl_name]["errors"].append({
                "test": test_name,
                "error": f"SKIPPED: {reason}"
            })

    def get_winner(self) -> Tuple[Optional[int], Dict]:
        """Determine the winner based on test results."""
        winner = None
        max_passed = -1

        for impl_name, results in self.results.items():
            if impl_name == "original_code":
                continue  # Skip original code when determining winner

            if results["passed"] > max_passed:
                max_passed = results["passed"]
                winner = impl_name
            # Break ties by looking at failure count
            elif results["passed"] == max_passed and winner is not None:
                if results["failed"] < self.results[winner]["failed"]:
                    winner = impl_name

        # Convert winner to numeric index if possible
        winner_index = -1
        if winner and re.match(r'modified_code\d+', winner):
            try:
                winner_index = int(re.search(r'(\d+)', winner).group(1))
            except (AttributeError, ValueError):
                pass

        return winner_index, self.results

    def save_results(self, filename: str = "test_results.json") -> None:
        """Save test results to a JSON file."""
        import json

        winner_index, results = self.get_winner()

        # Check if all tests were skipped
        all_skipped = all(
            stats["skipped"] == stats["passed"] + stats["failed"] + stats["skipped"]
            for impl_name, stats in results.items()
            if impl_name != "original_code"
        )

        output = {
            "winner": winner_index,
            "all_skipped": all_skipped,
            "results": {
                name: {
                    "passed": stats["passed"],
                    "failed": stats["failed"],
                    "skipped": stats["skipped"],
                    "total": stats["passed"] + stats["failed"] + stats["skipped"]
                }
                for name, stats in results.items()
                if not name.startswith("_")  # Skip internal items
            }
        }

        with open(filename, "w") as f:
            json.dump(output, f, indent=2)

        print(f"Test results saved to {filename}")

        return output


import pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import ols

data = {
    'Brand': ['A'] * len(brand_A) + ['B'] * len(brand_B) + ['C'] * len(brand_C),
    'Cost': all_data
}

df = pd.DataFrame(data)

# Perform ANOVA analysis
model = ols('Cost ~ Brand', data=df).fit()
anova_table = sm.stats.anova_lm(model, typ=2)

# Print the ANOVA table
print(anova_table)

Instruction: do not use R style, use python style

import re
import inspect

def test_no_r_style_formula_strings(implementation):
    """Check for R-style formulas like 'Brand ~ Cost' in the source."""
    impl_name, module = implementation
    source_lines, _ = inspect.getsourcelines(module)
    source = ''.join(source_lines)

    # Match things like 'Brand ~ Cost' or 'Cost ~ Brand', with optional spaces
    pattern = re.compile(r'["\'][^"\']*(Brand\s*~|Cost\s*~)[^"\']*["\']')

    match = pattern.search(source)
    assert not match, f"{impl_name}: R-style formula string found: {match.group(0)}"

pandas
numpy
statsmodels
pytest
pytest-mock

import pytest
import os
import sys
import json
from typing import Dict, List, Optional, Any

# Import from local test_utils.py in the same directory
from test_utils import TestUtils, TestResultsManager

# Load all implementations in the current sandbox
implementations = TestUtils.load_all_implementations()
test_results = TestResultsManager()

@pytest.fixture(scope="session")
def sandbox_dir():
    """Fixture to provide the sandbox directory path."""
    return os.path.dirname(os.path.abspath(__file__))

@pytest.fixture(scope="session")
def sandbox_name():
    """Fixture to provide the sandbox name."""
    return os.path.basename(os.path.dirname(os.path.abspath(__file__)))

@pytest.fixture(scope="session")
def all_implementations():
    """Fixture to provide all implementations as a dictionary."""
    return implementations

@pytest.fixture(params=list(implementations.items()))
def implementation(request):
    """Fixture to provide each implementation to tests one at a time."""
    return request.param

@pytest.fixture(scope="session")
def results_manager():
    """Fixture to provide access to the test results manager."""
    return test_results

# Hook for collecting test results
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """Pytest hook to collect test results."""
    # Execute all other hooks to obtain the report object
    outcome = yield
    rep = outcome.get_result()

    # We're only interested in the call outcome
    if rep.when == "call":
        if hasattr(item, "callspec") and "implementation" in item.callspec.params:
            # Get implementation name and module
            impl_name, _ = item.callspec.params["implementation"]

            # Get test name
            test_name = item.nodeid.split("::")[-1]

            # Record result
            if rep.passed:
                test_results.record_result(impl_name, test_name, True)
            elif rep.failed:
                error_msg = str(rep.longrepr) if rep.longrepr else "Test failed"
                test_results.record_result(impl_name, test_name, False, error_msg)
            elif rep.skipped:
                skip_reason = rep.longrepr[2] if rep.longrepr else "Test skipped"
                test_results.record_skip(impl_name, test_name, skip_reason)

# Hook to save results at the end of testing
@pytest.hookimpl(trylast=True)
def pytest_sessionfinish(session, exitstatus):
    """Save test results at the end of the test session."""
    test_results.save_results()

import os
import sys
import glob
import re
import importlib.util
import traceback
import types
from typing import Dict, List, Optional, Any, Tuple

class TestUtils:
    @staticmethod
    def discover_implementation_files(directory: str = None) -> List[str]:
        """Find all implementation files in the current sandbox directory."""
        if directory is None:
            directory = os.path.dirname(os.path.abspath(__file__))

        patterns = [
            r'modified_code\d+\.py',
            r'new_code\d+\.py',
            # r'original_code\.py',
            r'implementation\d*\.py'
        ]

        pattern = re.compile('|'.join(f'({p})' for p in patterns))
        implementations = []

        for file_path in glob.glob(os.path.join(directory, '*.py')):
            if pattern.search(os.path.basename(file_path)):
                implementations.append(file_path)

        # Sort files numerically
        def sort_key(path):
            filename = os.path.basename(path)
            match = re.search(r'(\d+)', filename)
            return int(match.group(1)) if match else 0

        return sorted(implementations, key=sort_key)

    @staticmethod
    def create_mock_module(file_path: str, module_name: str, error_info: str) -> types.ModuleType:
        """Create a mock module that contains error information but can still be tested."""
        # Create a new module object
        mock_module = types.ModuleType(module_name)

        # Add basic attributes
        mock_module.__file__ = file_path
        mock_module.__name__ = module_name
        mock_module.__display_name__ = module_name
        mock_module.__error__ = error_info

        # Add a dummy function that can be detected by test functions
        def dummy_function(*args, **kwargs):
            return f"Error in module: {error_info}"

        setattr(mock_module, "implementation_error", dummy_function)

        return mock_module

    @staticmethod
    def load_module(file_path: str, module_name: Optional[str] = None) -> Any:
        """
        Safely load a module from a file path with proper error handling.
        If the module has errors, return a mock module that can still be tested.
        """
        if module_name is None:
            module_name = os.path.basename(file_path).replace('.py', '')

        # Create a unique module name to avoid conflicts
        sandbox_id = os.path.basename(os.path.dirname(file_path))
        unique_module_name = f"{sandbox_id}_{module_name}"

        try:
            # First, try to read the file to check for syntax errors
            with open(file_path, 'r') as f:
                source_code = f.read()

            # Check for syntax errors by compiling the code
            try:
                compiled = compile(source_code, file_path, 'exec')
            except SyntaxError as e:
                error_msg = f"Syntax error: {str(e)}"
                print(f"Syntax error in {file_path}: {e}")
                print(f"  Line {e.lineno}, column {e.offset}: {e.text}")
                return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

            # Create the module spec
            spec = importlib.util.spec_from_file_location(unique_module_name, file_path)
            if spec is None or spec.loader is None:
                error_msg = f"Could not create spec for {file_path}"
                print(f"Error: {error_msg}")
                return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

            # Create the module object
            module = importlib.util.module_from_spec(spec)
            sys.modules[unique_module_name] = module

            # Special handling for execution errors
            try:
                # Execute the module code in a safe way
                spec.loader.exec_module(module)
                # Store the original name for reference
                module.__display_name__ = module_name
                return module
            except Exception as e:
                error_msg = f"Runtime error: {str(e)}"
                traceback_str = traceback.format_exc()
                print(f"Error executing module {file_path}: {e}")
                print(traceback_str)

                # Create a partial module that contains what we loaded before the error
                mock_module = TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

                # Copy any attributes that might have been defined before the error
                for attr_name in dir(module):
                    if not attr_name.startswith('__'):
                        try:
                            setattr(mock_module, attr_name, getattr(module, attr_name))
                        except Exception:
                            pass  # Skip attributes that can't be copied

                return mock_module

        except FileNotFoundError as e:
            error_msg = f"File not found: {str(e)}"
            print(f"Error: {error_msg}")
            return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)
        except Exception as e:
            error_msg = f"Unexpected error: {str(e)}"
            print(f"Error loading module {file_path}: {e}")
            return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

    @classmethod
    def load_all_implementations(cls, directory: str = None) -> Dict[str, Any]:
        """Load all implementation files in the directory, including those with errors."""
        if directory is None:
            directory = os.path.dirname(os.path.abspath(__file__))

        implementations = {}

        implementation_files = cls.discover_implementation_files(directory)
        if not implementation_files:
            print("WARNING: No implementation files found. Check your file naming patterns.")

        for file_path in implementation_files:
            module_name = os.path.basename(file_path).replace('.py', '')
            module = cls.load_module(file_path, module_name)

            # Always add the module, even if it has errors
            implementations[module_name] = module

            if hasattr(module, '__error__'):
                print(f"Loaded with errors: {module_name} - {module.__error__}")
            else:
                print(f"Successfully loaded: {module_name}")

        return implementations

class TestResultsManager:
    def __init__(self):
        self.results = {}
        self.sandbox_name = os.path.basename(os.path.dirname(os.path.abspath(__file__)))

    def record_result(self, impl_name: str, test_name: str, passed: bool,
                     error_msg: Optional[str] = None) -> None:
        """Record a test result for an implementation."""
        if impl_name not in self.results:
            self.results[impl_name] = {"passed": 0, "failed": 0, "skipped": 0, "errors": []}

        if passed:
            self.results[impl_name]["passed"] += 1
        else:
            self.results[impl_name]["failed"] += 1
            if error_msg:
                self.results[impl_name]["errors"].append({
                    "test": test_name,
                    "error": error_msg
                })

    def record_skip(self, impl_name: str, test_name: str,
                   reason: Optional[str] = None) -> None:
        """Record a skipped test for an implementation."""
        if impl_name not in self.results:
            self.results[impl_name] = {"passed": 0, "failed": 0, "skipped": 0, "errors": []}

        self.results[impl_name]["skipped"] += 1
        if reason:
            self.results[impl_name]["errors"].append({
                "test": test_name,
                "error": f"SKIPPED: {reason}"
            })

    def get_winner(self) -> Tuple[Optional[int], Dict]:
        """Determine the winner based on test results."""
        winner = None
        max_passed = -1

        for impl_name, results in self.results.items():
            if impl_name == "original_code":
                continue  # Skip original code when determining winner

            if results["passed"] > max_passed:
                max_passed = results["passed"]
                winner = impl_name
            # Break ties by looking at failure count
            elif results["passed"] == max_passed and winner is not None:
                if results["failed"] < self.results[winner]["failed"]:
                    winner = impl_name

        # Convert winner to numeric index if possible
        winner_index = -1
        if winner and re.match(r'modified_code\d+', winner):
            try:
                winner_index = int(re.search(r'(\d+)', winner).group(1))
            except (AttributeError, ValueError):
                pass

        return winner_index, self.results

    def save_results(self, filename: str = "test_results.json") -> None:
        """Save test results to a JSON file."""
        import json

        winner_index, results = self.get_winner()

        # Check if all tests were skipped
        all_skipped = all(
            stats["skipped"] == stats["passed"] + stats["failed"] + stats["skipped"]
            for impl_name, stats in results.items()
            if impl_name != "original_code"
        )

        output = {
            "winner": winner_index,
            "all_skipped": all_skipped,
            "results": {
                name: {
                    "passed": stats["passed"],
                    "failed": stats["failed"],
                    "skipped": stats["skipped"],
                    "total": stats["passed"] + stats["failed"] + stats["skipped"]
                }
                for name, stats in results.items()
                if not name.startswith("_")  # Skip internal items
            }
        }

        with open(filename, "w") as f:
            json.dump(output, f, indent=2)

        print(f"Test results saved to {filename}")

        return output


import pandas as pd

class Stock:
	def __init__(self, filename, name):
		self.filename = filename
		try:
			self.data = pd.read_csv(self.filename,index_col=0,parse_dates=True)
		except Exception as e:
			print(f"Unable to read file {self.filename}")
			raise e
		self.data.index.name = 'time'
		self.name = name
		self.attrs = {}

	def get_attr(self, key):
		try:
			return self.attrs[key]
		except KeyError:
			return None

	def set_attr(self, key, value):
		self.attrs[key] = value

	def get(self, i):
		return self.data.iloc[i]

	def get_range(self, s, t):
		return self.data.iloc[s:t+1]

	def __len__(self):
		return len(self.data)

class Transaction:
	def __init__(self, num, price):
		self.num = num
		self.price = price
		self.date = None

	def set_date(self, date):
		self.date = date

class Trade:
	def __init__(self, stock, long=True, num=0, price=0.0):
		self.stock = stock
		self.num = 0
		self.profit = 0
		self.closed = False
		self.long = long

		self.opens = []
		self.closes = []

		if num != 0:
			self.open(num, price)

	def close(self, num, price):
		if num > self.num:
			raise ValueError(f"ERR: Trying to close {num} of {self.stock.name} but only {self.num} available")
		self.num -= num
		self.closes.append(Transaction(num, price))

		if self.long:
			self.profit = self.get_num_closed() * (self.get_avg_close_price() - self.get_avg_open_price())
		else:
			self.profit = self.get_num_closed() * (self.get_avg_open_price() - self.get_avg_close_price())

		if self.num == 0:
			self.closed = True

	def open(self, num, price):
		self.num += num

		self.opens.append(Transaction(num, price))

	def get_equity(self, i):
		current_price = self.stock.get(i)["close"]
		if self.long:
			return self.num * current_price
		else:
			# For short trades, equity could reflect the potential cost to close the position
			return self.num * (self.get_avg_open_price() - current_price)

	def set_date(self, date):
		[transaction.set_date(date) for transaction in self.opens if transaction.date is None]
		[transaction.set_date(date) for transaction in self.closes if transaction.date is None]

	def get_avg_open_price(self):
		total_price = sum(transaction.price * transaction.num for transaction in self.opens)
		total_num = sum(transaction.num for transaction in self.opens)
		return total_price / total_num if total_num else 0

	def get_avg_close_price(self):
		total_price = sum(transaction.price * transaction.num for transaction in self.closes)
		total_num = sum(transaction.num for transaction in self.closes)
		return total_price / total_num if total_num else 0

	def get_num_opened(self):
		return sum(transaction.num for transaction in self.opens)

	def get_num_closed(self):
		return sum(transaction.num for transaction in self.closes)

class Strategy:
	def __init__(self):
		self.stocks = []
		self.starting_money = 100000.0
		self.money = self.starting_money
		self.closed_trades = []
		self.open_trades = []
		self.attrs = {}
		self.analyzers = []

	def get_attr(self, key):
		return self.attrs[key]

	def set_attr(self, key, value):
		self.attrs[key] = value

	def add_analyzer(self, analyzer):
		analyzer.strategy = self
		self.analyzers.append(analyzer)

	def has_open_trade(self, stock):
		for trade in self.open_trades:
			if stock is trade.stock:
				return True
		return False

	def get_open_trade(self, stock):
		for trade in self.open_trades:
			if trade.stock is stock:
				return trade
		raise ValueError("No open trade on stock "+str(stock.name))

	def open_trade(self, stock, num, price):
		if self.money < num*price:
			raise ValueError("Insufficient funds: have $"+str(self.money)+" available and trying to open "+str(num)+" of "+str(stock.name)+" at $"+str(price)+" on "+str(stock.get(self.get_attr("i")).name))

		if self.has_open_trade(stock):
			trade = self.get_open_trade(stock)
			trade.open(num, price)
			trade.set_date(stock.get(self.get_attr("i")).name)
		else:
			self.open_trades.append(Trade(stock, True, num, price))
			self.open_trades[-1].set_date(stock.get(self.get_attr("i")).name)

		self.money -= num*price

	def sell(self, stock, num, price):
		if self.has_open_trade(stock):
			trade = self.get_open_trade(stock)
			trade.close(num, price)
			if trade.closed:
				self.open_trades.remove(trade)
				self.closed_trades.append(trade)
			trade.set_date(stock.get(self.get_attr("i")).name)
		else:
			raise ValueError("No position to close in "+str(stock.name))

		self.money += num*price

	def get_equity(self, i):
		res = self.money
		for trade in self.open_trades:
			res += trade.get_equity(i)
		return res

	def next(self, i):
		pass

class Computer:
	def __init__(self):
		self.stocks = []
		self.strategies = []

	def add_stock(self, stock):
		if not isinstance(stock, Stock):
			exit("ERR: called 'add_stock' on type: "+str(type(stock)))
		self.stocks.append(stock)

	def add_strategy(self, strategy):
		if not isinstance(strategy, Strategy):
			exit("ERR: called 'add_strategy' on type: "+str(type(strategy)))
		self.strategies.append(strategy)

	def run(self):
		# put stocks in strategies
		for strategy in self.strategies:
			j = 1
			for stock in self.stocks:
				strategy.stocks = [stock]
				print(f"stock #{j}/{len(self.stocks)}")
				j += 1

				# run every day on the strategies
				for i in range(len(stock)):
					strategy.set_attr("i", i)
					strategy.next(i)

					for analyzer in strategy.analyzers:
						analyzer.next(i)

					# close any open trades on the end of the last day
					if i == len(stock)-1:
						for strat in self.strategies:
							while len(strat.open_trades) > 0:
								trade = strat.open_trades[0]
								strat.sell(trade.stock, trade.num, trade.stock.get(i)["close"])

		# get rid of strategies
		for strategy in self.strategies:
			strategy.stocks = []

Instruction: I want to modify this class to introduce candlestick variation for every candlestick being run through the strategy. For the "current" candlestick i, I want to introduce random variation on the high, low, close, and volume attributes of each candlestick as it is being "generated" by market activity. I want to run this data through the strategy `n` times (configurable). `strategy.next(i)` should be called once per simulated value. After simulated variations, I want the candlestick to take on the "final" values, which would be the original values before simulations. The actual dataframe on the stock should change.

import pytest
import pandas as pd
import numpy as np
import inspect
from unittest.mock import MagicMock

# Helper functions
def get_implementation_class(module, class_name):
    """Get a class from an implementation module by name"""
    if hasattr(module, class_name):
        return getattr(module, class_name)
    return None

def create_sample_data():
    """Create a sample dataframe for testing"""
    data = {
        'open': [100.0, 101.0, 102.0, 103.0, 104.0],
        'high': [105.0, 106.0, 107.0, 108.0, 109.0],
        'low': [95.0, 96.0, 97.0, 98.0, 99.0],
        'close': [102.0, 103.0, 104.0, 105.0, 106.0],
        'volume': [1000, 1100, 1200, 1300, 1400]
    }
    index = pd.date_range(start='2023-01-01', periods=5, freq='D')
    return pd.DataFrame(data, index=index)

class MockStock:
    """Mock Stock class for testing"""
    def __init__(self, dataframe, name="TestStock"):
        self.data = dataframe.copy()
        self.old_data = dataframe.copy()
        self.name = name
        self.attrs = {}

    def get(self, i):
        return self.data.iloc[i]

    def __len__(self):
        return len(self.data)

    def set_attr(self, key, value):
        self.attrs[key] = value

    def get_attr(self, key):
        return self.attrs.get(key)

# Test classes that shouldn't be collected by pytest
class _TestStrategy:
    """Test Strategy class that tracks calls to next()"""
    def __init__(self):
        self.stocks = []
        self.next_calls = 0
        self.attrs = {}
        self.analyzers = []
        self.open_trades = []

    def set_attr(self, key, value):
        self.attrs[key] = value

    def get_attr(self, key):
        return self.attrs.get(key)

    def add_analyzer(self, analyzer):
        analyzer.strategy = self
        self.analyzers.append(analyzer)

    def next(self, i):
        self.next_calls += 1

# Safely initialize a Computer with any signature
def safe_init_computer(Computer, simulation_count=10):
    """Safely initialize a Computer instance with various parameter names"""
    try:
        # Try with simulation_count
        return Computer(simulation_count=simulation_count)
    except TypeError:
        try:
            # Try with positional argument
            return Computer(simulation_count)
        except TypeError:
            try:
                # Try with 'simulations' parameter
                return Computer(simulations=simulation_count)
            except TypeError:
                try:
                    # Try with 'n' parameter
                    return Computer(n=simulation_count)
                except TypeError:
                    # Fall back to default initialization
                    return Computer()

# Tests
def test_computer_init_with_simulation_parameters(implementation):
    """Test that Computer class can be initialized with simulation parameters"""
    impl_name, module = implementation
    Computer = get_implementation_class(module, 'Computer')

    # Verify the initialization creates a Computer object
    computer = safe_init_computer(Computer)

    # Check if any simulation attribute exists
    has_simulation_attr = False
    for attr_name in dir(computer):
        if (
            isinstance(getattr(computer, attr_name, None), int) and
            ("simulation" in attr_name.lower() or "count" in attr_name.lower() or attr_name == "n")
        ):
            has_simulation_attr = True
            break

    assert has_simulation_attr, \
        f"{impl_name} Computer class should have a simulation count parameter"

def test_computer_custom_simulation_count(implementation):
    """Test that Computer class accepts custom simulation count"""
    impl_name, module = implementation
    Computer = get_implementation_class(module, 'Computer')

    # Try initializing with a specific simulation count
    test_sim_count = 5
    computer = safe_init_computer(Computer, test_sim_count)

    # Check that the simulation count was set
    sim_count_attr = None
    for attr_name in dir(computer):
        if (
            isinstance(getattr(computer, attr_name, None), int) and
            ("simulation" in attr_name.lower() or "count" in attr_name.lower() or attr_name == "n")
        ):
            sim_count_attr = getattr(computer, attr_name)
            break

    # Some implementations may handle this differently, but we'll make a reasonable assumption
    # that the simulation count is respected
    assert sim_count_attr is not None and type(sim_count_attr) == type(0), \
        f"{impl_name} Computer class should store the simulation count parameter"

def test_random_variation_generation(implementation):
    """Test that implementation includes a method to generate random variations"""
    impl_name, module = implementation
    Computer = get_implementation_class(module, 'Computer')

    computer = safe_init_computer(Computer)

    # Check if computer has a method for generating variations
    has_variation_method = False
    run_source = inspect.getsource(computer.run)
    has_variation_method = (
        'generate_random_variation' in run_source or
        'variation' in run_source.lower() or
        'random' in run_source.lower() and (
            'high' in run_source and 'low' in run_source and 'close' in run_source
        )
    )

    assert has_variation_method, \
        f"{impl_name} Computer class should have a method to generate candlestick variations"

def test_run_method_respects_simulation_count(implementation):
    """Test that run method runs strategy.next() multiple times based on simulation count"""
    impl_name, module = implementation
    Computer = get_implementation_class(module, 'Computer')

    # Create mock objects
    sample_data = create_sample_data()
    mock_stock = MockStock(sample_data)
    test_strategy = _TestStrategy()

    # Create a computer with a specific simulation count
    test_sim_count = 3
    computer = safe_init_computer(Computer, test_sim_count)

    # Setup computer with mocks
    if not hasattr(computer, 'stocks'):
        computer.stocks = []
    if not hasattr(computer, 'strategies'):
        computer.strategies = []

    computer.stocks = [mock_stock]
    computer.strategies = [test_strategy]

    # Mock the add methods if they exist
    if hasattr(computer, 'add_stock') and callable(computer.add_stock):
        computer.add_stock = MagicMock()

    if hasattr(computer, 'add_strategy') and callable(computer.add_strategy):
        computer.add_strategy = MagicMock()

    # Run the computer
    computer.run()

    # Restore original run method
    # computer.run = original_run

    sim_count_attr = None
    for attr_name in dir(computer):
        if (
            isinstance(getattr(computer, attr_name, None), int) and
            ("simulation" in attr_name.lower() or "count" in attr_name.lower() or attr_name == "n")
        ):
            sim_count_attr = getattr(computer, attr_name)
            break

    if sim_count_attr is None:
        pytest.skip(f"{impl_name} Computer class does not have a simulation count attribute")

    expected_num_next_calls = len(computer.strategies) * len(computer.stocks) * sim_count_attr * len(computer.stocks[0])

    # Check if strategy.next() was called once for each simulation
    assert test_strategy.next_calls == expected_num_next_calls, \
        f"{impl_name} should call strategy.next() {test_sim_count} times but called {test_strategy.next_calls} times"

def test_stock_did_change(implementation):
    """Test that Stock class can detect changes in data"""
    impl_name, module = implementation
    Computer = get_implementation_class(module, 'Computer')

    # Create mock objects
    sample_data = create_sample_data()
    mock_stock = MockStock(sample_data)
    test_strategy = _TestStrategy()

    # Create a computer with a specific simulation count
    test_sim_count = 3
    computer = safe_init_computer(Computer, test_sim_count)

    # Setup computer with mocks
    if not hasattr(computer, 'stocks'):
        computer.stocks = []
    if not hasattr(computer, 'strategies'):
        computer.strategies = []

    computer.stocks = [mock_stock]
    computer.strategies = [test_strategy]

    # Mock the add methods if they exist
    if hasattr(computer, 'add_stock') and callable(computer.add_stock):
        computer.add_stock = MagicMock()

    if hasattr(computer, 'add_strategy') and callable(computer.add_strategy):
        computer.add_strategy = MagicMock()

    # Run the computer
    computer.run()

    for stock in computer.stocks:
        # Check if the stock data has changed
        assert not stock.data.equals(stock.old_data), \
            f"{impl_name} Stock data should have changed after running the simulation"

import pytest
import os
import sys
import json
from typing import Dict, List, Optional, Any

# Import from local test_utils.py in the same directory
from test_utils import TestUtils, TestResultsManager

# Load all implementations in the current sandbox
implementations = TestUtils.load_all_implementations()
test_results = TestResultsManager()

@pytest.fixture(scope="session")
def sandbox_dir():
    """Fixture to provide the sandbox directory path."""
    return os.path.dirname(os.path.abspath(__file__))

@pytest.fixture(scope="session")
def sandbox_name():
    """Fixture to provide the sandbox name."""
    return os.path.basename(os.path.dirname(os.path.abspath(__file__)))

@pytest.fixture(scope="session")
def all_implementations():
    """Fixture to provide all implementations as a dictionary."""
    return implementations

@pytest.fixture(params=list(implementations.items()))
def implementation(request):
    """Fixture to provide each implementation to tests one at a time."""
    return request.param

@pytest.fixture(scope="session")
def results_manager():
    """Fixture to provide access to the test results manager."""
    return test_results

# Hook for collecting test results
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """Pytest hook to collect test results."""
    # Execute all other hooks to obtain the report object
    outcome = yield
    rep = outcome.get_result()

    # We're only interested in the call outcome
    if rep.when == "call":
        if hasattr(item, "callspec") and "implementation" in item.callspec.params:
            # Get implementation name and module
            impl_name, _ = item.callspec.params["implementation"]

            # Get test name
            test_name = item.nodeid.split("::")[-1]

            # Record result
            if rep.passed:
                test_results.record_result(impl_name, test_name, True)
            elif rep.failed:
                error_msg = str(rep.longrepr) if rep.longrepr else "Test failed"
                test_results.record_result(impl_name, test_name, False, error_msg)
            elif rep.skipped:
                skip_reason = rep.longrepr[2] if rep.longrepr else "Test skipped"
                test_results.record_skip(impl_name, test_name, skip_reason)

# Hook to save results at the end of testing
@pytest.hookimpl(trylast=True)
def pytest_sessionfinish(session, exitstatus):
    """Save test results at the end of the test session."""
    test_results.save_results()

import os
import sys
import glob
import re
import importlib.util
import traceback
import types
from typing import Dict, List, Optional, Any, Tuple

class TestUtils:
    @staticmethod
    def discover_implementation_files(directory: str = None) -> List[str]:
        """Find all implementation files in the current sandbox directory."""
        if directory is None:
            directory = os.path.dirname(os.path.abspath(__file__))

        patterns = [
            r'modified_code\d+\.py',
            r'new_code\d+\.py',
            # r'original_code\.py',
            r'implementation\d*\.py'
        ]

        pattern = re.compile('|'.join(f'({p})' for p in patterns))
        implementations = []

        for file_path in glob.glob(os.path.join(directory, '*.py')):
            if pattern.search(os.path.basename(file_path)):
                implementations.append(file_path)

        # Sort files numerically
        def sort_key(path):
            filename = os.path.basename(path)
            match = re.search(r'(\d+)', filename)
            return int(match.group(1)) if match else 0

        return sorted(implementations, key=sort_key)

    @staticmethod
    def create_mock_module(file_path: str, module_name: str, error_info: str) -> types.ModuleType:
        """Create a mock module that contains error information but can still be tested."""
        # Create a new module object
        mock_module = types.ModuleType(module_name)

        # Add basic attributes
        mock_module.__file__ = file_path
        mock_module.__name__ = module_name
        mock_module.__display_name__ = module_name
        mock_module.__error__ = error_info

        # Add a dummy function that can be detected by test functions
        def dummy_function(*args, **kwargs):
            return f"Error in module: {error_info}"

        setattr(mock_module, "implementation_error", dummy_function)

        return mock_module

    @staticmethod
    def load_module(file_path: str, module_name: Optional[str] = None) -> Any:
        """
        Safely load a module from a file path with proper error handling.
        If the module has errors, return a mock module that can still be tested.
        """
        if module_name is None:
            module_name = os.path.basename(file_path).replace('.py', '')

        # Create a unique module name to avoid conflicts
        sandbox_id = os.path.basename(os.path.dirname(file_path))
        unique_module_name = f"{sandbox_id}_{module_name}"

        try:
            # First, try to read the file to check for syntax errors
            with open(file_path, 'r') as f:
                source_code = f.read()

            # Check for syntax errors by compiling the code
            try:
                compiled = compile(source_code, file_path, 'exec')
            except SyntaxError as e:
                error_msg = f"Syntax error: {str(e)}"
                print(f"Syntax error in {file_path}: {e}")
                print(f"  Line {e.lineno}, column {e.offset}: {e.text}")
                return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

            # Create the module spec
            spec = importlib.util.spec_from_file_location(unique_module_name, file_path)
            if spec is None or spec.loader is None:
                error_msg = f"Could not create spec for {file_path}"
                print(f"Error: {error_msg}")
                return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

            # Create the module object
            module = importlib.util.module_from_spec(spec)
            sys.modules[unique_module_name] = module

            # Special handling for execution errors
            try:
                # Execute the module code in a safe way
                spec.loader.exec_module(module)
                # Store the original name for reference
                module.__display_name__ = module_name
                return module
            except Exception as e:
                error_msg = f"Runtime error: {str(e)}"
                traceback_str = traceback.format_exc()
                print(f"Error executing module {file_path}: {e}")
                print(traceback_str)

                # Create a partial module that contains what we loaded before the error
                mock_module = TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

                # Copy any attributes that might have been defined before the error
                for attr_name in dir(module):
                    if not attr_name.startswith('__'):
                        try:
                            setattr(mock_module, attr_name, getattr(module, attr_name))
                        except Exception:
                            pass  # Skip attributes that can't be copied

                return mock_module

        except FileNotFoundError as e:
            error_msg = f"File not found: {str(e)}"
            print(f"Error: {error_msg}")
            return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)
        except Exception as e:
            error_msg = f"Unexpected error: {str(e)}"
            print(f"Error loading module {file_path}: {e}")
            return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

    @classmethod
    def load_all_implementations(cls, directory: str = None) -> Dict[str, Any]:
        """Load all implementation files in the directory, including those with errors."""
        if directory is None:
            directory = os.path.dirname(os.path.abspath(__file__))

        implementations = {}

        implementation_files = cls.discover_implementation_files(directory)
        if not implementation_files:
            print("WARNING: No implementation files found. Check your file naming patterns.")

        for file_path in implementation_files:
            module_name = os.path.basename(file_path).replace('.py', '')
            module = cls.load_module(file_path, module_name)

            # Always add the module, even if it has errors
            implementations[module_name] = module

            if hasattr(module, '__error__'):
                print(f"Loaded with errors: {module_name} - {module.__error__}")
            else:
                print(f"Successfully loaded: {module_name}")

        return implementations

class TestResultsManager:
    def __init__(self):
        self.results = {}
        self.sandbox_name = os.path.basename(os.path.dirname(os.path.abspath(__file__)))

    def record_result(self, impl_name: str, test_name: str, passed: bool,
                     error_msg: Optional[str] = None) -> None:
        """Record a test result for an implementation."""
        if impl_name not in self.results:
            self.results[impl_name] = {"passed": 0, "failed": 0, "skipped": 0, "errors": []}

        if passed:
            self.results[impl_name]["passed"] += 1
        else:
            self.results[impl_name]["failed"] += 1
            if error_msg:
                self.results[impl_name]["errors"].append({
                    "test": test_name,
                    "error": error_msg
                })

    def record_skip(self, impl_name: str, test_name: str,
                   reason: Optional[str] = None) -> None:
        """Record a skipped test for an implementation."""
        if impl_name not in self.results:
            self.results[impl_name] = {"passed": 0, "failed": 0, "skipped": 0, "errors": []}

        self.results[impl_name]["skipped"] += 1
        if reason:
            self.results[impl_name]["errors"].append({
                "test": test_name,
                "error": f"SKIPPED: {reason}"
            })

    def get_winner(self) -> Tuple[Optional[int], Dict]:
        """Determine the winner based on test results."""
        winner = None
        max_passed = -1

        for impl_name, results in self.results.items():
            if impl_name == "original_code":
                continue  # Skip original code when determining winner

            if results["passed"] > max_passed:
                max_passed = results["passed"]
                winner = impl_name
            # Break ties by looking at failure count
            elif results["passed"] == max_passed and winner is not None:
                if results["failed"] < self.results[winner]["failed"]:
                    winner = impl_name

        # Convert winner to numeric index if possible
        winner_index = -1
        if winner and re.match(r'modified_code\d+', winner):
            try:
                winner_index = int(re.search(r'(\d+)', winner).group(1))
            except (AttributeError, ValueError):
                pass

        return winner_index, self.results

    def save_results(self, filename: str = "test_results.json") -> None:
        """Save test results to a JSON file."""
        import json

        winner_index, results = self.get_winner()

        # Check if all tests were skipped
        all_skipped = all(
            stats["skipped"] == stats["passed"] + stats["failed"] + stats["skipped"]
            for impl_name, stats in results.items()
            if impl_name != "original_code"
        )

        output = {
            "winner": winner_index,
            "all_skipped": all_skipped,
            "results": {
                name: {
                    "passed": stats["passed"],
                    "failed": stats["failed"],
                    "skipped": stats["skipped"],
                    "total": stats["passed"] + stats["failed"] + stats["skipped"]
                }
                for name, stats in results.items()
                if not name.startswith("_")  # Skip internal items
            }
        }

        with open(filename, "w") as f:
            json.dump(output, f, indent=2)

        print(f"Test results saved to {filename}")

        return output


class Graph:
    def __init__(self):
        self.adjacency_list = {}

    def add_vertex(self, vertex):
        if vertex not in self.adjacency_list:
            self.adjacency_list[vertex] = []

    def add_edge(self, vertex1, vertex2):
        if vertex1 in simport unittest

        class TestGraph(unittest.TestCase):

            def setUp(self):
                self.graph = Graph()

            def test_add_vertex(self):
                self.graph.add_vertex('A')
                self.assertEqual(self.graph.adjacency_list, {'A': []})
                self.graph.add_vertex('B')
                self.assertEqual(self.graph.adjacency_list, {'A': [], 'B': []})
                # Adding a duplicate vertex should not modify the graph
                self.graph.add_vertex('A')
                self.assertEqual(self.graph.adjacency_list, {'A': [], 'B': []})

            def test_add_edge(self):
                self.graph.add_vertex('A')
                self.graph.add_vertex('B')
                self.graph.add_edge('A', 'B')
                self.assertEqual(self.graph.adjacency_list, {'A': ['B'], 'B': ['A']})
                # Adding an edge with non-existent vertices should not modify the graph
                self.graph.add_edge('A', 'C')
                self.assertEqual(self.graph.adjacency_list, {'A': ['B'], 'B': ['A']})
                self.graph.add_edge('D','E')
                self.assertEqual(self.graph.adjacency_list, {'A': ['B'], 'B': ['A']})

            def test_remove_vertex(self):
                self.graph.add_vertex('A')
                self.graph.add_vertex('B')
                self.graph.add_edge('A','B')
                self.graph.remove_vertex('A')
                self.assertEqual(self.graph.adjacency_list, {'B': []})
                #removing a non-existent vertex shouldn't modify the graph
                self.graph.remove_vertex('C')
                self.assertEqual(self.graph.adjacency_list, {'B': []})

            def test_remove_edge(self):
                self.graph.add_vertex('A')
                self.graph.add_vertex('B')
                self.graph.add_edge('A','B')
                self.graph.remove_edge('A','B')
                self.assertEqual(self.graph.adjacency_list, {'A': [], 'B': []})
                # Removing a non-existent edge should not do anything
                self.graph.remove_edge('A','C')
                self.assertEqual(self.graph.adjacency_list, {'A': [], 'B': []})

            def test_dfs(self):
                self.graph.add_vertex('A')
                self.graph.add_vertex('B')
                self.graph.add_vertex('C')
                self.graph.add_edge('A', 'B')
                self.graph.add_edge('A', 'C')
                self.graph.add_edge('B','C')

                # Redirect stdout to capture the print output
                import io
                from contextlib import redirect_stdout

                f = io.StringIO()
                with redirect_stdout(f):
                    self.graph.dfs('A')
                output = f.getvalue().strip()
                self.assertIn("A B C",output) #DFS order can vary slightly
                self.assertIn("A C B",output)

            def test_bfs(self):
                 self.graph.add_vertex('A')
                 self.graph.add_vertex('B')
                 self.graph.add_vertex('C')
                 self.graph.add_edge('A', 'B')
                 self.graph.add_edge('A', 'C')
                 self.graph.add_edge('B','C')
                 import io
                 from contextlib import redirect_stdout

                 f = io.StringIO()
                 with redirect_stdout(f):
                     self.graph.bfs('A')
                 output = f.getvalue().strip()
                 self.assertEqual(output,"A B C")

        if __name__ == '__main__':
            unittest.main()
        elf.adjacency_list and vertex2 in self.adjacency_list:
            self.adjacency_list[vertex1].append(vertex2)
            self.adjacency_list[vertex2].append(vertex1)

    def __str__(self):
        return str(self.adjacency_list)
        def remove_vertex(self, vertex):
            if vertex in self.adjacency_list:
                for neighbor in self.adjacency_list[vertex]:
                    self.adjacency_list[neighbor].remove(vertex)
                del self.adjacency_list[vertex]

        def remove_edge(self, vertex1, vertex2):
            if vertex1 in self.adjacency_list and vertex2 in self.adjacency_list:
                if vertex2 in self.adjacency_list[vertex1]:
                    self.adjacency_list[vertex1].remove(vertex2)
                if vertex1 in self.adjacency_list[vertex2]:
                    self.adjacency_list[vertex2].remove(vertex1)
    def dfs(self, start_vertex, visited=None):
        """
        Perform a depth-first search (DFS) starting from the given vertex.
        Args:
            start_vertex: The starting vertex for the DFS.
            visited (set, optional): A set of already visited vertices. Defaults to None.
        Returns:
            None
        """
        if visited is None:
            visited = set()

        visited.add(start_vertex)
        print(start_vertex, end=' ')

        for neighbor in self.adjacency_list[start_vertex]:
            if neighbor not in visited:
                self.dfs(neighbor, visited)

    def bfs(self, start_vertex):
        visited = set()
        queue = [start_vertex]
        visited.add(start_vertex)

        while queue:
            vertex = queue.pop(0)
            print(vertex, end=' ')

            for neighbor in self.adjacency_list[vertex]:
                if neighbor not in visited:
                    visited.add(neighbor)
                    queue.append(neighbor)

# Example usage:
g = Graph()

g.add_vertex('B')
g.add_vertex('C')
g.add_edge('A', 'B')
g.add_edge('A', 'C')
g.add_edge('B', 'C')
print(g)
print("\nDFS starting from vertex 'A':")
g.dfs('A')
print("\nBFS starting from vertex 'A':")
g.bfs('A')

Instruction: remove

import pytest
import inspect
import io
from contextlib import redirect_stdout
import sys

def test_graph_class_exists(implementation):
    """Test that the Graph class exists in the implementation."""
    impl_name, module = implementation

    # Check if the module exposes Graph as a class or if the module itself
    # provides Graph-like functionality through its methods
    graph_exists = False

    if hasattr(module, 'Graph'):
        assert inspect.isclass(module.Graph), f"{impl_name}: Graph is not a class"
        graph_exists = True
    elif hasattr(module, 'adjacency_list'):
        # This is likely a module-level Graph-like object
        pytest.skip(f"{impl_name}: Module appears to be a Graph instance rather than containing a Graph class")
    else:
        for attr_name in dir(module):
            attr = getattr(module, attr_name)
            if inspect.isclass(attr) and hasattr(attr, 'adjacency_list'):
                # Found a class with adjacency_list that might be a Graph with different name
                graph_exists = True
                break

        if not graph_exists:
            assert False, f"{impl_name}: Graph class or equivalent not found"

def get_graph_class(module):
    """Helper function to find the Graph class or equivalent in a module."""
    if hasattr(module, 'Graph'):
        return module.Graph

    # Look for a class with adjacency_list that might be a Graph with different name
    for attr_name in dir(module):
        attr = getattr(module, attr_name)
        if inspect.isclass(attr) and hasattr(attr, 'adjacency_list'):
            return attr

    return None

def test_graph_has_required_methods(implementation):
    """Test that the Graph class has all required methods."""
    impl_name, module = implementation

    graph_class = get_graph_class(module)
    if graph_class is None:
        pytest.skip(f"{impl_name}: Could not find Graph class, skipping method check")

    required_methods = [
        'add_vertex', 'add_edge', 'remove_vertex', 'remove_edge', 'dfs', 'bfs'
    ]

    for method in required_methods:
        assert hasattr(graph_class, method), f"{impl_name}: Graph class is missing the '{method}' method"
        assert callable(getattr(graph_class, method)), f"{impl_name}: Graph.{method} is not callable"

def create_graph_instance(implementation):
    """Helper function to create a graph instance, handling different implementation structures."""
    impl_name, module = implementation

    graph_class = get_graph_class(module)
    if graph_class is None:
        pytest.skip(f"{impl_name}: Could not find Graph class, skipping test")

    return graph_class()

def test_remove_vertex_basic_functionality(implementation):
    """Test the basic functionality of remove_vertex method."""
    impl_name, module = implementation

    graph = create_graph_instance(implementation)

    # Setup
    graph.add_vertex('A')
    assert 'A' in graph.adjacency_list, f"{impl_name}: Failed to add vertex 'A'"

    # Test remove_vertex
    graph.remove_vertex('A')
    assert 'A' not in graph.adjacency_list, f"{impl_name}: Failed to remove vertex 'A'"

def test_remove_vertex_with_edges(implementation):
    """Test remove_vertex with connected edges."""
    impl_name, module = implementation

    graph = create_graph_instance(implementation)

    # Setup
    graph.add_vertex('A')
    graph.add_vertex('B')
    graph.add_vertex('C')
    graph.add_edge('A', 'B')
    graph.add_edge('A', 'C')

    # Test remove_vertex
    graph.remove_vertex('A')

    # Verify 'A' is removed and references to 'A' are removed from neighbors
    assert 'A' not in graph.adjacency_list, f"{impl_name}: Failed to remove vertex 'A'"
    assert 'A' not in graph.adjacency_list.get('B', []), f"{impl_name}: Reference to 'A' not removed from 'B'"
    assert 'A' not in graph.adjacency_list.get('C', []), f"{impl_name}: Reference to 'A' not removed from 'C'"

def test_remove_vertex_nonexistent(implementation):
    """Test remove_vertex with a nonexistent vertex."""
    impl_name, module = implementation

    graph = create_graph_instance(implementation)

    # Setup
    graph.add_vertex('A')
    graph.add_vertex('B')
    original_state = {k: list(v) for k, v in graph.adjacency_list.items()}

    # Test removing nonexistent vertex
    graph.remove_vertex('Z')

    # Verify graph state unchanged
    after_state = {k: list(v) for k, v in graph.adjacency_list.items()}
    assert original_state == after_state, f"{impl_name}: Graph modified when removing nonexistent vertex"

def test_remove_edge_basic_functionality(implementation):
    """Test the basic functionality of remove_edge method."""
    impl_name, module = implementation

    graph = create_graph_instance(implementation)

    # Setup
    graph.add_vertex('A')
    graph.add_vertex('B')
    graph.add_edge('A', 'B')

    # Test remove_edge
    graph.remove_edge('A', 'B')

    # Verify edge is removed from both vertices
    assert 'B' not in graph.adjacency_list['A'], f"{impl_name}: Edge not removed from vertex 'A'"
    assert 'A' not in graph.adjacency_list['B'], f"{impl_name}: Edge not removed from vertex 'B'"

def test_remove_edge_nonexistent(implementation):
    """Test remove_edge with a nonexistent edge."""
    impl_name, module = implementation

    graph = create_graph_instance(implementation)

    # Setup
    graph.add_vertex('A')
    graph.add_vertex('B')
    graph.add_vertex('C')
    graph.add_edge('A', 'B')

    # Test removing nonexistent edge
    graph.remove_edge('A', 'C')

    # Verify graph state maintained for existing edges
    assert 'B' in graph.adjacency_list['A'], f"{impl_name}: Existing edge 'A'-'B' affected"
    assert 'A' in graph.adjacency_list['B'], f"{impl_name}: Existing edge 'B'-'A' affected"

    # Test with nonexistent vertices
    graph.remove_edge('X', 'Y')
    # Should not raise any exceptions

def test_removes_directed_link(implementation):
    """Test remove_edge correctly handles one-way links if they somehow exist."""
    impl_name, module = implementation

    graph = create_graph_instance(implementation)

    # Setup - create a situation where A links to B but B doesn't link to A
    graph.add_vertex('A')
    graph.add_vertex('B')

    # Manually add one-way link
    try:
        graph.adjacency_list['A'].append('B')

        # Test remove_edge
        graph.remove_edge('A', 'B')

        # Verify edge is removed correctly
        assert 'B' not in graph.adjacency_list['A'], f"{impl_name}: One-way edge not removed correctly"
    except Exception as e:
        pytest.skip(f"{impl_name}: Cannot test directed links - {str(e)}")

def create_test_graph(graph):
    """Helper function to create a graph for testing traversal algorithms."""
    graph.add_vertex('A')
    graph.add_vertex('B')
    graph.add_vertex('D')
    graph.add_vertex('E')
    graph.add_edge('A', 'B')
    graph.add_edge('B', 'D')
    graph.add_edge('D', 'E')
    graph.add_edge('E', 'A')  # Create a cycle
    return graph

def test_integration_with_dfs(implementation):
    """Test that dfs works correctly after vertex and edge removal."""
    impl_name, module = implementation

    graph = create_graph_instance(implementation)

    # Create a graph with vertices that won't be directly connected after removal
    graph.add_vertex('A')
    graph.add_vertex('B')
    graph.add_vertex('C')
    graph.add_vertex('D')
    graph.add_edge('A', 'B')
    graph.add_edge('B', 'C')
    graph.add_edge('C', 'D')

    # No direct connection from A to D - must go through B and C

    # Remove the middle vertex, breaking the path
    graph.remove_vertex('C')

    try:
        # Capture DFS output
        f = io.StringIO()
        with redirect_stdout(f):
            graph.dfs('A')
        output = f.getvalue().strip()

        # Verify DFS behavior reflects the removal
        assert 'C' not in output, f"{impl_name}: Removed vertex 'C' still appears in DFS"
        assert 'A' in output and 'B' in output, f"{impl_name}: DFS missing expected vertices"
        # D should not be reachable from A after removing C
        assert 'D' not in output, f"{impl_name}: DFS includes vertex 'D' which should be unreachable"
    except (KeyError, AttributeError) as e:
        pytest.skip(f"{impl_name}: Implementation doesn't handle traversal after removal - {str(e)}")

def test_integration_with_bfs(implementation):
    """Test that bfs works correctly after vertex and edge removal."""
    impl_name, module = implementation

    graph = create_graph_instance(implementation)

    try:
        # Setup a graph with multiple paths
        graph.add_vertex('A')
        graph.add_vertex('B')
        graph.add_vertex('C')
        graph.add_vertex('D')
        graph.add_edge('A', 'B')
        graph.add_edge('A', 'C')
        graph.add_edge('B', 'D')
        graph.add_edge('C', 'D')

        # Remove an edge
        graph.remove_edge('C', 'D')

        # Capture BFS output
        f = io.StringIO()
        with redirect_stdout(f):
            graph.bfs('A')
        output = f.getvalue().strip()

        # BFS from A should still visit all vertices through the remaining path
        assert all(v in output for v in ['A', 'B', 'C', 'D']), f"{impl_name}: BFS missing expected vertices after edge removal"

        # Now remove a vertex that disrupts the remaining path
        graph.remove_vertex('B')

        f = io.StringIO()
        with redirect_stdout(f):
            graph.bfs('A')
        output = f.getvalue().strip()

        # Verify BFS behavior reflects the removals
        assert 'B' not in output, f"{impl_name}: Removed vertex 'B' still appears in BFS"
        assert 'D' not in output, f"{impl_name}: BFS includes vertex 'D' which should be unreachable"
    except (KeyError, AttributeError) as e:
        pytest.skip(f"{impl_name}: Implementation doesn't handle traversal after removal - {str(e)}")

def test_incorrect_indentation_fixed(implementation):
    """Test that the indentation issue in the original code has been fixed."""
    impl_name, module = implementation

    graph_class = get_graph_class(module)
    if graph_class is None:
        pytest.skip(f"{impl_name}: Could not find Graph class, skipping indentation check")

    # The original code had improper indentation for remove_vertex and remove_edge
    # This test checks if these methods are now correctly accessible
    try:
        graph = graph_class()

        # These methods should now be directly accessible without errors
        graph.add_vertex('A')
        graph.add_vertex('B')
        graph.add_edge('A', 'B')

        # These should not raise AttributeError if properly fixed
        graph.remove_vertex('A')
        graph.add_vertex('A')
        graph.add_vertex('B')
        graph.add_edge('A', 'B')
        graph.remove_edge('A', 'B')

        # If we got here, the methods were accessible
        assert True
    except AttributeError as e:
        assert False, f"{impl_name}: Method access error indicates indentation issue still exists - {str(e)}"

def test_add_vertex_missing_in_example(implementation):
    """Test that the example code properly adds vertex 'A' which was missing."""
    impl_name, module = implementation

    # Setup - create a new graph
    graph = create_graph_instance(implementation)

    # Add vertices including 'A' which was missing in the original example
    graph.add_vertex('A')
    graph.add_vertex('B')
    graph.add_vertex('C')

    # Create edges that include 'A'
    graph.add_edge('A', 'B')
    graph.add_edge('A', 'C')

    # Verify 'A' exists and has the correct connections
    assert 'A' in graph.adjacency_list, f"{impl_name}: Vertex 'A' not properly added"

    # Convert to set for order-independent comparison
    a_connections = set(graph.adjacency_list['A'])
    assert a_connections == {'B', 'C'}, f"{impl_name}: Vertex 'A' does not have correct connections"

import pytest
import os
import sys
import json
from typing import Dict, List, Optional, Any

# Import from local test_utils.py in the same directory
from test_utils import TestUtils, TestResultsManager

# Load all implementations in the current sandbox
implementations = TestUtils.load_all_implementations()
test_results = TestResultsManager()

@pytest.fixture(scope="session")
def sandbox_dir():
    """Fixture to provide the sandbox directory path."""
    return os.path.dirname(os.path.abspath(__file__))

@pytest.fixture(scope="session")
def sandbox_name():
    """Fixture to provide the sandbox name."""
    return os.path.basename(os.path.dirname(os.path.abspath(__file__)))

@pytest.fixture(scope="session")
def all_implementations():
    """Fixture to provide all implementations as a dictionary."""
    return implementations

@pytest.fixture(params=list(implementations.items()))
def implementation(request):
    """Fixture to provide each implementation to tests one at a time."""
    return request.param

@pytest.fixture(scope="session")
def results_manager():
    """Fixture to provide access to the test results manager."""
    return test_results

# Hook for collecting test results
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    """Pytest hook to collect test results."""
    # Execute all other hooks to obtain the report object
    outcome = yield
    rep = outcome.get_result()

    # We're only interested in the call outcome
    if rep.when == "call":
        if hasattr(item, "callspec") and "implementation" in item.callspec.params:
            # Get implementation name and module
            impl_name, _ = item.callspec.params["implementation"]

            # Get test name
            test_name = item.nodeid.split("::")[-1]

            # Record result
            if rep.passed:
                test_results.record_result(impl_name, test_name, True)
            elif rep.failed:
                error_msg = str(rep.longrepr) if rep.longrepr else "Test failed"
                test_results.record_result(impl_name, test_name, False, error_msg)
            elif rep.skipped:
                skip_reason = rep.longrepr[2] if rep.longrepr else "Test skipped"
                test_results.record_skip(impl_name, test_name, skip_reason)

# Hook to save results at the end of testing
@pytest.hookimpl(trylast=True)
def pytest_sessionfinish(session, exitstatus):
    """Save test results at the end of the test session."""
    test_results.save_results()

import os
import sys
import glob
import re
import importlib.util
import traceback
import types
from typing import Dict, List, Optional, Any, Tuple

class TestUtils:
    @staticmethod
    def discover_implementation_files(directory: str = None) -> List[str]:
        """Find all implementation files in the current sandbox directory."""
        if directory is None:
            directory = os.path.dirname(os.path.abspath(__file__))

        patterns = [
            r'modified_code\d+\.py',
            r'new_code\d+\.py',
            # r'original_code\.py',
            r'implementation\d*\.py'
        ]

        pattern = re.compile('|'.join(f'({p})' for p in patterns))
        implementations = []

        for file_path in glob.glob(os.path.join(directory, '*.py')):
            if pattern.search(os.path.basename(file_path)):
                implementations.append(file_path)

        # Sort files numerically
        def sort_key(path):
            filename = os.path.basename(path)
            match = re.search(r'(\d+)', filename)
            return int(match.group(1)) if match else 0

        return sorted(implementations, key=sort_key)

    @staticmethod
    def create_mock_module(file_path: str, module_name: str, error_info: str) -> types.ModuleType:
        """Create a mock module that contains error information but can still be tested."""
        # Create a new module object
        mock_module = types.ModuleType(module_name)

        # Add basic attributes
        mock_module.__file__ = file_path
        mock_module.__name__ = module_name
        mock_module.__display_name__ = module_name
        mock_module.__error__ = error_info

        # Add a dummy function that can be detected by test functions
        def dummy_function(*args, **kwargs):
            return f"Error in module: {error_info}"

        setattr(mock_module, "implementation_error", dummy_function)

        return mock_module

    @staticmethod
    def load_module(file_path: str, module_name: Optional[str] = None) -> Any:
        """
        Safely load a module from a file path with proper error handling.
        If the module has errors, return a mock module that can still be tested.
        """
        if module_name is None:
            module_name = os.path.basename(file_path).replace('.py', '')

        # Create a unique module name to avoid conflicts
        sandbox_id = os.path.basename(os.path.dirname(file_path))
        unique_module_name = f"{sandbox_id}_{module_name}"

        try:
            # First, try to read the file to check for syntax errors
            with open(file_path, 'r') as f:
                source_code = f.read()

            # Check for syntax errors by compiling the code
            try:
                compiled = compile(source_code, file_path, 'exec')
            except SyntaxError as e:
                error_msg = f"Syntax error: {str(e)}"
                print(f"Syntax error in {file_path}: {e}")
                print(f"  Line {e.lineno}, column {e.offset}: {e.text}")
                return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

            # Create the module spec
            spec = importlib.util.spec_from_file_location(unique_module_name, file_path)
            if spec is None or spec.loader is None:
                error_msg = f"Could not create spec for {file_path}"
                print(f"Error: {error_msg}")
                return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

            # Create the module object
            module = importlib.util.module_from_spec(spec)
            sys.modules[unique_module_name] = module

            # Special handling for execution errors
            try:
                # Execute the module code in a safe way
                spec.loader.exec_module(module)
                # Store the original name for reference
                module.__display_name__ = module_name
                return module
            except Exception as e:
                error_msg = f"Runtime error: {str(e)}"
                traceback_str = traceback.format_exc()
                print(f"Error executing module {file_path}: {e}")
                print(traceback_str)

                # Create a partial module that contains what we loaded before the error
                mock_module = TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

                # Copy any attributes that might have been defined before the error
                for attr_name in dir(module):
                    if not attr_name.startswith('__'):
                        try:
                            setattr(mock_module, attr_name, getattr(module, attr_name))
                        except Exception:
                            pass  # Skip attributes that can't be copied

                return mock_module

        except FileNotFoundError as e:
            error_msg = f"File not found: {str(e)}"
            print(f"Error: {error_msg}")
            return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)
        except Exception as e:
            error_msg = f"Unexpected error: {str(e)}"
            print(f"Error loading module {file_path}: {e}")
            return TestUtils.create_mock_module(file_path, unique_module_name, error_msg)

    @classmethod
    def load_all_implementations(cls, directory: str = None) -> Dict[str, Any]:
        """Load all implementation files in the directory, including those with errors."""
        if directory is None:
            directory = os.path.dirname(os.path.abspath(__file__))

        implementations = {}

        implementation_files = cls.discover_implementation_files(directory)
        if not implementation_files:
            print("WARNING: No implementation files found. Check your file naming patterns.")

        for file_path in implementation_files:
            module_name = os.path.basename(file_path).replace('.py', '')
            module = cls.load_module(file_path, module_name)

            # Always add the module, even if it has errors
            implementations[module_name] = module

            if hasattr(module, '__error__'):
                print(f"Loaded with errors: {module_name} - {module.__error__}")
            else:
                print(f"Successfully loaded: {module_name}")

        return implementations

class TestResultsManager:
    def __init__(self):
        self.results = {}
        self.sandbox_name = os.path.basename(os.path.dirname(os.path.abspath(__file__)))

    def record_result(self, impl_name: str, test_name: str, passed: bool,
                     error_msg: Optional[str] = None) -> None:
        """Record a test result for an implementation."""
        if impl_name not in self.results:
            self.results[impl_name] = {"passed": 0, "failed": 0, "skipped": 0, "errors": []}

        if passed:
            self.results[impl_name]["passed"] += 1
        else:
            self.results[impl_name]["failed"] += 1
            if error_msg:
                self.results[impl_name]["errors"].append({
                    "test": test_name,
                    "error": error_msg
                })

    def record_skip(self, impl_name: str, test_name: str,
                   reason: Optional[str] = None) -> None:
        """Record a skipped test for an implementation."""
        if impl_name not in self.results:
            self.results[impl_name] = {"passed": 0, "failed": 0, "skipped": 0, "errors": []}

        self.results[impl_name]["skipped"] += 1
        if reason:
            self.results[impl_name]["errors"].append({
                "test": test_name,
                "error": f"SKIPPED: {reason}"
            })

    def get_winner(self) -> Tuple[Optional[int], Dict]:
        """Determine the winner based on test results."""
        winner = None
        max_passed = -1

        for impl_name, results in self.results.items():
            if impl_name == "original_code":
                continue  # Skip original code when determining winner

            if results["passed"] > max_passed:
                max_passed = results["passed"]
                winner = impl_name
            # Break ties by looking at failure count
            elif results["passed"] == max_passed and winner is not None:
                if results["failed"] < self.results[winner]["failed"]:
                    winner = impl_name

        # Convert winner to numeric index if possible
        winner_index = -1
        if winner and re.match(r'modified_code\d+', winner):
            try:
                winner_index = int(re.search(r'(\d+)', winner).group(1))
            except (AttributeError, ValueError):
                pass

        return winner_index, self.results

    def save_results(self, filename: str = "test_results.json") -> None:
        """Save test results to a JSON file."""
        import json

        winner_index, results = self.get_winner()

        # Check if all tests were skipped
        all_skipped = all(
            stats["skipped"] == stats["passed"] + stats["failed"] + stats["skipped"]
            for impl_name, stats in results.items()
            if impl_name != "original_code"
        )

        output = {
            "winner": winner_index,
            "all_skipped": all_skipped,
            "results": {
                name: {
                    "passed": stats["passed"],
                    "failed": stats["failed"],
                    "skipped": stats["skipped"],
                    "total": stats["passed"] + stats["failed"] + stats["skipped"]
                }
                for name, stats in results.items()
                if not name.startswith("_")  # Skip internal items
            }
        }

        with open(filename, "w") as f:
            json.dump(output, f, indent=2)

        print(f"Test results saved to {filename}")

        return output