Skip to content

QTest: Data-Driven Unit Tests Are Hard To Understand

Today, I looked at the data-driven unit tests I had written nearly four weeks ago. It took me a couple of minutes to understand the tests again. Understanding my own tests should have been much easier.

Data-driven unit tests in QTest have a fundamental problem. The data goes in one function, say testWriteFrame_data, and the test case working on the data goes in another function, say testWriteFrame. I must go back and forth to understand the test case. While going back and forth, I typically forget one piece of information. So, I must do another round trip.

So, I sat down and converted each row of the table created by testWriteFrame_data into a test function of its own. The resulting test cases are much easier to understand. They have about the same code size as the original solution. But see for yourself.

Read More »QTest: Data-Driven Unit Tests Are Hard To Understand

Deploying Qt Projects to Embedded Devices with CMake

When you run a Qt project, QtCreator first deploys the project to a remote embedded Linux device via ssh and then runs the executable on the remote device. This feature enables nearly instant feedback how your Qt application works on the embedded device.

Deployment works fine with the INSTALLS variable of qmake. It does not work out of the box with the install functions of CMake. Fortunately, the Qt trolls provide a workaround. I’ll explain the workaround with an example CMakeLists.txt file.

Read More »Deploying Qt Projects to Embedded Devices with CMake

QCOMPARE with Custom Qt Types

When you write unit tests, you will have to compare the actual value and the expected value. A simplified example with QStrings would look like this.

void MyTest::testQCompare() {
    auto actualStr = QString{"abba"};
    auto expectedStr = QString{"juhu"};
    QCOMPARE(actualStr, expectedStr);
}

When you run this unit test, the QTest framework will print the actual value "abba" and the expected value "juhu". This is often enough to know what went wrong.

FAIL!  : MyTest::testQCompare() Compared values are not the same
   Actual   (actualStr)  : "abba"
   Expected (expectedStr): "juhu"

If two values of a custom type differ, the QTest framework will only print the first line: Compared values are not the same. This is not very helpful. How can you make QCOMPARE print the actual and expected for custom types as well?

Read More »QCOMPARE with Custom Qt Types

Printing Custom Qt Types with qDebug()

When you define your own C++ types for a Qt application, you want to print their values with qDebug(), qWarning() or qCritical() eventually.

auto frame1 = QCanBusFrame{0x18ef0201U, QByteArray::fromHex("018A010000000000")};
qDebug() << "frame1 =" << frame1;

The C++ compiler will complain with this error message:

error: no match for ‘operator<<’ (operand types are ‘QDebug’ and ‘QCanBusFrame’)

The error message tells you to write your own output operator for the QDebug stream and for QCanBusFrame.

A custom C++ type that wants to play nicely with Qt should always define a QDebug output operator. Even the developers of the Qt library seem to forget it sometimes, as the example of QCanBusFrame shows.

Read More »Printing Custom Qt Types with qDebug()

Lessons from Six Years as a Solo Consultant

The beginning of this month marked my sixth anniversary of becoming a solo consultant. I don’t regret my decision and cannot imagine ever giving up solo consulting. I was essential in the implementation of some really interesting projects like infotainment systems for cars and driver terminals for sugar beet and forage harvesters. The income from these projects gives me the financial freedom to enjoy life more than ever before.

I took my anniversary as the cause for reflecting about what I should have done differently. I learned three main lessons.

  • Hourly billing runs against the best interest of your customers. As a solo consultant, you have no interest in becoming more productive. The longer hours you work the higher your income. You can only change this, if you Don’t Bill by the Hour but charge a value-based fee.
  • Charging customers for the value you provide becomes much simpler if you have a strong brand. A strong brand will Make Potential Customers Call You. You don’t have to search for projects any more, but projects will find you.
  • Productised Services are part product and part service. The product part is the same for all customers, whereas the service part is specific to each customer. You maximise your profit by minimising the time you spend on the service part.

I will heed the conclusions drawn from these lessons and implement them step by step. How about you?

Read More »Lessons from Six Years as a Solo Consultant

Speaking CAN: Write Buffer Overflow

In the first post of the Speaking CAN series, we learned how the terminal sends a read-parameter request to the ECU and how the ECU sends a response with the value of the parameter back to the terminal. This works fine, as long as the terminal does not send too many requests too fast.

We set the size txqueuelen of the write or TX buffer to 10, which is the default for many Yocto-based Linux systems. If the terminal writes, say, 50 requests to the CAN bus without any pause, we’ll see several error messages No buffer space available in the log window of the terminal. The terminal caused an overflow of the write buffer in the CAN controller.

If we have a request-and-response scenario, the terminal can wait for the response, before it sends the next request. If the response does not arrive within a certain time, the terminal flags an error.

Avoiding a write buffer overflow becomes more difficult, if the terminal sends out messages without expecting a response or if the terminal expects a response only every, say, 200 messages (e.g., an acknowledgement how many messages the ECU has received). The solution is to configure the CAN controller of the terminal to receive its own messages.

Read More »Speaking CAN: Write Buffer Overflow

Speaking CAN: Request and Response

Controller Area Networks (CANs, for short) are used for the communication between electronic control units (ECUs) in cars, trucks, tractors, harvesters, construction machines, packaging lines and e-bikes. Qt provides the Qt CAN Bus API as part of its QtSerialBus module. Qt CAN Bus provides a clean and simple API to connect to a CAN bus, to send and receive CAN frames over the CAN bus.

When we build an HMI application to control a machine, we need a lot more functionality than the Qt CAN Bus API should offer. The HMI application must not send CAN frames too fast to avoid buffer overflows in the CAN controller. When the HMI application receives, say, more than 200 CAN frames per second and tries to display the changes, it will freeze. The HMI application must be able to recover from CAN bus errors. We do not want to write the code for serialising and deserialising thousands of CAN messages manually, but want to generate this code automatically.

I’ll show solutions to these problems in a loose series of blog posts called Speaking CAN. In this first post, I’ll explain how an HMI application running on a display computer retrieves the value of a parameter from an ECU. The terminal sends a request to the ECU over CAN and receives a response from the ECU over CAN.

Read More »Speaking CAN: Request and Response

Using Qt under LGPLv3

At Qt Day 2019 in Florence, Italy, I gave a talk Using Qt under LGPLv3. In the first part The obligations of Qt LGPLv3: made understandable, I explain in detail, how you must provide the source code, the license texts, the copyright notices, any modification notices and the installation information. You don’t have to provide the installation information for B2B products like driver terminals in commercial vehicles, industrial devices and medical devices in hospitals. In other words, you don’t have to satisfy the anti-tivoisation rule for B2B products.

Read More »Using Qt under LGPLv3