Closes #14
This commit is contained in:
1 parent
51fcdb88ac
commit
0708316a37
155 files changed
+5190
-2691
No files matched your search
@@ -0,0 +1,242 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2026 Dim Himro
|
||||
|
||||
#include "dbc_decode_builder.h"
|
||||
#include "dbc_decoder.h"
|
||||
#include "dbc_parser.h"
|
||||
#include <QTemporaryFile>
|
||||
#include <QtTest>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
static_assert(std::is_same_v<decltype(std::declval<const DecodeDatabase &>().GetFrames()),
|
||||
const std::vector<DecodeFrame> &>);
|
||||
static_assert(std::is_same_v<decltype(DecodedFrameValue::definition), std::shared_ptr<const DecodeFrame>>);
|
||||
static_assert(std::is_same_v<decltype(DecodedSignalValue::definition), std::shared_ptr<const DecodeSignal>>);
|
||||
|
||||
namespace {
|
||||
DecodeDatabase BuildText(const std::string &text) {
|
||||
QTemporaryFile file;
|
||||
if (!file.open() ||
|
||||
file.write(text.data(), static_cast<qint64>(text.size())) != static_cast<qint64>(text.size()) ||
|
||||
!file.flush())
|
||||
throw std::runtime_error("Cannot write reload fixture");
|
||||
return DbcDecodeBuilder().Build(DbcParser().ParseFile(file.fileName().toStdString()));
|
||||
}
|
||||
|
||||
const std::string firstDbc =
|
||||
"BO_ 17 OldFrame: 2 ECU\n"
|
||||
"SG_ State : 0|8@1+ (2,1) [1|511] \"old\" ECU\n"
|
||||
"SG_ Removed : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
|
||||
"VAL_ 17 State 1 \"Old label\";\n"
|
||||
"BO_ 18 Deleted: 1 ECU\n"
|
||||
"SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n";
|
||||
const std::string secondDbc =
|
||||
"BO_ 2147483665 Extended: 1 ECU\n"
|
||||
"SG_ State : 0|8@1+ (1,0) [0|255] \"\" ECU\n"
|
||||
"BO_ 17 NewFrame: 2 ECU\n"
|
||||
"SG_ Renamed : 8|8@1+ (1,0) [0|255] \"\" ECU\n"
|
||||
"SG_ State : 0|8@1+ (4,0) [0|1020] \"new\" ECU\n"
|
||||
"VAL_ 17 State 1 \"New label\";\n";
|
||||
}// namespace
|
||||
|
||||
class DbcLifetimeTest : public QObject {
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void reload();
|
||||
void independentSignalLifetime();
|
||||
void lookupLifetime();
|
||||
void keys();
|
||||
void duplicateKeys();
|
||||
void failedReload();
|
||||
void invalidResults();
|
||||
void retainedMultiplexStatuses();
|
||||
};
|
||||
|
||||
void DbcLifetimeTest::reload() {
|
||||
DecodeDatabase database = BuildText(firstDbc);
|
||||
RawCanFrame raw;
|
||||
raw.canId = 17U;
|
||||
raw.data = {1U, 42U};
|
||||
DecodedFrameValue oldResult = DbcDecoder().Decode(database, raw);
|
||||
const DecodedFrameValue copied = oldResult;
|
||||
DecodedFrameValue moved = std::move(oldResult);
|
||||
std::weak_ptr<const DecodeFrame> oldDefinition = moved.definition;
|
||||
database = BuildText(secondDbc);
|
||||
const DecodedFrameValue current = DbcDecoder().Decode(database, raw);
|
||||
QVERIFY(current.valid && moved.valid && copied.valid);
|
||||
QVERIFY(current.definition != moved.definition);
|
||||
QVERIFY(current.key == moved.key);
|
||||
QVERIFY(current.signals[1].key == moved.signals[0].key);
|
||||
QCOMPARE(moved.definition->name, std::string("OldFrame"));
|
||||
QCOMPARE(copied.definition, moved.definition);
|
||||
QCOMPARE(moved.signals[0].physicalValue, 3.0);
|
||||
QCOMPARE(current.signals[1].physicalValue, 4.0);
|
||||
QCOMPARE(moved.signals[0].definition->unit, std::string("old"));
|
||||
QCOMPARE(current.signals[1].definition->unit, std::string("new"));
|
||||
QCOMPARE(*moved.signals[0].GetValueDescription(), std::string("Old label"));
|
||||
QCOMPARE(*current.signals[1].GetValueDescription(), std::string("New label"));
|
||||
QCOMPARE(moved.signals[1].definition->name, std::string("Removed"));
|
||||
QVERIFY(!database.FindSignal(moved.signals[1].key));
|
||||
database = DecodeDatabase();
|
||||
QVERIFY(!oldDefinition.expired());
|
||||
QCOMPARE(current.definition->name, std::string("NewFrame"));
|
||||
QCOMPARE(*current.signals[1].GetValueDescription(), std::string("New label"));
|
||||
}
|
||||
|
||||
void DbcLifetimeTest::independentSignalLifetime() {
|
||||
DecodedSignalValue saved;
|
||||
std::weak_ptr<const DecodeSignal> definition;
|
||||
const std::string *label = nullptr;
|
||||
{
|
||||
DecodeDatabase database = BuildText(firstDbc);
|
||||
RawCanFrame raw;
|
||||
raw.canId = 17U;
|
||||
raw.data = {1U, 0U};
|
||||
DecodedFrameValue result = DbcDecoder().Decode(database, raw);
|
||||
saved = result.signals[0];
|
||||
definition = saved.definition;
|
||||
label = saved.GetValueDescription();
|
||||
result = DecodedFrameValue();
|
||||
database = BuildText(secondDbc);
|
||||
}
|
||||
QVERIFY(!definition.expired());
|
||||
QVERIFY(label != nullptr);
|
||||
QCOMPARE(*label, std::string("Old label"));
|
||||
QCOMPARE(saved.definition->factor, 2.0);
|
||||
DecodedSignalValue copied = saved;
|
||||
DecodedSignalValue moved = std::move(saved);
|
||||
QCOMPARE(moved.definition, copied.definition);
|
||||
saved = DecodedSignalValue();
|
||||
moved = DecodedSignalValue();
|
||||
QVERIFY(!definition.expired());
|
||||
copied = DecodedSignalValue();
|
||||
QVERIFY(definition.expired());
|
||||
}
|
||||
|
||||
void DbcLifetimeTest::lookupLifetime() {
|
||||
std::shared_ptr<const DecodeFrame> frame;
|
||||
std::shared_ptr<const DecodeSignal> signal;
|
||||
std::weak_ptr<const DecodeFrame> released;
|
||||
{
|
||||
DecodeDatabase database = BuildText(firstDbc);
|
||||
DecodeDatabase copy = database;
|
||||
frame = DbcDecoder().FindFrame(database, 17U, false);
|
||||
signal = copy.FindSignal(SignalKey(FrameKey(17U, false), "State"));
|
||||
QVERIFY(frame && signal);
|
||||
QCOMPARE(frame.get(), ©.GetFrames()[0]);
|
||||
QCOMPARE(signal.get(), &frame->signals[0]);
|
||||
released = frame;
|
||||
database = BuildText(secondDbc);
|
||||
DecodeDatabase moved = std::move(copy);
|
||||
QVERIFY(copy.GetFrames().empty());
|
||||
QVERIFY(!copy.FindFrame(FrameKey(17U, false)));
|
||||
QCOMPARE(moved.FindFrame(FrameKey(17U, false)), frame);
|
||||
}
|
||||
QCOMPARE(frame->name, std::string("OldFrame"));
|
||||
frame.reset();
|
||||
QVERIFY(!released.expired());
|
||||
QCOMPARE(signal->name, std::string("State"));
|
||||
signal.reset();
|
||||
QVERIFY(released.expired());
|
||||
}
|
||||
|
||||
void DbcLifetimeTest::keys() {
|
||||
const FrameKey frame(17U, false);
|
||||
const SignalKey state(frame, "State");
|
||||
const DecodeDatabase first = BuildText(firstDbc);
|
||||
const DecodeDatabase second = BuildText(secondDbc);
|
||||
QVERIFY(first.FindFrame(frame)->GetKey() == second.FindFrame(frame)->GetKey());
|
||||
QVERIFY(first.FindSignal(state) && second.FindSignal(state));
|
||||
QVERIFY(!second.FindFrame(FrameKey(18U, false)));
|
||||
QVERIFY(!second.FindSignal(SignalKey(frame, "Removed")));
|
||||
QVERIFY(second.FindSignal(SignalKey(frame, "Renamed")));
|
||||
QVERIFY(second.FindSignal(SignalKey(FrameKey(17U, true), "State")));
|
||||
QVERIFY(!second.FindSignal(SignalKey(FrameKey(999U, false), "State")));
|
||||
QVERIFY(first.FindSignal(SignalKey(FrameKey(18U, false), "State")));
|
||||
std::unordered_map<FrameKey, int, FrameKeyHasher> frames;
|
||||
frames.emplace(frame, 1);
|
||||
frames.emplace(FrameKey(17U, true), 2);
|
||||
QCOMPARE(frames.size(), std::size_t(2U));
|
||||
std::unordered_map<SignalKey, int, SignalKeyHasher> signals;
|
||||
signals.emplace(state, 1);
|
||||
signals.emplace(SignalKey(FrameKey(17U, true), "State"), 2);
|
||||
signals.emplace(SignalKey(frame, "Renamed"), 3);
|
||||
signals.emplace(SignalKey(FrameKey(18U, false), "State"), 4);
|
||||
QCOMPARE(signals.at(SignalKey(FrameKey(17U, false), "State")), 1);
|
||||
QCOMPARE(signals.size(), std::size_t(4U));
|
||||
}
|
||||
|
||||
void DbcLifetimeTest::duplicateKeys() {
|
||||
DecodeFrame frame;
|
||||
frame.canId = 17U;
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase({frame, frame}));
|
||||
DecodeSignal signal;
|
||||
signal.name = "State";
|
||||
frame.signals = {signal, signal};
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DecodeDatabase({frame}));
|
||||
DbcDatabase parsed;
|
||||
FrameInfo source;
|
||||
source.canId = 17U;
|
||||
parsed.frames = {source, source};
|
||||
QVERIFY_THROWS_EXCEPTION(std::invalid_argument, (void) DbcDecodeBuilder().Build(parsed));
|
||||
}
|
||||
|
||||
void DbcLifetimeTest::failedReload() {
|
||||
DecodeDatabase database = BuildText(firstDbc);
|
||||
const std::shared_ptr<const DecodeFrame> original = database.FindFrame(FrameKey(17U, false));
|
||||
QVERIFY_THROWS_EXCEPTION(std::runtime_error, database = BuildText("BO_ invalid\n"));
|
||||
QCOMPARE(database.FindFrame(FrameKey(17U, false)), original);
|
||||
database = BuildText("");
|
||||
QVERIFY(database.GetFrames().empty());
|
||||
QVERIFY(!database.FindFrame(FrameKey(17U, false)));
|
||||
QCOMPARE(original->name, std::string("OldFrame"));
|
||||
}
|
||||
|
||||
void DbcLifetimeTest::invalidResults() {
|
||||
RawCanFrame raw;
|
||||
raw.canId = 17U;
|
||||
DecodedFrameValue result = DbcDecoder().Decode(BuildText(firstDbc), raw);
|
||||
QVERIFY(result.valid);
|
||||
QVERIFY(!result.signals[0].valid);
|
||||
QCOMPARE(result.signals[0].status, SignalDecodeStatus::InsufficientPayload);
|
||||
QVERIFY(!result.signals[0].GetValueDescription());
|
||||
QCOMPARE(result.signals[0].definition->name, std::string("State"));
|
||||
result = DbcDecoder().Decode(DecodeDatabase(), raw);
|
||||
QVERIFY(!result.valid && !result.definition && result.signals.empty());
|
||||
QVERIFY(result.key == FrameKey(17U, false));
|
||||
QVERIFY(!DecodedSignalValue().GetValueDescription());
|
||||
QVERIFY(!DecodeDatabase().FindSignal(SignalKey(FrameKey(17U, false), "State")));
|
||||
}
|
||||
|
||||
void DbcLifetimeTest::retainedMultiplexStatuses() {
|
||||
const std::string text =
|
||||
"BO_ 17 Frame: 2 ECU\n"
|
||||
"SG_ Mode M : 0|1@1+ (1,0) [0|1] \"\" ECU\n"
|
||||
"SG_ State m1 : 8|8@1+ (1,0) [0|255] \"\" ECU\n";
|
||||
const std::vector<std::vector<std::uint8_t>> payloads = {{0U}, {}, {1U}};
|
||||
const SignalDecodeStatus statuses[] = {SignalDecodeStatus::Inactive,
|
||||
SignalDecodeStatus::SelectorUnavailable,
|
||||
SignalDecodeStatus::InsufficientPayload};
|
||||
for (std::size_t index = 0U; index < payloads.size(); ++index) {
|
||||
DecodeDatabase database = BuildText(text);
|
||||
RawCanFrame raw;
|
||||
raw.canId = 17U;
|
||||
raw.data = payloads[index];
|
||||
const DecodedSignalValue saved = DbcDecoder().Decode(database, raw).signals[1];
|
||||
database = BuildText(secondDbc);
|
||||
QCOMPARE(saved.status, statuses[index]);
|
||||
QVERIFY(!saved.valid && !saved.GetValueDescription());
|
||||
QCOMPARE(saved.definition->multiplexing.role, MultiplexRole::Branch);
|
||||
QVERIFY(saved.key == SignalKey(FrameKey(17U, false), "State"));
|
||||
}
|
||||
}
|
||||
|
||||
int RunLifetimeTests(int argc, char **argv) {
|
||||
DbcLifetimeTest test;
|
||||
return QTest::qExec(&test, argc, argv);
|
||||
}
|
||||
|
||||
#include "dbc_lifetime_test.moc"
|
||||
Reference in new issue
Block a user