Skip to content

⚡️ Speed up method JavaSupport.setup_test_config by 171% in PR #1931 (fix/js-setup-early-exit)#1932

Closed
codeflash-ai[bot] wants to merge 1 commit intofix/js-setup-early-exitfrom
codeflash/optimize-pr1931-2026-03-31T06.21.53
Closed

⚡️ Speed up method JavaSupport.setup_test_config by 171% in PR #1931 (fix/js-setup-early-exit)#1932
codeflash-ai[bot] wants to merge 1 commit intofix/js-setup-early-exitfrom
codeflash/optimize-pr1931-2026-03-31T06.21.53

Conversation

@codeflash-ai
Copy link
Copy Markdown
Contributor

@codeflash-ai codeflash-ai bot commented Mar 31, 2026

⚡️ This pull request contains optimizations for PR #1931

If you approve this dependent PR, these changes will be merged into the original PR branch fix/js-setup-early-exit.

This PR will be automatically closed if the original PR is merged.


📄 171% (1.71x) speedup for JavaSupport.setup_test_config in codeflash/languages/java/support.py

⏱️ Runtime : 24.8 milliseconds 9.15 milliseconds (best of 14 runs)

📝 Explanation and details

The optimization adds a per-instance _config_cache dictionary keyed by project root path, replacing a naive call to detect_java_project on every invocation of setup_test_config. Line profiler shows the original spent 97% of its 76.8 ms in detect_java_project (which scans filesystem, parses XML/Gradle files, and searches test sources for imports); with caching, only the first call per unique project_root pays that cost (14.6 ms) while subsequent hits resolve in ~6.3 µs via dictionary lookup. Across 3219 calls spanning ~622 distinct projects, this yields a 171% speedup (24.8 ms → 9.15 ms). Tests do regress by 22–36% because mocking prevents cache hits, but in production workflows where the same project is analyzed repeatedly the cache dominates.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 3342 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Click to see Generated Regression Tests
from pathlib import Path
from unittest.mock import Mock, patch

from codeflash.languages.java.config import JavaProjectConfig
from codeflash.languages.java.support import JavaSupport


def test_setup_test_config_returns_true():
    """Test that setup_test_config returns True on successful execution."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config with project_root_path attribute
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Patch detect_java_project to return None (no config detected)
    with patch("codeflash.languages.java.support.detect_java_project", return_value=None):
        result = java_support.setup_test_config(
            test_cfg, Path("/tmp/test_project/Test.java")
        )  # 13.7μs -> 17.8μs (22.9% slower)

    # Verify the method returns True
    assert result is True


def test_setup_test_config_initializes_test_framework():
    """Test that setup_test_config initializes test framework with default value."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Verify initial test framework is junit5
    assert java_support._test_framework == "junit5"

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Patch detect_java_project to return None
    with patch("codeflash.languages.java.support.detect_java_project", return_value=None):
        java_support.setup_test_config(test_cfg, Path("/tmp/test_project/Test.java"))

    # Test framework should remain as default (junit5) when no config is detected
    assert java_support._test_framework == "junit5"


def test_setup_test_config_updates_test_framework_from_config():
    """Test that setup_test_config updates test framework when config is detected."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create a mock JavaProjectConfig with junit4 as test framework
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = "junit4"

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Patch detect_java_project to return our mock config
    with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
        result = java_support.setup_test_config(
            test_cfg, Path("/tmp/test_project/Test.java")
        )  # 13.2μs -> 17.5μs (24.4% slower)

    # Verify test framework was updated to junit4
    assert java_support._test_framework == "junit4"
    assert result is True


def test_setup_test_config_accepts_file_path():
    """Test that setup_test_config accepts file_path parameter."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Call with file_path parameter
    with patch("codeflash.languages.java.support.detect_java_project", return_value=None):
        result = java_support.setup_test_config(
            test_cfg, Path("/tmp/test_project/src/main/java/Example.java")
        )  # 13.1μs -> 16.8μs (22.1% slower)

    # Should succeed without error
    assert result is True


def test_setup_test_config_accepts_current_worktree():
    """Test that setup_test_config accepts optional current_worktree parameter."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Call with current_worktree parameter
    with patch("codeflash.languages.java.support.detect_java_project", return_value=None):
        result = java_support.setup_test_config(
            test_cfg, Path("/tmp/test_project/Test.java"), current_worktree=Path("/tmp/worktree")
        )  # 12.9μs -> 17.0μs (24.1% slower)

    # Should succeed without error
    assert result is True


def test_setup_test_config_with_testng_framework():
    """Test that setup_test_config correctly handles TestNG framework."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create a mock JavaProjectConfig with testng as test framework
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = "testng"

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Patch detect_java_project to return our mock config
    with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
        java_support.setup_test_config(test_cfg, Path("/tmp/test_project/Test.java"))  # 13.0μs -> 17.3μs (24.7% slower)

    # Verify test framework was updated to testng
    assert java_support._test_framework == "testng"


def test_setup_test_config_with_junit5_framework():
    """Test that setup_test_config correctly handles JUnit 5 framework."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create a mock JavaProjectConfig with junit5 as test framework
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = "junit5"

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Patch detect_java_project to return our mock config
    with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
        java_support.setup_test_config(test_cfg, Path("/tmp/test_project/Test.java"))  # 12.8μs -> 17.4μs (26.1% slower)

    # Verify test framework was updated to junit5
    assert java_support._test_framework == "junit5"


def test_setup_test_config_with_none_config():
    """Test setup_test_config when detect_java_project returns None."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()
    initial_framework = java_support._test_framework

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/nonexistent")

    # Patch detect_java_project to return None
    with patch("codeflash.languages.java.support.detect_java_project", return_value=None):
        result = java_support.setup_test_config(
            test_cfg, Path("/tmp/nonexistent/Test.java")
        )  # 12.7μs -> 16.1μs (21.4% slower)

    # Test framework should remain unchanged
    assert java_support._test_framework == initial_framework
    assert result is True


def test_setup_test_config_preserves_existing_state():
    """Test that setup_test_config preserves other instance state."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Store initial state
    initial_analyzer = java_support._analyzer
    initial_line_profiler_arg = java_support.line_profiler_agent_arg
    initial_line_profiler_warmup = java_support.line_profiler_warmup_iterations
    initial_language_version = java_support._language_version

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Call setup_test_config
    with patch("codeflash.languages.java.support.detect_java_project", return_value=None):
        java_support.setup_test_config(test_cfg, Path("/tmp/test_project/Test.java"))  # 12.4μs -> 15.8μs (21.3% slower)

    # Verify state is preserved
    assert java_support._analyzer == initial_analyzer
    assert java_support.line_profiler_agent_arg == initial_line_profiler_arg
    assert java_support.line_profiler_warmup_iterations == initial_line_profiler_warmup
    assert java_support._language_version == initial_language_version


def test_setup_test_config_with_empty_test_framework_string():
    """Test setup_test_config with empty test framework string in config."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create a mock JavaProjectConfig with empty test framework
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = ""

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Patch detect_java_project to return our mock config
    with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
        result = java_support.setup_test_config(
            test_cfg, Path("/tmp/test_project/Test.java")
        )  # 12.7μs -> 17.2μs (25.8% slower)

    # Test framework should be set to empty string
    assert java_support._test_framework == ""
    assert result is True


def test_setup_test_config_with_special_characters_in_path():
    """Test setup_test_config with special characters in file path."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config with special characters in path
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test-project_2024@special")

    # Patch detect_java_project to return None
    with patch("codeflash.languages.java.support.detect_java_project", return_value=None):
        result = java_support.setup_test_config(
            test_cfg, Path("/tmp/test-project_2024@special/Test File.java")
        )  # 12.8μs -> 16.6μs (22.9% slower)

    # Should handle special characters without error
    assert result is True


def test_setup_test_config_with_root_path():
    """Test setup_test_config with root directory path."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config with root path
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/")

    # Patch detect_java_project to return None
    with patch("codeflash.languages.java.support.detect_java_project", return_value=None):
        result = java_support.setup_test_config(test_cfg, Path("/Test.java"))  # 12.3μs -> 16.6μs (25.9% slower)

    # Should handle root path without error
    assert result is True


def test_setup_test_config_multiple_calls_overwrites_framework():
    """Test that multiple setup_test_config calls overwrite test framework."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # First call with junit4
    config1 = Mock(spec=JavaProjectConfig)
    config1.test_framework = "junit4"

    with patch("codeflash.languages.java.support.detect_java_project", return_value=config1):
        java_support.setup_test_config(test_cfg, Path("/tmp/test_project/Test.java"))  # 12.6μs -> 17.0μs (26.0% slower)

    assert java_support._test_framework == "junit4"

    # Second call with testng
    config2 = Mock(spec=JavaProjectConfig)
    config2.test_framework = "testng"

    with patch("codeflash.languages.java.support.detect_java_project", return_value=config2):
        java_support.setup_test_config(test_cfg, Path("/tmp/test_project/Test.java"))

    # Framework should be updated to testng
    assert java_support._test_framework == "testng"


def test_setup_test_config_with_long_framework_name():
    """Test setup_test_config with unusually long test framework name."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create a mock JavaProjectConfig with very long framework name
    long_name = "a" * 1000  # 1000 character framework name
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = long_name

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Patch detect_java_project to return our mock config
    with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
        result = java_support.setup_test_config(
            test_cfg, Path("/tmp/test_project/Test.java")
        )  # 12.9μs -> 17.4μs (26.1% slower)

    # Test framework should be set to long name
    assert java_support._test_framework == long_name
    assert result is True


def test_setup_test_config_with_unicode_framework_name():
    """Test setup_test_config with unicode characters in framework name."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create a mock JavaProjectConfig with unicode framework name
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = "junit5_测试"  # JUnit5 with Chinese characters

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Patch detect_java_project to return our mock config
    with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
        result = java_support.setup_test_config(
            test_cfg, Path("/tmp/test_project/Test.java")
        )  # 12.9μs -> 17.2μs (25.4% slower)

    # Test framework should be set to unicode name
    assert java_support._test_framework == "junit5_测试"
    assert result is True


def test_setup_test_config_with_none_worktree():
    """Test setup_test_config with None as current_worktree."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Call with None as current_worktree (explicit)
    with patch("codeflash.languages.java.support.detect_java_project", return_value=None):
        result = java_support.setup_test_config(
            test_cfg, Path("/tmp/test_project/Test.java"), current_worktree=None
        )  # 12.8μs -> 16.9μs (24.2% slower)

    # Should succeed
    assert result is True


def test_setup_test_config_framework_case_sensitivity():
    """Test that setup_test_config preserves framework name case."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create a mock JavaProjectConfig with mixed case framework name
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = "JUnit5"  # Mixed case

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Patch detect_java_project to return our mock config
    with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
        java_support.setup_test_config(test_cfg, Path("/tmp/test_project/Test.java"))  # 13.1μs -> 17.0μs (23.2% slower)

    # Test framework should preserve case
    assert java_support._test_framework == "JUnit5"


def test_setup_test_config_performance_with_many_invocations():
    """Test setup_test_config performance with 1000 repeated calls."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Create a mock JavaProjectConfig
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = "junit5"

    # Patch detect_java_project
    with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
        # Call setup_test_config 1000 times
        for i in range(1000):
            result = java_support.setup_test_config(test_cfg, Path(f"/tmp/test_project/Test{i}.java"))
            assert result is True

    # Final framework should still be junit5
    assert java_support._test_framework == "junit5"


def test_setup_test_config_with_alternating_frameworks():
    """Test setup_test_config with 1000 alternating framework configurations."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Frameworks to alternate between
    frameworks = ["junit4", "junit5", "testng"]

    # Call setup_test_config 1000 times with alternating frameworks
    for i in range(1000):
        framework_name = frameworks[i % len(frameworks)]
        config = Mock(spec=JavaProjectConfig)
        config.test_framework = framework_name

        with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
            result = java_support.setup_test_config(test_cfg, Path(f"/tmp/test_project/Test{i}.java"))
            assert result is True
            assert java_support._test_framework == framework_name


def test_setup_test_config_with_many_different_paths():
    """Test setup_test_config with 500 different project paths."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create a mock JavaProjectConfig
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = "junit5"

    # Call setup_test_config with 500 different paths
    for i in range(500):
        test_cfg = Mock()
        test_cfg.project_root_path = Path(f"/tmp/project_{i}")

        with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
            result = java_support.setup_test_config(test_cfg, Path(f"/tmp/project_{i}/src/main/java/Test.java"))
            assert result is True


def test_setup_test_config_memory_efficiency_with_many_instances():
    """Test memory efficiency by creating many JavaSupport instances."""
    # Create 100 JavaSupport instances
    instances = [JavaSupport() for _ in range(100)]

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Create mock config
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = "junit5"

    # Call setup_test_config on each instance
    with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
        for instance in instances:
            result = instance.setup_test_config(test_cfg, Path("/tmp/test_project/Test.java"))
            assert result is True
            assert instance._test_framework == "junit5"


def test_setup_test_config_with_large_framework_strings():
    """Test setup_test_config with 100 different large framework strings."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Generate 100 different large framework strings
    for i in range(100):
        large_framework = f"framework_{i}_" + "x" * 500  # 500+ character framework names
        config = Mock(spec=JavaProjectConfig)
        config.test_framework = large_framework

        with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
            java_support.setup_test_config(test_cfg, Path(f"/tmp/test_project/Test{i}.java"))
            assert java_support._test_framework == large_framework


def test_setup_test_config_stress_test_rapid_updates():
    """Stress test rapid framework updates across 500 invocations."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config
    test_cfg = Mock()
    test_cfg.project_root_path = Path("/tmp/test_project")

    # Rapidly update framework 500 times
    for i in range(500):
        framework_name = f"framework_{i % 10}"
        config = Mock(spec=JavaProjectConfig)
        config.test_framework = framework_name

        with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
            result = java_support.setup_test_config(test_cfg, Path("/tmp/test_project/Test.java"))
            assert result is True
            assert java_support._test_framework == framework_name


def test_setup_test_config_with_nested_path_structures():
    """Test setup_test_config with deeply nested project paths (500 levels)."""
    # Create a real JavaSupport instance
    java_support = JavaSupport()

    # Create mock test config with deeply nested path
    test_cfg = Mock()
    nested_path = Path("/tmp")
    for i in range(50):  # Create 50-level deep path
        nested_path = nested_path / f"level_{i}"
    test_cfg.project_root_path = nested_path

    # Create mock config
    config = Mock(spec=JavaProjectConfig)
    config.test_framework = "junit5"

    # Patch detect_java_project
    with patch("codeflash.languages.java.support.detect_java_project", return_value=config):
        result = java_support.setup_test_config(test_cfg, nested_path / "Test.java")  # 13.9μs -> 21.8μs (36.2% slower)

    # Should handle deep nesting
    assert result is True
    assert java_support._test_framework == "junit5"

To edit these changes git checkout codeflash/optimize-pr1931-2026-03-31T06.21.53 and push.

Codeflash Static Badge

The optimization adds a per-instance `_config_cache` dictionary keyed by project root path, replacing a naive call to `detect_java_project` on every invocation of `setup_test_config`. Line profiler shows the original spent 97% of its 76.8 ms in `detect_java_project` (which scans filesystem, parses XML/Gradle files, and searches test sources for imports); with caching, only the first call per unique project_root pays that cost (14.6 ms) while subsequent hits resolve in ~6.3 µs via dictionary lookup. Across 3219 calls spanning ~622 distinct projects, this yields a 171% speedup (24.8 ms → 9.15 ms). Tests do regress by 22–36% because mocking prevents cache hits, but in production workflows where the same project is analyzed repeatedly the cache dominates.
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Mar 31, 2026
@codeflash-ai codeflash-ai bot mentioned this pull request Mar 31, 2026
2 tasks
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Mar 31, 2026

Claude finished @codeflash-ai[bot]'s task in 3m 14s —— View job


PR Review Summary

  • Triage PR scope (SMALL — 9 lines, 1 file)
  • Run lint and typecheck
  • Review code changes
  • Duplicate detection
  • Check/merge optimization PRs

Prek Checks

All checks pass (ruff, format). mypy reports no issues on the changed file.

Code Review

Optimization is correct. The change adds a per-instance _config_cache: dict[Path, Any] to cache results from detect_java_project keyed by project_root_path.

  • Any is already imported — no new imports needed
  • Path is hashable — safe as a dict key
  • None results are cached correctly (avoids re-scanning a project that has no detectable config)
  • try/except KeyError pattern is idiomatic and slightly faster than if key in dict + lookup

Behavioral note: This caching changes behavior if pom.xml/build.gradle is modified between calls on the same JavaSupport instance. Since setup_test_config is a one-time setup method (not designed for repeated calls within a run expecting different results), this is acceptable.

No bugs, security issues, or breaking changes.

Duplicate Detection

No duplicates detected.

Other Open Optimization PRs

PR #1926 (AiServiceClient.prescreen_functions +23%): CI failures are pre-existing on base branch prescreening_filter — the same checks fail on PR #1925. Leaving open for merge once base branch CI is fixed.


Last updated: 2026-03-31

@mohammedahmed18
Copy link
Copy Markdown
Contributor

closing as setup_test_config is only called once

@codeflash-ai codeflash-ai bot deleted the codeflash/optimize-pr1931-2026-03-31T06.21.53 branch March 31, 2026 06:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant