7#include <QTemporaryFile>
11#include <unordered_map>
14static_assert(std::is_same_v<decltype(std::declval<const DecodeDatabase &>().GetFrames()),
15 const std::vector<DecodeFrame> &>);
23 file.write(text.data(),
static_cast<qint64
>(text.size())) !=
static_cast<qint64
>(text.size()) ||
25 throw std::runtime_error(
"Cannot write reload fixture");
29 const std::string firstDbc =
30 "BO_ 17 OldFrame: 2 ECU\n"
31 "SG_ State : 0|8@1+ (2,1) [1|511] \"old\" ECU\n"
32 "SG_ Removed : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
33 "VAL_ 17 State 1 \"Old label\";\n"
34 "BO_ 18 Deleted: 1 ECU\n"
35 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n";
36 const std::string secondDbc =
37 "BO_ 2147483665 Extended: 1 ECU\n"
38 "SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
39 "BO_ 17 NewFrame: 2 ECU\n"
40 "SG_ Renamed : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
41 "SG_ State : 0|8@1+ (4,0) [0|1020] \"new\" ECU\n"
42 "VAL_ 17 State 1 \"New label\";\n";
45class DbcLifetimeTest :
public QObject {
49 void independentSignalLifetime();
50 void lookupLifetime();
54 void invalidResults();
55 void retainedMultiplexStatuses();
58void DbcLifetimeTest::reload() {
66 std::weak_ptr<const DecodeFrame> oldDefinition = moved.
definition;
67 database = BuildText(secondDbc);
71 QVERIFY(current.
key == moved.
key);
72 QVERIFY(current.signals[1].
key == moved.signals[0].
key);
73 QCOMPARE(moved.
definition->name, std::string(
"OldFrame"));
75 QCOMPARE(moved.signals[0].physicalValue, 3.0);
76 QCOMPARE(current.signals[1].physicalValue, 4.0);
77 QCOMPARE(moved.signals[0].
definition->unit, std::string(
"old"));
78 QCOMPARE(current.signals[1].
definition->unit, std::string(
"new"));
79 QCOMPARE(*moved.signals[0].GetValueDescription(), std::string(
"Old label"));
80 QCOMPARE(*current.signals[1].GetValueDescription(), std::string(
"New label"));
81 QCOMPARE(moved.signals[1].
definition->name, std::string(
"Removed"));
82 QVERIFY(!database.FindSignal(moved.signals[1].
key));
84 QVERIFY(!oldDefinition.expired());
85 QCOMPARE(current.
definition->name, std::string(
"NewFrame"));
86 QCOMPARE(*current.signals[1].GetValueDescription(), std::string(
"New label"));
89void DbcLifetimeTest::independentSignalLifetime() {
91 std::weak_ptr<const DecodeSignal> definition;
92 const std::string *label =
nullptr;
99 saved = result.signals[0];
103 database = BuildText(secondDbc);
105 QVERIFY(!definition.expired());
106 QVERIFY(label !=
nullptr);
107 QCOMPARE(*label, std::string(
"Old label"));
114 QVERIFY(!definition.expired());
116 QVERIFY(definition.expired());
119void DbcLifetimeTest::lookupLifetime() {
120 std::shared_ptr<const DecodeFrame> frame;
121 std::shared_ptr<const DecodeSignal> signal;
122 std::weak_ptr<const DecodeFrame> released;
128 QVERIFY(frame && signal);
129 QCOMPARE(frame.get(), ©.
GetFrames()[0]);
130 QCOMPARE(signal.get(), &frame->signals[0]);
132 database = BuildText(secondDbc);
138 QCOMPARE(frame->name, std::string(
"OldFrame"));
140 QVERIFY(!released.expired());
141 QCOMPARE(signal->name, std::string(
"State"));
143 QVERIFY(released.expired());
146void DbcLifetimeTest::keys() {
152 QVERIFY(first.FindSignal(state) && second.FindSignal(state));
154 QVERIFY(!second.FindSignal(
SignalKey(frame,
"Removed")));
155 QVERIFY(second.FindSignal(
SignalKey(frame,
"Renamed")));
159 std::unordered_map<FrameKey, int, FrameKeyHasher> frames;
160 frames.emplace(frame, 1);
161 frames.emplace(
FrameKey(17U,
true), 2);
162 QCOMPARE(frames.size(), std::size_t(2U));
163 std::unordered_map<SignalKey, int, SignalKeyHasher> signals;
164 signals.emplace(state, 1);
166 signals.emplace(
SignalKey(frame,
"Renamed"), 3);
169 QCOMPARE(signals.size(), std::size_t(4U));
172void DbcLifetimeTest::duplicateKeys() {
175 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DecodeDatabase({frame, frame}));
177 signal.
name =
"State";
178 frame.signals = {signal, signal};
179 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DecodeDatabase({frame}));
183 parsed.
frames = {source, source};
184 QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (
void)
DbcDecodeBuilder().Build(parsed));
187void DbcLifetimeTest::failedReload() {
189 const std::shared_ptr<const DecodeFrame> original = database.
FindFrame(
FrameKey(17U,
false));
190 QVERIFY_THROWS_EXCEPTION(std::runtime_error, database = BuildText(
"BO_ invalid\n"));
192 database = BuildText(
"");
195 QCOMPARE(original->name, std::string(
"OldFrame"));
198void DbcLifetimeTest::invalidResults() {
202 QVERIFY(result.
valid);
203 QVERIFY(!result.signals[0].
valid);
204 QCOMPARE(result.signals[0].status, SignalDecodeStatus::InsufficientPayload);
205 QVERIFY(!result.signals[0].GetValueDescription());
206 QCOMPARE(result.signals[0].
definition->name, std::string(
"State"));
208 QVERIFY(!result.
valid && !result.
definition && result.signals.empty());
214void DbcLifetimeTest::retainedMultiplexStatuses() {
215 const std::string text =
216 "BO_ 17 Frame: 2 ECU\n"
217 "SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
218 "SG_ State m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n";
219 const std::vector<std::vector<std::uint8_t>> payloads = {{0U}, {}, {1U}};
220 const SignalDecodeStatus statuses[] = {SignalDecodeStatus::Inactive,
221 SignalDecodeStatus::SelectorUnavailable,
222 SignalDecodeStatus::InsufficientPayload};
223 for (std::size_t index = 0U; index < payloads.size(); ++index) {
227 raw.
data = payloads[index];
229 database = BuildText(secondDbc);
230 QCOMPARE(saved.status, statuses[index]);
232 QCOMPARE(saved.
definition->multiplexing.role, MultiplexRole::Branch);
237int RunLifetimeTests(
int argc,
char **argv) {
238 DbcLifetimeTest test;
239 return QTest::qExec(&test, argc, argv);
242#include "dbc_lifetime_test.moc"
Converts parsed DBC data into runtime decode database.
DecodeDatabase Build(const DbcDatabase &source) const
Build runtime decode database.
Runtime CAN decoder using prebuilt decode database.
DecodedFrameValue Decode(const DecodeDatabase &database, const RawCanFrame &frame) const
Decode one raw CAN frame.
std::shared_ptr< const DecodeFrame > FindFrame(const DecodeDatabase &database, std::uint32_t canId, bool isExtended) const
Find frame definition by CAN ID.
Immutable runtime snapshot. Copies share storage; reload replaces a snapshot. Owning lookup handles a...
std::shared_ptr< const DecodeFrame > FindFrame(const FrameKey &key) const
const std::vector< DecodeFrame > & GetFrames() const
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Created: 2026-03-13 Author: Deeaitch (Dim. Himro)
Parsed DBC content stored in a simple internal form.
std::vector< FrameInfo > frames
Runtime-ready frame definition.
Runtime-ready signal definition.
std::shared_ptr< const DecodeFrame > definition
One decoded signal value.
std::shared_ptr< const DecodeSignal > definition
const std::string * GetValueDescription() const
Describes one CAN frame from a DBC file.
Stable frame identifier: normalized CAN ID and frame type.
Raw CAN frame used for runtime or trace decoding.
std::vector< std::uint8_t > data