eliminate faults. First stable version works as separate .exe file with configurations file

This commit is contained in:
2026-05-23 01:49:30 +03:00
parent 47395d02d6
commit f2457f2bb6
16 changed files with 280 additions and 126 deletions

View File

@@ -11,6 +11,8 @@
"Path": {
"MK_elf_file": "C:/Danila/work/sputnik_test/src/sputnik/Debug/c.out",
"MK_map_file": "C:/Danila/work/sputnik_test/src/sputnik/Debug/sputnik.map",
"test_plan_path": "C:/Danila/work/embedded_test_stand/configs/test_plan.json",
"test_cases_path": "C:/Danila/work/embedded_test_stand/configs/test_cases/",
"test_log_path": "C:/Danila/work/embedded_test_stand/logs/",
"stand_report_html_path": "C:/Danila/work/embedded_test_stand/logs/report.html",
"map_parser": "C:/Danila/work/embedded_test_stand/python/iar_parser/map_parser.py",

View File

@@ -2,8 +2,11 @@
"meta": {
"name": "endurance_50_60_marchFTE_5",
"description": "start endurance test in NAND Flash",
"input_data_type": "text",
"parameters": {
"_comment": "protocol_ID = 1 for text format from UART, see protocol_ID_en",
"protocol_ID": 1,
"version": 1,
"cmd_from_binary_file": true,
"cmd_code": 22,
"block_check_cnt": 2,
"blocks": [50, 60]
@@ -31,5 +34,5 @@
"end": null
},
"timeout_msec": 9000
"timeout_msec": 20000
}

View File

@@ -2,8 +2,11 @@
"meta": {
"name": "get_savedBadBlockMap_fromNAND",
"description": "get bad block map from NAND Flash into RAM",
"input_data_type": "text",
"parameters": {
"_comment": "protocol_ID = 1 for text format from UART, see protocol_ID_en",
"protocol_ID": 1,
"version": 1,
"cmd_from_binary_file": true,
"cmd_code": 4,
"badBlockMapOperMode": 2
}

View File

@@ -2,8 +2,11 @@
"meta": {
"name": "randomDataTest_100_110",
"description": "start random data test in all target of NAND Flash",
"input_data_type": "text",
"parameters": {
"_comment": "protocol_ID = 1 for text format from UART, see protocol_ID_en",
"protocol_ID": 1,
"version": 1,
"cmd_from_binary_file": true,
"cmd_code": 19,
"mode_random": 2,
"seed":1,

View File

@@ -2,8 +2,11 @@
"meta": {
"name": "set_micron_MT29F16G08AJADAWP",
"description": "check settings for micron_MT29F16G08AJADAWP",
"input_data_type": "text",
"parameters": {
"_comment": "protocol_ID = 1 for text format from UART, see protocol_ID_en",
"protocol_ID": 1,
"version": 1,
"cmd_from_binary_file": true,
"cmd_code": 5,
"endurance": 100000,
"timerPeriod": 100,

View File

@@ -2,8 +2,11 @@
"meta": {
"name": "start_March-FTE_all_targ0_repeat2",
"description": "start March-FTE for all target",
"input_data_type": "text",
"parameters": {
"_comment": "protocol_ID = 1 for text format from UART, see protocol_ID_en",
"protocol_ID": 1,
"version": 1,
"cmd_from_binary_file": true,
"cmd_code": 20,
"target": 0,
"repeat": 2

View File

@@ -2,8 +2,11 @@
"meta": {
"name": "start_March-FTE_by_blocks",
"description": "start March-FTE by specified blocks",
"input_data_type": "text",
"parameters": {
"_comment": "protocol_ID = 1 for text format from UART, see protocol_ID_en",
"protocol_ID": 1,
"version": 1,
"cmd_from_binary_file": true,
"cmd_code": 21,
"mode_March_FTE": 2,
"begin_block": 20,

View File

@@ -6,9 +6,6 @@
"test_set_micron_MT29F16G08AJADAWP.json",
"test_readSnapshot_HK.json",
"test_get_badBlockMap_fromNAND.json",
"test_readSnapshot_HK.json",
"test_erase_all.json",
"test_abort.json",
"test_readSnapshot_HK.json"
],
@@ -19,6 +16,9 @@
"test_readSnapshot_HK.json",
"test_get_badBlockMap_fromNAND.json",
"test_readSnapshot_HK.json",
"test_erase_all.json",
"test_abort.json",
"test_readSnapshot_HK.json",
"test_start_March_FTE_by_blocks.json",
"test_readSnapshot_HK.json",
"test_randomDataTest_100_110.json",
@@ -28,6 +28,9 @@
"test_readSnapshot_HK.json",
"test_start_March_FTE_all_targ0_repeat2.json",
"test_abort.json",
"test_readSnapshot_HK.json",
"test_start_retention_rand_25_bin.json",
"test_abort.json",
"test_readSnapshot_HK.json"
],
@@ -35,10 +38,17 @@
"test_abort.json"
],
"endurance": [
"test_endurance_50_60_marchFTE_5_100.json",
"test_abort.json"
],
"one": [
"test_UART_cmdSetDataInterface.json",
"test_readSnapshot_HK.json",
"test_read_NANDctrlOper.json"
"#test_UART_cmdSetDataInterface.json",
"test_start_retention_rand_25_bin.json",
"test_abort.json",
"#test_readSnapshot_HK.json",
"#test_read_NANDctrlOper.json"
]
}
}

View File

@@ -30,6 +30,14 @@
#include "html_report.h"
#include "crc8.h"
typedef enum
{
protocol_ID_none = 0,
UART_text = 1,
UART_bin_readStruct = 2,
PROTOCOL_ID_MAX = 255
} protocol_ID_en;
struct comSet_t {
int addrRS485;
QString name;
@@ -56,6 +64,8 @@ typedef comSet_t comSettings_t;
typedef struct {
QString MK_elf_file;
QString MK_map_file;
QString test_plan_path;
QString test_cases_path;
QString test_log_path;
QString stand_report_html_path;
QString map_parser;

View File

@@ -1,14 +1,21 @@
#include "protocol_handler.h"
ProtocolHandler::ProtocolHandler(const QJsonObject& expectations)
ProtocolHandler::ProtocolHandler(const QJsonObject& expectations,
QString caseFile,
QString jsonObjName)
{
expStr = jsonObjName;
json.setJsonFile(caseFile);
exp = expectations;
}
void ProtocolHandler::feed(const QByteArray& data)
bool ProtocolHandler::feed(const QByteArray& data, QString& msgError)
{
try
{
QString text = QString::fromUtf8(data);
bool status = false;
buffer += text;
messages.push_back(text);
@@ -16,44 +23,88 @@ void ProtocolHandler::feed(const QByteArray& data)
QJsonValue startExp = exp["start"];
QJsonValue endExp = exp["end"];
if (startExp.isNull())
{ startReceived = true; }
if (startExp.isNull()) { startReceived = true; }
if (!startReceived)
{ checkStart(); }
{
status = checkStart(msgError);
if (status == false) { return status; }
}
if (startReceived)
{ checkSequence(); }
{
status = checkSequence(msgError);
if (status == false) { return status; }
}
if (endExp.isNull())
{ endReceived = true; }
if (endExp.isNull()) { endReceived = true; }
if (!endReceived)
{ checkEnd(); }
{
status = checkEnd(msgError);
if (status == false) { return status; }
}
return true;
}
catch (ErrOpenFile &errOpen)
{
msgError = QString("%1, path: %2").arg(errOpen.getMessage(), errOpen.getFilePath());
return false;
}
catch (ErrInJsonSet &jsonSet)
{
if (jsonSet.checkErrFromJson())
{
msgError = QString("%1. %2 : %3. file: %4")
.arg(jsonSet.getIntro(), jsonSet.getErrMsg(),
jsonSet.getErrFromJson(), jsonSet.getFname());
return false;
}
else
{
msgError = QString("%1. %2: \nJSON parameter: %3\nJSON object: %4.\nfile: %5")
.arg(jsonSet.getIntro(), jsonSet.getErrMsg(), jsonSet.getParam(),
jsonSet.getJsonObj(), jsonSet.getFname());
return false;
}
}
}
void ProtocolHandler::checkStart()
bool ProtocolHandler::checkStart(QString &msgErr)
{
if (!buffer.contains("{"))
return;
{
msgErr = "missing '{' in received data UART";
return false;
}
int start = buffer.indexOf("{");
int end = buffer.indexOf("}");
if (end < 0)
return;
{
msgErr = "missing '}' in received data UART";
return false;
}
QString jsonText = buffer.mid(start, end - start + 1);
QJsonDocument doc = QJsonDocument::fromJson(jsonText.toUtf8());
if (!doc.isObject()) return;
if (!doc.isObject())
{
msgErr = "error in first message in received data UART";
return false;
}
QJsonObject msg = doc.object();
QJsonObject expected = exp["start"].toObject()["fields"].toObject();
QJsonObject expected = exp["start"]
.toObject()["fields"]
.toObject();
if (expected.isEmpty())
{
msgErr = "missing 'start' or 'fields' field";
return false;
}
bool ok = true;
@@ -80,49 +131,66 @@ void ProtocolHandler::checkStart()
}
buffer = buffer.mid(end + 1);
return true;
}
void ProtocolHandler::checkSequence()
bool ProtocolHandler::checkSequence(QString& msgErr)
{
QJsonArray seq = exp["sequence"].toArray();
QString type, value;
while (sequenceIndex < seq.size())
{
QJsonObject current =
seq[sequenceIndex].toObject();
QJsonObject current = seq[sequenceIndex].toObject();
QString type = current["type"].toString();
QString value = current["value"].toString();
if (current.isEmpty())
{
msgErr = "incorrect 'sequence' group";
return false;
}
json.jsonGetStrValue(current, "type", type, "sequence");
json.jsonGetStrValue(current, "value", value, "sequence");
bool matched = false;
if (type == "contains")
{
matched = buffer.contains(value);
}
{ matched = buffer.contains(value); }
else if (type == "regex")
{
QRegularExpression re(value);
matched = re.match(buffer).hasMatch();
}
else {
msgErr = QString("unsupported format '%1' in 'sequence' group!").arg(type);
return false;
}
if (matched)
if (!matched)
{
msgErr = QString("in received UART data the following string is not found: %1").arg(value);
return false;
}
sequenceIndex++;
}
}
return true;
}
void ProtocolHandler::checkEnd()
bool ProtocolHandler::checkEnd(QString &msgErr)
{
QJsonObject endExp = exp["end"].toObject();
QString type, value;
if (endExp.isEmpty()) return;
if (endExp.isEmpty())
{
msgErr = "incorrect 'end' group";
return false;
}
QString type = endExp["type"].toString();
QString value = endExp["value"].toString();
json.jsonGetStrValue(endExp, "type", type, "end");
json.jsonGetStrValue(endExp, "value", value, "end");
if (type == "contains")
{
@@ -130,8 +198,6 @@ void ProtocolHandler::checkEnd()
{
endReceived = true;
// sequence incomplete
QJsonArray seq = exp["sequence"].toArray();
if (sequenceIndex < seq.size())
@@ -140,16 +206,21 @@ void ProtocolHandler::checkEnd()
m["stage"] = "sequence";
m["step"] = sequenceIndex;
m["expected"] =
seq[sequenceIndex].toObject()
m["expected"] = seq[sequenceIndex].toObject()
.toVariantMap()["value"];
m["actual_buffer"] = buffer.right(200);
mismatches.append(m);
}
}
}
else
{
msgErr = QString("not support %1 in 'end' group").arg(type);
return false;
}
return true;
}
bool ProtocolHandler::isDone(QString &msg) const
@@ -158,37 +229,21 @@ bool ProtocolHandler::isDone(QString &msg) const
bool endOk = true;
QJsonValue startExp = exp["start"];
QJsonValue endExp = exp["end"];
if (!startExp.isNull())
{
startOk = startReceived;
}
{ startOk = startReceived; }
if (!endExp.isNull())
{
endOk = endReceived;
}
{ endOk = endReceived; }
if (!startOk)
{
msg.append("\nstart sequence did not receive");
}
{ msg.append("\nstart sequence did not receive"); }
if (!endOk)
{
msg.append("\nend sequence did not receive");
}
{ msg.append("\nend sequence did not receive"); }
int seqSize =
exp["sequence"]
.toArray()
.size();
int seqSize = exp["sequence"].toArray().size();
return (
startOk
&& sequenceIndex >= seqSize
&& endOk
);
return (startOk && sequenceIndex >= seqSize && endOk);
}

View File

@@ -1,26 +1,29 @@
#pragma once
#include "declarations.h"
#include <json_processor.h>
class ProtocolHandler
{
public:
ProtocolHandler(const QJsonObject& expectations);
void feed(const QByteArray& data);
ProtocolHandler(const QJsonObject& expectations,
QString caseFile, QString jsonObjName);
bool feed(const QByteArray& data, QString& msgError);
bool isDone(QString &msg) const;
QStringList messages;
QVariantList mismatches;
private:
void checkStart();
void checkSequence();
void checkEnd();
bool checkStart(QString& msgErr);
bool checkSequence(QString& msgErr);
bool checkEnd(QString& msgErr);
private:
QJsonObject exp;
QString expStr;
JsonProcessor json;
QString buffer;
bool startReceived = false;

View File

@@ -4,13 +4,22 @@ 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);
JsonProcessor json;
QJsonObject jsonObj;
QString jsonPath = "Path";
path_t path;
json.openJsonFile("../config.json", jsonObj);
QJsonObject objPath = jsonObj.value(jsonPath).toObject();
json.jsonGetStrValue(objPath, "test_plan_path", path.test_plan_path, jsonPath);
json.jsonGetStrValue(objPath, "test_cases_path", path.test_cases_path, jsonPath);
QFile f(path.test_plan_path);
QStringList result;
if (!f.open(QIODevice::ReadOnly))
{
throw(ErrOpenFile("cannot open json file", fname));
throw(ErrOpenFile("cannot open json file", path.test_plan_path));
}
QJsonObject root = QJsonDocument::fromJson(f.readAll()).object();
@@ -19,7 +28,7 @@ QStringList TestLoader::loadCases(const QString& group,
if (arr.count() == 0)
{
throw(ErrOpenFile("invalid format in json file!", fname));
throw(ErrOpenFile("invalid format in json file!", path.test_plan_path));
}
@@ -39,7 +48,7 @@ QStringList TestLoader::loadCases(const QString& group,
}
for (int i = 0; i < repeat; ++i)
{ result << QString("C:/Danila/work/embedded_test_stand/configs/test_cases/%1").arg(name); }
{ result << QString("%1%2").arg(path.test_cases_path, name); }
}

View File

@@ -1,11 +1,14 @@
#pragma once
#include "exceptions_handle.h"
#include "json_processor.h"
class TestLoader
{
public:
static QStringList loadCases(const QString& group,
const QString& caseFilter = QString(),
int repeat = 1);
};

View File

@@ -40,6 +40,27 @@ std::vector<TestCaseParam> getJsonTests()
errOpen.getMessage(),errOpen.getFilePath()));
return result;
}
catch (ErrInJsonSet &jsonSet)
{
if (jsonSet.checkErrFromJson())
{
QString msg = QString("%1. %2 : %3. file: %4")
.arg(jsonSet.getIntro(), jsonSet.getErrMsg(),
jsonSet.getErrFromJson(), jsonSet.getFname());
qDebug(logCritical()) << msg;
Logger::writeToConsol(msg);
return result;
}
else
{
QString msg = QString("%1. %2: \nJSON parameter: %3\nJSON object: %4.\nfile: %5")
.arg(jsonSet.getIntro(), jsonSet.getErrMsg(), jsonSet.getParam(),
jsonSet.getJsonObj(), jsonSet.getFname());
qDebug(logCritical()) << msg;
Logger::writeToConsol(msg);
return result;
}
}
for (const QString &path : list)
{

View File

@@ -217,27 +217,50 @@ bool UARTFixture::validateTextResponse(const QByteArray& rx,
passed = false; return passed;
}
ProtocolHandler handler(exp);
handler.feed(rx);
passed = handler.isDone(protocolMsg);
ProtocolHandler handler(exp, caseFile, "expectations");
passed = handler.feed(rx, protocolMsg);
if (!passed)
{ error = QString("Protocol validation failed:\n%1").arg(protocolMsg); }
{
error = QString("Protocol validation failed: %1, testCase: %2")
.arg(protocolMsg, caseFile);
writeToLog(error, false);
}
else
{
passed = handler.isDone(protocolMsg);
if (!passed)
{
error = QString("Protocol validation failed: %1, testCase: %2")
.arg(protocolMsg, caseFile);
writeToLog(error, false);
}
}
Logger::saveTestLog(logFile, handler, cfg, passed, error);
return passed;
}
bool UARTFixture::runTextCase(const QJsonObject& cfg, QString caseFile, QString jsonObjName)
bool UARTFixture::runCaseText(const QJsonObject& cfg, const QJsonObject& param,
QString caseFile, QString jsonObjName)
{
QByteArray tx;
QString binaryFname;
json.jsonGetStrValue(cfg, "binary", binaryFname, jsonObjName);
bool binaryFile = json.jsonGetBoolValue(param, "cmd_from_binary_file", "parameters");
if (binaryFile)
{
QString binaryPath;
json.jsonGetStrValue(cfg, "binary", binaryPath, "");
if (!loadBinaryFile(binaryFname, tx)) { return false; }
}
else
{
writeToLog(QString("command supports from binary file only: %1").arg(caseFile), false);
return false;
}
if (!sendCommand(tx)) { return false; }
QByteArray rx = receiveResponse(cfg);
@@ -531,24 +554,15 @@ bool UARTFixture::validateBinaryResponse(const QByteArray& rx, const QJsonObject
return true;
}
bool UARTFixture::runBinaryCase(const QJsonObject& cfg, const QJsonObject& meta,
bool UARTFixture::runCaseReadStructureBin(const QJsonObject& cfg, const QJsonObject& param,
QString caseFile)
{
QByteArray tx;
QString struct_name, struct_type, structJsonFileName;
QJsonObject param, structJson;
QJsonObject structJson;
// find struct_name from test_case
QString parameters = "parameters";
param = meta[parameters].toObject();
if (param.isEmpty())
{
writeToLog(QString("'parameters' field not found in %1").arg(caseFile), false);
return false;
}
json.jsonGetStrValue(param, "struct_name", struct_name, parameters);
json.jsonGetStrValue(param, "struct_type", struct_type, parameters);
json.jsonGetStrValue(param, "struct_name", struct_name, "parameters");
json.jsonGetStrValue(param, "struct_type", struct_type, "parameters");
structJsonFileName = QString("%1%2.json").arg(path.iar_json_out, struct_type);
@@ -572,25 +586,33 @@ bool UARTFixture::runBinaryCase(const QJsonObject& cfg, const QJsonObject& meta,
void UARTFixture::runCase(const QString& caseFile)
{
QJsonObject cfg, meta;
QJsonObject cfg, meta, param;
json.setJsonFile(caseFile);
try
{
if (!loadTestCase(caseFile, cfg, meta)) { return; }
bool passed = false;
QString rx_data_type;
json.jsonGetStrValue(meta, "input_data_type", rx_data_type, "meta");
if (rx_data_type == "text")
{ passed = runTextCase(cfg, caseFile, "meta"); }
else if (rx_data_type == "binary")
{ passed = runBinaryCase(cfg, meta, caseFile); }
QString parameters = "parameters";
param = meta[parameters].toObject();
if (param.isEmpty())
{
writeToLog(QString("'parameters' field not found in %1").arg(caseFile), false);
return;
}
protocol_ID_en protocolID = static_cast<protocol_ID_en>(
json.jsonGetIntValue(param, "protocol_ID", parameters));
if (protocolID == UART_text)
{ passed = runCaseText(cfg, param, caseFile, "meta"); }
else if (protocolID == UART_bin_readStruct)
{ passed = runCaseReadStructureBin(cfg, param, caseFile); }
else
{
writeToLog(QString("unknown input_data_type (%1) in file: %2")
.arg(rx_data_type, caseFile), false);
.arg(QString(protocolID), caseFile), false);
passed = false;
updateStats(passed);
return;

View File

@@ -101,7 +101,8 @@ protected:
bool validateTextResponse(const QByteArray& rx,
const QJsonObject& cfg, QString caseFile);
bool runTextCase(const QJsonObject& cfg, QString caseFile, QString jsonObjName);
bool runCaseText(const QJsonObject& cfg, const QJsonObject &param,
QString caseFile, QString jsonObjName);
bool prepareBinaryCommand(const QJsonObject& cfg, QByteArray& data, QString &caseFile);
bool validateStructField(const QByteArray& rxData,
@@ -112,7 +113,7 @@ protected:
bool validateBinaryResponse(const QByteArray& rx, const QJsonObject& cfg,
const QJsonObject &structJson, QString &structJsonFname,
QString caseFile);
bool runBinaryCase(const QJsonObject& cfg, const QJsonObject &meta,
bool runCaseReadStructureBin(const QJsonObject& cfg, const QJsonObject &param,
QString caseFile);
bool loadTestCase(const QString& caseFile,