chrome-devtools-frontend
Version:
Chrome DevTools UI
140 lines (120 loc) • 6.39 kB
text/typescript
// Copyright 2023 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import type {Chrome} from '../../../extension-api/ExtensionAPI.js';
import * as Platform from '../../core/platform/platform.js';
import * as SDK from '../../core/sdk/sdk.js';
import type * as Protocol from '../../generated/protocol.js';
import {createTarget} from '../../testing/EnvironmentHelpers.js';
import {TestPlugin} from '../../testing/LanguagePluginHelpers.js';
import {describeWithMockConnection} from '../../testing/MockConnection.js';
import * as Workspace from '../workspace/workspace.js';
import * as Bindings from './bindings.js';
const {urlString} = Platform.DevToolsPath;
describe('DebuggerLanguagePlugins', () => {
describe('ExtensionRemoteObject', () => {
describe('isLinearMemoryInspectable', () => {
it('yields false when the extension object has no linear memory address', () => {
const callFrame = sinon.createStubInstance(SDK.DebuggerModel.CallFrame);
const extensionObject: Chrome.DevTools.RemoteObject = {
type: 'object',
hasChildren: false,
};
const plugin = new TestPlugin('TestPlugin');
const remoteObject =
new Bindings.DebuggerLanguagePlugins.ExtensionRemoteObject(callFrame, extensionObject, plugin);
assert.isFalse(remoteObject.isLinearMemoryInspectable());
});
it('yields true when the extension object has a linear memory address', () => {
const callFrame = sinon.createStubInstance(SDK.DebuggerModel.CallFrame);
const extensionObject: Chrome.DevTools.RemoteObject = {
type: 'object',
linearMemoryAddress: 42,
hasChildren: false,
};
const plugin = new TestPlugin('TestPlugin');
const remoteObject =
new Bindings.DebuggerLanguagePlugins.ExtensionRemoteObject(callFrame, extensionObject, plugin);
assert.isTrue(remoteObject.isLinearMemoryInspectable());
});
});
});
describe('DebuggerLanguagePluginManager', () => {
describeWithMockConnection('getFunctionInfo', () => {
let target: SDK.Target.Target;
let pluginManager: Bindings.DebuggerLanguagePlugins.DebuggerLanguagePluginManager;
const MISSING_DWO_FILE = 'test.dwo';
const MISSING_DEBUG_FILES: SDK.DebuggerModel.MissingDebugFiles = {
resourceUrl: urlString`${MISSING_DWO_FILE}`,
initiator: {
target: null,
frameId: null,
extensionId: 'chrome-extension-id',
initiatorUrl: urlString`chrome-extension-id`,
},
};
const FUNCTION_NAME = 'test';
class Plugin extends TestPlugin {
override getFunctionInfo(_rawLocation: Chrome.DevTools.RawLocation):
Promise<{frames: Chrome.DevTools.FunctionInfo[], missingSymbolFiles: string[]}|
{frames: Chrome.DevTools.FunctionInfo[]}|{missingSymbolFiles: string[]}> {
return Promise.resolve({missingSymbolFiles: []});
}
override handleScript(_: SDK.Script.Script) {
return true;
}
override addRawModule(_rawModuleId: string, _symbolsURL: string, _rawModule: Chrome.DevTools.RawModule):
Promise<string[]> {
return Promise.resolve(['https://script-host/script.js']);
}
}
beforeEach(() => {
target = createTarget();
const workspace = Workspace.Workspace.WorkspaceImpl.instance();
const targetManager = target.targetManager();
const resourceMapping = new Bindings.ResourceMapping.ResourceMapping(targetManager, workspace);
const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance(
{forceNew: true, resourceMapping, targetManager});
pluginManager = debuggerWorkspaceBinding.pluginManager;
});
function createAndRegisterScript(): SDK.Script.Script {
const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel) as SDK.DebuggerModel.DebuggerModel;
const scriptUrl = urlString`https://script-host/script.js`;
return debuggerModel.parsedScriptSource(
'0' as Protocol.Runtime.ScriptId, scriptUrl, 0, 0, 0, 0, 0, '', null, false, undefined, false, false, 0,
null, null, null, null, null, null, null);
}
it('correctly processes missing debug info if available', async () => {
const plugin = new Plugin('TestPlugin');
sinon.stub(plugin, 'getFunctionInfo').returns(Promise.resolve({missingSymbolFiles: [MISSING_DWO_FILE]}));
pluginManager.addPlugin(plugin);
const script = createAndRegisterScript();
const location = sinon.createStubInstance(SDK.DebuggerModel.Location);
const result = await pluginManager.getFunctionInfo(script, location);
Platform.assertNotNullOrUndefined(result);
assert.deepEqual(result, {missingSymbolFiles: [MISSING_DEBUG_FILES]});
});
it('correctly returns frames if available', async () => {
const plugin = new Plugin('TestPlugin');
sinon.stub(plugin, 'getFunctionInfo').returns(Promise.resolve({frames: [{name: FUNCTION_NAME}]}));
pluginManager.addPlugin(plugin);
const script = createAndRegisterScript();
const location = sinon.createStubInstance(SDK.DebuggerModel.Location);
const result = await pluginManager.getFunctionInfo(script, location);
Platform.assertNotNullOrUndefined(result);
assert.deepEqual(result, {frames: [{name: FUNCTION_NAME}]});
});
it('correctly returns frames and missing debug info if both are available', async () => {
const plugin = new Plugin('TestPlugin');
sinon.stub(plugin, 'getFunctionInfo')
.returns(Promise.resolve({frames: [{name: FUNCTION_NAME}], missingSymbolFiles: [MISSING_DWO_FILE]}));
pluginManager.addPlugin(plugin);
const script = createAndRegisterScript();
const location = sinon.createStubInstance(SDK.DebuggerModel.Location);
const result = await pluginManager.getFunctionInfo(script, location);
Platform.assertNotNullOrUndefined(result);
assert.deepEqual(result, {frames: [{name: FUNCTION_NAME}], missingSymbolFiles: [MISSING_DEBUG_FILES]});
});
});
});
});