first version of embedded test stand done
This commit is contained in:
42
src/core/cli_parser.cpp
Normal file
42
src/core/cli_parser.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "cli_parser.h"
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
TestOptions CLIParser::parse(int argc, char* argv[])
|
||||
{
|
||||
TestOptions opt;
|
||||
|
||||
QStringList args;
|
||||
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
args << argv[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.size(); ++i)
|
||||
{
|
||||
QString a = args[i];
|
||||
|
||||
if (a == "--group" && i + 1 < args.size())
|
||||
{
|
||||
opt.group = args[i + 1];
|
||||
}
|
||||
|
||||
else if (a == "--case" && i + 1 < args.size())
|
||||
{
|
||||
opt.caseFilter = args[i + 1];
|
||||
}
|
||||
|
||||
else if (a == "--repeat" && i + 1 < args.size())
|
||||
{
|
||||
opt.repeat = args[i + 1].toInt();
|
||||
}
|
||||
|
||||
else if (a == "--delay" && i + 1 < args.size())
|
||||
{
|
||||
opt.delayMs = args[i + 1].toInt();
|
||||
}
|
||||
}
|
||||
|
||||
return opt;
|
||||
}
|
||||
12
src/core/cli_parser.h
Normal file
12
src/core/cli_parser.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef CLI_PARSER_H
|
||||
#define CLI_PARSER_H
|
||||
|
||||
#include "test_options.h"
|
||||
|
||||
class CLIParser
|
||||
{
|
||||
public:
|
||||
static TestOptions parse(int argc, char* argv[]);
|
||||
};
|
||||
|
||||
#endif // CLI_PARSER_H
|
||||
68
src/core/declarations.h
Normal file
68
src/core/declarations.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef DECLARATIONS_H
|
||||
#define DECLARATIONS_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonArray>
|
||||
#include <QHash>
|
||||
#include <QHashIterator>
|
||||
#include "QLoggingCategory"
|
||||
#include "QProcess"
|
||||
#include <QRegularExpression>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QSettings>
|
||||
#include <QTextCodec>
|
||||
#include <QtCore>
|
||||
#include <QTextStream>
|
||||
#include <QtSerialPort/QSerialPort>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <QVector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "loggingCategories.h"
|
||||
#include "html_report.h"
|
||||
|
||||
struct comSet_t {
|
||||
int addrRS485;
|
||||
QString name;
|
||||
qint32 baudRate;
|
||||
QSerialPort::DataBits dataBits;
|
||||
QSerialPort::Parity parity;
|
||||
QSerialPort::StopBits stopBits;
|
||||
QSerialPort::FlowControl flowControl;
|
||||
|
||||
comSet_t()
|
||||
{
|
||||
addrRS485 = 1;
|
||||
name = "";
|
||||
baudRate = QSerialPort::Baud9600;
|
||||
dataBits = QSerialPort::Data8;
|
||||
parity = QSerialPort::NoParity;
|
||||
stopBits = QSerialPort::OneStop;
|
||||
flowControl = QSerialPort::NoFlowControl;
|
||||
}
|
||||
|
||||
};
|
||||
typedef comSet_t comSettings_t;
|
||||
|
||||
typedef struct {
|
||||
QString MK_elf_file;
|
||||
QString MK_map_file;
|
||||
QString test_log_path;
|
||||
QString stand_report_html_path;
|
||||
QString map_parser;
|
||||
QString elf_parser;
|
||||
QString iar_json_out;
|
||||
QString local_python;
|
||||
} path_t;
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // DECLARATIONS_H
|
||||
94
src/core/exceptions_handle.h
Normal file
94
src/core/exceptions_handle.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#ifndef EXCEPTIONS_HANDLE_H
|
||||
#define EXCEPTIONS_HANDLE_H
|
||||
|
||||
#include "declarations.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*!
|
||||
* \brief the ErrOpenFile class for handling exceptions when opening file
|
||||
*/
|
||||
class ErrOpenFile: public exception
|
||||
{
|
||||
public:
|
||||
ErrOpenFile(QString message, QString filePath)
|
||||
{
|
||||
this->message = message;
|
||||
this->filePath = filePath;
|
||||
}
|
||||
|
||||
QString getFilePath()
|
||||
{ return filePath; }
|
||||
|
||||
QString getMessage()
|
||||
{ return message; }
|
||||
|
||||
virtual ~ErrOpenFile() throw() {}
|
||||
|
||||
private:
|
||||
QString message;
|
||||
QString filePath;
|
||||
};
|
||||
|
||||
|
||||
//class ErrOpenFile: public exception
|
||||
//{
|
||||
//public:
|
||||
// ErrOpenFile(QString fname, QString errMsg)
|
||||
// {
|
||||
// this->fname = fname;
|
||||
// this->intro = "ошибка чтения файла";
|
||||
// this->errMsg = errMsg;
|
||||
// }
|
||||
// QString getFname() { return fname; }
|
||||
// QString getIntro() { return intro; }
|
||||
// QString getErrMsg() { return errMsg; }
|
||||
// virtual ~ErrOpenFile() throw() {}
|
||||
|
||||
//private:
|
||||
// QString fname;
|
||||
// QString intro;
|
||||
// QString errMsg;
|
||||
//};
|
||||
|
||||
class ErrInJsonSet: public exception
|
||||
{
|
||||
public:
|
||||
ErrInJsonSet(QString fname, QString jsonObjectsList, QString param, QString errMsg)
|
||||
{
|
||||
this->fname = fname;
|
||||
this->jsonObjectsList = jsonObjectsList;
|
||||
this->param = param;
|
||||
this->intro = "ошибка чтения файла настроек";
|
||||
this->errMsg = errMsg;
|
||||
this->errFromJsonFlag = false;
|
||||
}
|
||||
ErrInJsonSet(QString fname, QString errFromJson)
|
||||
{
|
||||
this->fname = fname;
|
||||
this->errFromJsonFlag = true;
|
||||
this->intro = "ошибка чтения файла настроек";
|
||||
this->errFromJson = errFromJson;
|
||||
}
|
||||
QString getFname() { return fname; }
|
||||
QString getJsonObj() { return jsonObjectsList; }
|
||||
QString getParam() { return param; }
|
||||
QString getIntro() { return intro; }
|
||||
QString getErrMsg() { return errMsg; }
|
||||
QString getErrFromJson() { return errFromJson; }
|
||||
bool checkErrFromJson() { return errFromJsonFlag; }
|
||||
virtual ~ErrInJsonSet() throw() {}
|
||||
|
||||
private:
|
||||
QString fname;
|
||||
QString intro;
|
||||
QString jsonObjectsList;
|
||||
QString param;
|
||||
QString errMsg;
|
||||
QString errFromJson;
|
||||
bool errFromJsonFlag = false;
|
||||
};
|
||||
|
||||
|
||||
#endif // EXCEPTIONS_HANDLE_H
|
||||
|
||||
158
src/core/html_report.cpp
Normal file
158
src/core/html_report.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
#include "html_report.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
|
||||
static QMap<QString,int> extractSummary(
|
||||
const QString& content)
|
||||
{
|
||||
QMap<QString,int> s;
|
||||
|
||||
QStringList lines =
|
||||
content.split("\n");
|
||||
|
||||
for (QString line : lines)
|
||||
{
|
||||
if (line.startsWith("TOTAL:"))
|
||||
s["total"] =
|
||||
line.split(":")[1].trimmed().toInt();
|
||||
|
||||
if (line.startsWith("PASSED:"))
|
||||
s["passed"] =
|
||||
line.split(":")[1].trimmed().toInt();
|
||||
|
||||
if (line.startsWith("FAILED:"))
|
||||
s["failed"] =
|
||||
line.split(":")[1].trimmed().toInt();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void HtmlReport::generate(
|
||||
const QString& logFile,
|
||||
const QString& htmlFile,
|
||||
QString curTime)
|
||||
{
|
||||
QFile f(logFile);
|
||||
|
||||
f.open(QIODevice::ReadOnly);
|
||||
|
||||
QString content =
|
||||
f.readAll();
|
||||
|
||||
f.close();
|
||||
|
||||
auto summary =
|
||||
extractSummary(content);
|
||||
|
||||
QString html;
|
||||
|
||||
html += R"(
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body {
|
||||
font-family: monospace;
|
||||
background:#111;
|
||||
color:#eee;
|
||||
}
|
||||
|
||||
.pass {
|
||||
color:lightgreen;
|
||||
}
|
||||
|
||||
.fail {
|
||||
color:red;
|
||||
}
|
||||
|
||||
details {
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
summary {
|
||||
cursor:pointer;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space:pre-wrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
)";
|
||||
|
||||
html += QString("<h1>Tests Report (%1)</h1>").arg(curTime);
|
||||
|
||||
html += QString(R"(
|
||||
<h2>SUMMARY</h2>
|
||||
|
||||
<div>
|
||||
<b>Total:</b> %1<br>
|
||||
<b style='color:lightgreen;'>Passed:</b> %2<br>
|
||||
<b style='color:red;'>Failed:</b> %3<br>
|
||||
</div>
|
||||
)")
|
||||
.arg(summary["total"])
|
||||
.arg(summary["passed"])
|
||||
.arg(summary["failed"]);
|
||||
|
||||
QStringList tests =
|
||||
content.split("<<<TEST_START>>>");
|
||||
|
||||
for (QString t : tests)
|
||||
{
|
||||
if (!t.contains("TEST:"))
|
||||
continue;
|
||||
|
||||
QString cls =
|
||||
t.contains("STATUS: FAIL")
|
||||
? "fail"
|
||||
: "pass";
|
||||
|
||||
QString title =
|
||||
t.split("\n")[1];
|
||||
|
||||
html += QString(R"(
|
||||
<details>
|
||||
<summary class="%1">%2</summary>
|
||||
<pre>%3</pre>
|
||||
</details>
|
||||
)")
|
||||
.arg(cls)
|
||||
.arg(title)
|
||||
.arg(t.toHtmlEscaped());
|
||||
}
|
||||
|
||||
int passRate = 0;
|
||||
|
||||
if (summary["total"] > 0)
|
||||
{
|
||||
passRate =
|
||||
(100 * summary["passed"])
|
||||
/ summary["total"];
|
||||
}
|
||||
|
||||
html += QString(R"(
|
||||
<div style="background:#333;width:300px;">
|
||||
<div style="
|
||||
background:green;
|
||||
width:%1%;
|
||||
color:white;">
|
||||
%1%
|
||||
</div>
|
||||
</div>
|
||||
)").arg(passRate);
|
||||
|
||||
html += "</body></html>";
|
||||
|
||||
QFile out(htmlFile);
|
||||
|
||||
out.open(QIODevice::WriteOnly);
|
||||
|
||||
out.write(html.toUtf8());
|
||||
|
||||
out.close();
|
||||
}
|
||||
14
src/core/html_report.h
Normal file
14
src/core/html_report.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef HTML_REPORT_H
|
||||
#define HTML_REPORT_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class HtmlReport
|
||||
{
|
||||
public:
|
||||
|
||||
static void generate(const QString& logFile,
|
||||
const QString& htmlFile, QString curTime);
|
||||
};
|
||||
|
||||
#endif // HTML_REPORT_H
|
||||
182
src/core/json_processor.cpp
Normal file
182
src/core/json_processor.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "json_processor.h"
|
||||
|
||||
JsonProcessor::JsonProcessor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void JsonProcessor::setJsonFile(QString jsonPath_)
|
||||
{
|
||||
jsonPath = jsonPath_;
|
||||
}
|
||||
|
||||
void JsonProcessor::openJsonFile(QString jsonPath, QJsonObject &jsonObj)
|
||||
{
|
||||
QByteArray bytes;
|
||||
|
||||
QFile file(jsonPath);
|
||||
if (file.open( QIODevice::ReadOnly))
|
||||
{
|
||||
bytes = file.readAll();
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{ throw(ErrOpenFile(jsonPath, "файл не найден")); }
|
||||
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonError.errorString())); return; }
|
||||
else
|
||||
{
|
||||
if (doc.isObject())
|
||||
{ jsonObj = doc.object(); }
|
||||
else
|
||||
{ throw(ErrInJsonSet(jsonPath, "неверный формат json: " + jsonPath)); }
|
||||
}
|
||||
}
|
||||
|
||||
void JsonProcessor::saveJsonDataInFile(QString jsonPath, QJsonObject jsonObj)
|
||||
{
|
||||
QJsonDocument document;
|
||||
document.setObject(jsonObj);
|
||||
QByteArray bytes = document.toJson( QJsonDocument::Indented );
|
||||
QFile file(jsonPath);
|
||||
if( file.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate ) )
|
||||
{
|
||||
QTextStream iStream( &file );
|
||||
iStream.setCodec( "utf-8" );
|
||||
iStream << bytes;
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{ throw(ErrOpenFile(jsonPath, "файл не найден")); }
|
||||
}
|
||||
|
||||
/* common functions */
|
||||
void JsonProcessor::jsonConvertStruct(QString jsonObjName, QJsonObject obj,
|
||||
QString jsonParamName, QVector<QString> *setToStructArr, int arrSize)
|
||||
{
|
||||
setToStructArr->clear();
|
||||
QJsonArray *param = new QJsonArray;
|
||||
*param = obj[jsonParamName].toArray();
|
||||
if (param->size() != arrSize)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonObjName, jsonParamName, "параметр отсутствует или кол-во элементов в параметре должно быть равно " + QString::number(arrSize))); }
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < arrSize; i++)
|
||||
{ setToStructArr->push_back(param->at(i).toString()); }
|
||||
}
|
||||
}
|
||||
|
||||
void JsonProcessor::jsonConvertStruct(QJsonObject obj, QString jsonParamName,
|
||||
QVector<QString> *setToStructArr)
|
||||
{
|
||||
setToStructArr->clear();
|
||||
QJsonArray *param = new QJsonArray;
|
||||
*param = obj[jsonParamName].toArray();
|
||||
for (int i = 0; i < param->size(); i++)
|
||||
{ setToStructArr->push_back(param->at(i).toString()); }
|
||||
}
|
||||
|
||||
void JsonProcessor::jsonConvertStruct(QJsonObject obj,
|
||||
QString jsonParamName, QVector<double> *setToStructArr)
|
||||
{
|
||||
setToStructArr->clear();
|
||||
QJsonArray *param = new QJsonArray;
|
||||
*param = obj[jsonParamName].toArray();
|
||||
for (int i = 0; i < param->size(); i++)
|
||||
{ setToStructArr->push_back(param->at(i).toDouble()); }
|
||||
}
|
||||
|
||||
void JsonProcessor::jsonConvertStruct(QString jsonObjName, QJsonObject obj,
|
||||
QString jsonParamName, double *setToStructArr, int arrSize)
|
||||
{
|
||||
QJsonArray param = obj[jsonParamName].toArray();
|
||||
if (param.size() != arrSize)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonObjName, jsonParamName,
|
||||
"параметр отсутствует или кол-во элементов в параметре должно быть равно " + QString::number(arrSize))); }
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < arrSize; i++)
|
||||
{ setToStructArr[i] = param[i].toDouble(); }
|
||||
}
|
||||
}
|
||||
|
||||
void JsonProcessor::jsonConvertStruct(QString jsonObjName, QJsonObject obj,
|
||||
QString jsonParamName, bool *setToStructArr, int arrSize)
|
||||
{
|
||||
QJsonArray param = obj[jsonParamName].toArray();
|
||||
if (param.size() != arrSize)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonObjName, jsonParamName,
|
||||
"параметр отсутствует или кол-во элементов в параметре должно быть равно " + QString::number(arrSize))); }
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < arrSize; i++)
|
||||
{ setToStructArr[i] = param[i].toBool(); }
|
||||
}
|
||||
}
|
||||
|
||||
bool JsonProcessor::jsonGetBoolValue(QJsonObject obj, QString param, QString jsonGeneral)
|
||||
{
|
||||
QJsonValue val = obj[param];
|
||||
if (val == QJsonValue::Null)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonGeneral, param, "отсутствует параметр")); }
|
||||
return val.toBool(false);
|
||||
}
|
||||
|
||||
void JsonProcessor::jsonGetStrValue(QJsonObject obj, QString paramName, QString ¶mValue, QString jsonObjName)
|
||||
{
|
||||
QString val = obj[paramName].toString();
|
||||
if (val == NULL)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonObjName, paramName, "parameter missing")); }
|
||||
else
|
||||
{ paramValue = val; }
|
||||
}
|
||||
|
||||
|
||||
void JsonProcessor::jsonSetComPortSettings(QString jsonObjName, QJsonObject obj, comSettings_t &com)
|
||||
{
|
||||
com.addrRS485 = obj["RS485_address"].toInt();
|
||||
if (com.addrRS485 < 1 ||
|
||||
com.addrRS485 > 254)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonObjName, "RS485_address", "отсутствует параметр")); }
|
||||
|
||||
com.name = obj["COM_port"].toString();
|
||||
if (com.name == NULL) { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_port", "отсутствует параметр")); }
|
||||
|
||||
com.baudRate = (quint32)obj["COM_baudRate"].toInt();
|
||||
if (com.baudRate == 0) { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_baudRate", "отсутствует параметр")); }
|
||||
|
||||
com.dataBits = (QSerialPort::DataBits)obj["COM_bits"].toInt();
|
||||
if (com.dataBits < QSerialPort::Data5 ||
|
||||
com.dataBits > QSerialPort::Data8)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_bits", "отсутствует параметр")); }
|
||||
|
||||
com.parity = (QSerialPort::Parity)obj["COM_parity"].toInt();
|
||||
if (com.parity < QSerialPort::UnknownParity ||
|
||||
com.parity == 1 ||
|
||||
com.parity > QSerialPort::MarkParity)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_parity", "отсутствует параметр")); }
|
||||
|
||||
com.stopBits = (QSerialPort::StopBits)obj["COM_stopBits"].toInt();
|
||||
if (com.stopBits < QSerialPort::OneStop ||
|
||||
com.stopBits > QSerialPort::OneAndHalfStop)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_stopBits", "отсутствует параметр")); }
|
||||
|
||||
com.flowControl = (QSerialPort::FlowControl)obj["COM_flowCotrol"].toInt();
|
||||
if (com.flowControl < QSerialPort::NoFlowControl ||
|
||||
com.flowControl > QSerialPort::SoftwareControl)
|
||||
{ throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_flowControl", "отсутствует параметр")); }
|
||||
}
|
||||
|
||||
void JsonProcessor::jsonSaveComPortSettings(QJsonObject &obj, comSettings_t &com)
|
||||
{
|
||||
obj.insert("RS485_address", com.addrRS485);
|
||||
obj.insert("COM_port", com.name);
|
||||
obj.insert("COM_baudRate", com.baudRate);
|
||||
obj.insert("COM_bits", com.dataBits);
|
||||
obj.insert("COM_parity", com.parity);
|
||||
obj.insert("COM_stopBits", com.stopBits);
|
||||
obj.insert("COM_flowControl", com.flowControl);
|
||||
}
|
||||
39
src/core/json_processor.h
Normal file
39
src/core/json_processor.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef JSONPROCESSOR_H
|
||||
#define JSONPROCESSOR_H
|
||||
|
||||
#include "exceptions_handle.h"
|
||||
|
||||
class JsonProcessor
|
||||
{
|
||||
|
||||
public:
|
||||
JsonProcessor();
|
||||
|
||||
virtual void setJsonFile(QString jsonPath_);
|
||||
virtual void openJsonFile(QString jsonPath, QJsonObject &jsonObj);
|
||||
virtual void saveJsonDataInFile(QString jsonPath, QJsonObject jsonObj);
|
||||
|
||||
virtual void jsonConvertStruct(QString jsonObjName, QJsonObject obj,
|
||||
QString jsonParamName, QVector<QString> *setToStructArr, int arrSize);
|
||||
virtual void jsonConvertStruct(QJsonObject obj,
|
||||
QString jsonParamName, QVector<QString> *setToStructArr);
|
||||
virtual void jsonConvertStruct(QJsonObject obj,
|
||||
QString jsonParamName, QVector<double> *setToStructArr);
|
||||
virtual void jsonConvertStruct(QString jsonObjName, QJsonObject obj,
|
||||
QString jsonParamName, double *setToStructArr, int arrSize);
|
||||
virtual void jsonConvertStruct(QString jsonObjName, QJsonObject obj,
|
||||
QString jsonParamName, bool *setToStructArr, int arrSize);
|
||||
|
||||
virtual bool jsonGetBoolValue(QJsonObject obj, QString param, QString jsonPSgeneral);
|
||||
|
||||
virtual void jsonSetComPortSettings(QString jsonObjName, QJsonObject obj, comSettings_t &com);
|
||||
virtual void jsonSaveComPortSettings(QJsonObject &obj, comSettings_t &com);
|
||||
|
||||
virtual void jsonGetStrValue(QJsonObject obj, QString paramName, QString ¶mValue, QString jsonObjName);
|
||||
|
||||
|
||||
private:
|
||||
QString jsonPath = "";
|
||||
};
|
||||
|
||||
#endif // JSONPROCESSOR_H
|
||||
127
src/core/logger.cpp
Normal file
127
src/core/logger.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "logger.h"
|
||||
|
||||
// Умный указатель на файл логирования
|
||||
QScopedPointer<QFile> m_logFile;
|
||||
|
||||
void Logger::write(const QString& file, const QString& text)
|
||||
{
|
||||
QFile f(file);
|
||||
|
||||
f.open(QIODevice::Append | QIODevice::Text);
|
||||
|
||||
QTextStream out(&f);
|
||||
|
||||
out << text;
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
void Logger::saveTestLog(
|
||||
const QString& filename,
|
||||
const ProtocolHandler& handler,
|
||||
const QJsonObject& cfg,
|
||||
bool passed,
|
||||
const QString& error)
|
||||
{
|
||||
QString log;
|
||||
|
||||
log += "\n<<<TEST_START>>>\n";
|
||||
|
||||
log += "TEST: ";
|
||||
log += cfg["meta"].toObject()["name"].toString();
|
||||
log += "\n";
|
||||
|
||||
log += "TIME: ";
|
||||
log += QDateTime::currentDateTime().toString("dd.MM.yyyy hh:mm:ss");
|
||||
log += "\n";
|
||||
|
||||
log += passed ? "STATUS: PASS\n" : "STATUS: FAIL\n";
|
||||
|
||||
if (!error.isEmpty())
|
||||
{
|
||||
log += "\n--- ERROR ---\n";
|
||||
log += error;
|
||||
log += "\n";
|
||||
}
|
||||
|
||||
if (!handler.mismatches.isEmpty())
|
||||
{
|
||||
log += "\n--- MISMATCHES ---\n";
|
||||
|
||||
QJsonDocument doc(QJsonArray::fromVariantList(handler.mismatches));
|
||||
|
||||
log += doc.toJson();
|
||||
log += "\n";
|
||||
}
|
||||
|
||||
log += "\n--- FULL LOG ---\n";
|
||||
|
||||
for (const QString& s : handler.messages)
|
||||
{
|
||||
log += s;
|
||||
log += "\n";
|
||||
}
|
||||
|
||||
log += "<<<TEST_END>>>\n";
|
||||
|
||||
write(filename, log);
|
||||
}
|
||||
|
||||
void Logger::appendSummary(const QString& filename, int total,
|
||||
int passed, int failed)
|
||||
{
|
||||
QString s;
|
||||
|
||||
s += "\n";
|
||||
s += "############################################################\n";
|
||||
s += "SUMMARY\n";
|
||||
s += "############################################################\n";
|
||||
|
||||
s += QString("TOTAL: %1\n").arg(total);
|
||||
s += QString("PASSED: %1\n").arg(passed);
|
||||
s += QString("FAILED: %1\n").arg(failed);
|
||||
|
||||
write(filename, s);
|
||||
}
|
||||
|
||||
void Logger::setupLog()
|
||||
{
|
||||
// Устанавливаем файл логирования
|
||||
|
||||
m_logFile.reset(new QFile("C:/Danila/work/embedded_test_stand/logs/test_stand.log"));
|
||||
// Открываем файл логирования
|
||||
if (!m_logFile.data()->open(QFile::Append | QFile::Text))
|
||||
{ printf("\nlog file is not opened!\n"); }
|
||||
// Устанавливаем обработчик
|
||||
qInstallMessageHandler(writeStandLog);
|
||||
}
|
||||
|
||||
void Logger::writeToConsol(QString msg)
|
||||
{
|
||||
QTextStream out(stdout);
|
||||
out.setCodec("IBM 866");
|
||||
out << msg << Qt::endl;
|
||||
}
|
||||
|
||||
void Logger::writeStandLog(QtMsgType type,
|
||||
const QMessageLogContext &context,
|
||||
const QString &msg)
|
||||
{
|
||||
// Открываем поток записи в файл
|
||||
QTextStream out(m_logFile.data());
|
||||
// Записываем дату записи
|
||||
out << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz ");
|
||||
// По типу определяем, к какому уровню относится сообщение
|
||||
switch (type)
|
||||
{
|
||||
case QtInfoMsg: out << "INF "; break;
|
||||
case QtDebugMsg: out << "DBG "; break;
|
||||
case QtWarningMsg: out << "WRN "; break;
|
||||
case QtCriticalMsg: out << "CRT "; break;
|
||||
case QtFatalMsg: out << "FTL "; break;
|
||||
}
|
||||
// Записываем в вывод категорию сообщения и само сообщение
|
||||
out << context.category << ": " << msg << Qt::endl;
|
||||
out.flush(); // Очищаем буферизированные данные
|
||||
|
||||
}
|
||||
39
src/core/logger.h
Normal file
39
src/core/logger.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include "protocol_handler.h"
|
||||
|
||||
class ProtocolHandler;
|
||||
class QJsonObject;
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
|
||||
static void saveTestLog(
|
||||
const QString& filename,
|
||||
const ProtocolHandler& handler,
|
||||
const QJsonObject& cfg,
|
||||
bool passed,
|
||||
const QString& error = "");
|
||||
|
||||
static void appendSummary(
|
||||
const QString& filename,
|
||||
int total,
|
||||
int passed,
|
||||
int failed);
|
||||
|
||||
static void setupLog();
|
||||
static void writeStandLog(QtMsgType type, const QMessageLogContext &context,
|
||||
const QString &msg);
|
||||
|
||||
static void writeToConsol(QString msg);
|
||||
|
||||
private:
|
||||
|
||||
static void write(
|
||||
const QString& file,
|
||||
const QString& text);
|
||||
};
|
||||
|
||||
#endif // LOGGER_H
|
||||
6
src/core/loggingCategories.cpp
Normal file
6
src/core/loggingCategories.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "loggingCategories.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(logDebug, "Debug")
|
||||
Q_LOGGING_CATEGORY(logInfo, "Info")
|
||||
Q_LOGGING_CATEGORY(logWarning, "Warning")
|
||||
Q_LOGGING_CATEGORY(logCritical, "Critical")
|
||||
11
src/core/loggingCategories.h
Normal file
11
src/core/loggingCategories.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef LOGGINGCATEGORIES_H
|
||||
#define LOGGINGCATEGORIES_H
|
||||
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(logDebug)
|
||||
Q_DECLARE_LOGGING_CATEGORY(logInfo)
|
||||
Q_DECLARE_LOGGING_CATEGORY(logWarning)
|
||||
Q_DECLARE_LOGGING_CATEGORY(logCritical)
|
||||
|
||||
#endif // LOGGINGCATEGORIES_H
|
||||
194
src/core/protocol_handler.cpp
Normal file
194
src/core/protocol_handler.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
#include "protocol_handler.h"
|
||||
|
||||
|
||||
ProtocolHandler::ProtocolHandler(const QJsonObject& expectations)
|
||||
{
|
||||
exp = expectations;
|
||||
}
|
||||
|
||||
void ProtocolHandler::feed(const QByteArray& data)
|
||||
{
|
||||
QString text = QString::fromUtf8(data);
|
||||
|
||||
buffer += text;
|
||||
messages.push_back(text);
|
||||
|
||||
QJsonValue startExp = exp["start"];
|
||||
QJsonValue endExp = exp["end"];
|
||||
|
||||
if (startExp.isNull())
|
||||
{ startReceived = true; }
|
||||
|
||||
if (!startReceived)
|
||||
{ checkStart(); }
|
||||
|
||||
if (startReceived)
|
||||
{ checkSequence(); }
|
||||
|
||||
if (endExp.isNull())
|
||||
{ endReceived = true; }
|
||||
|
||||
if (!endReceived)
|
||||
{ checkEnd(); }
|
||||
}
|
||||
|
||||
void ProtocolHandler::checkStart()
|
||||
{
|
||||
if (!buffer.contains("{"))
|
||||
return;
|
||||
|
||||
int start = buffer.indexOf("{");
|
||||
int end = buffer.indexOf("}");
|
||||
|
||||
if (end < 0)
|
||||
return;
|
||||
|
||||
QString jsonText = buffer.mid(start, end - start + 1);
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(jsonText.toUtf8());
|
||||
|
||||
if (!doc.isObject()) return;
|
||||
|
||||
QJsonObject msg = doc.object();
|
||||
|
||||
QJsonObject expected = exp["start"]
|
||||
.toObject()["fields"]
|
||||
.toObject();
|
||||
|
||||
bool ok = true;
|
||||
|
||||
for (QString key : expected.keys())
|
||||
{
|
||||
if (msg[key] != expected[key])
|
||||
{
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{ startReceived = true; }
|
||||
else
|
||||
{
|
||||
QVariantMap m;
|
||||
|
||||
m["stage"] = "start";
|
||||
m["expected"] = expected;
|
||||
m["actual"] = msg.toVariantMap();
|
||||
|
||||
mismatches.append(m);
|
||||
}
|
||||
|
||||
buffer = buffer.mid(end + 1);
|
||||
}
|
||||
|
||||
void ProtocolHandler::checkSequence()
|
||||
{
|
||||
QJsonArray seq = exp["sequence"].toArray();
|
||||
|
||||
while (sequenceIndex < seq.size())
|
||||
{
|
||||
QJsonObject current =
|
||||
seq[sequenceIndex].toObject();
|
||||
|
||||
QString type = current["type"].toString();
|
||||
QString value = current["value"].toString();
|
||||
|
||||
bool matched = false;
|
||||
|
||||
if (type == "contains")
|
||||
{
|
||||
matched = buffer.contains(value);
|
||||
}
|
||||
else if (type == "regex")
|
||||
{
|
||||
QRegularExpression re(value);
|
||||
matched = re.match(buffer).hasMatch();
|
||||
}
|
||||
|
||||
if (matched)
|
||||
{
|
||||
sequenceIndex++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ProtocolHandler::checkEnd()
|
||||
{
|
||||
QJsonObject endExp = exp["end"].toObject();
|
||||
|
||||
if (endExp.isEmpty()) return;
|
||||
|
||||
QString type = endExp["type"].toString();
|
||||
QString value = endExp["value"].toString();
|
||||
|
||||
if (type == "contains")
|
||||
{
|
||||
if (buffer.contains(value))
|
||||
{
|
||||
endReceived = true;
|
||||
|
||||
// sequence incomplete
|
||||
|
||||
QJsonArray seq = exp["sequence"].toArray();
|
||||
|
||||
if (sequenceIndex < seq.size())
|
||||
{
|
||||
QVariantMap m;
|
||||
|
||||
m["stage"] = "sequence";
|
||||
m["step"] = sequenceIndex;
|
||||
m["expected"] =
|
||||
seq[sequenceIndex].toObject()
|
||||
.toVariantMap()["value"];
|
||||
|
||||
m["actual_buffer"] = buffer.right(200);
|
||||
|
||||
mismatches.append(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ProtocolHandler::isDone(QString &msg) const
|
||||
{
|
||||
bool startOk = true;
|
||||
bool endOk = true;
|
||||
|
||||
QJsonValue startExp = exp["start"];
|
||||
|
||||
QJsonValue endExp = exp["end"];
|
||||
|
||||
if (!startExp.isNull())
|
||||
{
|
||||
startOk = startReceived;
|
||||
}
|
||||
|
||||
if (!endExp.isNull())
|
||||
{
|
||||
endOk = endReceived;
|
||||
}
|
||||
|
||||
if (!startOk)
|
||||
{
|
||||
msg.append("\nstart sequence did not receive");
|
||||
}
|
||||
|
||||
if (!endOk)
|
||||
{
|
||||
msg.append("\nend sequence did not receive");
|
||||
}
|
||||
|
||||
int seqSize =
|
||||
exp["sequence"]
|
||||
.toArray()
|
||||
.size();
|
||||
|
||||
return (
|
||||
startOk
|
||||
&& sequenceIndex >= seqSize
|
||||
&& endOk
|
||||
);
|
||||
}
|
||||
31
src/core/protocol_handler.h
Normal file
31
src/core/protocol_handler.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "declarations.h"
|
||||
|
||||
class ProtocolHandler
|
||||
{
|
||||
public:
|
||||
ProtocolHandler(const QJsonObject& expectations);
|
||||
|
||||
void feed(const QByteArray& data);
|
||||
|
||||
bool isDone(QString &msg) const;
|
||||
|
||||
QStringList messages;
|
||||
QVariantList mismatches;
|
||||
|
||||
private:
|
||||
void checkStart();
|
||||
void checkSequence();
|
||||
void checkEnd();
|
||||
|
||||
private:
|
||||
QJsonObject exp;
|
||||
QString buffer;
|
||||
|
||||
bool startReceived = false;
|
||||
bool endReceived = false;
|
||||
|
||||
int sequenceIndex = 0;
|
||||
|
||||
};
|
||||
47
src/core/test_loader.cpp
Normal file
47
src/core/test_loader.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "test_loader.h"
|
||||
|
||||
QStringList TestLoader::loadCases(const QString& group,
|
||||
const QString& caseFilter,
|
||||
int repeat)
|
||||
{
|
||||
QString fname = "C:/Danila/work/embedded_test_stand/configs/test_plan.json";
|
||||
QFile f(fname);
|
||||
QStringList result;
|
||||
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
{
|
||||
throw(ErrOpenFile("cannot open json file", fname));
|
||||
}
|
||||
|
||||
QJsonObject root = QJsonDocument::fromJson(f.readAll()).object();
|
||||
|
||||
const QJsonArray arr = root["groups"].toObject()[group].toArray();
|
||||
|
||||
if (arr.count() == 0)
|
||||
{
|
||||
throw(ErrOpenFile("invalid format in json file!", fname));
|
||||
}
|
||||
|
||||
|
||||
for (auto v : arr)
|
||||
{
|
||||
QString name = v.toString();
|
||||
|
||||
// skip test
|
||||
if (name.startsWith("#"))
|
||||
continue;
|
||||
|
||||
// filter
|
||||
if (!caseFilter.isEmpty())
|
||||
{
|
||||
if (!name.contains(caseFilter))
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < repeat; ++i)
|
||||
{ result << QString("C:/Danila/work/embedded_test_stand/configs/test_cases/%1").arg(name); }
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
11
src/core/test_loader.h
Normal file
11
src/core/test_loader.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "exceptions_handle.h"
|
||||
|
||||
class TestLoader
|
||||
{
|
||||
public:
|
||||
static QStringList loadCases(const QString& group,
|
||||
const QString& caseFilter = QString(),
|
||||
int repeat = 1);
|
||||
};
|
||||
1
src/core/test_options.cpp
Normal file
1
src/core/test_options.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "test_options.h"
|
||||
17
src/core/test_options.h
Normal file
17
src/core/test_options.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef TEST_OPTIONS_H
|
||||
#define TEST_OPTIONS_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
struct TestOptions
|
||||
{
|
||||
QString group = "full";
|
||||
|
||||
QString caseFilter;
|
||||
|
||||
int repeat = 1;
|
||||
|
||||
int delayMs = 0;
|
||||
};
|
||||
|
||||
#endif // TEST_OPTIONS_H
|
||||
12
src/core/test_stats.h
Normal file
12
src/core/test_stats.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef TEST_STATS_H
|
||||
#define TEST_STATS_H
|
||||
|
||||
struct TestStats
|
||||
{
|
||||
int total = 0;
|
||||
int passed = 0;
|
||||
int failed = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // TEST_STATS_H
|
||||
59
src/core/uart.cpp
Normal file
59
src/core/uart.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "uart.h"
|
||||
|
||||
UART::UART()
|
||||
{
|
||||
}
|
||||
|
||||
UART::~UART()
|
||||
{
|
||||
}
|
||||
|
||||
void UART::connect(comSettings_t set)
|
||||
{
|
||||
serial.setPortName(set.name);
|
||||
serial.setBaudRate(set.baudRate);
|
||||
serial.setDataBits(set.dataBits);
|
||||
serial.setParity(set.parity);
|
||||
serial.setStopBits(set.stopBits);
|
||||
serial.setFlowControl(set.flowControl);
|
||||
|
||||
|
||||
if (!serial.open(QIODevice::ReadWrite))
|
||||
{
|
||||
throw(ErrOpenFile(QString("error opening com-port: %1").
|
||||
arg(serial.errorString()), set.name));
|
||||
}
|
||||
}
|
||||
|
||||
bool UART::send(const QByteArray& data, QString &err)
|
||||
{
|
||||
bool st = false;
|
||||
int status = serial.write(data);
|
||||
serial.flush();
|
||||
|
||||
if (status == -1)
|
||||
{ return false; }
|
||||
else
|
||||
{ st = serial.waitForBytesWritten(3000); }
|
||||
|
||||
err = serial.errorString();
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
QByteArray UART::receive(int timeoutMs)
|
||||
{
|
||||
QByteArray result;
|
||||
|
||||
while (serial.waitForReadyRead(timeoutMs))
|
||||
{
|
||||
result += serial.readAll();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void UART::close()
|
||||
{
|
||||
serial.close();
|
||||
}
|
||||
22
src/core/uart.h
Normal file
22
src/core/uart.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "exceptions_handle.h"
|
||||
|
||||
class UART
|
||||
{
|
||||
public:
|
||||
UART();
|
||||
~UART();
|
||||
|
||||
public:
|
||||
void connect(comSettings_t set);
|
||||
|
||||
bool send(const QByteArray& data, QString &err);
|
||||
|
||||
QByteArray receive(int timeoutMs);
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
QSerialPort serial;
|
||||
};
|
||||
Reference in New Issue
Block a user