Exercise: Code Quality

In this week's class we practice error detection and fixing with Qt's unit test framework, debugger, and breakpoint and debug printings.

The code to be debugged is almost the same date class as previously. In addition, you can use the unit tests written for the class and the static analysis created by SonarQube. The purpose is to use unit tests to find, locate, and fix the bugs in the class. The bugs are there on purpose or by accident.

During the exercise you must answer a couple of questions. It is also useful as a refreshment of debugging skills.

The files needed in this task can be pulled from version control. They are in repository ``student_template_project` https://course-gitlab.tuni.fi/tie-0240x-ohjelmointi-3-programming-3-2020-2021/student_template_project. You must set it as a remote to your local repository. The codes needed in this exercise are in the directory EX_debug. Note that you do not have rights to update the repository.

You can open the whole by opening the top level project file WE-debugging.pro in QtCreator. The file structure of the exercise is as follows:

  • WE-debugging.pro
    • DateProject
      • DateProject.pro
        • main.cc, date.hh, date.cc
    • UnitTest
      • UnitTest.pro
        • tst_unittest.cc

Detecting errors

When you build the top level project, QtCreator builds both the "application" and unit tests. If you choose a sub-project and run from there, QtCreator runs the chosen sub-project.

In this exercise it is sufficient to concentrate on errors that can be detected by the given unit test. You can write additional tests (more detailed tests can make it easier to locate errors). Running the given unit test is sufficient to detect only those errors that are meant to be detected.

Note! A single bug may cause failure of several test cases and correspondingly failure of a test case can be due to several bugs (in other words, if you find one bug and fix it, the test case can still fail).

SonarQube: static analysis

You can look at SonarQube's analysis of the exercise code here. Check it out.

Code smells are any point in the source code that possible contain a deeper problem. Smells are not necessarily bugs. From the functional point of view the code is correct. They indicate that there is inclarity, weakness or some other issue that can hinder development or increase the risk of bugs.

Technical debt in turn depicts the amount of time it will take to fix the quality attributes of the code. This includes refactoring, technical improvements and making the code clearer as well as other similat updating to clear out easy hacks. Technical debt is seen as something that at some point needs to be done in order to avoid problems. The debt taken must be paid at some point.

Here, as the code base is small, the most useful information is available under coverage. Here the coverage shows the situation where the unit tests have been executed once in release mode. Click on the percentage to see more information. You can investigate the coverage on code file level.

A+ presents the exercise submission form here.

Searching for errors and test printings

In debugging, it is sometimes useful to add test printings, which informs about values of variables and execution phase of the program. Qt has certain objects for debug printings. (Often, pure test printings are not enough, but you should (also) use debugger and its break points).

qDebug is a function, which returns an object (stream) to which you can print with operator << in a normal way. Printing ends automatically with carriage return, and elements to be printed are separated with white spaces:

#include <QtDebug>
...
qDebug() << "One value is" << a << "and the other is" << b;

Using Qt debugger

Unit tests can be run in QtCreator's debugger by pressing the "green arrow and bug" button in bottom left (if you have above it chosen that you want run unit tests in debug state).

QtCreator moves to debug state, where you use e.g. the following tools (more detailed information):

  • Current execution point of the program is shown as an arrow in the left in code window.
  • Call trace/stack window below the code window shows the current call hierarchy of functions. By double clicking a row in the hierarchy, the code window (and data window) moves to show the function in question.
  • Stepping and run buttons can be found at the top of call trace/stack window.
    • Green ball continues execution.
    • Red square stops debugging. Use this to prevent program execution as daemon and wasting of memory.
    • "Step over" button executes program until the next code line is reached. (It continues "over" function calls.)
    • "Step into" button executes program until the next code line is reached and it stops also, if execution moves to another function. (It jumps "into" functions.)
    • "Step out" button executes program until the current function is returned from.
  • Break points can be added by clicking in the code window on the left side of row number. Clicking adds/removes the red ball meaning a break point (break points can also be seen as a list in call trace/stack window on the right). When running a program in debugger, execution stops at every break point. By choosing "edit breakpoint" with the right click of the mouse, you can e.g. change the breakpoint as conditional (condition), when stopping happens only if the given condition is true. Another useful way is to adjust, how many times (ignore count) the breakpoint must be reached before stopping.
  • Data window can be seen on the right from the code window. It shows the values of all local variables and member variables of the object. If the latest action is "step out", then it shows also the return value of the function.

Fun stuff: Add CI

If you want, you can add the test cases of this exercise also to CI (that's where unit tests go).

Posting submission...