From 10b631bc07c3c6d3f1888c6c48f64018456da1cc Mon Sep 17 00:00:00 2001 From: Danila Date: Wed, 20 May 2026 15:15:42 +0300 Subject: [PATCH] 1. parser binary data get info about structure from elf and test_case.json. 2. Rewrite runCase. All works --- configs/test_plan.json | 14 +- src/core/exceptions_handle.h | 25 +- src/core/json_processor.cpp | 34 +- src/core/logger.cpp | 37 ++ src/core/logger.h | 4 + src/tests/test_runner.cpp | 106 ----- src/tests/uart_fixture.cpp | 752 +++++++++++++++++++++-------------- src/tests/uart_fixture.h | 30 +- 8 files changed, 563 insertions(+), 439 deletions(-) diff --git a/configs/test_plan.json b/configs/test_plan.json index 25f067d..4ac119d 100644 --- a/configs/test_plan.json +++ b/configs/test_plan.json @@ -2,20 +2,30 @@ "groups": { "smoke": [ "test_UART_cmdSetDataInterface.json", + "test_readSnapshot_HK.json", "test_set_micron_MT29F16G08AJADAWP.json", - "test_get_badBlockMap_fromNAND.json" + "test_readSnapshot_HK.json", + "test_get_badBlockMap_fromNAND.json", + "test_readSnapshot_HK.json" ], "full": [ "test_UART_cmdSetDataInterface.json", + "test_readSnapshot_HK.json", "test_set_micron_MT29F16G08AJADAWP.json", + "test_readSnapshot_HK.json", "test_get_badBlockMap_fromNAND.json", + "test_readSnapshot_HK.json", "test_start_March_FTE_by_blocks.json", + "test_readSnapshot_HK.json", "test_randomDataTest_100_110.json", + "test_readSnapshot_HK.json", "test_endurance_50_60_marchFTE_5.json", "test_abort.json", + "test_readSnapshot_HK.json", "test_start_March_FTE_all_targ0_repeat2.json", - "test_abort.json" + "test_abort.json", + "test_readSnapshot_HK.json" ], "abort": [ diff --git a/src/core/exceptions_handle.h b/src/core/exceptions_handle.h index 5cb3b21..3eefd88 100644 --- a/src/core/exceptions_handle.h +++ b/src/core/exceptions_handle.h @@ -30,27 +30,6 @@ private: 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: @@ -59,7 +38,7 @@ public: this->fname = fname; this->jsonObjectsList = jsonObjectsList; this->param = param; - this->intro = "ошибка чтения файла настроек"; + this->intro = "error reading json file"; this->errMsg = errMsg; this->errFromJsonFlag = false; } @@ -67,7 +46,7 @@ public: { this->fname = fname; this->errFromJsonFlag = true; - this->intro = "ошибка чтения файла настроек"; + this->intro = "error reading json file"; this->errFromJson = errFromJson; } QString getFname() { return fname; } diff --git a/src/core/json_processor.cpp b/src/core/json_processor.cpp index ea27f4b..956224a 100644 --- a/src/core/json_processor.cpp +++ b/src/core/json_processor.cpp @@ -21,7 +21,7 @@ void JsonProcessor::openJsonFile(QString jsonPath, QJsonObject &jsonObj) file.close(); } else - { throw(ErrOpenFile(jsonPath, "файл не найден")); } + { throw(ErrOpenFile(jsonPath, "file not found")); } QJsonParseError jsonError; QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonError); @@ -32,7 +32,7 @@ void JsonProcessor::openJsonFile(QString jsonPath, QJsonObject &jsonObj) if (doc.isObject()) { jsonObj = doc.object(); } else - { throw(ErrInJsonSet(jsonPath, "неверный формат json: " + jsonPath)); } + { throw(ErrInJsonSet(jsonPath, "incorrect json format: " + jsonPath)); } } } @@ -50,7 +50,7 @@ void JsonProcessor::saveJsonDataInFile(QString jsonPath, QJsonObject jsonObj) file.close(); } else - { throw(ErrOpenFile(jsonPath, "файл не найден")); } + { throw(ErrOpenFile(jsonPath, "file not found")); } } /* common functions */ @@ -61,7 +61,11 @@ void JsonProcessor::jsonConvertStruct(QString jsonObjName, QJsonObject obj, QJsonArray *param = new QJsonArray; *param = obj[jsonParamName].toArray(); if (param->size() != arrSize) - { throw(ErrInJsonSet(jsonPath, jsonObjName, jsonParamName, "параметр отсутствует или кол-во элементов в параметре должно быть равно " + QString::number(arrSize))); } + { + throw(ErrInJsonSet(jsonPath, jsonObjName, jsonParamName, + "parameter is missing or elements count in parameter must be equal " + + QString::number(arrSize))); + } else { for (int i = 0; i < arrSize; i++) @@ -95,7 +99,7 @@ void JsonProcessor::jsonConvertStruct(QString jsonObjName, QJsonObject obj, QJsonArray param = obj[jsonParamName].toArray(); if (param.size() != arrSize) { throw(ErrInJsonSet(jsonPath, jsonObjName, jsonParamName, - "параметр отсутствует или кол-во элементов в параметре должно быть равно " + QString::number(arrSize))); } + "parameter is missing or elements count in parameter must be equal " + QString::number(arrSize))); } else { for (int i = 0; i < arrSize; i++) @@ -109,7 +113,7 @@ void JsonProcessor::jsonConvertStruct(QString jsonObjName, QJsonObject obj, QJsonArray param = obj[jsonParamName].toArray(); if (param.size() != arrSize) { throw(ErrInJsonSet(jsonPath, jsonObjName, jsonParamName, - "параметр отсутствует или кол-во элементов в параметре должно быть равно " + QString::number(arrSize))); } + "parameter is missing or elements count in parameter must be equal " + QString::number(arrSize))); } else { for (int i = 0; i < arrSize; i++) @@ -121,7 +125,7 @@ bool JsonProcessor::jsonGetBoolValue(QJsonObject obj, QString param, QString jso { QJsonValue val = obj[param]; if (val == QJsonValue::Null) - { throw(ErrInJsonSet(jsonPath, jsonGeneral, param, "отсутствует параметр")); } + { throw(ErrInJsonSet(jsonPath, jsonGeneral, param, "parameter is missing")); } return val.toBool(false); } @@ -129,7 +133,7 @@ void JsonProcessor::jsonGetStrValue(QJsonObject obj, QString paramName, QString { QString val = obj[paramName].toString(); if (val == NULL) - { throw(ErrInJsonSet(jsonPath, jsonObjName, paramName, "parameter missing")); } + { throw(ErrInJsonSet(jsonPath, jsonObjName, paramName, "parameter is missing")); } else { paramValue = val; } } @@ -140,34 +144,34 @@ void JsonProcessor::jsonSetComPortSettings(QString jsonObjName, QJsonObject obj, com.addrRS485 = obj["RS485_address"].toInt(); if (com.addrRS485 < 1 || com.addrRS485 > 254) - { throw(ErrInJsonSet(jsonPath, jsonObjName, "RS485_address", "отсутствует параметр")); } + { throw(ErrInJsonSet(jsonPath, jsonObjName, "RS485_address", "paremeter is missing")); } com.name = obj["COM_port"].toString(); - if (com.name == NULL) { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_port", "отсутствует параметр")); } + if (com.name == NULL) { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_port", "paremeter is missing")); } com.baudRate = (quint32)obj["COM_baudRate"].toInt(); - if (com.baudRate == 0) { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_baudRate", "отсутствует параметр")); } + if (com.baudRate == 0) { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_baudRate", "paremeter is missing")); } com.dataBits = (QSerialPort::DataBits)obj["COM_bits"].toInt(); if (com.dataBits < QSerialPort::Data5 || com.dataBits > QSerialPort::Data8) - { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_bits", "отсутствует параметр")); } + { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_bits", "paremeter is missing")); } 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", "отсутствует параметр")); } + { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_parity", "paremeter is missing")); } com.stopBits = (QSerialPort::StopBits)obj["COM_stopBits"].toInt(); if (com.stopBits < QSerialPort::OneStop || com.stopBits > QSerialPort::OneAndHalfStop) - { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_stopBits", "отсутствует параметр")); } + { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_stopBits", "paremeter is missing")); } com.flowControl = (QSerialPort::FlowControl)obj["COM_flowCotrol"].toInt(); if (com.flowControl < QSerialPort::NoFlowControl || com.flowControl > QSerialPort::SoftwareControl) - { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_flowControl", "отсутствует параметр")); } + { throw(ErrInJsonSet(jsonPath, jsonObjName, "COM_flowControl", "paremeter is missing")); } } void JsonProcessor::jsonSaveComPortSettings(QJsonObject &obj, comSettings_t &com) diff --git a/src/core/logger.cpp b/src/core/logger.cpp index 094a493..e47ff49 100644 --- a/src/core/logger.cpp +++ b/src/core/logger.cpp @@ -16,6 +16,43 @@ void Logger::write(const QString& file, const QString& text) f.close(); } +void Logger::saveTestLog(const QString& filename, const QJsonObject& cfg, + bool passed, QString& error, QString mismatches) +{ + QString log; + + log += "\n<<>>\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 (!mismatches.isEmpty()) + { + log += "\n--- MISMATCHES ---\n"; + log += mismatches; + log += "\n"; + } + + log += "<<>>\n"; + + write(filename, log); +} + + void Logger::saveTestLog( const QString& filename, const ProtocolHandler& handler, diff --git a/src/core/logger.h b/src/core/logger.h index e03a43d..d6fb300 100644 --- a/src/core/logger.h +++ b/src/core/logger.h @@ -17,6 +17,10 @@ public: bool passed, const QString& error = ""); + + static void saveTestLog(const QString& filename, const QJsonObject& cfg, + bool passed, QString &error, QString mismatches); + static void appendSummary( const QString& filename, int total, diff --git a/src/tests/test_runner.cpp b/src/tests/test_runner.cpp index 8d53545..4a811d6 100644 --- a/src/tests/test_runner.cpp +++ b/src/tests/test_runner.cpp @@ -44,7 +44,6 @@ std::vector getJsonTests() for (const QString &path : list) { QFile f(path); - QString testName = "unknown"; if (f.open(QIODevice::ReadOnly)) @@ -75,108 +74,3 @@ INSTANTIATE_TEST_SUITE_P( return info.param.name.toStdString(); } ); - - - -//TEST_F(UARTFixture, SmokeTests) -//{ -// TestStats stats; -// QString msgItog; -// QString msg; -// QStringList tests; - -// bool fail = extract_struct_from_elf("Snapshot_HK_t"); -// if (fail == true) { return; } - -// try -// { -// tests = TestLoader::loadCases(g_options.group, -// g_options.caseFilter, -// g_options.repeat); -// } -// catch (ErrOpenFile &errOpen) -// { -// qDebug(logCritical()) << QString("%1, path: %2") -// .arg(errOpen.getMessage(), -// errOpen.getFilePath()); - -// Logger::writeToConsol(QString("\nERROR: %1, path: %2\n").arg( -// errOpen.getMessage(),errOpen.getFilePath())); -// return; -// } - -// QString curTime = QDateTime::currentDateTime().toString("dd_MM_yyyy_hh_mm_ss"); -// QString curTime1 = QDateTime::currentDateTime().toString("dd.MM.yyyy hh:mm:ss"); - -// QString logFile = QString("%1%2.log").arg(path.test_log_path, curTime); -// QString htmlFile = path.stand_report_html_path; - -// QFile::remove(logFile); - -// for (const QString &caseFile : qAsConst(tests)) -// { -// QFile caseFileJson(caseFile); -// QString error; - -// if (!caseFileJson.open(QIODevice::ReadOnly)) - -// { -// msg = QString("%1, path: %2").arg("json test case not found", caseFile); -// msgItog.append(msg); - -// qDebug(logCritical()) << msg << Qt::endl; -// Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); -// } - -// QJsonObject cfg = -// QJsonDocument::fromJson(caseFileJson.readAll()).object(); -// QString binaryFile = cfg["binary"].toString(); -// QFile cmdFile(binaryFile); - -// if (!cmdFile.open(QIODevice::ReadOnly)) -// { -// msg = QString("%1, path: %2").arg("command binary not found", binaryFile); -// msgItog.append(msg); - -// qDebug(logCritical()) << msg << Qt::endl; -// Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); -// } - -// QByteArray tx_cmd = cmdFile.readAll(); -// QString err; -// if (!uart.send(tx_cmd, err)) -// { -// msg = QString("%1: %2").arg("UART data did not send", err); -// msgItog.append(msg); - -// qDebug(logCritical()) << msg << Qt::endl; -// Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); -// } -// QByteArray rx_data = uart.receive(cfg["timeout_msec"].toInt()); - -// ProtocolHandler handler(cfg["expectations"].toObject()); -// handler.feed(rx_data); -// QString msg1; -// bool passed = handler.isDone(msg1); - -// if (!passed) -// { -// msgItog.append(msg1); -// error = QString("Protocol validation failed: \n %1").arg(msgItog); -// } - -// Logger::saveTestLog(logFile, handler, cfg, passed, error); -// stats.total++; -// if (passed) stats.passed++; -// else stats.failed++; -// EXPECT_TRUE(passed); - -// if (g_options.delayMs > 0) -// { -// QThread::msleep(g_options.delayMs); -// } -// } - -// Logger::appendSummary(logFile, stats.total, stats.passed, stats.failed); -// HtmlReport::generate(logFile, htmlFile, curTime1); -//} diff --git a/src/tests/uart_fixture.cpp b/src/tests/uart_fixture.cpp index c99a780..db371f1 100644 --- a/src/tests/uart_fixture.cpp +++ b/src/tests/uart_fixture.cpp @@ -1,4 +1,6 @@ #include "uart_fixture.h" +#include "../core/cli_parser.h" +#include "../core/html_report.h" UART UARTFixture::uart; comSettings_t UARTFixture::comPortSettings; @@ -6,44 +8,9 @@ Stats_t UARTFixture::stats; path_t UARTFixture::path; UART_cmdReadMemory_t UARTFixture::iData; char UARTFixture::cmdDataBuf[UART_DATA_MAX_BYTES]; +QVector UARTFixture::struct_names; QString UARTFixture::logFile; - -int UARTFixture::extract_struct_from_elf(QString structTypeName) -{ - QProcess process; - QString program = path.local_python; - - QStringList arguments; - arguments << path.elf_parser - << "-f" << path.MK_elf_file - << "-t" << structTypeName - << "-o" << path.iar_json_out; - - process.start(program, arguments); - - if (!process.waitForFinished(3000)) { - QString msg = QString("timeout processing scipt %1").arg(path.map_parser); - - qDebug(logCritical()) << msg; - Logger::writeToConsol(QString("\nERROR: %1").arg(msg)); - return 1; - } - - int exitCode = process.exitCode(); - - if (exitCode != 0) { - QString msg = QString("scipt %1 return error code %2: %3") - .arg(path.map_parser, - QString(exitCode), - QString::fromUtf8(process.readAllStandardError())); - - qDebug(logCritical()) << msg; - Logger::writeToConsol(QString("\nERROR: %1").arg(msg)); - return 1; - } - - return 0; -} +extern TestOptions g_options; int UARTFixture::convert_map_file() { @@ -58,11 +25,9 @@ int UARTFixture::convert_map_file() process.start(program, arguments); - if (!process.waitForFinished(3000)) { - QString msg = QString("timeout processing scipt %1").arg(path.map_parser); - - qDebug(logCritical()) << msg; - Logger::writeToConsol(QString("\nERROR: %1").arg(msg)); + if (!process.waitForFinished(3000)) + { + writeToLog(QString("timeout processing scipt %1").arg(path.map_parser), false); return 1; } @@ -70,12 +35,10 @@ int UARTFixture::convert_map_file() if (exitCode != 0) { QString msg = QString("scipt %1 return error code %2: %3") - .arg(path.map_parser, - QString(exitCode), + .arg(path.map_parser, QString(exitCode), QString::fromUtf8(process.readAllStandardError())); - qDebug(logCritical()) << msg; - Logger::writeToConsol(QString("\nERROR: %1").arg(msg)); + writeToLog(msg, false); return 1; } @@ -107,12 +70,8 @@ int UARTFixture::readConfig() } catch (ErrOpenFile &errOpen) { - qDebug(logCritical()) << QString("%1, path: %2") - .arg(errOpen.getMessage(), - errOpen.getFilePath()); - - Logger::writeToConsol(QString("\nERROR: %1, path: %2\n").arg( - errOpen.getMessage(),errOpen.getFilePath())); + QString msg = QString("%1, path: %2").arg(errOpen.getMessage(), errOpen.getFilePath()); + writeToLog(msg, false); return 1; } catch (ErrInJsonSet &jsonSet) @@ -120,26 +79,17 @@ int UARTFixture::readConfig() 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(QString("\nERROR: %1").arg(msg)); + .arg(jsonSet.getIntro(), jsonSet.getErrMsg(), + jsonSet.getErrFromJson(), jsonSet.getFname()); + writeToLog(msg, false); return 1; } 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(QString("\nERROR: %1").arg(msg)); + .arg(jsonSet.getIntro(), jsonSet.getErrMsg(), jsonSet.getParam(), + jsonSet.getJsonObj(), jsonSet.getFname()); + writeToLog(msg, false); return 1; } } @@ -166,12 +116,8 @@ void UARTFixture::SetUpTestSuite() } catch (ErrOpenFile &errOpen) { - qDebug(logCritical()) << QString("%1, path: %2") - .arg(errOpen.getMessage(), - errOpen.getFilePath()); - - Logger::writeToConsol(QString("\nERROR: %1, path: %2\n").arg( - errOpen.getMessage(),errOpen.getFilePath())); + QString msg = QString("%1, path: %2").arg(errOpen.getMessage(), errOpen.getFilePath()); + writeToLog(msg, false); return; } } @@ -186,12 +132,6 @@ void UARTFixture::TearDownTestSuite() HtmlReport::generate(logFile, htmlFile, curTime1); } - -#include "../core/cli_parser.h" -#include "../core/html_report.h" - -extern TestOptions g_options; - TEST_P(UARTFixture, JsonCase) { TestCaseParam param = GetParam(); @@ -199,190 +139,434 @@ TEST_P(UARTFixture, JsonCase) runCase(caseFile); } -void UARTFixture::writeToLog(QString errMsg, bool passed) + +bool UARTFixture::loadTestCase(const QString& caseFile, + QJsonObject& cfg, QJsonObject &meta) { - if (!passed) - { - stats.failed++; - qDebug(logCritical()) << errMsg << Qt::endl; - Logger::writeToConsol(QString("\nERROR: %1\n").arg(errMsg)); - } - else { - stats.passed++; - } - - EXPECT_TRUE(passed); - stats.total++; -} - -void UARTFixture::runCase( - const QString& caseFile) -{ - QString msgItog; - QString msg; - QString err; - QString error; - bool passed = false; - QString protocolMsg; - QString test_case_path = ""; - QFile caseFileJson(caseFile); if (!caseFileJson.open(QIODevice::ReadOnly)) - { writeToLog(QString("%1, path: %2").arg("json test case not found", caseFile), false); } - - QJsonObject cfg = - QJsonDocument::fromJson(caseFileJson.readAll()).object(); - - QJsonObject meta = cfg["meta"].toObject(); - - if (meta.isEmpty()) { - msg = QString("%1, path: %2").arg("incorrect json file: ", caseFile); - msgItog.append(msg); - - qDebug(logCritical()) << msg << Qt::endl; - Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); + writeToLog(QString("%1, path: %2").arg("json test case not found", caseFile), false); + return false; } + cfg = QJsonDocument::fromJson(caseFileJson.readAll()).object(); + meta = cfg["meta"].toObject(); - if (meta["input_data_type"].toString() == "binary") + if (cfg.isEmpty() || meta.isEmpty()) { - QJsonObject param = meta["parameters"].toObject(); + writeToLog(QString("%1, path: %2").arg("incorrect json file", caseFile), false); + return false; + } - bool ok = false; - iData.version = param["version"].toInt(); - - if (param["addr_user_set"].toBool() == true) - { iData.addr_begin = param["addr"].toString().toUInt(&ok, 16); } - - if (param["size_user_set"].toBool() == true) - { iData.size_bytes = param["size"].toInt(); } + return true; +} - if (iData.size_bytes >= UART_DATA_MAX_BYTES - 1) +void UARTFixture::updateStats(bool passed) +{ + stats.total++; + + if (passed) { stats.passed++; } + else { stats.failed++; } + + EXPECT_TRUE(passed); +} + +bool UARTFixture::sendCommand(const QByteArray &data) +{ + QString err; + + if (!uart.send(data, err)) + { + writeToLog(QString("UART send failed: %1").arg(err), false); + return false; + } + + return true; +} + +QByteArray UARTFixture::receiveResponse(const QJsonObject &cfg) +{ + int timeout = cfg["timeout_msec"].toInt(); + return uart.receive(timeout); +} + + +bool UARTFixture::loadBinaryFile(const QString& fileName, QByteArray& data) +{ + QFile binaryFile(fileName); + + if (!binaryFile.open(QIODevice::ReadOnly)) + { + writeToLog(QString("command binary not found: %1").arg(fileName), false); + return false; + } + + data = binaryFile.readAll(); + return true; +} + +bool UARTFixture::validateTextResponse(const QByteArray& rx, + const QJsonObject& cfg, QString caseFile) +{ + QString protocolMsg; + QString error; + bool passed = false; + + QJsonObject exp = cfg["expectations"].toObject(); + + if (exp.isEmpty()) + { + writeToLog(QString("'expectations' field not found in %1").arg(caseFile), false); + passed = false; return passed; + } + + ProtocolHandler handler(exp); + + handler.feed(rx); + + passed = handler.isDone(protocolMsg); + + if (!passed) + { error = QString("Protocol validation failed:\n%1").arg(protocolMsg); } + + Logger::saveTestLog(logFile, handler, cfg, passed, error); + + return passed; +} + +bool UARTFixture::runTextCase(const QJsonObject& cfg, QString caseFile) +{ + QByteArray tx; + QString binaryFname = cfg["binary"].toString(); + + if (binaryFname.isEmpty()) + { + writeToLog(QString("'binary' field not found in %1").arg(caseFile), false); + return false; + } + + if (!loadBinaryFile(binaryFname, tx)) { return false; } + if (!sendCommand(tx)) { return false; } + + QByteArray rx = receiveResponse(cfg); + + return validateTextResponse(rx, cfg, caseFile); +} + + +bool UARTFixture::prepareBinaryCommand(const QJsonObject& cfg, QByteArray& data) +{ +// bool ok = false; + QJsonObject meta, param; + meta = cfg["meta"].toObject(); + param = meta["parameters"].toObject(); + + if (meta.isEmpty() || param.isEmpty()) + { + writeToLog(QString("'meta' or 'parameters' field not found"), false); + return false; + } + + iData.version = param["version"].toInt(); + + if (param["addr_user_set"].toBool()) + { iData.size_bytes = param["size"].toInt(); } + + if (iData.size_bytes >= UART_DATA_MAX_BYTES) + { + writeToLog(QString("structure too big"), false); + return false; + } + + if (param["cmd_from_binary_file"].toBool()) + { return loadBinaryFile(cfg["binary"].toString(), data); } + + formCmdDataInBuffer(); + + data = QByteArray::fromRawData(cmdDataBuf, UART_DATA_MAX_BYTES); + + return true; +} + +bool UARTFixture::validateStructField(const QByteArray& rxData, + const QJsonObject& fieldCfg, + const QJsonObject& structJson, + QString& errMsg, + QString& errMsgMismatch, + QString caseFile) +{ + bool found = false; + QJsonObject fieldInfo; + QString fieldName, fieldType, op; + bool isArray = false; + QJsonArray fields; + + fieldName = fieldCfg["field"].toString(); + if (fieldName.isEmpty()) + { + writeToLog(QString("'field' field not found or incorrect in %1").arg(caseFile), false); + return false; + } + fieldType = fieldCfg["type"].toString(); + if (fieldType.isEmpty()) + { + writeToLog(QString("'type' field not found or incorrect in %1").arg(caseFile), false); + return false; + } + op = fieldCfg["op"].toString("=="); + if (op.isEmpty()) + { + writeToLog(QString("'op' field not found or incorrect in %1").arg(caseFile), false); + return false; + } + + isArray = fieldCfg["array"].toBool(false); + fields = structJson["fields"].toArray(); + + for (const QJsonValue& v : qAsConst(fields)) + { + QJsonObject obj = v.toObject(); + + if (obj["name"].toString() == fieldName) { - msg = QString("%1: %2") - .arg("UART data did not send: ", - QString("required structure size %1 too big for UART transmission (MAX: %2). See test_case file: %3") - .arg(QString(iData.size_bytes), QString(UART_DATA_MAX_BYTES), - test_case_path)); - msgItog.append(msg); - - qDebug(logCritical()) << msg << Qt::endl; - Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); - return; + fieldInfo = obj; + found = true; + break; } + } + + if (!found) + { + errMsg = QString("field '%1' not found in struct json").arg(fieldName); + return false; + } + + // offset and size from ELF JSON + int offset = fieldInfo["offset"].toInt(); + int size = fieldInfo["size"].toInt(); + + if ((offset + size) > rxData.size()) + { + errMsg = QString("field '%1' out of bounds (offset=%2 size=%3 rx_size=%4)") + .arg(fieldName).arg(offset).arg(size).arg(rxData.size()); + return false; + } + + // ARRAY CHECK + if (isArray) + { + // exclude list + QVector excludeIdx; + QJsonArray excludeArr = fieldCfg["exclude"].toArray(); + + for (const QJsonValue& v : qAsConst(excludeArr)) + { excludeIdx.push_back(v.toInt()); } + + // element array size + int elemSize = 1; + + if (fieldType == "uint8" || fieldType == "int8") { elemSize = 1; } + else if (fieldType == "uint16" || fieldType == "int16") { elemSize = 2; } + else if (fieldType == "uint32" || fieldType == "int32" || fieldType == "float") { elemSize = 4; } + else if (fieldType == "uint64" || fieldType == "int64" || fieldType == "double") { elemSize = 8; } else { - formCmdDataInBuffer(); - QByteArray data; - - if (param["cmd_from_binary_file"].toBool() == true) - { - QString binaryFile = cfg["binary"].toString(); - QFile cmdFile(binaryFile); - if (!cmdFile.open(QIODevice::ReadOnly)) - { - msg = QString("%1, path: %2").arg("command binary not found", binaryFile); - msgItog.append(msg); - - qDebug(logCritical()) << msg << Qt::endl; - Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); - } - - data = cmdFile.readAll(); - } - else - { - QByteArray data1(QByteArray::fromRawData(cmdDataBuf, UART_DATA_MAX_BYTES)); - data = data1; - } - - if (!uart.send(data, err)) - { - msg = QString("%1: %2").arg("UART data did not send", err); - msgItog.append(msg); - - qDebug(logCritical()) << msg << Qt::endl; - Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); - } - - + errMsg = QString("unsupported array type '%1'").arg(fieldType); + return false; } - QByteArray rx_data = uart.receive(cfg["timeout_msec"].toInt()); + int arrayCount = size / elemSize; - Snapshot_HK_t snapshot; - memcpy(&snapshot, rx_data.constData(), sizeof(snapshot)); + // expected value + double expectValue = fieldCfg["expect"].toDouble(); - EXPECT_EQ(snapshot.version, 1); - EXPECT_EQ(snapshot.size, 222); - EXPECT_EQ(snapshot.system_error, 0); - EXPECT_EQ(snapshot.guard.all, 0); - - EXPECT_EQ(snapshot.uart.errorLastOperation, 0); - - QVector excludedIdx; - excludedIdx.push_back(0); - - for (int i = 0; i < 20; i++) + // iterate array + for (int i = 0; i < arrayCount; i++) { - bool excludeFlag = false; + bool skip = false; + for (int ex : excludeIdx) + { if (ex == i) { skip = true; break; } } - for (int j = 0; j < excludedIdx.size(); j++) + if (skip) { continue; } + + // read value by type + double actual = 0; + const char* ptr = rxData.constData() + offset + i * elemSize; + + if (fieldType == "uint8") actual = *(reinterpret_cast(ptr)); + else if (fieldType == "int8") actual = *(reinterpret_cast(ptr)); + else if (fieldType == "uint16") actual = *(reinterpret_cast(ptr)); + else if (fieldType == "int16") actual = *(reinterpret_cast(ptr)); + else if (fieldType == "uint32") actual = *(reinterpret_cast(ptr)); + else if (fieldType == "int32") actual = *(reinterpret_cast(ptr)); + else if (fieldType == "uint64") actual = static_cast(*(reinterpret_cast(ptr))); + else if (fieldType == "int64") actual = static_cast(*(reinterpret_cast(ptr))); + else if (fieldType == "float") actual = *(reinterpret_cast(ptr)); + else if (fieldType == "double") actual = *(reinterpret_cast(ptr)); + + // compare + bool ok = false; + if (op == "==") ok = (actual == expectValue); + else if (op == "!=") ok = (actual != expectValue); + else if (op == ">") ok = (actual > expectValue); + else if (op == "<") ok = (actual < expectValue); + else if (op == ">=") ok = (actual >= expectValue); + else if (op == "<=") ok = (actual <= expectValue); + + if (!ok) { - if (i == excludedIdx[j]) - { excludeFlag = true; break; } - + errMsgMismatch = QString("array check failed: %1[%2] actual=%3 expect %4 %5") + .arg(fieldName).arg(i).arg(actual).arg(op).arg(expectValue); + return false; } - - if (excludeFlag == false) - { EXPECT_EQ(snapshot.uart.error[i], 0); } } - - passed = true; + return true; } - else if (meta["input_data_type"].toString() == "text") + + // SINGLE VARIABLE CHECK + const char* ptr = rxData.constData() + offset; + + double actual = 0; + + if (fieldType == "uint8") { actual = *(reinterpret_cast(ptr)); } + else if (fieldType == "int8") { actual = *(reinterpret_cast(ptr)); } + else if (fieldType == "uint16") { actual = *(reinterpret_cast(ptr)); } + else if (fieldType == "int16") { actual = *(reinterpret_cast(ptr)); } + else if (fieldType == "uint32") { actual = *(reinterpret_cast(ptr)); } + else if (fieldType == "int32") { actual = *(reinterpret_cast(ptr)); } + else if (fieldType == "uint64") { actual = static_cast(*(reinterpret_cast(ptr))); } + else if (fieldType == "int64") { actual = static_cast(*(reinterpret_cast(ptr))); } + else if (fieldType == "float") { actual = *(reinterpret_cast(ptr)); } + else if (fieldType == "double") { actual = *(reinterpret_cast(ptr)); } + else { - QString binaryFile = cfg["binary"].toString(); - QFile cmdFile(binaryFile); - if (!cmdFile.open(QIODevice::ReadOnly)) - { - msg = QString("%1, path: %2").arg("command binary not found", binaryFile); - msgItog.append(msg); - - qDebug(logCritical()) << msg << Qt::endl; - Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); - } - - QByteArray tx_cmd = cmdFile.readAll(); - if (!uart.send(tx_cmd, err)) - { - msg = QString("%1: %2").arg("UART data did not send", err); - msgItog.append(msg); - - qDebug(logCritical()) << msg << Qt::endl; - Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); - } - - QByteArray rx_data = uart.receive(cfg["timeout_msec"].toInt()); - - ProtocolHandler handler(cfg["expectations"].toObject()); - handler.feed(rx_data); - - if (!passed) - { error = QString("Protocol validation failed:\n%1").arg(protocolMsg); } - - passed = handler.isDone(protocolMsg); - Logger::saveTestLog(logFile, handler, cfg, passed, error); + errMsg = QString("unsupported field type '%1'").arg(fieldType); + return false; } + double expectValue = fieldCfg["expect"].toDouble(); - stats.total++; - if (passed) stats.passed++; - else stats.failed++; - EXPECT_TRUE(passed); + bool ok = false; + if (op == "==") { ok = (actual == expectValue); } + else if (op == "!=") { ok = (actual != expectValue); } + else if (op == ">") { ok = (actual > expectValue); } + else if (op == "<") { ok = (actual < expectValue); } + else if (op == ">=") { ok = (actual >= expectValue); } + else if (op == "<=") { ok = (actual <= expectValue); } + + if (!ok) + { + errMsgMismatch = QString("field check failed: %1 actual=%2 expect %3 %4") + .arg(fieldName).arg(actual).arg(op).arg(expectValue); + return false; + } + return true; +} + +bool UARTFixture::validateBinaryResponse(const QByteArray& rx, const QJsonObject& cfg, + const QJsonObject& structJson, QString caseFile) +{ + QJsonArray checks = cfg["struct_check"].toArray(); + QString err, errMsgMismatch; + bool passed; + + if (checks.isEmpty()) + { + writeToLog(QString("'struct_check' field not found in %1").arg(caseFile), false); + return false; + } + + for (const QJsonValue& v: qAsConst(checks)) + { + passed = validateStructField(rx, v.toObject(), structJson, + err, errMsgMismatch, caseFile); + if (!passed) { writeToLog(QString("%1\n%2").arg(err, errMsgMismatch) , false); break; } + } + + Logger::saveTestLog(logFile, cfg, passed, err, errMsgMismatch); + return true; +} + +bool UARTFixture::runBinaryCase(const QJsonObject& cfg, const QJsonObject& meta, + QString caseFile) +{ + QByteArray tx; + QString struct_name, struct_type, structJsonFileName; + QJsonObject param, structJson; + + // find struct_name from test_case + param = meta["parameters"].toObject(); + if (param.isEmpty()) + { + writeToLog(QString("'parameters' field not found in %1").arg(caseFile), false); + return false; + } + struct_name = param["struct_name"].toString(); + if (struct_name.isEmpty()) + { + writeToLog(QString("'struct_name' field incorrect or not found in %1").arg(caseFile), false); + return false; + } + struct_type = param["struct_type"].toString(); + if (struct_type.isEmpty()) + { + writeToLog(QString("'struct_type' field incorrect or not found in %1").arg(caseFile), false); + return false; + } + structJsonFileName = QString("%1%2.json").arg(path.iar_json_out, struct_type); + + // extract from elf about struct info + if (!struct_names.contains(struct_name)) + { + struct_names.push_back(struct_name); + bool fail = extract_struct_from_elf(struct_type); + if (fail == true) { return false; } + } + + // open json-file with specified struct info and get json object + QFile fileJson(structJsonFileName); + if (!fileJson.open(QIODevice::ReadOnly)) + { + writeToLog(QString("%1 %2, path: %3") + .arg("json file not found fo struct", struct_name, structJsonFileName), false); + return false; + } + + structJson = QJsonDocument::fromJson(fileJson.readAll()).object(); + + if (!prepareBinaryCommand(cfg, tx)) { return false; } + if (!sendCommand(tx)) { return false; } + + QByteArray rx = receiveResponse(cfg); + return validateBinaryResponse(rx, cfg, structJson, caseFile); +} + +void UARTFixture::runCase(const QString& caseFile) +{ + QJsonObject cfg, meta; + bool passed = false; + + if (!loadTestCase(caseFile, cfg, meta)) { return; } + + QString rx_data_type = meta["input_data_type"].toString(); + + if (rx_data_type == "text") + { passed = runTextCase(cfg, caseFile); } + else if (rx_data_type == "binary") + { passed = runBinaryCase(cfg, meta, caseFile); } + else + { + writeToLog(QString("unknown input_data_type (%1) in file: %2") + .arg(rx_data_type, caseFile), false); + passed = false; + updateStats(passed); + return; + } + + updateStats(passed); if (g_options.delayMs > 0) { QThread::msleep(g_options.delayMs); } @@ -415,62 +599,48 @@ void UARTFixture::formCmdDataInBuffer() void UARTFixture::TearDown() { -// QThread::msleep(200); -// QString msgItog; -// QString msg; -// QString err; -// QString test_case_path = ""; - -// iData.version = 0x1; -// iData.addr_begin = 0x08125600; -// iData.size_bytes = 222; - -// if (iData.size_bytes >= UART_DATA_MAX_BYTES - 1) -// { -// msg = QString("%1: %2") -// .arg("UART data did not send: ", -// QString("required structure size %1 too big for UART transmission (MAX: %2). See test_case file: %3") -// .arg(QString(iData.size_bytes), QString(UART_DATA_MAX_BYTES), -// test_case_path)); -// msgItog.append(msg); - -// qDebug(logCritical()) << msg << Qt::endl; -// Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); -// return; -// } -// else -// { -// formCmdDataInBuffer(); - -// QByteArray data(QByteArray::fromRawData(cmdDataBuf, UART_DATA_MAX_BYTES)); - - -// QString pathData = "data.bin"; -// QFile file(pathData); - -// if (!file.open(QIODevice::WriteOnly)) -// { printf("\t\n ERROR open file!"); } -// else -// { -// QDataStream out(&file); -// out.writeRawData(data, UART_DATA_MAX_BYTES); -// file.close(); -// } - -// uart.send(data, err); - -// if (!uart.send(data, err)) -// { -// msg = QString("%1: %2").arg("UART data did not send", err); -// msgItog.append(msg); - -// qDebug(logCritical()) << msg << Qt::endl; -// Logger::writeToConsol(QString("\nERROR: %1\n").arg(msg)); -// } -//// QByteArray rx_data = uart.receive(cfg["timeout_msec"].toInt()); -// QByteArray rx_data = uart.receive(1000); -// printf(rx_data); -// } - -// QThread::msleep(200); +} + +int UARTFixture::extract_struct_from_elf(QString structTypeName) +{ + QProcess process; + QString program = path.local_python; + + QStringList arguments; + arguments << path.elf_parser + << "-f" << path.MK_elf_file + << "-t" << structTypeName + << "-o" << path.iar_json_out; + + process.start(program, arguments); + + if (!process.waitForFinished(3000)) + { + writeToLog(QString("timeout script processig: %1").arg(path.elf_parser), false); + return 1; + } + + int exitCode = process.exitCode(); + + if (exitCode != 0) { + QString msg = QString("scipt %1 return error code %2: %3") + .arg(path.elf_parser, QString(exitCode), + QString::fromUtf8(process.readAllStandardError())); + + writeToLog(msg, false); + return 1; + } + + return 0; +} + +void UARTFixture::writeToLog(const QString& errMsg, bool passed) +{ + if (!passed) + { + qDebug(logCritical()) << errMsg << Qt::endl; + Logger::writeToConsol(QString("\nERROR: %1\n").arg(errMsg)); + } + else { + } } diff --git a/src/tests/uart_fixture.h b/src/tests/uart_fixture.h index affa980..5068516 100644 --- a/src/tests/uart_fixture.h +++ b/src/tests/uart_fixture.h @@ -80,6 +80,7 @@ protected: static QString logFile; static UART_cmdReadMemory_t iData; static char cmdDataBuf[UART_DATA_MAX_BYTES]; + static QVector struct_names; static void serializeCmdDataBuf(void *dataStruct, int countBytes); static void formCmdDataInBuffer(); @@ -88,12 +89,37 @@ protected: static int readConfig(); static int convert_map_file(); - static int extract_struct_from_elf(QString structTypeName); void TearDown() override; void runCase(const QString& caseFile); + void updateStats(bool passed); + bool sendCommand(const QByteArray &data); + QByteArray receiveResponse(const QJsonObject &cfg); - static void writeToLog(QString errMsg, bool passed); + bool loadBinaryFile(const QString& fileName, QByteArray& data); + + bool validateTextResponse(const QByteArray& rx, + const QJsonObject& cfg, QString caseFile); + bool runTextCase(const QJsonObject& cfg, QString caseFile); + + bool prepareBinaryCommand(const QJsonObject& cfg, QByteArray& data); + + bool validateStructField(const QByteArray& rxData, + const QJsonObject& fieldCfg, + const QJsonObject& structJson, + QString& errMsg, + QString &errMsgMismatch, QString caseFile); + bool validateBinaryResponse(const QByteArray& rx, const QJsonObject& cfg, + const QJsonObject &structJson, QString caseFile); + bool runBinaryCase(const QJsonObject& cfg, const QJsonObject &meta, + QString caseFile); + + bool loadTestCase(const QString& caseFile, + QJsonObject& cfg, + QJsonObject& meta); + + static void writeToLog(const QString &errMsg, bool passed); + static int extract_struct_from_elf(QString structTypeName); private: };