agent: Add a regression test to ensure title updates do not loop (#52395)

## Context

Follow up to #52388 
## How to Review

<!-- Help reviewers focus their attention:
- For small PRs: note what to focus on (e.g., "error handling in
foo.rs")
- For large PRs (>400 LOC): provide a guided tour — numbered list of
files/commits to read in order. (The `large-pr` label is applied
automatically.)
     - See the review process guidelines for comment conventions -->

## Self-Review Checklist

<!-- Check before requesting review: -->
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Release Notes:

- N/A
This commit is contained in:
Bennet Bo Fenner authored and GitHub committed 2026-03-25 09:16:42 +00:00
1 parent b0d596c5e5
commit fd4b46405e
1 file changed
+64
+64
View File
@@ -3002,6 +3002,70 @@ mod internal_tests {
});
}
#[gpui::test]
async fn test_rapid_title_changes_do_not_loop(cx: &mut TestAppContext) {
// Regression test: rapid title changes must not cause a propagation loop
// between Thread and AcpThread via handle_thread_title_updated.
init_test(cx);
let fs = FakeFs::new(cx.executor());
fs.insert_tree("/", json!({ "a": {} })).await;
let project = Project::test(fs.clone(), [], cx).await;
let thread_store = cx.new(|cx| ThreadStore::new(cx));
let agent = cx.update(|cx| {
NativeAgent::new(thread_store.clone(), Templates::new(), None, fs.clone(), cx)
});
let connection = Rc::new(NativeAgentConnection(agent.clone()));
let acp_thread = cx
.update(|cx| {
connection
.clone()
.new_session(project.clone(), PathList::new(&[Path::new("")]), cx)
})
.await
.unwrap();
let session_id = acp_thread.read_with(cx, |thread, _| thread.session_id().clone());
let thread = agent.read_with(cx, |agent, _| {
agent.sessions.get(&session_id).unwrap().thread.clone()
});
let title_updated_count = Rc::new(std::cell::RefCell::new(0usize));
cx.update(|cx| {
let count = title_updated_count.clone();
cx.subscribe(
&thread,
move |_entity: Entity<Thread>, _event: &TitleUpdated, _cx: &mut App| {
let new_count = {
let mut count = count.borrow_mut();
*count += 1;
*count
};
assert!(
new_count <= 2,
"TitleUpdated fired {new_count} times; \
title updates are looping"
);
},
)
.detach();
});
thread.update(cx, |thread, cx| thread.set_title("first".into(), cx));
thread.update(cx, |thread, cx| thread.set_title("second".into(), cx));
cx.run_until_parked();
thread.read_with(cx, |thread, _| {
assert_eq!(thread.title(), Some("second".into()));
});
acp_thread.read_with(cx, |acp_thread, _| {
assert_eq!(acp_thread.title(), Some("second".into()));
});
assert_eq!(*title_updated_count.borrow(), 2);
}
fn thread_entries(
thread_store: &Entity<ThreadStore>,
cx: &mut TestAppContext,