Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testable search #505

Merged
merged 9 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion BuildDltViewer.pro
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS += qdlt src plugin commander
CONFIG += c++11
CONFIG += c++1z

ICON = Project.icns
QMAKE_INFO_PLIST = Info.plist
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
set(LINUX TRUE)
endif()

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

Expand Down
12 changes: 11 additions & 1 deletion qdlt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ add_library(qdlt SHARED
qdltsettingsmanager.cpp
qdltexporter.cpp
qdltimporter.cpp
fieldnames.cpp)
fieldnames.cpp
dltmessagematcher.cpp
dltmessagematcher.h)

target_compile_definitions(qdlt PRIVATE
BYTE_ORDER=LITTLE_ENDIAN
Expand Down Expand Up @@ -116,3 +118,11 @@ foreach(SDK_EXAMPLE IN ITEMS ${SDK_EXAMPLES})
DESTINATION "${DLT_ADDITIONAL_FILES_INSTALLATION_PATH}/src"
COMPONENT qdlt_sdk)
endforeach()


find_package(GTest)
# configure unit tests only if gtest found on the system
if (GTest_FOUND)
enable_testing()
add_subdirectory(tests)
endif()
66 changes: 66 additions & 0 deletions qdlt/dltmessagematcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include "dltmessagematcher.h"

#include <qdltmsg.h>

DltMessageMatcher::DltMessageMatcher() {}

bool DltMessageMatcher::match(const QDltMsg &msg, const Pattern& pattern) const
{
if (!matchAppId(msg.getApid()) || !matchCtxId(msg.getCtid()))
return false;

if (!matchTimestampRange(msg.getTimestamp())) {
return false;
}

bool matchFound = false;
if (m_headerSearchEnabled) {
auto header = msg.toStringHeader();
if (m_messageIdFormat)
header += ' ' + QString::asprintf(m_messageIdFormat->toUtf8(), msg.getMessageId());
if (std::holds_alternative<QRegularExpression>(pattern)) {
matchFound = header.contains(std::get<QRegularExpression>(pattern));
} else {
const auto& searchText = std::get<QString>(pattern);
matchFound = searchText.isEmpty() || header.contains(searchText, m_caseSensitivity);
}
}

if (matchFound)
return true;

if (m_payloadSearchEnabled) {
const auto payload = msg.toStringPayload();
if (std::holds_alternative<QRegularExpression>(pattern)) {
matchFound = payload.contains(std::get<QRegularExpression>(pattern));
} else {
const auto& searchText = std::get<QString>(pattern);
matchFound = payload.isEmpty() || payload.contains(searchText, m_caseSensitivity);
}
}

return matchFound;
}

bool DltMessageMatcher::matchAppId(const QString& appId) const
{
return m_appId.isEmpty() || appId.compare(m_appId, m_caseSensitivity) == 0;
}

bool DltMessageMatcher::matchCtxId(const QString& ctxId) const
{
return m_ctxId.isEmpty() || ctxId.compare(m_ctxId, m_caseSensitivity) == 0;
}

bool DltMessageMatcher::matchTimestampRange(unsigned int ts) const
{
if (!m_timestampRange)
return true;

// timestamp is displayed as floating number in UI and hence user provides timestamp ranges as floating numbers too
// in DltMsg stores timestamp as integer which is transformed to UI display floating number by QltMgs::toStringHeader
// method more or less as follows
const auto uiTs = static_cast<double>(ts) / 10'000;

return (m_timestampRange->start <= uiTs) && (uiTs <= m_timestampRange->end);
}
72 changes: 72 additions & 0 deletions qdlt/dltmessagematcher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#ifndef DLTMESSAGEMATCHER_H
#define DLTMESSAGEMATCHER_H

#include "export_rules.h"

#include <QString>
#include <QRegularExpression>

#include <optional>
#include <variant>

class QDltMsg;

class QDLT_EXPORT DltMessageMatcher
{
public:
using Pattern = std::variant<QString, QRegularExpression>;

DltMessageMatcher();

void setCaseSentivity(Qt::CaseSensitivity caseSensitivity) {
m_caseSensitivity = caseSensitivity;
}

void setSearchAppId(const QString& appId) {
m_appId = appId;
}

void setSearchCtxId(const QString& ctxId) {
m_ctxId = ctxId;
}

void setTimestapmRange(double start, double end) {
m_timestampRange = {start, end};
}

void setHeaderSearchEnabled(bool enabled) {
m_headerSearchEnabled = enabled;
}

void setPayloadSearchEnabled(bool enabled) {
m_payloadSearchEnabled = enabled;
}

void setMessageIdFormat(const QString& msgIdFormat) {
m_messageIdFormat = msgIdFormat;
}

bool match(const QDltMsg& message, const Pattern& pattern) const;
private:
bool matchAppId(const QString& appId) const;
bool matchCtxId(const QString& ctxId) const;
bool matchTimestampRange(unsigned int ts) const;
private:
QString m_ctxId;
QString m_appId;

struct TimestampRange {
double start;
double end;
};
std::optional<TimestampRange> m_timestampRange;

Qt::CaseSensitivity m_caseSensitivity{Qt::CaseInsensitive};

bool m_headerSearchEnabled{true};
bool m_payloadSearchEnabled{true};

std::optional<QString> m_messageIdFormat;
};

#endif // DLTMESSAGEMATCHER_H
6 changes: 4 additions & 2 deletions qdlt/qdlt.pro
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
PROJECT = qdlt
TEMPLATE = lib

CONFIG += c++11
CONFIG += c++1z
DEFINES += QDLT_LIBRARY
*-gcc* {
QMAKE_CFLAGS += -std=gnu99
Expand All @@ -11,7 +11,7 @@ DEFINES += QDLT_LIBRARY
}

*-g++* {
QMAKE_CXXFLAGS += -std=gnu++0x
QMAKE_CXXFLAGS += -std=c++17
QMAKE_CXXFLAGS += -Wall
QMAKE_CXXFLAGS += -Wextra
QMAKE_CXXFLAGS += -DPLUGIN_INSTALLATION_PATH=\\\"$$PREFIX/usr/share/dlt-viewer/plugins\\\"
Expand Down Expand Up @@ -65,6 +65,7 @@ SOURCES += \
qdltexporter.cpp \
fieldnames.cpp \
qdltimporter.cpp \
dltmessagematcher.cpp \

HEADERS += qdlt.h \
export_rules.h \
Expand Down Expand Up @@ -96,6 +97,7 @@ HEADERS += qdlt.h \
qdltexporter.h \
fieldnames.h \
qdltimporter.h \
dltmessagematcher.h \

unix:VERSION = 1.0.0

Expand Down
10 changes: 10 additions & 0 deletions qdlt/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
add_executable(test_tools
test_dltmessagematcher.cpp
)
target_link_libraries(
test_tools
PRIVATE
GTest::gtest_main
qdlt
)

122 changes: 122 additions & 0 deletions qdlt/tests/test_dltmessagematcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#include <gtest/gtest.h>

#include "dltmessagematcher.h"
#include <qdltmsg.h>

TEST(DltMessageMatcher, matchAppId) {
QDltMsg msg;
msg.setApid("Bla");

DltMessageMatcher matcher;

// case insensitive search
matcher.setSearchAppId("bla");
EXPECT_TRUE(matcher.match(msg, QString{}));

// case sensitive search
matcher.setCaseSentivity(Qt::CaseSensitive);
EXPECT_FALSE(matcher.match(msg, QString{}));
}

TEST(DltMessageMatcher, matchCtxId) {
QDltMsg msg;
msg.setCtid("Bla");

DltMessageMatcher matcher;

// case insensitive search
matcher.setSearchCtxId("bla");
EXPECT_TRUE(matcher.match(msg, QString{}));

// case sensitive search
matcher.setCaseSentivity(Qt::CaseSensitive);
EXPECT_FALSE(matcher.match(msg, QString{}));
}

TEST(DltMessageMatcher, matchTimestampRange) {
QDltMsg msg;
msg.setTimestamp(50000);


DltMessageMatcher matcher;

// no timestamp range is set
EXPECT_TRUE(matcher.match(msg, QString{}));

// in the range
matcher.setTimestapmRange(static_cast<double>(msg.getTimestamp() - 10) / 10'000,
static_cast<double>(msg.getTimestamp() + 10) / 10'000);
EXPECT_TRUE(matcher.match(msg, QString{}));

// range is to the left
matcher.setTimestapmRange(static_cast<double>(msg.getTimestamp() - 100) / 10'000,
static_cast<double>(msg.getTimestamp() - 10) / 10'000);
EXPECT_FALSE(matcher.match(msg, QString{}));

// range is to the right
matcher.setTimestapmRange(static_cast<double>(msg.getTimestamp() + 10) / 10'000,
static_cast<double>(msg.getTimestamp() + 100) / 10'000);
EXPECT_FALSE(matcher.match(msg, QString{}));
}

TEST(DltMessageMatcher, matchMessageHeader) {
QDltMsg msg;
msg.setMicroseconds(4242);
msg.setTimestamp(45);
msg.setMessageCounter(2);
msg.setEcuid("ecuId");
msg.setApid("appId");
msg.setCtid("ctxId");
msg.setSessionid(56);
msg.setType(QDltMsg::DltTypeNwTrace);
msg.setSubtype(3);
msg.setMode(QDltMsg::DltModeNonVerbose);
msg.setNumberOfArguments(255);
msg.setTime(123456789);

DltMessageMatcher matcher;
matcher.setHeaderSearchEnabled(true);
matcher.setPayloadSearchEnabled(false);

// simple text match
// empty header matches
EXPECT_TRUE(matcher.match(msg, ""));
EXPECT_TRUE(matcher.match(msg, "2 ecuId appId"));
EXPECT_FALSE(matcher.match(msg, "4243"));

// regexp match
// simple text as regexp
EXPECT_TRUE(matcher.match(msg, QRegularExpression("ctxId")));
// actual regexp: message starts with a date
EXPECT_TRUE(matcher.match(msg, QRegularExpression("^\\d\\d\\d\\d/\\d\\d/\\d\\d ")));
// actual regexp: somewhere in the middle there is "appId"-word separated from a next word with a space,
// at the end of the string there is a number
EXPECT_TRUE(matcher.match(msg, QRegularExpression("appId \\w+ .+\\d+$")));
}

TEST(DltMessageMatcher, matchMessagePayload) {
QDltMsg msg;
msg.setIndex(42);
msg.setEcuid("efgh");
msg.setMode(QDltMsg::DltModeNonVerbose);
auto ba = QString{"abcd"}.toUtf8();
msg.setPayload(ba);
// version number is set to make QDltMsg::toStringPayload produce string with payload
msg.setVersionNumber(2);

DltMessageMatcher matcher;
matcher.setHeaderSearchEnabled(false);
matcher.setPayloadSearchEnabled(true);

// simple text match
// empty header matches
EXPECT_TRUE(matcher.match(msg, ""));
EXPECT_FALSE(matcher.match(msg, "efgh"));
EXPECT_TRUE(matcher.match(msg, "abc"));

//regexp match
// simple text as regexp
EXPECT_TRUE(matcher.match(msg, QRegularExpression("cd")));
// actual regexp, match string "[0] abcd|61 62 63 64"
EXPECT_TRUE(matcher.match(msg, QRegularExpression("^\\[\\d\\]\\s+\\w+|[\\d\\s]+$")));
}
Loading