Skip to content

Commit

Permalink
Testable search (COVESA#505)
Browse files Browse the repository at this point in the history
* Make tempPayload a local var
* establish tools lib and tests infra
* rm redundant SearchDialog::is_TimeStampRangeValid state var
If timestamp range search is selected
the loop will only continue if range is valid. It does not make sense
to check in functions below if range is valid since otherwise
that code cannot be even reached
* implement matching for ctx and app Ids
* use ctxid and appid match functionality in app
* TimestampRange
* use new timestamp filtering
* text search using dltmessagematcher
* fix build
move matcher to qdlt lib
enable c++17 in qmake config
The new code uses variant and optional
added in c++17 standard while qmake config was
setup restricted to c++11
enable tests only if gtest package found
Set c++17 standard in cmake
export class for windows linker

Signed-off-by: Viktor Kopp <[email protected]>
  • Loading branch information
vifactor authored Sep 9, 2024
1 parent d504c42 commit c1a6854
Show file tree
Hide file tree
Showing 11 changed files with 331 additions and 201 deletions.
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

0 comments on commit c1a6854

Please sign in to comment.