3#include "dbc_tree_model.h"
4#include <QAbstractItemModelTester>
5#include <QGuiApplication>
8#include <QTemporaryFile>
12static std::unique_ptr<TreeNode> ParsedTree() {
13 QTemporaryFile fixture;
14 if (!fixture.open())
throw std::runtime_error(
"Fixture creation failed");
15 fixture.write(
"BO_ 256 Status: 8 ECU\n SG_ State : 0|8@1+ (1,0) [0|255] \"rpm\" ECU\n"
16 " SG_ Other : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
17 "BO_ 257 Second: 8 ECU\n SG_ Value : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
18 "CM_ BO_ 256 \"Frame comment\";\nCM_ SG_ 256 State \"Signal comment\";\n"
19 "VAL_ 256 State -1 \"Negative\" 0 \"Off\" 18446744073709551615 \"Maximum\";\n");
24class ModelTest :
public QObject {
27 void additionalMetadata() {
30 frame.
name =
"Extended";
31 frame.
canId = 0x18F00442U;
34 frame.
comment =
"Frame tooltip";
36 signal.
name =
"SignedMotorola";
42 signal.
comment =
"Signal tooltip";
43 frame.signals.push_back(signal);
44 database.
frames.push_back(frame);
47 const QModelIndex parent = model.
index(0, 0);
48 QCOMPARE(model.
data(model.
index(0, DbcTreeModel::ByteOrder, parent)).toString(), QString(
"Motorola"));
49 QCOMPARE(model.
data(model.
index(0, DbcTreeModel::Signed, parent)).toString(), QString(
"Signed"));
50 QCOMPARE(model.
data(model.
index(0, DbcTreeModel::CanId)).toString(), QString(
"0X18F00442"));
51 const QStringList headers = {
"Name",
"CAN ID",
"Payload (bytes)",
"Start bit",
52 "Length (bits)",
"Byte order",
"Signedness",
"Unit",
"Comment"};
53 for (
int column = 0; column < model.
columnCount(); ++column) {
54 const QModelIndex frameIndex = model.
index(0, column);
55 const QModelIndex signalIndex = model.
index(0, column, parent);
56 QCOMPARE(model.
headerData(column, Qt::Horizontal).toString(), headers.at(column));
57 QVERIFY(!model.
headerData(column, Qt::Horizontal, Qt::ToolTipRole).isValid());
58 QCOMPARE(model.
data(frameIndex, Qt::ToolTipRole).toString(), QString(
"Frame tooltip"));
59 QCOMPARE(model.
data(signalIndex, Qt::ToolTipRole).toString(), QString(
"Signal tooltip"));
60 QCOMPARE(model.
data(frameIndex, DbcTreeModel::CommentRole).toString(), QString(
"Frame tooltip"));
61 QCOMPARE(model.
data(signalIndex, DbcTreeModel::CommentRole).toString(), QString(
"Signal tooltip"));
62 QCOMPARE(model.
data(frameIndex, DbcTreeModel::CanIdRole).toUInt(), frame.
canId);
63 QCOMPARE(model.
data(frameIndex, DbcTreeModel::PayloadLengthRole).toInt(), 8);
64 QCOMPARE(model.
data(frameIndex, DbcTreeModel::ExtendedRole).toBool(),
true);
65 QCOMPARE(model.
data(signalIndex, DbcTreeModel::StartBitRole).toUInt(), 7U);
66 QCOMPARE(model.
data(signalIndex, DbcTreeModel::BitLengthRole).toUInt(), 16U);
67 QCOMPARE(model.
data(signalIndex, DbcTreeModel::LittleEndianRole).toBool(),
false);
68 QCOMPARE(model.
data(signalIndex, DbcTreeModel::SignedRole).toBool(),
true);
69 QCOMPARE(model.
data(signalIndex, DbcTreeModel::UnitRole).toString(), QString(
"rpm"));
70 const QVariant descriptions = model.
data(signalIndex, DbcTreeModel::ValueDescriptionsRole);
71 QVERIFY(descriptions.isValid());
72 QVERIFY(descriptions.toList().isEmpty());
73 QVERIFY(!model.
data(frameIndex, Qt::UserRole + 1000).isValid());
74 QVERIFY(!model.
data(signalIndex, Qt::UserRole + 1000).isValid());
75 QVERIFY(!model.setData(frameIndex,
"Changed", Qt::EditRole));
76 QVERIFY(!model.setData(signalIndex,
"Changed", Qt::EditRole));
78 QCOMPARE(model.
data(parent).toString(), QString(
"Extended"));
79 QCOMPARE(model.
data(model.
index(0, 0, parent)).toString(), QString(
"SignedMotorola"));
81 void emptyRootAndRejectedReplacement() {
83 QAbstractItemModelTester tester(&model, QAbstractItemModelTester::FailureReportingMode::QtTest);
85 QPersistentModelIndex retained(model.
index(0, 0));
86 QSignalSpy resets(&model, &QAbstractItemModel::modelReset);
87 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, model.
SetTree(std::make_unique<TreeNode>(
SignalInfo())));
88 QCOMPARE(resets.count(), 0);
89 QVERIFY(retained.isValid());
92 QCOMPARE(resets.count(), 1);
93 QVERIFY(!retained.isValid());
95 QVERIFY(!model.
index(0, 0).isValid());
99 QAbstractItemModelTester tester(&model, QAbstractItemModelTester::FailureReportingMode::QtTest);
103 for (
int row = 0; row < model.
rowCount(); ++row) {
104 const QModelIndex frame = model.
index(row, 0);
105 for (
int column = 0; column < model.
columnCount(); ++column) {
106 const QModelIndex frameColumn = model.
index(row, column);
107 QVERIFY(!model.
parent(frameColumn).isValid());
108 QCOMPARE(model.
rowCount(frameColumn), column == 0 ? (row == 0 ? 2 : 1) : 0);
109 for (
int signalRow = 0; signalRow < model.
rowCount(frame); ++signalRow) {
110 const QModelIndex signal = model.
index(signalRow, column, frame);
111 QCOMPARE(model.
parent(signal), frame);
112 QCOMPARE(model.
rowCount(signal), 0);
113 QVERIFY(!model.
index(0, 0, signal).isValid());
114 QCOMPARE(model.
flags(signal), Qt::ItemIsEnabled | Qt::ItemIsSelectable);
118 QVERIFY(!model.
index(-1, 0).isValid());
119 QVERIFY(!model.
index(2, 0).isValid());
120 QVERIFY(!model.
index(0, -1).isValid());
122 QVERIFY(!model.
index(0, 0, model.
index(0, 1)).isValid());
123 QVERIFY(!model.
parent({}).isValid());
124 QVERIFY(!model.
data({}).isValid());
125 QCOMPARE(model.
flags({}), Qt::NoItemFlags);
128 const QModelIndex foreign = other.
index(0, 0);
129 QVERIFY(!model.
index(0, 0, foreign).isValid());
130 QVERIFY(!model.
parent(foreign).isValid());
131 QCOMPARE(model.
rowCount(foreign), 0);
133 QVERIFY(!model.
data(foreign).isValid());
134 QCOMPARE(model.
flags(foreign), Qt::NoItemFlags);
139 const QModelIndex frame = model.
index(0, 0);
140 const QModelIndex signal = model.
index(0, 0, frame);
141 QCOMPARE(model.
data(frame).toString(), QString(
"Status"));
142 QCOMPARE(model.
data(model.
index(0, DbcTreeModel::CanId)).toString(), QString(
"0X100"));
143 QCOMPARE(model.
data(frame, DbcTreeModel::CanIdRole).toUInt(), 256U);
144 QCOMPARE(model.
data(frame, DbcTreeModel::PayloadLengthRole).toInt(), 8);
145 QCOMPARE(model.
data(signal, DbcTreeModel::UnitRole).toString(), QString(
"rpm"));
146 const QVariantList expectedSignal = {
"State", QVariant(), QVariant(), 0U, 8U,
147 "Intel",
"Unsigned",
"rpm",
"Signal comment"};
148 const QVariantList expectedFrame = {
"Status",
"0X100", 8, QVariant(), QVariant(),
149 QVariant(), QVariant(), QVariant(),
"Frame comment"};
150 for (
int column = 0; column < model.
columnCount(); ++column) {
151 QCOMPARE(model.
data(model.
index(0, column, frame)), expectedSignal.at(column));
152 QCOMPARE(model.
data(model.
index(0, column)), expectedFrame.at(column));
154 QCOMPARE(model.
data(frame, DbcTreeModel::NodeTypeRole).toInt(),
static_cast<int>(NodeType::Frame));
155 QCOMPARE(model.
data(signal, DbcTreeModel::NodeTypeRole).toInt(),
static_cast<int>(NodeType::Signal));
156 QCOMPARE(model.
data(frame, DbcTreeModel::ExtendedRole).toBool(),
false);
157 QVERIFY(!model.
data(frame, DbcTreeModel::ValueDescriptionsRole).isValid());
158 QVERIFY(model.
data(model.
index(1, 0, frame), DbcTreeModel::ValueDescriptionsRole).toList().isEmpty());
160 QCOMPARE(model.
data(signal, Qt::ToolTipRole).toString(), QString(
"Signal comment"));
161 QCOMPARE(model.
data(signal, DbcTreeModel::StartBitRole).toUInt(), 0U);
162 QCOMPARE(model.
data(signal, DbcTreeModel::BitLengthRole).toUInt(), 8U);
163 QCOMPARE(model.
data(signal, DbcTreeModel::LittleEndianRole).toBool(),
true);
164 QCOMPARE(model.
data(signal, DbcTreeModel::SignedRole).toBool(),
false);
165 QVERIFY(!model.
data(signal, DbcTreeModel::CanIdRole).isValid());
166 QVERIFY(!model.
data(frame, DbcTreeModel::UnitRole).isValid());
167 QVERIFY(!model.
data(frame, Qt::EditRole).isValid());
168 const QVariantList values = model.
data(signal, DbcTreeModel::ValueDescriptionsRole).toList();
169 QCOMPARE(values.size(), 3);
170 QCOMPARE(values.at(0).toMap().value(
"value").toLongLong(), -1LL);
171 QCOMPARE(values.at(2).toMap().value(
"value").toULongLong(), std::numeric_limits<qulonglong>::max());
172 QCOMPARE(values.at(2).toMap().value(
"description").toString(), QString(
"Maximum"));
173 for (
int column = 0; column < model.
columnCount(); ++column) {
174 QVERIFY(model.
headerData(column, Qt::Horizontal).isValid());
175 QCOMPARE(model.
data(model.
index(0, column, frame), DbcTreeModel::ValueDescriptionsRole), QVariant(values));
177 QVERIFY(!model.
headerData(-1, Qt::Horizontal).isValid());
179 QVERIFY(!model.
headerData(0, Qt::Vertical).isValid());
183 QAbstractItemModelTester tester(&model, QAbstractItemModelTester::FailureReportingMode::QtTest);
184 QSignalSpy resets(&model, &QAbstractItemModel::modelReset);
186 QPersistentModelIndex frame(model.
index(0, 0));
187 QPersistentModelIndex signal(model.
index(0, 4, frame));
189 QVERIFY(!frame.isValid());
190 QVERIFY(!signal.isValid());
192 QPersistentModelIndex replacement(model.
index(1, 0));
194 QVERIFY(!replacement.isValid());
196 QVERIFY(!model.
index(0, 0).isValid());
198 QCOMPARE(resets.count(), 4);
201 QFAIL(
"A frame cannot be the invisible model root");
202 }
catch (
const std::invalid_argument &) {
204 QCOMPARE(resets.count(), 4);
207int main(
int argc,
char **argv) {
209 if (qEnvironmentVariableIsEmpty(
"QT_QPA_PLATFORM"))
210 qputenv(
"QT_QPA_PLATFORM",
"offscreen");
211 QGuiApplication application(argc, argv);
213 return QTest::qExec(&tests, argc, argv);
215#include "dbc_tree_model_test.moc"
Builds a simple tree from parsed DBC database.
std::unique_ptr< TreeNode > Build(const DbcDatabase &database) const
Build tree representation of parsed DBC data.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Return the child count, capped at the Qt integer limit.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Return the fixed metadata column count for this model.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
Return a child index; only column zero can have children.
void SetTree(std::unique_ptr< TreeNode > root)
Replace the tree and invalidate indexes; reject roots with a parent or non-root type.
Qt::ItemFlags flags(const QModelIndex &index) const override
Allow selecting and enabling valid indexes without editing.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Return horizontal display column titles.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
Return display text or typed metadata for an index.
QModelIndex parent(const QModelIndex &child) const override
Return the parent in column zero, or an invalid index for frames.
Tree node for later use in model/view or other hierarchy consumers.
DBC file parser and interpretation options.
int main(int argc, char *argv[])
Parse a DBC file and demonstrate metadata lookup and CAN decoding.
Parsed DBC content stored in a simple internal form.
std::vector< FrameInfo > frames
Describes one CAN frame from a DBC file.
Describes one signal inside a DBC frame.