mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-12 17:45:49 +08:00
[unittest] Add test for synchronizing on instrumentation break
This adds a test for checking if the functionality of `restoreBreakpointsForScript` in the BreakpointManager. This function is used in order to synchronize breakpoints on instrumentation breaks. Bug: 1229541 Change-Id: I56d33acf329da03948b612c4e003e996302497bf Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3598877 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Danil Somsikov <dsv@chromium.org> Commit-Queue: Kim-Anh Tran <kimanh@chromium.org>
This commit is contained in:
committed by
Devtools-frontend LUCI CQ
parent
8e1a99324b
commit
facefdbfe3
@@ -206,6 +206,11 @@ export class ExperimentsSupport {
|
||||
this.#enabledTransiently.add(experimentName);
|
||||
}
|
||||
|
||||
disableForTest(experimentName: string): void {
|
||||
this.checkExperiment(experimentName);
|
||||
this.#enabledTransiently.delete(experimentName);
|
||||
}
|
||||
|
||||
clearForTest(): void {
|
||||
this.#experiments = [];
|
||||
this.#experimentNames.clear();
|
||||
|
||||
@@ -652,7 +652,7 @@ export class DebuggerModel extends SDKModel<EventTypes> {
|
||||
this.evaluateOnCallFrameCallback = callback;
|
||||
}
|
||||
|
||||
setSynchronizeBreakpointsCallback(callback: (script: Script) => Promise<void>): void {
|
||||
setSynchronizeBreakpointsCallback(callback: ((script: Script) => Promise<void>)|null): void {
|
||||
this.#synchronizeBreakpointsCallback = callback;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,11 +100,12 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
|
||||
|
||||
modelAdded(debuggerModel: SDK.DebuggerModel.DebuggerModel): void {
|
||||
if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.INSTRUMENTATION_BREAKPOINTS)) {
|
||||
debuggerModel.setSynchronizeBreakpointsCallback(this.#restoreBreakpointsForScript.bind(this));
|
||||
debuggerModel.setSynchronizeBreakpointsCallback(this.restoreBreakpointsForScript.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
modelRemoved(): void {
|
||||
modelRemoved(debuggerModel: SDK.DebuggerModel.DebuggerModel): void {
|
||||
debuggerModel.setSynchronizeBreakpointsCallback(null);
|
||||
}
|
||||
|
||||
async copyBreakpoints(fromURL: Platform.DevToolsPath.UrlString, toSourceCode: Workspace.UISourceCode.UISourceCode):
|
||||
@@ -117,7 +118,7 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
|
||||
|
||||
// This method explicitly awaits the source map (if necessary) and the uiSourceCodes
|
||||
// required to set all breakpoints that are related to this script.
|
||||
async #restoreBreakpointsForScript(script: SDK.Script.Script): Promise<void> {
|
||||
async restoreBreakpointsForScript(script: SDK.Script.Script): Promise<void> {
|
||||
if (!Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.INSTRUMENTATION_BREAKPOINTS)) {
|
||||
return;
|
||||
}
|
||||
@@ -416,6 +417,10 @@ export class Breakpoint implements SDK.TargetManager.SDKModelObserver<SDK.Debugg
|
||||
modelBreakpoint.removeEventListeners();
|
||||
}
|
||||
|
||||
modelBreakpoint(debuggerModel: SDK.DebuggerModel.DebuggerModel): ModelBreakpoint|undefined {
|
||||
return this.#modelBreakpoints.get(debuggerModel);
|
||||
}
|
||||
|
||||
addUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode): void {
|
||||
if (!this.uiSourceCodes.has(uiSourceCode)) {
|
||||
this.uiSourceCodes.add(uiSourceCode);
|
||||
@@ -626,6 +631,10 @@ export class ModelBreakpoint {
|
||||
}
|
||||
}
|
||||
|
||||
get currentState(): Breakpoint.State|null {
|
||||
return this.#currentState;
|
||||
}
|
||||
|
||||
resetLocations(): void {
|
||||
for (const uiLocation of this.#uiLocations.values()) {
|
||||
this.#breakpoint.uiLocationRemoved(uiLocation);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
const {assert} = chai;
|
||||
import * as Root from '../../../../../front_end/core/root/root.js';
|
||||
import * as SDK from '../../../../../front_end/core/sdk/sdk.js';
|
||||
import * as Workspace from '../../../../../front_end/models/workspace/workspace.js';
|
||||
import * as Bindings from '../../../../../front_end/models/bindings/bindings.js';
|
||||
@@ -14,51 +15,110 @@ import {createUISourceCode} from '../../helpers/UISourceCodeHelpers.js';
|
||||
import {assertNotNullOrUndefined} from '../../../../../front_end/core/platform/platform.js';
|
||||
|
||||
describeWithRealConnection('BreakpointManager', () => {
|
||||
it('allows awaiting on scheduled update in debugger', async () => {
|
||||
const breakpointManager = Bindings.BreakpointManager.BreakpointManager.instance();
|
||||
const URL = 'file:///tmp/example.html' as Platform.DevToolsPath.UrlString;
|
||||
const SCRIPT_ID = 'SCRIPT_ID' as Protocol.Runtime.ScriptId;
|
||||
const BREAKPOINT_ID = 'BREAKPOINT_ID' as Protocol.Debugger.BreakpointId;
|
||||
const JS_MIME_TYPE = 'text/javascript';
|
||||
|
||||
let target: SDK.Target.Target;
|
||||
let breakpointManager: Bindings.BreakpointManager.BreakpointManager;
|
||||
class TestDebuggerModel extends SDK.DebuggerModel.DebuggerModel {
|
||||
constructor(target: SDK.Target.Target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
async setBreakpointByURL(
|
||||
_url: Platform.DevToolsPath.UrlString, _lineNumber: number, _columnNumber?: number,
|
||||
_condition?: string): Promise<SDK.DebuggerModel.SetBreakpointResult> {
|
||||
return Promise.resolve(
|
||||
{breakpointId: BREAKPOINT_ID, locations: [new SDK.DebuggerModel.Location(this, SCRIPT_ID, 42)]});
|
||||
}
|
||||
|
||||
scriptForId(scriptId: string): SDK.Script.Script|null {
|
||||
if (scriptId === SCRIPT_ID) {
|
||||
return new SDK.Script.Script(
|
||||
this, scriptId as Protocol.Runtime.ScriptId, URL, 0, 0, 0, 0, 0, '', false, false, undefined, false, 0,
|
||||
null, null, null, null, null, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function createFakeScriptMapping(debuggerModel: TestDebuggerModel, SCRIPT_ID: Protocol.Runtime.ScriptId):
|
||||
Bindings.DebuggerWorkspaceBinding.DebuggerSourceMapping {
|
||||
const sdkLocation = new SDK.DebuggerModel.Location(debuggerModel, SCRIPT_ID as Protocol.Runtime.ScriptId, 13);
|
||||
const mapping = {
|
||||
rawLocationToUILocation: (_: SDK.DebuggerModel.Location) => null,
|
||||
uiLocationToRawLocations:
|
||||
(_uiSourceCode: Workspace.UISourceCode.UISourceCode, _lineNumber: number,
|
||||
_columnNumber?: number) => [sdkLocation],
|
||||
};
|
||||
return mapping;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
breakpointManager = Bindings.BreakpointManager.BreakpointManager.instance();
|
||||
assertNotNullOrUndefined(breakpointManager);
|
||||
|
||||
const URL = 'file:///tmp/example.html' as Platform.DevToolsPath.UrlString;
|
||||
const SCRIPT_ID = 'SCRIPT_ID' as Protocol.Runtime.ScriptId;
|
||||
const BREAKPOINT_ID = 'BREAKPOINT_ID' as Protocol.Debugger.BreakpointId;
|
||||
const {uiSourceCode, project} = createUISourceCode({url: URL, mimeType: 'text/javascript'});
|
||||
const targetManager = SDK.TargetManager.TargetManager.instance();
|
||||
const target = targetManager.mainTarget();
|
||||
assertNotNullOrUndefined(target);
|
||||
const mainTarget = targetManager.mainTarget();
|
||||
assertNotNullOrUndefined(mainTarget);
|
||||
target = mainTarget;
|
||||
});
|
||||
|
||||
class TestDebuggerModel extends SDK.DebuggerModel.DebuggerModel {
|
||||
constructor(target: SDK.Target.Target) {
|
||||
super(target);
|
||||
}
|
||||
it('allows awaiting the restoration of breakpoints', async () => {
|
||||
Root.Runtime.experiments.enableForTest(Root.Runtime.ExperimentName.INSTRUMENTATION_BREAKPOINTS);
|
||||
|
||||
async setBreakpointByURL(
|
||||
_url: Platform.DevToolsPath.UrlString, _lineNumber: number, _columnNumber?: number,
|
||||
_condition?: string): Promise<SDK.DebuggerModel.SetBreakpointResult> {
|
||||
return Promise.resolve(
|
||||
{breakpointId: BREAKPOINT_ID, locations: [new SDK.DebuggerModel.Location(debuggerModel, SCRIPT_ID, 42)]});
|
||||
}
|
||||
const {uiSourceCode} = createUISourceCode({url: URL, mimeType: JS_MIME_TYPE});
|
||||
const breakpoint = await breakpointManager.setBreakpoint(uiSourceCode, 0, 0, '', true);
|
||||
|
||||
scriptForId(scriptId: string): SDK.Script.Script|null {
|
||||
if (scriptId === SCRIPT_ID) {
|
||||
return new SDK.Script.Script(
|
||||
this, scriptId as Protocol.Runtime.ScriptId, URL, 0, 0, 0, 0, 0, '', false, false, undefined, false, 0,
|
||||
null, null, null, null, null, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Create a new DebuggerModel and notify the breakpoint engine about it.
|
||||
const debuggerModel = new TestDebuggerModel(target);
|
||||
breakpoint.modelAdded(debuggerModel);
|
||||
|
||||
// Make sure that we await all updates that are triggered by adding the model.
|
||||
await breakpoint.updateBreakpoint();
|
||||
|
||||
// Retrieve the ModelBreakpoint that is linked to our DebuggerModel.
|
||||
const modelBreakpoint = breakpoint.modelBreakpoint(debuggerModel);
|
||||
assertNotNullOrUndefined(modelBreakpoint);
|
||||
|
||||
// Make sure that we do not have a linked script yet.
|
||||
assertNotNullOrUndefined(modelBreakpoint.currentState);
|
||||
assert.lengthOf(modelBreakpoint.currentState.positions, 1);
|
||||
assert.isEmpty(modelBreakpoint.currentState.positions[0].scriptId);
|
||||
|
||||
// Create a fake mapping that can be used to set a breakpoint.
|
||||
const mapping = createFakeScriptMapping(debuggerModel, SCRIPT_ID);
|
||||
Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().addSourceMapping(mapping);
|
||||
|
||||
// Now await restoring the breakpoint.
|
||||
// A successful restore should update the ModelBreakpoint of the DebuggerModel
|
||||
// to reflect a state, in which we have successfully set a breakpoint (i.e. a script id
|
||||
// is available).
|
||||
const script = debuggerModel.scriptForId(SCRIPT_ID);
|
||||
assertNotNullOrUndefined(script);
|
||||
await breakpointManager.restoreBreakpointsForScript(script);
|
||||
assertNotNullOrUndefined(modelBreakpoint.currentState);
|
||||
assert.lengthOf(modelBreakpoint.currentState.positions, 1);
|
||||
assert.strictEqual(modelBreakpoint.currentState.positions[0].scriptId, SCRIPT_ID);
|
||||
|
||||
// Clean up.
|
||||
breakpointManager.removeBreakpoint(breakpoint, true);
|
||||
breakpointManager.modelRemoved(debuggerModel);
|
||||
Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().removeSourceMapping(mapping);
|
||||
Root.Runtime.experiments.disableForTest(Root.Runtime.ExperimentName.INSTRUMENTATION_BREAKPOINTS);
|
||||
});
|
||||
|
||||
it('allows awaiting on scheduled update in debugger', async () => {
|
||||
const {uiSourceCode, project} = createUISourceCode({url: URL, mimeType: JS_MIME_TYPE});
|
||||
|
||||
const debuggerModel = new TestDebuggerModel(target);
|
||||
const breakpoint = await breakpointManager.setBreakpoint(uiSourceCode, 42, 0, '', true);
|
||||
|
||||
const modelBreakpoint = new Bindings.BreakpointManager.ModelBreakpoint(
|
||||
debuggerModel, breakpoint, breakpointManager.debuggerWorkspaceBinding);
|
||||
const mapping = {
|
||||
rawLocationToUILocation: (_: SDK.DebuggerModel.Location) => null,
|
||||
uiLocationToRawLocations:
|
||||
(_uiSourceCode: Workspace.UISourceCode.UISourceCode, _lineNumber: number,
|
||||
_columnNumber?: number) => [new SDK.DebuggerModel.Location(debuggerModel, SCRIPT_ID, 13)],
|
||||
};
|
||||
const mapping = createFakeScriptMapping(debuggerModel, SCRIPT_ID);
|
||||
Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().addSourceMapping(mapping);
|
||||
assert.isNull(breakpoint.currentState);
|
||||
const update = modelBreakpoint.scheduleUpdateInDebugger();
|
||||
|
||||
Reference in New Issue
Block a user