This commit is contained in:
deeaitch committed 2026-10-07 20:18:37 -04:00
1 parent ef8e21db2e
commit a6016248f8
11 files changed
+529 -252

No files matched your search

+33 -8
View File
@@ -1,14 +1,20 @@
# QtTest regression tests
For instrumented builds, LCOV HTML reports, Qt Creator coverage configuration,
and Coco installation, see [Measuring code coverage](../README.md#measuring-code-coverage).
For instrumented builds, LCOV HTML reports, and Qt Creator coverage configuration,
see [Measuring code coverage](../README.md#measuring-code-coverage).
For the complete IDE build/run configuration set, see
[Configure all build variants in Qt Creator](../README.md#configure-all-build-variants-in-qt-creator).
The Coverage configuration uses `coverage_report.sh` after its instrumented
build; the script takes the source and build directories as its two arguments.
Open the repository's root `dbc.pro` in Qt Creator. It contains `core` (a static
C++17 library) and `tests` (the `dbc_tests` QtTest executable). Core is built first;
tests link its archive rather than compiling its implementation again.
Qt Core and Qt Test development packages are required for tests only.
The optional model tests also link QtGui, which is provided by Qt base development
packages. The adapter library itself still links only QtCore.
Build from the repository root with Qt 6 (use `qmake` for Qt 5):
Build from the repository root with the installed Qt 6.8.2:
```sh
mkdir build
@@ -18,8 +24,8 @@ make -j
make check
```
Or run `./tests/dbc_tests` directly. Both parser and decoder suites run, and the
executable returns nonzero when either suite fails. Decoder cases appear as
Or run `./tests/dbc_tests` directly. Parser, decoder, and additional coverage
suites run, and the executable returns nonzero when any suite fails. Decoder cases appear as
individual QtTest data rows; parser checks are grouped into named test slots.
Checks also run in release builds.
@@ -34,7 +40,8 @@ make
make check
```
`parser_tests.pri` and `decoder_tests.pri` include the test sources.
`parser_tests.pri` and `decoder_tests.pri` include the original test sources;
`tests.pro` also includes `dbc_coverage_test.cpp`.
`../core/core.pri` supplies the library include path and linkage.
## Decoder coverage
@@ -73,14 +80,32 @@ README for its compiler command. It checks parents, sibling positions, null
children, bounds, and unique_ptr transfer.
Enable `CONFIG+=dbc_qt` in the root qmake invocation to build `dbc_qt` and
`dbc_model_tests` alongside existing tests. Qt 5.11+ or Qt 6 is required.
`dbc_model_tests` alongside existing tests. Qt 6.8.2 is required.
`make check` runs all suites. Model tests parse a temporary DBC fixture and use
QAbstractItemModelTester, verify navigation across columns, invalid/foreign
indexes, metadata and exact VAL_ integers, and persistent-index invalidation
when reloading or clearing the tree. No GUI/display server is needed.
when reloading or clearing the tree. Their QGuiApplication registers graphical
metatypes used by QAbstractItemModelTester, avoiding the invalid QPixmap type
warning in Qt 6.8.2. The test executable defaults to `QT_QPA_PLATFORM=offscreen`
when the variable is unset or empty, so no display server is needed. An explicit
platform setting is preserved.
Basic multiplexing coverage includes selector declarations after branches,
Intel/Motorola branch overlap, signed active values and VAL_ lookup, unconditional
signals, inactive branches, missing selector/branch payloads, exact UINT64_MAX
selector matching, malformed runtime selectors, source locations, and rejection
of invalid markers, selector references/ranges, overlaps, and SG_MUL_VAL_.
## Additional regression coverage
`dbc_coverage_test.cpp` runs as part of `dbc_tests`. It covers signed boundaries
below 64 bits, deterministic extraction comparisons for both byte orders across
lengths 1–64, frame lookup failures, exact decode statuses, malformed runtime
multiplexing, PGN boundaries, comment handling, file formatting, metadata copying
and source lifetime, physical conversion, and TypeFactory registration/creation.
Direct resolver tests construct ambiguous databases through the public metadata
API, verify source locations, and check that ambiguous targets receive no values.
All QtTest targets use Qt 6.8.2 and `QVERIFY_THROWS_EXCEPTION`. Exception tests
explicitly discard `nodiscard` results when only the exception is being checked.
The standalone tree test requires only a C++17 compiler.
+44
View File
@@ -0,0 +1,44 @@
#!/bin/sh
# Run after building dbc.pro with GCC coverage and CONFIG+=dbc_qt.
set -eu
if [ "$#" -ne 2 ]; then
echo "Usage: $0 SOURCE_DIRECTORY BUILD_DIRECTORY" >&2
exit 2
fi
dbc_source_dir=$(cd "$1" && pwd)
dbc_build_dir=$(cd "$2" && pwd)
cd "$dbc_build_dir"
if [ ! -f core/dbc_decoder.gcno ] || [ ! -f qt/dbc_tree_model.gcno ]; then
echo "Build with --coverage and CONFIG+=dbc_qt before generating the report." >&2
exit 1
fi
mkdir -p standalone
"${CXX:-c++}" -std=c++17 -O0 -g --coverage -fprofile-abs-path \
-I"$dbc_source_dir/core" \
"$dbc_source_dir/tests/tree_node_test.cpp" \
"$dbc_source_dir/core/tree_node.cpp" \
"$dbc_source_dir/core/dbc_tree_builder.cpp" \
-o standalone/tree_node_tests
lcov --capture --initial --directory . --branch-coverage \
--include "$dbc_source_dir/core/*" --include "$dbc_source_dir/qt/*" \
--output-file baseline.info --quiet
lcov --zerocounters --directory . --quiet
./tests/dbc_tests
./tests/dbc_model_tests
./standalone/tree_node_tests
lcov --capture --directory . --branch-coverage \
--include "$dbc_source_dir/core/*" --include "$dbc_source_dir/qt/*" \
--output-file executed.info --quiet
lcov --add-tracefile baseline.info --add-tracefile executed.info \
--branch-coverage --output-file coverage.info --quiet
genhtml coverage.info --branch-coverage --demangle-cpp \
--output-directory html --quiet
lcov --summary coverage.info --branch-coverage
echo "HTML report: $dbc_build_dir/html/index.html"
+253 -176
View File
@@ -1,64 +1,65 @@
// Additional regression coverage. Kept separate for review before the first run.
#include "dbc_parser.h"
#include "dbc_decode_builder.h"
#include "dbc_decoder.h"
#include "dbc_tree_builder.h"
#include "dbc_parser.h"
#include "dbc_statement_handler.h"
#include "dbc_tree_builder.h"
#include "typefactory.h"
#include <QtTest>
#include <QTemporaryFile>
#include <QtTest>
#include <array>
#include <limits>
#include <stdexcept>
namespace {
DbcDatabase ParseText(const std::string &text) {
QTemporaryFile file;
if (!file.open()) throw std::runtime_error("Cannot create DBC fixture");
if (file.write(text.data(), static_cast<qint64>(text.size())) != static_cast<qint64>(text.size()) || !file.flush())
throw std::runtime_error("Cannot write DBC fixture");
return DbcParser().ParseFile(file.fileName().toStdString());
}
DecodeDatabase SingleSignal(const DecodeSignal &signal) {
DecodeDatabase database;
DecodeFrame frame;
frame.canId = 17U;
frame.signals.push_back(signal);
database.frames.push_back(frame);
database.frameIndexByKey.emplace(FrameKey(17U, false), 0U);
return database;
}
// Independent reference: collect the visited bits first, then weight them.
// Motorola uses a linear MSB-first coordinate instead of sawtooth traversal.
std::uint64_t ReferenceExtract(const std::vector<std::uint8_t> &data,
ByteOrder order, unsigned start, unsigned length) {
std::uint64_t expected = 0;
for (unsigned i = 0; i < length; ++i) {
const unsigned coordinate = order == ByteOrder::Intel
? start + i : (start / 8U) * 8U + (7U - start % 8U) + i;
const unsigned bit = order == ByteOrder::Intel ? coordinate % 8U : 7U - coordinate % 8U;
const unsigned weight = order == ByteOrder::Intel ? i : length - 1U - i;
expected |= std::uint64_t((data.at(coordinate / 8U) >> bit) & 1U) << weight;
DbcDatabase ParseText(const std::string &text) {
QTemporaryFile file;
if (!file.open()) throw std::runtime_error("Cannot create DBC fixture");
if (file.write(text.data(), static_cast<qint64>(text.size())) != static_cast<qint64>(text.size()) || !file.flush())
throw std::runtime_error("Cannot write DBC fixture");
return DbcParser().ParseFile(file.fileName().toStdString());
}
return expected;
}
struct FactoryBase {
virtual ~FactoryBase() = default;
virtual int Value() const = 0;
};
struct FactoryFirst : FactoryBase {
explicit FactoryFirst(std::unique_ptr<int> value) : value(std::move(value)) {}
int Value() const override { return *value; }
std::unique_ptr<int> value;
};
struct FactorySecond : FactoryFirst {
using FactoryFirst::FactoryFirst;
int Value() const override { return *value + 1; }
};
}
DecodeDatabase SingleSignal(const DecodeSignal &signal) {
DecodeDatabase database;
DecodeFrame frame;
frame.canId = 17U;
frame.signals.push_back(signal);
database.frames.push_back(frame);
database.frameIndexByKey.emplace(FrameKey(17U, false), 0U);
return database;
}
// Independent reference: collect the visited bits first, then weight them.
// Motorola uses a linear MSB-first coordinate instead of sawtooth traversal.
std::uint64_t ReferenceExtract(const std::vector<std::uint8_t> &data,
ByteOrder order, unsigned start, unsigned length) {
std::uint64_t expected = 0;
for (unsigned i = 0; i < length; ++i) {
const unsigned coordinate = order == ByteOrder::Intel
? start + i
: (start / 8U) * 8U + (7U - start % 8U) + i;
const unsigned bit = order == ByteOrder::Intel ? coordinate % 8U : 7U - coordinate % 8U;
const unsigned weight = order == ByteOrder::Intel ? i : length - 1U - i;
expected |= std::uint64_t((data.at(coordinate / 8U) >> bit) & 1U) << weight;
}
return expected;
}
struct FactoryBase {
virtual ~FactoryBase() = default;
virtual int Value() const = 0;
};
struct FactoryFirst : FactoryBase {
explicit FactoryFirst(std::unique_ptr<int> value) : value(std::move(value)) {}
int Value() const override { return *value; }
std::unique_ptr<int> value;
};
struct FactorySecond : FactoryFirst {
using FactoryFirst::FactoryFirst;
int Value() const override { return *value + 1; }
};
}// namespace
class CoverageTest : public QObject {
Q_OBJECT
@@ -100,12 +101,12 @@ void CoverageTest::frameLookup() {
raw.canId = 17U;
DecodeDatabase empty;
QVERIFY(decoder.FindFrame(empty, 17U, false) == nullptr);
const auto absent = decoder.Decode(empty, raw);
const DecodedFrameValue absent = decoder.Decode(empty, raw);
QVERIFY(!absent.valid);
QVERIFY(absent.definition == nullptr);
QVERIFY(absent.signals.empty());
DecodeSignal signal;
auto database = SingleSignal(signal);
DecodeDatabase database = SingleSignal(signal);
QVERIFY(decoder.FindFrame(database, 18U, false) == nullptr);
QVERIFY(decoder.FindFrame(database, 17U, true) == nullptr);
raw.isExtended = true;
@@ -119,7 +120,7 @@ void CoverageTest::frameLookup() {
QVERIFY(!decoder.Decode(database, raw).valid);
database.frameIndexByKey.at(FrameKey(17U, false)) = 0U;
database.frames[0].signals.clear();
const auto matched = decoder.Decode(database, raw);
const DecodedFrameValue matched = decoder.Decode(database, raw);
QVERIFY(matched.valid);
QCOMPARE(matched.definition, &database.frames[0]);
QVERIFY(matched.signals.empty());
@@ -130,11 +131,11 @@ void CoverageTest::signedBoundaries_data() {
QTest::addColumn<unsigned>("length");
QTest::addColumn<qlonglong>("expected");
for (int order = 0; order < 2; ++order) {
for (unsigned length : {1U, 2U, 7U, 8U, 31U, 32U, 63U}) {
for (unsigned length: {1U, 2U, 7U, 8U, 31U, 32U, 63U}) {
const std::int64_t half = std::int64_t(1) << (length - 1U);
const std::array<std::int64_t, 4> values{{0, -1, -half, half - 1}};
for (unsigned index = 0; index < values.size(); ++index) {
const auto name = QByteArray::number(order) + "-" + QByteArray::number(length) + "-" + QByteArray::number(index);
const QByteArray name = QByteArray::number(order) + "-" + QByteArray::number(length) + "-" + QByteArray::number(index);
QTest::newRow(name.constData()) << order << length << qlonglong(values[index]);
}
}
@@ -159,8 +160,8 @@ void CoverageTest::signedBoundaries() {
const unsigned position = order == 0 ? i % 8U : 7U - i % 8U;
raw.data[i / 8U] |= static_cast<std::uint8_t>(((bits >> weight) & 1U) << position);
}
const auto database = SingleSignal(signal);
const auto result = DbcDecoder().Decode(database, raw);
const DecodeDatabase database = SingleSignal(signal);
const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
QVERIFY(result.signals[0].valid);
QVERIFY(std::holds_alternative<std::int64_t>(result.signals[0].rawValue));
QCOMPARE(std::get<std::int64_t>(result.signals[0].rawValue), static_cast<std::int64_t>(expected));
@@ -173,7 +174,7 @@ void CoverageTest::extraction_data() {
for (int order = 0; order < 2; ++order)
for (unsigned start = 0; start < 16U; ++start)
for (unsigned length = 1; length <= 64U; ++length) {
const auto name = QByteArray::number(order) + "-" + QByteArray::number(start) + "-" + QByteArray::number(length);
const QByteArray name = QByteArray::number(order) + "-" + QByteArray::number(start) + "-" + QByteArray::number(length);
QTest::newRow(name.constData()) << order << start << length;
}
}
@@ -186,17 +187,18 @@ void CoverageTest::extraction() {
signal.byteOrder = order == 0 ? ByteOrder::Intel : ByteOrder::Motorola;
signal.startBit = start;
signal.length = length;
const auto database = SingleSignal(signal);
const DecodeDatabase database = SingleSignal(signal);
RawCanFrame raw;
raw.canId = 17U;
const std::vector<std::vector<std::uint8_t>> patterns = {
std::vector<std::uint8_t>(10U, 0U), std::vector<std::uint8_t>(10U, 255U),
{0xA5, 0x3C, 0x81, 0x7E, 0x12, 0xD4, 0x69, 0xB0, 0x5A, 0xC3}};
for (const auto &data : patterns) {
std::vector<std::uint8_t>(10U, 0U),
std::vector<std::uint8_t>(10U, 255U),
{0xA5, 0x3C, 0x81, 0x7E, 0x12, 0xD4, 0x69, 0xB0, 0x5A, 0xC3}};
for (const std::vector<std::uint8_t> &data: patterns) {
raw.data = data;
const unsigned firstCoordinate = order == 0 ? start : (start / 8U) * 8U + 7U - start % 8U;
raw.data.resize((firstCoordinate + length + 7U) / 8U);
const auto decoded = DbcDecoder().Decode(database, raw);
const DecodedFrameValue decoded = DbcDecoder().Decode(database, raw);
QVERIFY(decoded.signals[0].valid);
QCOMPARE(std::get<std::uint64_t>(decoded.signals[0].rawValue), ReferenceExtract(data, signal.byteOrder, start, length));
}
@@ -210,7 +212,7 @@ void CoverageTest::invalidSignalStatus_data() {
QTest::addColumn<int>("status");
for (int order = 0; order < 2; ++order) {
const unsigned start = order == 0 ? 0U : 7U;
const auto prefix = QByteArray::number(order);
const QByteArray prefix = QByteArray::number(order);
QTest::newRow((prefix + "-zero").constData()) << order << start << 0U << 1U << int(SignalDecodeStatus::InvalidDefinition);
QTest::newRow((prefix + "-65").constData()) << order << start << 65U << 9U << int(SignalDecodeStatus::InvalidDefinition);
QTest::newRow((prefix + "-empty").constData()) << order << start << 8U << 0U << int(SignalDecodeStatus::InsufficientPayload);
@@ -220,14 +222,20 @@ void CoverageTest::invalidSignalStatus_data() {
}
void CoverageTest::invalidSignalStatus() {
QFETCH(int, order); QFETCH(unsigned, start); QFETCH(unsigned, length);
QFETCH(unsigned, bytes); QFETCH(int, status);
QFETCH(int, order);
QFETCH(unsigned, start);
QFETCH(unsigned, length);
QFETCH(unsigned, bytes);
QFETCH(int, status);
DecodeSignal signal;
signal.startBit = start; signal.length = length;
signal.startBit = start;
signal.length = length;
signal.byteOrder = order == 0 ? ByteOrder::Intel : ByteOrder::Motorola;
const auto database = SingleSignal(signal);
RawCanFrame raw; raw.canId = 17U; raw.data.assign(bytes, 255U);
const auto decoded = DbcDecoder().Decode(database, raw);
const DecodeDatabase database = SingleSignal(signal);
RawCanFrame raw;
raw.canId = 17U;
raw.data.assign(bytes, 255U);
const DecodedFrameValue decoded = DbcDecoder().Decode(database, raw);
QVERIFY(decoded.valid);
QVERIFY(!decoded.signals[0].valid);
QCOMPARE(int(decoded.signals[0].status), status);
@@ -244,19 +252,28 @@ void CoverageTest::malformedRuntimeMultiplexing_data() {
void CoverageTest::malformedRuntimeMultiplexing() {
QFETCH(int, kind);
DecodeSignal selector; selector.length = 1U; selector.multiplexing.role = MultiplexRole::Selector;
auto database = SingleSignal(selector);
DecodeSignal branch; branch.startBit = 8U; branch.length = 8U;
branch.multiplexing.role = MultiplexRole::Branch; branch.multiplexing.value = 1U;
DecodeSignal selector;
selector.length = 1U;
selector.multiplexing.role = MultiplexRole::Selector;
DecodeDatabase database = SingleSignal(selector);
DecodeSignal branch;
branch.startBit = 8U;
branch.length = 8U;
branch.multiplexing.role = MultiplexRole::Branch;
branch.multiplexing.value = 1U;
database.frames[0].signals.push_back(branch);
DecodeSignal ordinary; ordinary.startBit = 16U; ordinary.length = 8U;
DecodeSignal ordinary;
ordinary.startBit = 16U;
ordinary.length = 8U;
database.frames[0].signals.push_back(ordinary);
if (kind == 0) database.frames[0].signals.push_back(selector);
if (kind == 1) database.frames[0].signals[0].valueType = ValueType::Signed;
if (kind == 2) database.frames[0].signals[0].length = 65U;
if (kind == 3) database.frames[0].signals[1].multiplexing.value = 2U;
RawCanFrame raw; raw.canId = 17U; raw.data = {1U, 42U, 99U};
const auto result = DbcDecoder().Decode(database, raw);
RawCanFrame raw;
raw.canId = 17U;
raw.data = {1U, 42U, 99U};
const DecodedFrameValue result = DbcDecoder().Decode(database, raw);
QCOMPARE(int(result.signals[1].status), int(SignalDecodeStatus::InvalidDefinition));
QVERIFY(!result.signals[1].valid);
QVERIFY(result.signals[2].valid);
@@ -268,28 +285,34 @@ void CoverageTest::malformedRuntimeMultiplexing() {
}
void CoverageTest::pgn_data() {
QTest::addColumn<unsigned>("id"); QTest::addColumn<unsigned>("expected"); QTest::addColumn<bool>("hasPgn");
QTest::addColumn<unsigned>("id");
QTest::addColumn<unsigned>("expected");
QTest::addColumn<bool>("hasPgn");
QTest::newRow("standard") << 0x7FFU << 0U << false;
for (unsigned dp : {0U, 1U})
for (unsigned pf : {239U, 240U})
for (unsigned ps : {0U, 0xABU}) {
for (unsigned dp: {0U, 1U})
for (unsigned pf: {239U, 240U})
for (unsigned ps: {0U, 0xABU}) {
const unsigned id = 0x80000000U | (dp << 24U) | (pf << 16U) | (ps << 8U) | 0x42U;
const unsigned expected = (dp << 16U) | (pf == 239U ? 0xEF00U : 0xF000U | ps);
const auto name = QByteArray::number(dp) + "-" + QByteArray::number(pf) + "-" + QByteArray::number(ps);
const QByteArray name = QByteArray::number(dp) + "-" + QByteArray::number(pf) + "-" + QByteArray::number(ps);
QTest::newRow(name.constData()) << id << expected << true;
}
}
void CoverageTest::pgn() {
QFETCH(unsigned, id); QFETCH(unsigned, expected); QFETCH(bool, hasPgn);
const auto parsed = ParseText("BO_ " + std::to_string(id) + " Frame: 8 ECU\n");
QFETCH(unsigned, id);
QFETCH(unsigned, expected);
QFETCH(bool, hasPgn);
const DbcDatabase parsed = ParseText("BO_ " + std::to_string(id) + " Frame: 8 ECU\n");
QCOMPARE(parsed.frames[0].hasPgn, hasPgn);
QCOMPARE(parsed.frames[0].pgn, expected);
}
void CoverageTest::comments_data() {
QTest::addColumn<QString>("before"); QTest::addColumn<QString>("after");
QTest::addColumn<QString>("frameComment"); QTest::addColumn<QString>("signalComment");
QTest::addColumn<QString>("before");
QTest::addColumn<QString>("after");
QTest::addColumn<QString>("frameComment");
QTest::addColumn<QString>("signalComment");
QTest::newRow("tabs") << QString::fromUtf8("") << QString::fromUtf8("CM_\tBO_ 1 \"Frame\";\nCM_\tSG_ 1 State \"Signal\";\n") << QString::fromUtf8("Frame") << QString::fromUtf8("Signal");
QTest::newRow("empty-overwrite") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 \"Old\";\nCM_ BO_ 1 \"\";\nCM_ SG_ 1 State \"Old\";\nCM_ SG_ 1 State \"\";\n") << QString::fromUtf8("") << QString::fromUtf8("");
QTest::newRow("last-wins") << QString::fromUtf8("") << QString::fromUtf8("CM_ BO_ 1 \"First\";\nCM_ BO_ 1 \"Last\";\nCM_ SG_ 1 State \"First\";\nCM_ SG_ 1 State \"Last\";\n") << QString::fromUtf8("Last") << QString::fromUtf8("Last");
@@ -300,8 +323,11 @@ void CoverageTest::comments_data() {
}
void CoverageTest::comments() {
QFETCH(QString, before); QFETCH(QString, after); QFETCH(QString, frameComment); QFETCH(QString, signalComment);
const auto parsed = ParseText(before.toStdString() + "BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n" + after.toStdString());
QFETCH(QString, before);
QFETCH(QString, after);
QFETCH(QString, frameComment);
QFETCH(QString, signalComment);
const DbcDatabase parsed = ParseText(before.toStdString() + "BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n" + after.toStdString());
QCOMPARE(parsed.frames[0].comment, frameComment.toStdString());
QCOMPARE(parsed.frames[0].signals[0].comment, signalComment.toStdString());
}
@@ -384,12 +410,12 @@ void CoverageTest::ambiguousValueReferences() {
QFAIL("The resolver must reject an ambiguous target");
} catch (const std::runtime_error &error) {
const std::string message = duplicateFrames ? "Ambiguous VAL_ frame reference."
: "Ambiguous VAL_ signal reference: State";
: "Ambiguous VAL_ signal reference: State";
QCOMPARE(std::string(error.what()), path + ":37: " + message);
}
// Ambiguity must be detected before applying any descriptions.
for (const auto &candidate : database.frames)
for (const auto &candidateSignal : candidate.signals)
for (const FrameInfo &candidate: database.frames)
for (const SignalInfo &candidateSignal: candidate.signals)
QVERIFY(candidateSignal.valueDescriptions.empty());
}
@@ -415,8 +441,8 @@ void CoverageTest::valueReferenceScope() {
handler.Handle("VAL_ 2147483649 State 0 \"Extended\";", context);
context.ResolveValueDescriptions("manual database.dbc");
const auto *standard = FindValueDescription(database.frames[0].signals[0].valueDescriptions, std::uint64_t(0));
const auto *extended = FindValueDescription(database.frames[1].signals[0].valueDescriptions, std::uint64_t(0));
const std::string *standard = FindValueDescription(database.frames[0].signals[0].valueDescriptions, std::uint64_t(0));
const std::string *extended = FindValueDescription(database.frames[1].signals[0].valueDescriptions, std::uint64_t(0));
QVERIFY(standard != nullptr);
QVERIFY(extended != nullptr);
QCOMPARE(*standard, std::string("Standard"));
@@ -425,7 +451,8 @@ void CoverageTest::valueReferenceScope() {
}
void CoverageTest::fileFormatting_data() {
QTest::addColumn<QString>("text"); QTest::addColumn<unsigned>("frames");
QTest::addColumn<QString>("text");
QTest::addColumn<unsigned>("frames");
QTest::newRow("empty") << QString::fromUtf8("") << 0U;
QTest::newRow("whitespace") << QString::fromUtf8(" \t\r\n\n\t") << 0U;
QTest::newRow("unknown-only") << QString::fromUtf8("VERSION \"test\"\nBU_: ECU\nUNKNOWN_ data\n") << 0U;
@@ -434,14 +461,15 @@ void CoverageTest::fileFormatting_data() {
}
void CoverageTest::fileFormatting() {
QFETCH(QString, text); QFETCH(unsigned, frames);
const auto parsed = ParseText(text.toStdString());
QFETCH(QString, text);
QFETCH(unsigned, frames);
const DbcDatabase parsed = ParseText(text.toStdString());
QCOMPARE(parsed.frames.size(), std::size_t(frames));
if (frames) {
QCOMPARE(parsed.frames[0].signals.size(), std::size_t(1));
if (text.contains("\r\nBO_")) {
QCOMPARE(parsed.frames[0].comment, std::string("Comment"));
const auto *description = FindValueDescription(parsed.frames[0].signals[0].valueDescriptions, std::uint64_t(0));
const std::string *description = FindValueDescription(parsed.frames[0].signals[0].valueDescriptions, std::uint64_t(0));
QVERIFY(description != nullptr);
QCOMPARE(*description, std::string("Off"));
}
@@ -451,31 +479,33 @@ void CoverageTest::fileFormatting() {
void CoverageTest::missingFile() {
QTemporaryFile file;
QVERIFY(file.open());
const auto path = file.fileName();
const QString path = file.fileName();
QVERIFY(file.remove());
try { DbcParser().ParseFile(path.toStdString()); QFAIL("Missing file must throw"); }
catch (const std::runtime_error &error) {
try {
DbcParser().ParseFile(path.toStdString());
QFAIL("Missing file must throw");
} catch (const std::runtime_error &error) {
QCOMPARE(std::string(error.what()), "Failed to open DBC file: " + path.toStdString());
}
}
void CoverageTest::builders() {
const auto emptyRuntime = DbcDecodeBuilder().Build(DbcDatabase());
const DecodeDatabase emptyRuntime = DbcDecodeBuilder().Build(DbcDatabase());
QVERIFY(emptyRuntime.frames.empty());
QVERIFY(emptyRuntime.frameIndexByKey.empty());
const auto emptyTree = DbcTreeBuilder().Build(DbcDatabase());
const std::unique_ptr<TreeNode> emptyTree = DbcTreeBuilder().Build(DbcDatabase());
QCOMPARE(emptyTree->GetType(), NodeType::Root);
QCOMPARE(emptyTree->GetName(), std::string("dbc"));
QCOMPARE(emptyTree->GetChildCount(), std::size_t(0));
QVERIFY(emptyTree->GetFrame() == nullptr && emptyTree->GetSignal() == nullptr);
const auto expected = ParseText(
"BO_ 256 Standard: 3 ECU1\n"
"SG_ Mode M : 0|8@1+ (2,3) [0|513] \"mode\" ECU1,ECU2\n"
"SG_ Branch m1 : 15|8@0- (-2,5) [-249|261] \"rpm\" ECU2,ECU3\n"
"SG_ Always : 16|8@1+ (0.25,-1) [-1|62.75] \"V\" ECU3\n"
"CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Branch \"Branch comment\";\n"
"VAL_ 256 Branch -1 \"Negative\" 1 \"Positive\";\n"
"BO_ 2163212866 Extended: 8 ECU4\n");
const DbcDatabase expected = ParseText(
"BO_ 256 Standard: 3 ECU1\n"
"SG_ Mode M : 0|8@1+ (2,3) [0|513] \"mode\" ECU1,ECU2\n"
"SG_ Branch m1 : 15|8@0- (-2,5) [-249|261] \"rpm\" ECU2,ECU3\n"
"SG_ Always : 16|8@1+ (0.25,-1) [-1|62.75] \"V\" ECU3\n"
"CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 Branch \"Branch comment\";\n"
"VAL_ 256 Branch -1 \"Negative\" 1 \"Positive\";\n"
"BO_ 2163212866 Extended: 8 ECU4\n");
DecodeDatabase runtime;
std::unique_ptr<TreeNode> tree;
{
@@ -500,44 +530,62 @@ void CoverageTest::builders() {
QVERIFY(node->GetSignal() == nullptr);
QCOMPARE(runtime.frameIndexByKey.at(FrameKey(frame.canId, frame.isExtended)), i);
const FrameInfo &copied = *node->GetFrame();
QCOMPARE(copied.name, frame.name); QCOMPARE(decodedFrame.name, frame.name);
QCOMPARE(copied.canId, frame.canId); QCOMPARE(decodedFrame.canId, frame.canId);
QCOMPARE(copied.isExtended, frame.isExtended); QCOMPARE(decodedFrame.isExtended, frame.isExtended);
QCOMPARE(copied.dlc, frame.dlc); QCOMPARE(decodedFrame.dlc, frame.dlc);
QCOMPARE(copied.pgn, frame.pgn); QCOMPARE(decodedFrame.pgn, frame.pgn);
QCOMPARE(copied.hasPgn, frame.hasPgn); QCOMPARE(decodedFrame.hasPgn, frame.hasPgn);
QCOMPARE(copied.transmitter, frame.transmitter); QCOMPARE(decodedFrame.transmitter, frame.transmitter);
QCOMPARE(copied.comment, frame.comment); QCOMPARE(decodedFrame.comment, frame.comment);
QCOMPARE(copied.name, frame.name);
QCOMPARE(decodedFrame.name, frame.name);
QCOMPARE(copied.canId, frame.canId);
QCOMPARE(decodedFrame.canId, frame.canId);
QCOMPARE(copied.isExtended, frame.isExtended);
QCOMPARE(decodedFrame.isExtended, frame.isExtended);
QCOMPARE(copied.dlc, frame.dlc);
QCOMPARE(decodedFrame.dlc, frame.dlc);
QCOMPARE(copied.pgn, frame.pgn);
QCOMPARE(decodedFrame.pgn, frame.pgn);
QCOMPARE(copied.hasPgn, frame.hasPgn);
QCOMPARE(decodedFrame.hasPgn, frame.hasPgn);
QCOMPARE(copied.transmitter, frame.transmitter);
QCOMPARE(decodedFrame.transmitter, frame.transmitter);
QCOMPARE(copied.comment, frame.comment);
QCOMPARE(decodedFrame.comment, frame.comment);
QCOMPARE(node->GetChildCount(), frame.signals.size());
QCOMPARE(copied.signals.size(), frame.signals.size());
QCOMPARE(decodedFrame.signals.size(), frame.signals.size());
for (std::size_t j = 0; j < frame.signals.size(); ++j) {
const auto &original = frame.signals[j];
const auto &target = decodedFrame.signals[j];
const auto *leaf = node->GetChild(j);
const SignalInfo &original = frame.signals[j];
const DecodeSignal &target = decodedFrame.signals[j];
const TreeNode *leaf = node->GetChild(j);
QVERIFY(leaf && leaf->GetSignal());
QCOMPARE(leaf->GetType(), NodeType::Signal);
QCOMPARE(leaf->GetName(), original.name);
QVERIFY(leaf->GetFrame() == nullptr);
// Check both the frame payload copy and each child payload copy.
for (const SignalInfo *copy : {&copied.signals[j], leaf->GetSignal()}) {
QCOMPARE(copy->name, original.name); QCOMPARE(copy->startBit, original.startBit);
QCOMPARE(copy->length, original.length); QCOMPARE(copy->isLittleEndian, original.isLittleEndian);
QCOMPARE(copy->isSigned, original.isSigned); QCOMPARE(copy->factor, original.factor);
QCOMPARE(copy->offset, original.offset); QCOMPARE(copy->minimum, original.minimum);
QCOMPARE(copy->maximum, original.maximum); QCOMPARE(copy->unit, original.unit);
QCOMPARE(copy->comment, original.comment); QVERIFY(copy->receivers == original.receivers);
for (const SignalInfo *copy: {&copied.signals[j], leaf->GetSignal()}) {
QCOMPARE(copy->name, original.name);
QCOMPARE(copy->startBit, original.startBit);
QCOMPARE(copy->length, original.length);
QCOMPARE(copy->isLittleEndian, original.isLittleEndian);
QCOMPARE(copy->isSigned, original.isSigned);
QCOMPARE(copy->factor, original.factor);
QCOMPARE(copy->offset, original.offset);
QCOMPARE(copy->minimum, original.minimum);
QCOMPARE(copy->maximum, original.maximum);
QCOMPARE(copy->unit, original.unit);
QCOMPARE(copy->comment, original.comment);
QVERIFY(copy->receivers == original.receivers);
QVERIFY(copy->valueDescriptions == original.valueDescriptions);
QCOMPARE(copy->multiplexing.role, original.multiplexing.role);
QCOMPARE(copy->multiplexing.value, original.multiplexing.value);
}
QCOMPARE(target.name, original.name); QCOMPARE(target.startBit, original.startBit);
QCOMPARE(target.name, original.name);
QCOMPARE(target.startBit, original.startBit);
QCOMPARE(target.length, original.length);
QCOMPARE(target.byteOrder, original.isLittleEndian ? ByteOrder::Intel : ByteOrder::Motorola);
QCOMPARE(target.valueType, original.isSigned ? ValueType::Signed : ValueType::Unsigned);
QCOMPARE(target.factor, original.factor); QCOMPARE(target.offset, original.offset);
QCOMPARE(target.minimum, original.minimum); QCOMPARE(target.maximum, original.maximum);
QCOMPARE(target.unit, original.unit); QCOMPARE(target.comment, original.comment);
QCOMPARE(target.factor, original.factor);
QCOMPARE(target.offset, original.offset);
QCOMPARE(target.minimum, original.minimum);
QCOMPARE(target.maximum, original.maximum);
QCOMPARE(target.unit, original.unit);
QCOMPARE(target.comment, original.comment);
QVERIFY(target.receivers == original.receivers);
QVERIFY(target.valueDescriptions == original.valueDescriptions);
QCOMPARE(target.multiplexing.role, original.multiplexing.role);
@@ -547,93 +595,122 @@ void CoverageTest::builders() {
}
void CoverageTest::physicalConversion_data() {
QTest::addColumn<double>("factor"); QTest::addColumn<double>("offset"); QTest::addColumn<double>("expected");
QTest::addColumn<double>("factor");
QTest::addColumn<double>("offset");
QTest::addColumn<double>("expected");
QTest::newRow("zero-factor") << 0.0 << 7.0 << 7.0;
QTest::newRow("negative-factor") << -2.0 << 5.0 << -15.0;
QTest::newRow("above-declared-range") << 2.0 << 3.0 << 23.0;
}
void CoverageTest::physicalConversion() {
QFETCH(double, factor); QFETCH(double, offset); QFETCH(double, expected);
DecodeSignal signal; signal.length = 8U; signal.factor = factor; signal.offset = offset;
signal.minimum = 0.0; signal.maximum = 1.0;
const auto database = SingleSignal(signal);
RawCanFrame raw; raw.canId = 17U; raw.data = {10U};
const auto decoded = DbcDecoder().Decode(database, raw);
QFETCH(double, factor);
QFETCH(double, offset);
QFETCH(double, expected);
DecodeSignal signal;
signal.length = 8U;
signal.factor = factor;
signal.offset = offset;
signal.minimum = 0.0;
signal.maximum = 1.0;
const DecodeDatabase database = SingleSignal(signal);
RawCanFrame raw;
raw.canId = 17U;
raw.data = {10U};
const DecodedFrameValue decoded = DbcDecoder().Decode(database, raw);
QVERIFY(decoded.signals[0].valid);
QCOMPARE(std::get<std::uint64_t>(decoded.signals[0].rawValue), std::uint64_t(10));
QCOMPARE(decoded.signals[0].physicalValue, expected);
}
void CoverageTest::multiplexingScenarios() {
const auto selectorOnly = DbcDecodeBuilder().Build(ParseText(
"BO_ 1 Frame: 1 ECU\nSG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"));
RawCanFrame raw; raw.canId = 1U; raw.data = {1U};
const auto only = DbcDecoder().Decode(selectorOnly, raw);
const DecodeDatabase selectorOnly = DbcDecodeBuilder().Build(ParseText(
"BO_ 1 Frame: 1 ECU\nSG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"));
RawCanFrame raw;
raw.canId = 1U;
raw.data = {1U};
const DecodedFrameValue only = DbcDecoder().Decode(selectorOnly, raw);
QCOMPARE(only.signals.size(), std::size_t(1));
QVERIFY(only.signals[0].valid);
const auto database = DbcDecodeBuilder().Build(ParseText(
"BO_ 1 First: 3 ECU\n"
"SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
"SG_ A m0 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
"SG_ B m0 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
"BO_ 2 Second: 2 ECU\n"
"SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
"SG_ A m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"));
const DecodeDatabase database = DbcDecodeBuilder().Build(ParseText(
"BO_ 1 First: 3 ECU\n"
"SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
"SG_ A m0 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
"SG_ B m0 : 16|8@1+ (1,0) [0|255] \"\" ECU\n"
"BO_ 2 Second: 2 ECU\n"
"SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
"SG_ A m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n"));
raw.data = {0U, 42U, 99U};
const auto first = DbcDecoder().Decode(database, raw);
const DecodedFrameValue first = DbcDecoder().Decode(database, raw);
QVERIFY(first.signals[1].valid && first.signals[2].valid);
QCOMPARE(std::get<std::uint64_t>(first.signals[1].rawValue), std::uint64_t(42));
QCOMPARE(std::get<std::uint64_t>(first.signals[2].rawValue), std::uint64_t(99));
raw.canId = 2U;
const auto secondInactive = DbcDecoder().Decode(database, raw);
const DecodedFrameValue secondInactive = DbcDecoder().Decode(database, raw);
QCOMPARE(secondInactive.signals[1].status, SignalDecodeStatus::Inactive);
raw.data[0] = 1U;
QVERIFY(DbcDecoder().Decode(database, raw).signals[1].valid);
raw.canId = 1U;
const auto firstInactive = DbcDecoder().Decode(database, raw);
const DecodedFrameValue firstInactive = DbcDecoder().Decode(database, raw);
QCOMPARE(firstInactive.signals[1].status, SignalDecodeStatus::Inactive);
QCOMPARE(firstInactive.signals[2].status, SignalDecodeStatus::Inactive);
}
void CoverageTest::multiplexingErrorLocations_data() {
QTest::addColumn<QString>("text"); QTest::addColumn<unsigned>("line"); QTest::addColumn<QString>("message");
QTest::addColumn<QString>("text");
QTest::addColumn<unsigned>("line");
QTest::addColumn<QString>("message");
const QString header = "BO_ 1 Frame: 3 ECU\n\n";
const QString selector = "SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n";
const QString branch = "SG_ A m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n";
QTest::newRow("missing-selector") << header + branch << 3U << QString::fromUtf8("Multiplexed branch has no selector.");
QTest::newRow("second-selector") << header + selector + "\nSG_ Other M : 16|1@1+ (1,0) [0|1] \"\" ECU\n" << 5U << QString::fromUtf8("Multiplexing requires one unsigned selector per frame.");
QTest::newRow("signed-selector") << header + "SG_ Mode M : 0|1@1- (1,0) [-1|0] \"\" ECU\n" << 3U << QString::fromUtf8("Multiplexing requires one unsigned selector per frame.");
QTest::newRow("second-selector") << header + selector + "\nSG_ Other M : 16|1@1+ (1,0) [0|1] \"\" ECU\n"
<< 5U << QString::fromUtf8("Multiplexing requires one unsigned selector per frame.");
QTest::newRow("signed-selector") << header + "SG_ Mode M : 0|1@1- (1,0) [-1|0] \"\" ECU\n"
<< 3U << QString::fromUtf8("Multiplexing requires one unsigned selector per frame.");
QTest::newRow("branch-range") << header + "SG_ A m2 : 8|8@1+ (1,0) [0|255] \"\" ECU\n" + selector << 3U << QString::fromUtf8("Branch value exceeds selector raw range.");
QTest::newRow("same-branch-overlap") << header + selector + branch + "SG_ B m1 : 15|8@0+ (1,0) [0|255] \"\" ECU\n" << 5U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
QTest::newRow("selector-overlap") << header + selector + "SG_ A m1 : 0|1@1+ (1,0) [0|1] \"\" ECU\n" << 4U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
QTest::newRow("unconditional-overlap") << header + selector + branch + "SG_ Always : 8|8@1+ (1,0) [0|255] \"\" ECU\n" << 5U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
QTest::newRow("ordinary-overlap") << header + "SG_ A : 8|8@1+ (1,0) [0|255] \"\" ECU\nSG_ B : 15|8@0+ (1,0) [0|255] \"\" ECU\n" << 4U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
QTest::newRow("extended-multiplexing") << header + "SG_MUL_VAL_ 1 A Mode 1-2;\n" << 3U << QString::fromUtf8("Extended and nested multiplexing are unsupported.");
QTest::newRow("nested-marker") << header + "SG_ A m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n" << 3U << QString::fromUtf8("Invalid or unsupported multiplexing marker.");
QTest::newRow("same-branch-overlap") << header + selector + branch + "SG_ B m1 : 15|8@0+ (1,0) [0|255] \"\" ECU\n"
<< 5U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
QTest::newRow("selector-overlap") << header + selector + "SG_ A m1 : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
<< 4U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
QTest::newRow("unconditional-overlap") << header + selector + branch + "SG_ Always : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
<< 5U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
QTest::newRow("ordinary-overlap") << header + "SG_ A : 8|8@1+ (1,0) [0|255] \"\" ECU\nSG_ B : 15|8@0+ (1,0) [0|255] \"\" ECU\n"
<< 4U << QString::fromUtf8("Signals overlap without mutually exclusive multiplexing conditions.");
QTest::newRow("extended-multiplexing") << header + "SG_MUL_VAL_ 1 A Mode 1-2;\n"
<< 3U << QString::fromUtf8("Extended and nested multiplexing are unsupported.");
QTest::newRow("nested-marker") << header + "SG_ A m1M : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
<< 3U << QString::fromUtf8("Invalid or unsupported multiplexing marker.");
}
void CoverageTest::multiplexingErrorLocations() {
QFETCH(QString, text); QFETCH(unsigned, line); QFETCH(QString, message);
QTemporaryFile file; QVERIFY(file.open());
QFETCH(QString, text);
QFETCH(unsigned, line);
QFETCH(QString, message);
QTemporaryFile file;
QVERIFY(file.open());
const QByteArray content = text.toUtf8();
QCOMPARE(file.write(content), qint64(content.size())); QVERIFY(file.flush());
try { DbcParser().ParseFile(file.fileName().toStdString()); QFAIL("Expected multiplexing rejection"); }
catch (const std::runtime_error &error) {
QCOMPARE(file.write(content), qint64(content.size()));
QVERIFY(file.flush());
try {
DbcParser().ParseFile(file.fileName().toStdString());
QFAIL("Expected multiplexing rejection");
} catch (const std::runtime_error &error) {
QCOMPARE(QString::fromStdString(error.what()), file.fileName() + ":" + QString::number(line) + ": " + message);
}
}
void CoverageTest::escapedValueDescriptions() {
const auto parsed = ParseText(
"BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
R"dbc(VAL_ 1 State -0 "Ноль" 1 "C:\\trace" 2 "\\\"quoted\"\\";)dbc" "\n");
const auto &values = parsed.frames[0].signals[0].valueDescriptions;
const DbcDatabase parsed = ParseText(
"BO_ 1 Frame: 1 ECU\nSG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
R"dbc(VAL_ 1 State -0 "Ноль" 1 "C:\\trace" 2 "\\\"quoted\"\\";)dbc"
"\n");
const ValueDescriptions &values = parsed.frames[0].signals[0].valueDescriptions;
QCOMPARE(values.size(), std::size_t(3));
QVERIFY(values.find(RawSignalValue(std::int64_t(0))) == values.end());
const auto *zero = FindValueDescription(values, std::int64_t(0));
const auto *path = FindValueDescription(values, std::uint64_t(1));
const auto *quoted = FindValueDescription(values, std::uint64_t(2));
const std::string *zero = FindValueDescription(values, std::int64_t(0));
const std::string *path = FindValueDescription(values, std::uint64_t(1));
const std::string *quoted = FindValueDescription(values, std::uint64_t(2));
QVERIFY(zero && path && quoted);
QCOMPARE(*zero, std::string("Ноль"));
QCOMPARE(*path, std::string("C:\\trace"));
@@ -649,11 +726,11 @@ void CoverageTest::factory() {
QVERIFY_THROWS_EXCEPTION(std::out_of_range, static_cast<void>(factory.create("missing", std::make_unique<int>(1))));
factory.registerType<FactoryFirst>("value");
QVERIFY(factory.contains("value"));
auto argument = std::make_unique<int>(42);
const auto first = factory.create("value", std::move(argument));
std::unique_ptr<int> argument = std::make_unique<int>(42);
const std::shared_ptr<FactoryBase> first = factory.create("value", std::move(argument));
QVERIFY(argument == nullptr);
QCOMPARE(first->Value(), 42);
const auto creator = factory.creator("value");
const TypeFactory<std::string, FactoryBase, std::unique_ptr<int>>::CreatorFn creator = factory.creator("value");
QCOMPARE(creator(std::make_unique<int>(7))->Value(), 7);
factory.registerType<FactorySecond>("value");
QCOMPARE(factory.create("value", std::make_unique<int>(42))->Value(), 43);
+13 -5
View File
@@ -2,9 +2,10 @@
#include "dbc_tree_builder.h"
#include "dbc_tree_model.h"
#include <QAbstractItemModelTester>
#include <QGuiApplication>
#include <QSignalSpy>
#include <QTemporaryFile>
#include <QStringList>
#include <QTemporaryFile>
#include <QtTest>
#include <limits>
@@ -50,8 +51,8 @@ private Q_SLOTS:
const QStringList headers = {"Name", "CAN ID", "Payload (bytes)", "Start bit",
"Length (bits)", "Byte order", "Signedness", "Unit", "Comment"};
for (int column = 0; column < model.columnCount(); ++column) {
const auto frameIndex = model.index(0, column);
const auto signalIndex = model.index(0, column, parent);
const QModelIndex frameIndex = model.index(0, column);
const QModelIndex signalIndex = model.index(0, column, parent);
QCOMPARE(model.headerData(column, Qt::Horizontal).toString(), headers.at(column));
QVERIFY(!model.headerData(column, Qt::Horizontal, Qt::ToolTipRole).isValid());
QCOMPARE(model.data(frameIndex, Qt::ToolTipRole).toString(), QString("Frame tooltip"));
@@ -66,7 +67,7 @@ private Q_SLOTS:
QCOMPARE(model.data(signalIndex, DbcTreeModel::LittleEndianRole).toBool(), false);
QCOMPARE(model.data(signalIndex, DbcTreeModel::SignedRole).toBool(), true);
QCOMPARE(model.data(signalIndex, DbcTreeModel::UnitRole).toString(), QString("rpm"));
const auto descriptions = model.data(signalIndex, DbcTreeModel::ValueDescriptionsRole);
const QVariant descriptions = model.data(signalIndex, DbcTreeModel::ValueDescriptionsRole);
QVERIFY(descriptions.isValid());
QVERIFY(descriptions.toList().isEmpty());
QVERIFY(!model.data(frameIndex, Qt::UserRole + 1000).isValid());
@@ -203,5 +204,12 @@ private Q_SLOTS:
QCOMPARE(resets.count(), 4);
}
};
QTEST_GUILESS_MAIN(ModelTest)
int main(int argc, char **argv) {
// Model tests need QtGui's metatypes, but do not need a display server.
if (qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM"))
qputenv("QT_QPA_PLATFORM", "offscreen");
QGuiApplication application(argc, argv);
ModelTest tests;
return QTest::qExec(&tests, argc, argv);
}
#include "dbc_tree_model_test.moc"
+1 -2
View File
@@ -1,7 +1,6 @@
TEMPLATE = app
TARGET = dbc_model_tests
QT += core testlib
QT -= gui
QT += core gui testlib
CONFIG += console testcase c++17 no_keywords
CONFIG -= app_bundle debug_and_release debug_and_release_target
include(../qt/qt.pri)
+1
View File
@@ -1,4 +1,5 @@
TEMPLATE = app
include(../qt/qt_version.pri)
TARGET = dbc_tests
QT += core testlib
QT -= gui