Files
wehub-resource-sync 4e0f4422d0
build-docs / deploy (push) Waiting to run
Check Markdown links / markdown-link-check (push) Waiting to run
Pytest / test (3.11) (push) Waiting to run
Pytest / test (3.12) (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:25:42 +08:00

130 lines
5.1 KiB
Python

import os
import threading
from concurrent.futures import ThreadPoolExecutor
from unittest.mock import MagicMock
import pytest
from sweagent.agent.problem_statement import GithubIssue, TextProblemStatement
from sweagent.run.hooks.apply_patch import SaveApplyPatchHook
from sweagent.run.hooks.open_pr import OpenPRConfig, OpenPRHook
from sweagent.types import AgentRunResult
@pytest.fixture
def open_pr_hook_init_for_sop():
hook = OpenPRHook(config=OpenPRConfig(skip_if_commits_reference_issue=True))
hook._token = os.environ.get("GITHUB_TOKEN", "")
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/1")
return hook
@pytest.fixture
def agent_run_result():
return AgentRunResult(
info={
"submission": "asdf",
"exit_status": "submitted",
},
trajectory=[],
)
def test_should_open_pr_fail_submission(open_pr_hook_init_for_sop, agent_run_result):
hook = open_pr_hook_init_for_sop
agent_run_result.info["submission"] = None
assert not hook.should_open_pr(agent_run_result)
def test_should_open_pr_fail_exit(open_pr_hook_init_for_sop, agent_run_result):
hook = open_pr_hook_init_for_sop
agent_run_result.info["exit_status"] = "fail"
assert not hook.should_open_pr(agent_run_result)
def test_should_open_pr_fail_invalid_url(open_pr_hook_init_for_sop, agent_run_result):
hook = open_pr_hook_init_for_sop
agent_run_result.info["data_path"] = "asdf"
assert not hook.should_open_pr(agent_run_result)
def test_should_open_pr_fail_closed(open_pr_hook_init_for_sop, agent_run_result):
hook = open_pr_hook_init_for_sop
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/16")
assert not hook.should_open_pr(agent_run_result)
def test_should_open_pr_fail_assigned(open_pr_hook_init_for_sop, agent_run_result):
hook = open_pr_hook_init_for_sop
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/17")
assert not hook.should_open_pr(agent_run_result)
def test_should_open_pr_fail_locked(open_pr_hook_init_for_sop, agent_run_result):
hook = open_pr_hook_init_for_sop
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/18")
assert not hook.should_open_pr(agent_run_result)
def test_should_open_pr_fail_has_pr(open_pr_hook_init_for_sop, agent_run_result):
hook = open_pr_hook_init_for_sop
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/19")
assert not hook.should_open_pr(agent_run_result)
def test_should_open_pr_success_has_pr_override(open_pr_hook_init_for_sop, agent_run_result):
hook = open_pr_hook_init_for_sop
hook._problem_statement = GithubIssue(github_url="https://github.com/swe-agent/test-repo/issues/19")
hook._config.skip_if_commits_reference_issue = False
assert hook.should_open_pr(agent_run_result)
def test_save_apply_patch_hook_concurrent_workers_save_to_correct_dirs(tmp_path):
"""Regression test for #1284: concurrent workers must not overwrite each
other's per-instance state (_problem_statement, _env) in SaveApplyPatchHook.
Before the fix, a single shared hook instance stored _problem_statement as a
plain instance attribute. When two workers both called on_instance_start()
before either finished, the second write overwrote the first, causing both
workers to save their patch into the same (wrong) output directory.
The fix uses threading.local() so every worker thread sees its own copy.
"""
hook = SaveApplyPatchHook(show_success_message=False)
hook._output_dir = tmp_path
# Barrier ensures both workers have called on_instance_start before either
# proceeds to on_instance_completed, making the race window deterministic.
barrier = threading.Barrier(2)
def worker(instance_id: str, patch_content: str) -> None:
ps = TextProblemStatement(text=f"Issue for {instance_id}", id=instance_id)
env = MagicMock()
env.repo = None # skip apply_patch_locally path
hook.on_instance_start(index=0, env=env, problem_statement=ps)
# Hold here until both threads have written their problem_statement so
# that the race window is guaranteed to be open.
barrier.wait()
result = AgentRunResult(
info={"submission": patch_content, "exit_status": "submitted"},
trajectory=[],
)
hook.on_instance_completed(result=result)
with ThreadPoolExecutor(max_workers=2) as pool:
fa = pool.submit(worker, "instance-A", "patch A content")
fb = pool.submit(worker, "instance-B", "patch B content")
fa.result()
fb.result()
patch_a = tmp_path / "instance-A" / "instance-A.patch"
patch_b = tmp_path / "instance-B" / "instance-B.patch"
assert patch_a.exists(), "Patch for instance-A was not saved to its own directory"
assert patch_b.exists(), "Patch for instance-B was not saved to its own directory"
assert patch_a.read_text() == "patch A content"
assert patch_b.read_text() == "patch B content"