UNPKG

asmimproved-dbgmits

Version:

Provides the ability to control GDB and LLDB programmatically via GDB/MI.

341 lines (314 loc) 14.5 kB
// Copyright (c) 2015 Vadim Macagon // MIT License, see LICENSE file for full terms. require('source-map-support').install(); import * as chai from 'chai'; import chaiAsPromised = require('chai-as-promised'); import * as bunyan from 'bunyan'; import * as dbgmits from '../lib/index'; import { beforeEachTestWithLogger, logSuite as log, startDebugSession, runToFunc, runToFuncAndStepOut } from './test_utils'; chai.use(chaiAsPromised); // aliases var expect = chai.expect; import DebugSession = dbgmits.DebugSession; import IWatchInfo = dbgmits.IWatchInfo; // the directory in which Gruntfile.js resides is also Mocha's working directory, // so any relative paths will be relative to that directory var localTargetExe: string = './build/Debug/watch_tests_target'; log(describe("Debug Session", () => { describe("Watch Manipulation", () => { var debugSession: DebugSession; beforeEachTestWithLogger((logger: bunyan.Logger) => { debugSession = startDebugSession(logger); return debugSession.setExecutableFile(localTargetExe); }); afterEach(() => { return debugSession.end(); }); describe("#addWatch", () => { it("adds a new floating watch for a local variable in an outer frame", () => { return runToFunc(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { // add a new watch for a local variable in funcWithMoreVariablesToWatch() return debugSession.addWatch('f', { threadId: 1, frameLevel: 1, isFloating: true }) .then((watch: IWatchInfo) => { expect(watch.id).not.to.be.empty; expect(watch.childCount).to.equal(0); expect(watch.value).to.equal('9.5'); expect(watch.expressionType).to.equal('float'); expect(watch.threadId).to.equal(1); expect(watch.isDynamic).to.be.false; expect(watch.hasMoreChildren).to.be.false; expect(watch.displayHint).to.be.undefined; }); }); }); it("adds a new fixed watch for a local variable in the current frame @skipOnGDB", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { // add a new watch for a local variable in funcWithMoreVariablesToWatch() return debugSession.addWatch('e') .then((watch: IWatchInfo) => { expect(watch.id).not.to.be.empty; expect(watch.childCount).to.equal(2); expect(watch.expressionType).to.equal('Point'); expect(watch.threadId).to.equal(1); expect(watch.isDynamic).to.be.false; expect(watch.hasMoreChildren).to.be.false; expect(watch.displayHint).to.be.undefined; }); }); }); // GDB groups C++ struct/class members under private/public/protected pseudo-members, // so we need a separate test just for GDB it("adds a new fixed watch for a local variable in the current frame @skipOnLLDB", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { // add a new watch for a local variable in funcWithMoreVariablesToWatch() return debugSession.addWatch('e') .then((watch: IWatchInfo) => { expect(watch.id).not.to.be.empty; expect(watch.childCount).to.equal(1); expect(watch.expressionType).to.equal('Point'); expect(watch.threadId).to.equal(1); expect(watch.isDynamic).to.be.false; expect(watch.hasMoreChildren).to.be.false; expect(watch.displayHint).to.be.undefined; }); }); }); }); it("#removeWatch", () => { return runToFunc(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { // add a new watch for a local variable in funcWithMoreVariablesToWatch() return debugSession.addWatch('f', { threadId: 1, frameLevel: 1, isFloating: true }) .then((watch: IWatchInfo) => { return debugSession.removeWatch(watch.id); }); }); }); describe("#updateWatch", () => { it("updates a fixed watch for a local variable after the value changes", () => { // check the change in the value of the variable was detected by the watch var onStepOverUpdateWatch = (watch: IWatchInfo) => { return new Promise<void>((resolve, reject) => { debugSession.once(dbgmits.EVENT_STEP_FINISHED, (stepNotify: dbgmits.IStepFinishedEvent) => { debugSession.updateWatch(watch.id, dbgmits.VariableDetailLevel.All) .then((changelist: dbgmits.IWatchUpdateInfo[]) => { expect(changelist.length).to.be.equal(1); var firstEntry = changelist[0]; expect(firstEntry.id).to.equal(watch.id); expect(firstEntry.hasTypeChanged).to.be.false; expect(firstEntry.value).not.to.equal(watch.value); expect(firstEntry.isInScope).to.be.true; expect(firstEntry.isObsolete).to.be.false; }) .then(resolve) .catch(reject); } ); }); }; return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { // create a watch on a variable and step over the line that alters the value of the variable return debugSession.addWatch('f') .then((watch: IWatchInfo) => { return Promise.all([ onStepOverUpdateWatch(watch), debugSession.stepOverLine() ]); }); }); }); }); describe("#getWatchChildren", () => { it("gets a list of members of a simple variable under watch", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch', () => { return debugSession.addWatch('f') .then((watch: IWatchInfo) => { return debugSession.getWatchChildren(watch.id); }) .then((children: dbgmits.IWatchChildInfo[]) => { // watches on simple variables shouldn't have any children expect(children.length).to.equal(0); }); }); }); it("gets a list of members of a pointer variable under watch", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch', () => { return debugSession.addWatch('g') .then((watch: IWatchInfo) => { return debugSession.getWatchChildren(watch.id); }) .then((children: dbgmits.IWatchChildInfo[]) => { // watches on pointer variables should have a single child expect(children.length).to.equal(1); expect(children[0].expressionType).to.equal('float'); }); }); }); it("gets a list of members of a struct variable under watch @skipOnGDB", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { return debugSession.addWatch('e') .then((watch: IWatchInfo) => { return debugSession.getWatchChildren(watch.id, { detail: dbgmits.VariableDetailLevel.None }); }) .then((children: dbgmits.IWatchChildInfo[]) => { // watches on variables of aggregate types should have one or more children expect(children.length).to.equal(2); expect(children[0].expressionType).to.equal('float'); expect(children[1].expressionType).to.equal('float'); }); }); }); // GDB groups C++ struct/class members under private/public/protected pseudo-members, // so we need a separate test just for GDB it("gets a list of members of a struct variable under watch @skipOnLLDB", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { return debugSession.addWatch('e') .then((watch: IWatchInfo) => { return debugSession.getWatchChildren(watch.id); }) .then((children: dbgmits.IWatchChildInfo[]) => { // should be just one child named 'public' expect(children.length).to.equal(1); return debugSession.getWatchChildren( children[0].id, { detail: dbgmits.VariableDetailLevel.None } ); }) .then((children: dbgmits.IWatchChildInfo[]) => { // watches on variables of aggregate types should have one or more children expect(children.length).to.equal(2); expect(children[0].expressionType).to.equal('float'); expect(children[1].expressionType).to.equal('float'); }); }); }); it("gets a subset of members of a struct variable under watch @skipOnGDB", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { return debugSession.addWatch('e') .then((watch: IWatchInfo) => { return debugSession.getWatchChildren( watch.id, { detail: dbgmits.VariableDetailLevel.None, from: 0, to: 1 }); }) .then((children: dbgmits.IWatchChildInfo[]) => { // watches on variables of aggregate types should have one or more children expect(children.length).to.equal(1); expect(children[0].expressionType).to.equal('float'); }); }); }); // GDB groups C++ struct/class members under private/public/protected pseudo-members, // so we need a separate test just for GDB it("gets a subset of members of a struct variable under watch @skipOnLLDB", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { return debugSession.addWatch('e') .then((watch: IWatchInfo) => { return debugSession.getWatchChildren(watch.id); }) .then((children: dbgmits.IWatchChildInfo[]) => { // should be just one child named 'public' expect(children.length).to.equal(1); return debugSession.getWatchChildren( children[0].id, { detail: dbgmits.VariableDetailLevel.None, from: 0, to: 1 } ); }) .then((children: dbgmits.IWatchChildInfo[]) => { // watches on variables of aggregate types should have one or more children expect(children.length).to.equal(1); expect(children[0].expressionType).to.equal('float'); }); }); }); }); it("#setWatchValueFormat", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch', () => { let watchId: string; // watch an integer variable return debugSession.addWatch('e') .then((watch: IWatchInfo) => { watchId = watch.id; return debugSession.setWatchValueFormat(watchId, dbgmits.WatchFormatSpec.Binary); }) // FIXME: binary values are formatted differently between LLDB-MI and GDB-MI .then((value: string) => { expect(value).to.match(/(0b)?101/); }) .then(() => { return debugSession.setWatchValueFormat(watchId, dbgmits.WatchFormatSpec.Decimal); }) .then((value: string) => { expect(value).to.equal('5'); }) .then(() => { return debugSession.setWatchValueFormat(watchId, dbgmits.WatchFormatSpec.Hexadecimal); }) .then((value: string) => { expect(value).to.equal('0x5'); }) .then(() => { return debugSession.setWatchValueFormat(watchId, dbgmits.WatchFormatSpec.Octal); }) .then((value: string) => { expect(value).to.equal('05'); }) .then(() => { return debugSession.setWatchValueFormat(watchId, dbgmits.WatchFormatSpec.Default); }) .then((value: string) => { expect(value).to.equal('5'); }); }); }); it("#getWatchValue", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch', () => { return debugSession.addWatch('e') .then((watch: IWatchInfo) => { return debugSession.getWatchValue(watch.id); }) .then((value: string) => { expect(value).to.equal('5'); }); }); }); it("#setWatchValue", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch', () => { var newValue = '999'; return debugSession.addWatch('e') .then((watch: IWatchInfo) => { return debugSession.setWatchValue(watch.id, newValue); }) .then((value: string) => { expect(value).to.equal(newValue); }); }); }); describe("#getWatchAttributes", () => { it("gets the attributes for a watch on a variable of a simple type", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch', () => { return debugSession.addWatch('e') .then((watch: IWatchInfo) => { return debugSession.getWatchAttributes(watch.id); }) .then((attrs: dbgmits.WatchAttribute[]) => { expect(attrs.length).to.equal(1); expect(attrs[0]).to.equal(dbgmits.WatchAttribute.Editable); }); }); }); // FIXME: re-enable this test for LLDB when LLDB-MI starts returning 'noneditable' attributes // like it should it("gets the attributes for a watch on a variable of an aggregate type @skipOnLLDB", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { return debugSession.addWatch('e') .then((watch: IWatchInfo) => { return debugSession.getWatchAttributes(watch.id); }) .then((attrs: dbgmits.WatchAttribute[]) => { expect(attrs.length).to.equal(1); expect(attrs[0]).to.equal(dbgmits.WatchAttribute.NonEditable); }); }); }); }); it("#getWatchExpression", () => { return runToFuncAndStepOut(debugSession, 'funcWithMoreVariablesToWatch_Inner', () => { return debugSession.addWatch('f') .then((watch: IWatchInfo) => { return debugSession.getWatchExpression(watch.id); }) .then((expr: string) => { expect(expr).to.equal('f'); }); }); }); }); }));