mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 08:07:49 +00:00
test: retry first edit-path calls and wait for effects in flaky tests (#1271)
A provider call that starts a plugin instance (or reads files) can run past the edit-path soft deadline on a slow runner and returns nothing; the tests now ask again, as the editor does. The AI completion test cancels the slow request only once the server has it, and measures from the cancel. The SourcePreview test waits for the effect that marks the selection scope. Closes #1269
This commit is contained in:
@@ -609,8 +609,9 @@ fn providers_of_several_plugins_run_concurrently() {
|
||||
text: text.into(),
|
||||
};
|
||||
let at = ide_core::Pos::new(0, 0);
|
||||
// Activation is not what is measured.
|
||||
assert_eq!(host.provide_completion(&doc("fast"), at, None).len(), 2);
|
||||
// Activation is not what is measured: a first call that starts an instance
|
||||
// can run past the edit-path budget, and its answer is dropped.
|
||||
assert!(eventually(|| host.provide_completion(&doc("fast"), at, None).len() == 2));
|
||||
|
||||
let started = Instant::now();
|
||||
let items = host.provide_completion(&doc("sleep:500"), at, None);
|
||||
@@ -1676,22 +1677,21 @@ fn official_ai_completion_cancels_superseded_requests() {
|
||||
};
|
||||
let at = ide_core::Pos::new(1, 4);
|
||||
let host = Arc::new(f);
|
||||
// A slow request, superseded 200 ms later by the next keystroke's.
|
||||
// A slow request, superseded by the next keystroke's once the server has it
|
||||
// (starting the instance comes first, and takes longer on a slow runner).
|
||||
let first = ide_core::CancellationToken::new();
|
||||
let slow = {
|
||||
let (host, doc, first) = (host.clone(), doc.clone(), first.clone());
|
||||
std::thread::spawn(move || {
|
||||
let started = Instant::now();
|
||||
let r = host.host.provide_inline_completion(&doc, at, &first);
|
||||
(r, started.elapsed())
|
||||
})
|
||||
std::thread::spawn(move || (host.host.provide_inline_completion(&doc, at, &first), Instant::now()))
|
||||
};
|
||||
std::thread::sleep(Duration::from_millis(200));
|
||||
assert!(eventually(|| seen.lock().len() == 1), "the first request reaches the server");
|
||||
let cancelled = Instant::now();
|
||||
first.cancel();
|
||||
let started = Instant::now();
|
||||
let second = host.host.provide_inline_completion(&doc, at, &ide_core::CancellationToken::new());
|
||||
let took = started.elapsed();
|
||||
let (old, old_took) = slow.join().unwrap();
|
||||
let (old, old_done) = slow.join().unwrap();
|
||||
let old_took = old_done.saturating_duration_since(cancelled);
|
||||
assert!(old.is_none(), "the superseded request yields nothing");
|
||||
assert!(old_took < Duration::from_secs(1), "cancelled, not waited out: {old_took:?}");
|
||||
assert_eq!(second.map(|(_, s)| s.insert_text).as_deref(), Some("a + b"));
|
||||
@@ -1869,7 +1869,12 @@ fn official_mybatis_navigates_between_interface_and_xml() {
|
||||
assert_eq!((target.path, target.range.start), (xml_path.clone(), ide_core::Pos::new(1, 14)));
|
||||
|
||||
let xdoc = doc(&xml_path, "xml", xml);
|
||||
let to_java = f.host.provide_definition(&xdoc, ide_core::Pos::new(1, 16));
|
||||
// Reads the interface from disk: on a slow machine that too can run past the budget.
|
||||
let mut to_java = Vec::new();
|
||||
assert!(eventually(|| {
|
||||
to_java = f.host.provide_definition(&xdoc, ide_core::Pos::new(1, 16));
|
||||
!to_java.is_empty()
|
||||
}));
|
||||
assert_eq!(
|
||||
to_java.iter().map(|l| (l.path.clone(), l.range.start)).collect::<Vec<_>>(),
|
||||
[(java_path.clone(), ide_core::Pos::new(3, 9))]
|
||||
@@ -2437,7 +2442,12 @@ fn completion_quick_fixes_problems_and_event_kinds_of_api_1() {
|
||||
};
|
||||
let at = ide_core::Pos::new(0, 0);
|
||||
|
||||
let items = f.host.provide_completion(&doc("rich"), at, None);
|
||||
// The first call starts the instance, which may take longer than the edit-path budget.
|
||||
let mut items = Vec::new();
|
||||
assert!(eventually(|| {
|
||||
items = f.host.provide_completion(&doc("rich"), at, None);
|
||||
!items.is_empty()
|
||||
}));
|
||||
let item = &items[0].1;
|
||||
assert_eq!(
|
||||
(item.insert_text.as_str(), item.snippet, item.filter_text.as_deref()),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { SourcePreview } from "./SourcePreview";
|
||||
|
||||
/** A stable loader reference (a new function reloads, like the real module-level ones). */
|
||||
@@ -24,6 +24,7 @@ describe("SourcePreview", () => {
|
||||
const { container } = render(<SourcePreview path="/p/a.ts" line={0} load={loadOf("// TODO")} />);
|
||||
await screen.findByText("// TODO");
|
||||
// The scroll pane is a selection scope: drags stay inside, text stays copyable.
|
||||
expect(container.querySelector("[data-selection-scope]")).toBeTruthy();
|
||||
// Set by an effect, which can run after the text is on screen.
|
||||
await waitFor(() => expect(container.querySelector("[data-selection-scope]")).toBeTruthy());
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user