In the previous post, we implemented the minimum required commands to attach a GDB, namely “?”, “g”, “G”, “m”, “M”, “s” and “c”. We can go ahead adding support for more commands, but I’d like to take a step back here to do another important thing that we have been ignoring; unit testing.

There is no further progress made in terms of protocol implementation in this post, so you may skip this one if you already know how to setup unit testing in Java Script.

4.1 Unit Testing

4.1.1 Set up testing environment

Up until now, we have been implementing commands and used GDB to test if a command is successfully implemented. So we 1) code, 2) start the GDB Server, 3) attach GDB, 4) run the command, 5) find issues, 6) fix an issue and go back to 2). In order to both expedite the development and write robust code, I’d like to introduce unit tests to this project. The first thing I should have done before even attaching a GDB was to introduce unit tests. But late is better than never, so let’s go it.

A quick Google search gave me 10+ options of testing frameworks to use. After some more reading, I decided to go with Jest. There are two reasons I picked Jest:

  • It’s seemingly popular and well supported
  • Has everything I need in one package such as mocking and coverage (as opposed to my 2nd choice Mocha)

I’m sure other testing frameworks have their benefits too, but I think Jest suits this project. The following command got me installed Jest.

$ npm install --save-dev jest

I’ve also added the following configuration to my package.json

"scripts": {
"test": "jest",
},

Let’s add a test file at src/__test__/index.test.js.

Let’s also export the int32ToBytes so that it can be tested.

And let’s get it a spin.

It failed with this mysterious “Cannot use import statement outside a module” error. After some more Googling, I learned what was wrong. Apparently I was was writing ECMAScript (JavaScript) 6 a.k.a ES6 without really knowing it. In fact, import, export and the arrow expressions are all introduced since ES6. The last time I wrote JavaScript was back in 2012 using Google Closure Compiler. I’m not sure if closure is till been actively used these days. Web Development evolves so fast in the past few years that makes me fell like a dinosaur… Anyhow, according to the internet, I need to install a npm package called Babel. As its name suggests, Babel is a compiler which can transcompile ES6+ into ES5.

Babel is a JavaScript compiler

Babel is a toolchain that is mainly used to convert ECMAScript 2015+ code into a backwards compatible version of JavaScript in current and older browsers or environments.

We need to install the following packages via npm.

$ npm install --save-dev babel-jest @babel/core @babel/preset-env

And add a babel.config.json with the following contents to the project root directory.

We need to do a little more cleaning up to our index.js before we can run the test. index.js will start a server when executed, so we need to make sure that it doesn’t happen in the testing environment.

What we did here is to move the code that start a server into runServer function and only execute that function if the environment is not test.

We are finally ready to run the tests.

Voila! Our first unit test passed! We needed to go through a few hoops to get here, but once the testing environment is ready, it’s going to be much easier to make progress going forward.

4.2.2 Refactoring index.js

Before jumping on adding more tests, let’s spend sometime reorganize the code a little bit so that it’s easier to write tests. Here’s what I’m going to do

  • Extract command handling functions into a separate class
  • Extract GDB Server Stub into its own class
  • Keep index.js as the program’s entry point where its only responsibility is to set up and run the server

I’ve extracted the command handling part as GDBCommandHandler class. It works as the base class for actual command handlers such as the one we had for an MIPS R3000 emulator. It replies all commands with an empty string indicating it’s unsupported.

Now that we separated the responsibility of actual command handling from the server stub, let’s clean up the server stub itself too.

You can see that the GDBServerStub now is only responsible for handling messages, checking checksum and calling the handler functions. Let’s add some unit tests for this class.

With Jest, we can easily mock the handler class so that GDB Server Stub is independent from the actual handler.

Let’s keep going on to our next step. We can now separate the CPU emulator portion / debugger portion to its own files. r3000.js and r3000.test.js. The full files are getting a little bit too big to put on here, so I’m going to just put some snippets here.

Unsurprisingly, I was able to discover a few bugs through unit tests.

  • I didn’t know << operator in JavaScript is a 32-bit signed operation where 1 << 31 equals to -1. I had to add a >>> 0 at the end of my shifting operation to convert the value to unsigned.
  • I implemented write memory wrong where it would write more data than requested

It would had been a lot harder to discover these issues were it for simple unit testing. I was reluctant at the first because there were too many options and I didn’t want to spend time researching which one suited this project, but it didn’t take too much time to set it up after all. I also installed an VS Code extension for Jest. It automatically runs Jest whenever I make a change. It makes me want to write more tests, so I like it a lot.

The source code for this version can be found here if you are interested: https://github.com/nomtats/gdbserver-stub/tree/0396b2ed93f0435b7e1ba6c152ad3b642c023d10

4.2 Logging Framework

I have been using log = console.log as my “logging system”. I know there is probably 100 logging frameworks out there that does this properly. I did some Googling as usual to find a page with top logging libraries listed https://openbase.io/packages/top-nodejs-logging-libraries and an some articles such as https://medium.com/@samngms/which-npm-logging-module-to-choose-debug-winston-log4js-and-bunyan-1045fb4b0475 that discusses different libraries.

After 20 mins of reading, I decided to go with debug which is a terrible name for SEO, but apparently the most popular logging library on NPM. It seems simple enough to integrate. Since this project is a simple library, I think it’s more than good enough.

Here’s a snippet of my setup. I don’t know if this is the right way to do it yet, but it works well enough for now.

The screenshot below shows its outputs on the terminal. It’s pretty neat that it does automatic color coding for different namespaces.

That’d be all for today. We’ll get back on track to implement support for more commands. I may have to implement an R3000 emulator for this project at some point.

--

--