Introducing the moldable GTDebugger

Have you ever had problems with identifying the difference between an expected result and the actual result when an equality assertion failed in a test case? It can be a daunting task especially if you have to compare two long collections or strings.

Let’s look at an example. The code below simulates a problem in a test that compares two seemingly similar collections. As an exercise, execute this in a Moose image and find where the difference comes from in the debugger. Seriously, please give it a try before going on. It only takes a couple of minutes:

testClass := TestCase subclass: #ATest
      instanceVariableNames: ''
      classVariableNames: ''
      poolDictionaries: ''
      category: 'ATest'.
 testClass compile: 'testEquals
      self
           assert: #(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 51 51 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 81 81 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)
           equals: #(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100)'.
 (Smalltalk globals at: #ATest) new
      setTestSelector: #testEquals;
      debug

Executing the code should raise a debugger like this one:

Default-debugger.png

Did you manage to find why the assertion fails? It’s probably not so easy. If you did manage to find it, did you actually manage both causes? Yes, there are two.

The problem is that the debugger does not make it easy for you to spot these differences. It bombards you with information about all sorts of irrelevant variables while all you care about is a simple diff view between the two compared values. This is recurrent problem that requires dedicated tool support.

Andrei and I (well, Andrei did the hard job while I was mainly talking) worked on the concept of a moldable debugger. The idea is simple: debugging is an analysis activity that has contextual needs, and as such, it benefits from custom tool support. Given that we cannot foresee your specific context, we provide an infrastructure with which you can easily craft your own dedicated debugger.

How does this work in practice?

Let’s turn to our assertion problem. SUnit is an example of a specific library that has specific needs. In our case, if assert:equals: is present in the debugged stack, we want to show a diff view.

To see it in action, re-trigger the debugger with the above snippet, then click on the small triangle from on top of the first pane (the actual user interface is going to change in the future, but for now it is enough to show the concept), and then choose ‘'Available Debuggers/SUnit Debugger’. You will get a new debugger like the one below.

Sunit-debugger.png

Was it easier to spot the differences? My guess is yes.

The GTDebugger implements the idea of a moldable debugger as part of the GToolkit project, and it is by default installed in the Moose image. It can also be loaded in a fresh Pharo image using:

Gofer new
     url: 'http://www.smalltalkhub.com/mc/Moose/GToolkit/main';
     configuration;
     load.
(Smalltalk globals at: #ConfigurationOfGToolkit) loadDevelopment
(Smalltalk globals at: #GTInspector) registerToolsOn: Smalltalk tools.
(Smalltalk globals at: #GTDebugger) registerToolsOn: Smalltalk tools.
(Smalltalk globals at: #GTDebugger) registerExtraToolsOn: Smalltalk tools.
(Smalltalk globals at: #GTSUnitDebugger) registerToolsOn: Smalltalk tools. 

Andrei has actually implemented a couple of other such dedicated debuggers for PetitParser, Glamour and Announcements:

(Smalltalk globals at: #GTPPDebugger) registerToolsOn: Smalltalk tools.
(Smalltalk globals at: #GTGlamourDebugger) registerToolsOn: Smalltalk tools.
(Smalltalk globals at: #ACGlamourDebugger) registerToolsOn: Smalltalk tools. 

Each of these debuggers features dedicated presentations and interactions. For example, the PetitParser debugger presents the parser graphically, and allows you to move smartly between various parsing events (like next time the stream position gets modified).

To give it a shot, debug this step through it and then switch the PetitParser debugger:

PPArithmeticParser new parse: '1+23.45-6^78' 

Petitparser-debugger.png

Similarly, the Glamour debugger shows the current browser visually, and offers several Glamour-specific operations. For example, if you want to see it in action, you can execute the following code, and switch to the Glamour debugger:

GLMFinder new
          show: [ :a | a halt list display: [ :x | 1 to: x ] ];
          openOn: 42 

Glamour-debugger.png

All in all, the GTDebugger builds on a moldable metaphor and complements the GTInspector by allowing you to:

  • craft your custom debugger with custom presentations and actions, and
  • switch to a contextual debugger dynamically depending on what you need.

A tool that matches the context of your problem can provide an order of magnitude productivity increase for solving that problem. Given that debugging is a pervasive activity in software development, having a multitude of custom debuggers around can have a significant productivity impact.

This is a new paradigm that requires a different mindset. First, you have to start utilizing contextual feedback. Second, you have to get comfortable with the idea of crafting your own tools. And third, you have to get used to all this not being expensive.

Posted by Tudor Girba at 28 December 2013, 1:19 pm with tags analysis, tooling, moose, pharo link
|