| /* |
| * Copyright (c) 2014 The Native Client Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* globals ASSERT_EQ, ASSERT_LE, ASSERT_GE, EXPECT_EQ, TEST_F, TEST */ |
| /* globals chrometest, TestModuleTest, waitForExtraModuleCount */ |
| /* globals waitIgnoringTerminal */ |
| |
| 'use strict'; |
| |
| function GdbExtensionTestModuleTest() { |
| TestModuleTest.call(this); |
| this.gdbExt = null; |
| } |
| GdbExtensionTestModuleTest.prototype = new TestModuleTest(); |
| |
| GdbExtensionTestModuleTest.prototype.setUp = function() { |
| var self = this; |
| return Promise.resolve().then(function() { |
| return TestModuleTest.prototype.setUp.call(self); |
| }).then(function() { |
| return self.newGdbExtPort(self.process.naclDebugPort); |
| }).then(function(gdbExt) { |
| self.gdbExt = gdbExt; |
| }); |
| }; |
| |
| GdbExtensionTestModuleTest.prototype.tearDown = function() { |
| var self = this; |
| return Promise.resolve().then(function() { |
| self.gdbExt.disconnect(); |
| return TestModuleTest.prototype.tearDown.call(self); |
| }); |
| }; |
| |
| /** |
| * Create a new port to the GDB extension. |
| * @param {integer} debugTcpPort Tcp port of the module to manage. |
| * @return {Promise.Port} Promise a port to the GDB extension. |
| */ |
| GdbExtensionTestModuleTest.prototype.newGdbExtPort = function(debugTcpPort) { |
| var keepPort = null; |
| return Promise.resolve().then(function() { |
| return chrometest.proxyExtension('Native Client GDB'); |
| }).then(function(gdbExtPort) { |
| keepPort = gdbExtPort; |
| keepPort.postMessage({'name': 'setDebugTcpPort', |
| 'debugTcpPort': debugTcpPort}); |
| return keepPort.wait(); |
| }).then(function(msg) { |
| ASSERT_EQ('setDebugTcpPortReply', msg.name, |
| 'expect debug extension port reply'); |
| return Promise.resolve(keepPort); |
| }); |
| }; |
| |
| /** |
| * Start up GDB and detach. |
| * @return {Promise} A promise to wait. |
| */ |
| GdbExtensionTestModuleTest.prototype.runGdb = function() { |
| var self = this; |
| var keepPort = null; |
| return Promise.resolve().then(function() { |
| return chrometest.getAllProcesses(); |
| }).then(function(oldProcesses) { |
| // Start gdb on the target process. |
| self.gdbExt.postMessage({'name': 'runGdb'}); |
| return waitForExtraModuleCount(1, oldProcesses); |
| }).then(function(newModules) { |
| var gdbProcess = newModules[0]; |
| return self.newGdbExtPort(gdbProcess.naclDebugPort); |
| }).then(function(gdbExtForGdb) { |
| keepPort = gdbExtForGdb; |
| keepPort.postMessage({'name': 'rspDetach'}); |
| return keepPort.wait(); |
| }).then(function(msg) { |
| ASSERT_EQ('rspDetachReply', msg.name, 'expect successful detach'); |
| keepPort.disconnect(); |
| // Wait for load. |
| return self.gdbExt.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('load', msg.name, 'expecting a load'); |
| // Expecting 16-19 lines out. |
| return self.waitForGdbPrompt(16, 19); |
| }); |
| }; |
| |
| /** |
| * Wait for the gdb prompt. |
| * |
| * Expect variable (within a range) amount of output from GDB prior to the |
| * "(gdb)" prompt returning. |
| * @param {integer} min Minimum number of writes (lines plus echo) expected |
| * from GDB. |
| * @param {integer} max Maximum number of writes (lines plus echo) expected |
| * from GDB. |
| * @return {Promise} A promise to wait. |
| */ |
| GdbExtensionTestModuleTest.prototype.waitForGdbPrompt = function(min, max) { |
| var self = this; |
| var lineCount = 0; |
| function checkLine(msg) { |
| ASSERT_EQ('message', msg.name, 'expecting a message'); |
| var prefix = msg.data.slice(0, 3); |
| var data = msg.data.slice(3); |
| EXPECT_EQ('gdb', prefix, 'expected gdb term message'); |
| if (data == '(gdb) ') { |
| ASSERT_GE(lineCount, min, 'expect gdb to emit more lines first'); |
| // Done. |
| return; |
| } else { |
| lineCount++; |
| ASSERT_LE(lineCount, max, 'expect limited text, got extra: ' + data); |
| // Recurse. |
| return self.gdbExt.wait().then(checkLine); |
| } |
| } |
| return self.gdbExt.wait().then(checkLine); |
| }; |
| |
| /** |
| * Wait for expected writes from gdb. |
| * @param {Array.String} writes A list of writes to expect from GDB. |
| * @return {Promise} A promise to wait. |
| */ |
| GdbExtensionTestModuleTest.prototype.waitForGdbReply = function(lines) { |
| var self = this; |
| function checkLine() { |
| if (lines.length === 0) { |
| return; |
| } |
| return Promise.resolve().then(function() { |
| return self.gdbExt.wait(); |
| }).then(function(msg) { |
| ASSERT_EQ('message', msg.name, 'expecting a message'); |
| var prefix = msg.data.slice(0, 3); |
| var data = msg.data.slice(3); |
| EXPECT_EQ('gdb', prefix, 'expected gdb term message'); |
| EXPECT_EQ(lines[0], data, 'expect matching line'); |
| lines = lines.slice(1); |
| return checkLine(); |
| }); |
| } |
| return checkLine(); |
| }; |
| |
| /** |
| * Send a command to gdb (as keystrokes). |
| * @param {String} cmd. |
| */ |
| GdbExtensionTestModuleTest.prototype.sendGdbCommand = function(cmd) { |
| var self = this; |
| self.gdbExt.postMessage( |
| {'name': 'input', 'msg': {'gdb': cmd}}); |
| }; |
| |
| /** |
| * Send a command to gdb and wait for echo followed by a reply. |
| * |
| * Checks for (gdb) prompt after the command. |
| * @param {String} cmd The command to send. |
| * @param {Array.String} expected A list of expected write back from gdb. |
| * @return {Promise} A promise to do it. |
| */ |
| GdbExtensionTestModuleTest.prototype.gdbCommand = function(cmd, expected) { |
| var self = this; |
| return Promise.resolve().then(function() { |
| self.sendGdbCommand(cmd); |
| var response = []; |
| for (var i = 0; i < cmd.length; i++) { |
| response.push(cmd[i]); |
| } |
| response = response.concat(expected); |
| response.push('(gdb) '); |
| return self.waitForGdbReply(response); |
| }); |
| }; |
| |
| /** |
| * Exit GDB and check for expected behavior. |
| * @return {Promise} A promise to do it. |
| */ |
| GdbExtensionTestModuleTest.prototype.exitGdb = function() { |
| var self = this; |
| return Promise.resolve().then(function() { |
| var msg = 'kill\ny\n'; |
| self.sendGdbCommand(msg); |
| // The kill y/n prompt comes back with one less write than the number of |
| // character for some reason. |
| var len = msg.length - 1; |
| return self.waitForGdbPrompt(len, len); |
| }).then(function() { |
| self.sendGdbCommand('quit\n'); |
| return waitIgnoringTerminal(self.gdbExt); |
| }).then(function(msg) { |
| EXPECT_EQ('exited', msg.name, 'expect exit'); |
| EXPECT_EQ(0, msg.returncode, 'expect 0'); |
| }); |
| }; |
| |
| |
| TEST_F(GdbExtensionTestModuleTest, 'testRspKill', function() { |
| var self = this; |
| self.gdbExt.postMessage({'name': 'rspKill'}); |
| return self.gdbExt.wait().then(function(msg) { |
| EXPECT_EQ('rspKillReply', msg.name, 'reply should be right'); |
| }); |
| }); |
| |
| |
| TEST_F(GdbExtensionTestModuleTest, 'testRspDetach', function() { |
| var self = this; |
| self.gdbExt.postMessage({'name': 'rspDetach'}); |
| return self.gdbExt.wait().then(function(msg) { |
| EXPECT_EQ('rspDetachReply', msg.name, 'reply should be right'); |
| // Wait a bit for the module to start. |
| return chrometest.sleep(500); |
| }).then(function() { |
| self.object.postMessage('ping'); |
| return self.object.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('pong', msg.data); |
| self.object.postMessage('exit'); |
| }); |
| }); |
| |
| |
| TEST_F(GdbExtensionTestModuleTest, 'testRspContinueOk', function() { |
| var self = this; |
| self.gdbExt.postMessage({'name': 'rspContinue'}); |
| // Wait a bit for the module to start. |
| return chrometest.sleep(500).then(function() { |
| self.object.postMessage('ping'); |
| return self.object.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('pong', msg.data); |
| self.object.postMessage('exit'); |
| return self.gdbExt.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('rspContinueReply', msg.name); |
| EXPECT_EQ('exited', msg.type, |
| 'expected module exit but got: ' + msg.reply); |
| EXPECT_EQ(0, msg.number, 'expected 0 exit code'); |
| }); |
| }); |
| |
| |
| TEST_F(GdbExtensionTestModuleTest, 'testRspContinueFault', function() { |
| var self = this; |
| self.gdbExt.postMessage({'name': 'rspContinue'}); |
| // Wait a bit for the module to start. |
| return chrometest.sleep(500).then(function() { |
| self.object.postMessage('ping'); |
| return self.object.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('pong', msg.data); |
| self.object.postMessage('fault'); |
| return self.gdbExt.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('rspContinueReply', msg.name); |
| EXPECT_EQ('signal', msg.type, |
| 'expected module signal but got: ' + msg.reply); |
| self.gdbExt.postMessage({'name': 'rspKill'}); |
| return self.gdbExt.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('rspKillReply', msg.name, 'reply should be right'); |
| }); |
| }); |
| |
| |
| TEST_F(GdbExtensionTestModuleTest, 'testRspContinueTwice', function() { |
| var self = this; |
| return Promise.resolve().then(function() { |
| self.gdbExt.postMessage({'name': 'rspContinue'}); |
| // Wait a bit for the module to start. |
| return chrometest.sleep(500); |
| }).then(function() { |
| self.object.postMessage('ping'); |
| return self.object.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('pong', msg.data); |
| // Continue a second time (should disconnect and reconnect). |
| self.gdbExt.postMessage({'name': 'rspContinue'}); |
| // Wait for it to start. |
| return chrometest.sleep(500); |
| }).then(function() { |
| self.object.postMessage('ping'); |
| return self.object.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('pong', msg.data); |
| self.object.postMessage('exit'); |
| return self.gdbExt.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('rspContinueReply', msg.name); |
| EXPECT_EQ('exited', msg.type, |
| 'expected module exit but got: ' + msg.reply); |
| EXPECT_EQ(0, msg.number, 'expected 0 exit code'); |
| }); |
| }); |
| |
| |
| TEST_F(GdbExtensionTestModuleTest, 'testGdbStart', function() { |
| var self = this; |
| return self.runGdb().then(function() { |
| return self.exitGdb(); |
| }); |
| }); |
| |
| |
| TEST_F(GdbExtensionTestModuleTest, 'testGdbLoadSymbols', function() { |
| var self = this; |
| return self.runGdb().then(function() { |
| return self.gdbCommand('remote get irt irt\n', [ |
| 'Successfully fetched file "irt".\n', |
| ]); |
| }).then(function() { |
| return self.gdbCommand('nacl-irt irt\n', [ |
| 'Reading symbols from irt...', |
| '(no debugging symbols found)...done.\n', |
| ]); |
| }).then(function() { |
| return self.gdbCommand('remote get nexe nexe\n', [ |
| 'Successfully fetched file "nexe".\n', |
| ]); |
| }).then(function() { |
| // Loads symbols from nexe without prompting. |
| return self.gdbCommand('nacl-irt nexe\n', [ |
| 'Reading symbols from nexe...', |
| 'done.\n', |
| ]); |
| }).then(function() { |
| return self.exitGdb(); |
| }); |
| }); |
| |
| |
| TEST('GdbTest', 'testInstallCheck', function() { |
| var self = this; |
| return Promise.resolve().then(function() { |
| return chrometest.proxyExtension('Native Client GDB'); |
| }).then(function(gdbExt) { |
| self.gdbExt = gdbExt; |
| self.gdbExt.postMessage({name: 'installCheck'}); |
| return self.gdbExt.wait(); |
| }).then(function(msg) { |
| EXPECT_EQ('installCheckReply', msg.name, 'we are installed'); |
| self.gdbExt.disconnect(); |
| }); |
| }); |