c++unit-testingmakefilecpputest

Debugging (breakpoints / etc) in VSCode with different makefiles for parts of the codebase


I'm working on an ESP-IDF based project that runs on ESP32 microcontrollers.

The project has a bunch of different C++ libraries (ESP-IDF calls them components) I've written. Normally I compile the whole project and it gets installed on the ESP32, and everything works great.

I've been writing tests, and how I make the tests work is a little different than the standard build process. For each set of tests, I am testing just one of my C++ components. (for instance "Wireless" or "UserInputs", etc) I mock out the rest of my components and the ESP-IDF code that my code uses, and this lets me just test "Wireless", for instance.

To do this, I'm using CppUTest and a series of makefiles. The makefile structure is based on the structure here: https://github.com/memfault/interrupt/tree/master/example/unit-testing/minimal

And here's the article I followed that is describing that makefile/testing setup. https://interrupt.memfault.com/blog/unit-testing-basics#setting-up-cpputest

So, there's a main makefile, and it finds all the per-component makefiles. Those per-component makefiles specify which .cpp files to compile, what folders to find your imports in, where your tests are, etc. And all that works great.

The situation I'm in is that I want to be able to run the debugger in VSCode to set breakpoints, pause execution, and inspect my variables at a given point in my code.

Just doing this in the tests is enough. I don't need debugger in my main ESP-IDF build process.

But I'm having the most challenging time working with this kind of setup. Because there's not just ONE make file.

Here's the core of what I want to do. I want to be able to set a breakpoint, and then do something to tell my code to compile with a given list of .cpp files, and header import locations. Just like in those per-component test make files. And I want my code to execute to that breakpoint and then give me the sauce in VSCode.

Any suggestions on how I can work in this direction would be very helpful.


Solution

  • I was able to work this out. Here's what I did...

    Firstly, I ran my tests. Now when I look in unit_tests/build/ I see sub folders for each make file. And in those subfolders there are executables. For instance, unit_tests/build/data/data_tests

    I made a .vscode/launch.json file in my repo. It's looks like this...

    {
      // Use IntelliSense to learn about possible attributes.
      // Hover to view descriptions of existing attributes.
      // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
      "version": "0.2.0",
      "configurations": [
        {
          "name": "(gdb) Launch for Dispatch Component",
          "type": "cppdbg",
          "request": "launch",
          "program": "${workspaceFolder}/unit_tests/build/dispatch/dispatch_tests",
          "stopAtEntry": false,
          "cwd": "${fileDirname}",
          "environment": [],
          "externalConsole": false,
          "MIMode": "gdb",
          "setupCommands": [
              {
                  "description": "Enable pretty-printing for gdb",
                  "text": "-enable-pretty-printing",
                  "ignoreFailures": true
              },
              {
                  "description":  "Set Disassembly Flavor to Intel",
                  "text": "-gdb-set disassembly-flavor intel",
                  "ignoreFailures": true
              }
          ]
        },
        {
          "name": "(gdb) Launch for Wireless Component",
          "type": "cppdbg",
          "request": "launch",
          "program": "${workspaceFolder}/unit_tests/build/wireless/wireless_tests",
          "stopAtEntry": false,
          "cwd": "${fileDirname}",
          "environment": [],
          "externalConsole": false,
          "MIMode": "gdb",
          "setupCommands": [
              {
                  "description": "Enable pretty-printing for gdb",
                  "text": "-enable-pretty-printing",
                  "ignoreFailures": true
              },
              {
                  "description":  "Set Disassembly Flavor to Intel",
                  "text": "-gdb-set disassembly-flavor intel",
                  "ignoreFailures": true
              }
          ]
        },
        {
          "name": "(gdb) Launch for Data Component",
          "type": "cppdbg",
          "request": "launch",
          "program": "${workspaceFolder}/unit_tests/build/data/data_tests",
          "stopAtEntry": false,
          "cwd": "${fileDirname}",
          "environment": [],
          "externalConsole": false,
          "MIMode": "gdb",
          "setupCommands": [
              {
                  "description": "Enable pretty-printing for gdb",
                  "text": "-enable-pretty-printing",
                  "ignoreFailures": true
              },
              {
                  "description":  "Set Disassembly Flavor to Intel",
                  "text": "-gdb-set disassembly-flavor intel",
                  "ignoreFailures": true
              }
          ]
        },
      ]
    }
    

    Notice that in that launch.json file there are multiple configurations. One for each component/make file. The name of each configuration says what component it's for, and the program field points to the executable that the tests built.

    If I'm in the Data component and want to drop into a breakpoint, here's what I do.

    1. ensure that there's a test called by the Makefile_data.mk that hits my breakpoint.
    2. click the Run and Debug button on the left in VSCode. (the bug with the play button)
    3. click the dropdown at the top of Run and Debug and select Launch for Data Component
    4. Hit play.

    Now my tests run and when they get to the breakpoint in the code in my Data component they will pause. Pretty slick!