mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-11 02:31:38 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30821f4c98 | ||
|
|
a00f0c3dd6 |
No files matched your search
@@ -50,6 +50,7 @@ vi.mock("@/platform/javaLaunch", async (original) => ({
|
||||
vi.mock("./buildUpdate", () => ({ runUpdateBuild: mocks.runBuild }));
|
||||
vi.mock("./tomcat", () => ({ buildTomcatArtifact: mocks.build, syncTomcatOutputs: mocks.sync }));
|
||||
vi.mock("./store", () => ({
|
||||
rerunConfig: (s: Pick<RunSession, "config">) => mocks.state.configs.find((c) => c.id === s.config.id) ?? s.config,
|
||||
startRun: mocks.start,
|
||||
stopSession: mocks.stop,
|
||||
waitForExit: mocks.wait,
|
||||
@@ -105,6 +106,16 @@ describe("Tomcat application updates", () => {
|
||||
expect(mocks.wait.mock.invocationCallOrder[0]).toBeLessThan(mocks.action.mock.invocationCallOrder[0]!);
|
||||
expect(mocks.action).toHaveBeenCalledWith("run.debugConfig", session.config);
|
||||
});
|
||||
it("restarts with the configuration as edited since the server started", async () => {
|
||||
const edited = { ...session.config, name: "Tomcat (edited)" };
|
||||
mocks.state.configs = [edited];
|
||||
try {
|
||||
await updateTomcat(session, "restart");
|
||||
expect(mocks.action).toHaveBeenCalledWith("run.debugConfig", edited);
|
||||
} finally {
|
||||
mocks.state.configs = [];
|
||||
}
|
||||
});
|
||||
it("redeploys when the debugger cannot perform HotSwap", async () => {
|
||||
mocks.action.mockResolvedValue(false);
|
||||
await updateTomcat(session, "classes");
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { showContextMenu } from "@/ui";
|
||||
import { buildTomcatArtifact, syncTomcatOutputs } from "./tomcat";
|
||||
import { runUpdateBuild } from "./buildUpdate";
|
||||
import { startRun, stopSession, useRun, waitForExit, type RunSession } from "./store";
|
||||
import { rerunConfig, startRun, stopSession, useRun, waitForExit, type RunSession } from "./store";
|
||||
import { isUpdatable, updateAllowed, type UpdateMode } from "./updatePolicies";
|
||||
|
||||
export type UpdateSession = Pick<RunSession, "id" | "config"> & { debug?: DebugServiceSession };
|
||||
@@ -72,14 +72,15 @@ export async function updateApplication(session: UpdateSession, mode: string): P
|
||||
const debugging =
|
||||
!!session.debug || useDebugRunSessions.getState().sessions.some((s) => s.configId === session.config.id);
|
||||
if (mode === "restart") {
|
||||
const config = rerunConfig(session);
|
||||
if (session.debug) {
|
||||
await session.debug.stop();
|
||||
await executeAction("run.debugConfig", session.config);
|
||||
await executeAction("run.debugConfig", config);
|
||||
} else if (debugging) {
|
||||
await stopSession(session.id);
|
||||
await waitForExit([session.id]);
|
||||
await executeAction("run.debugConfig", session.config);
|
||||
} else await startRun(session.config, session.id);
|
||||
await executeAction("run.debugConfig", config);
|
||||
} else await startRun(config, session.id);
|
||||
return;
|
||||
}
|
||||
await maybeEditorService()?.saveAll();
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
} from "./configs";
|
||||
import { contextConfig, fileRole } from "./context";
|
||||
import { envFromRows } from "./EnvDialog";
|
||||
import { defaultSelection } from "./store";
|
||||
import { configRemoved, defaultSelection, rerunConfig, useRun, type RunSession } from "./store";
|
||||
|
||||
const cfg = (over: Partial<RunConfig> & { id: string }): RunConfig => ({
|
||||
name: over.id,
|
||||
@@ -344,3 +344,38 @@ describe("context configuration", () => {
|
||||
expect(contextConfig("tools/gen.py", [])).toMatchObject({ command: "python3", args: ["tools/gen.py"] });
|
||||
});
|
||||
});
|
||||
|
||||
describe("rerunning a session after its configuration changed (#1497)", () => {
|
||||
const session = (config: RunConfig, baseConfig?: RunConfig): RunSession => ({
|
||||
id: "s",
|
||||
config,
|
||||
baseConfig,
|
||||
runId: null,
|
||||
status: "finished",
|
||||
exitCode: 0,
|
||||
startedAt: 1,
|
||||
endedAt: 2,
|
||||
stdinClosed: true,
|
||||
});
|
||||
|
||||
it("launches the configuration as saved now, or the one it ran with once deleted", () => {
|
||||
const ran = cfg({ id: "app", command: "python", args: ["init.py"] });
|
||||
const edited = { ...ran, args: ["main.py"] };
|
||||
useRun.setState({ configs: [edited] });
|
||||
expect(rerunConfig(session(ran))).toBe(edited);
|
||||
expect(configRemoved("app")).toBe(false);
|
||||
useRun.setState({ configs: [] });
|
||||
expect(rerunConfig(session(ran))).toBe(ran);
|
||||
expect(configRemoved("app")).toBe(true);
|
||||
// A run that was never a listed configuration isn't "removed".
|
||||
expect(configRemoved("build:task")).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps the tests of a derived run (Rerun Failed Tests)", () => {
|
||||
const base = cfg({ id: "tests", isTest: true });
|
||||
const derived = { ...base, args: ["-k", "failing"] };
|
||||
useRun.setState({ configs: [base] });
|
||||
expect(rerunConfig(session(derived, base))).toBe(derived);
|
||||
useRun.setState({ configs: [] });
|
||||
});
|
||||
});
|
||||
@@ -256,6 +256,17 @@ describe("configurations that ran", () => {
|
||||
expect(items).toHaveLength(1);
|
||||
expect(items[0]!.children!.map((c) => [c.label, c.detail])).toEqual([["dev", "Running"]]);
|
||||
});
|
||||
|
||||
it("drops a deleted configuration once its run has ended (#1497)", () => {
|
||||
const removed = (id: string) => id === "dev";
|
||||
const ended = runServiceItems([], [session("s1", dev, "finished", 1)], [], ["dev"], {}, [], removed);
|
||||
expect(ended).toEqual([]);
|
||||
const live = runServiceItems([], [session("s1", dev, "running", 1)], [], ["dev"], {}, [], removed);
|
||||
expect(live[0]!.children!.map((c) => [c.label, c.detail])).toEqual([["dev", "Running"]]);
|
||||
// Never a saved configuration (a build task, an external tool): its finished run stays.
|
||||
const adHoc = runServiceItems([], [session("s2", scratch, "finished", 1)], [], [], {}, [], removed);
|
||||
expect(adHoc[0]!.children!.map((c) => c.key)).toEqual(["run:config:context:file"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("embedded web server port", () => {
|
||||
|
||||
@@ -29,7 +29,16 @@ import { commandLine, configIcon, KIND_OPTIONS, kindLabel } from "./configs";
|
||||
import { SessionPanel } from "./RunView";
|
||||
import { showApplicationUpdate } from "./applicationUpdate";
|
||||
import { isUpdatable } from "./updatePolicies";
|
||||
import { loadConfigs, persistTemporary, startRun, stopSession, useRun, type RunSession } from "./store";
|
||||
import {
|
||||
configRemoved,
|
||||
loadConfigs,
|
||||
persistTemporary,
|
||||
rerunConfig,
|
||||
startRun,
|
||||
stopSession,
|
||||
useRun,
|
||||
type RunSession,
|
||||
} from "./store";
|
||||
import {
|
||||
currentServiceSession,
|
||||
currentServiceSessions,
|
||||
@@ -230,6 +239,7 @@ function executionDetail(s: ServiceSession | undefined): string | undefined {
|
||||
/**
|
||||
* Groups by configuration type: added types list all their configurations, other
|
||||
* types only those that ran. A default type (Spring Boot) without configurations is hidden.
|
||||
* A configuration deleted since it ran (`removed`) stays only while its run is live.
|
||||
*/
|
||||
export function runServiceItems(
|
||||
configs: readonly RunConfig[],
|
||||
@@ -238,11 +248,13 @@ export function runServiceItems(
|
||||
ran: readonly string[] = [],
|
||||
order: ServiceOrder = {},
|
||||
debug: readonly DebugServiceSession[] = [],
|
||||
removed: (id: string) => boolean = () => false,
|
||||
): ServiceTreeItem[] {
|
||||
const executions = serviceSessions(sessions, debug);
|
||||
const latest = currentServiceSessions(executions);
|
||||
const kinds = [...added];
|
||||
for (const s of executions) if (!kinds.includes(s.config.kind)) kinds.push(s.config.kind);
|
||||
const shown = (s: ServiceSession) => liveServiceSession(s) || !removed(s.config.id);
|
||||
for (const s of executions) if (shown(s) && !kinds.includes(s.config.kind)) kinds.push(s.config.kind);
|
||||
for (const id of ran) {
|
||||
const kind = configs.find((c) => c.id === id)?.kind;
|
||||
if (kind && !kinds.includes(kind)) kinds.push(kind);
|
||||
@@ -252,7 +264,7 @@ export function runServiceItems(
|
||||
? configs.filter((c) => c.kind === kind)
|
||||
: ran.map((id) => configs.find((c) => c.id === id)).filter((c): c is RunConfig => !!c && c.kind === kind);
|
||||
for (const s of latest.values())
|
||||
if (s.config.kind === kind && !entries.some((c) => c.id === s.config.id)) entries.push(s.config);
|
||||
if (s.config.kind === kind && shown(s) && !entries.some((c) => c.id === s.config.id)) entries.push(s.config);
|
||||
if (!entries.length && DEFAULT_TYPES.includes(kind)) return [];
|
||||
const children = orderServices(entries, order[kind]).map((c): ServiceTreeItem => {
|
||||
const s = latest.get(c.id);
|
||||
@@ -298,7 +310,15 @@ function useRoots(): ServiceTreeItem[] {
|
||||
}, [root, typesRoot, ranRoot]);
|
||||
return useMemo(
|
||||
() =>
|
||||
runServiceItems(configs, sessions, typesRoot === root ? types : [], ranRoot === root ? ran : [], order, debug),
|
||||
runServiceItems(
|
||||
configs,
|
||||
sessions,
|
||||
typesRoot === root ? types : [],
|
||||
ranRoot === root ? ran : [],
|
||||
order,
|
||||
debug,
|
||||
configRemoved,
|
||||
),
|
||||
[configs, sessions, types, typesRoot, ran, ranRoot, root, order, debug],
|
||||
);
|
||||
}
|
||||
@@ -335,7 +355,7 @@ function run(r: Resolved) {
|
||||
if (debug) debug.rerun?.();
|
||||
else if (r.session?.run) {
|
||||
const s = r.session.run;
|
||||
void startRun(s.config, s.id, s.baseConfig, { show: false });
|
||||
void startRun(rerunConfig(s), s.id, s.baseConfig, { show: false });
|
||||
} else if (r.config) void startRun(r.config, undefined, undefined, { show: false });
|
||||
}
|
||||
|
||||
|
||||
@@ -1010,7 +1010,7 @@ function notifyExit(sessionId: string, code: number | null) {
|
||||
toolWindowService().show("run", true);
|
||||
},
|
||||
},
|
||||
{ label: "Rerun", run: () => void startRun(session.config, sessionId) },
|
||||
{ label: "Rerun", run: () => void startRun(rerunConfig(session), sessionId, session.baseConfig) },
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -1070,9 +1070,35 @@ export function waitForExit(sessionIds: readonly string[], timeoutMs = 10_000):
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* What rerunning a session launches: its configuration as saved now (edited since, as IDEA), or
|
||||
* the one it ran with when that was deleted or never saved. A derived run (Rerun Failed Tests) keeps its tests.
|
||||
*/
|
||||
export function rerunConfig(session: Pick<RunSession, "config" | "baseConfig">): RunConfig {
|
||||
if (session.baseConfig) return session.config;
|
||||
return useRun.getState().configs.find((c) => c.id === session.config.id) ?? session.config;
|
||||
}
|
||||
|
||||
/** The session's original configuration, as saved now (see {@link rerunConfig}). */
|
||||
function baseConfigOf(session: RunSession): RunConfig {
|
||||
const base = session.baseConfig ?? session.config;
|
||||
return useRun.getState().configs.find((c) => c.id === base.id) ?? base;
|
||||
}
|
||||
|
||||
/** Ids of every configuration listed in this window, so a run of one deleted since can be told apart. */
|
||||
const listedConfigIds = new Set<string>();
|
||||
useRun.subscribe((s, prev) => {
|
||||
if (s.configs !== prev.configs) for (const c of s.configs) listedConfigIds.add(c.id);
|
||||
});
|
||||
|
||||
/** The configuration was saved or temporary in this window and has been removed since. */
|
||||
export function configRemoved(id: string): boolean {
|
||||
return listedConfigIds.has(id) && !useRun.getState().configs.some((c) => c.id === id);
|
||||
}
|
||||
|
||||
export function rerunSession(sessionId: string) {
|
||||
const session = getSession(sessionId);
|
||||
if (session) void startRun(session.config, sessionId, session.baseConfig);
|
||||
if (session) void startRun(rerunConfig(session), sessionId, session.baseConfig && baseConfigOf(session));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1082,7 +1108,7 @@ export function rerunSession(sessionId: string) {
|
||||
*/
|
||||
export function debugSession(sessionId: string) {
|
||||
const session = getSession(sessionId);
|
||||
if (session) void executeAction("run.debugConfig", session.baseConfig ?? session.config);
|
||||
if (session) void executeAction("run.debugConfig", baseConfigOf(session));
|
||||
}
|
||||
|
||||
/** Paths of the failed tests of a session's last run. */
|
||||
@@ -1095,7 +1121,7 @@ export function failedTests(sessionId: string): string[][] {
|
||||
export function rerunFailed(sessionId: string): boolean {
|
||||
const session = getSession(sessionId);
|
||||
if (!session || session.status === "running" || session.status === "starting") return false;
|
||||
const base = session.baseConfig ?? session.config;
|
||||
const base = baseConfigOf(session);
|
||||
const derived = rerunFailedConfig(base, failedTests(sessionId));
|
||||
if (!derived) return false;
|
||||
void startRun(derived, sessionId, base);
|
||||
@@ -1116,7 +1142,7 @@ export async function runSelectedTests(
|
||||
if (!session || !tests) return false;
|
||||
const nodes = nodeIds.map((id) => tests.get(id)).filter((n) => !!n);
|
||||
const paths = leafTestPaths(nodes);
|
||||
const base = session.baseConfig ?? session.config;
|
||||
const base = baseConfigOf(session);
|
||||
const name = nodes.length === 1 ? nodes[0]!.name : `${paths.length} tests`;
|
||||
const derived = testsConfig(base, paths, name);
|
||||
if (!derived) return false;
|
||||
|
||||
Reference in new issue
Block a user