Merge pull request #6652 from opentx/raphaelcoeffic/cpn-storage-gtests

[WiP] companion storage tests
This commit is contained in:
Bertrand Songis
2019-08-23 09:12:19 +02:00
committed by GitHub
14 changed files with 336 additions and 26 deletions
+4
View File
@@ -326,6 +326,10 @@ set(simu_SRCS simulator.cpp )
add_executable(${SIMULATOR_NAME} MACOSX_BUNDLE ${WIN_EXECUTABLE_TYPE} ${simu_SRCS} ${icon_RC})
target_link_libraries(${SIMULATOR_NAME} PRIVATE ${CPN_COMMON_LIB})
if(NOT MSVC)
add_subdirectory(tests)
endif()
############# Install ####################
# Generate list of simulator plugins, used by all platforms
+8 -10
View File
@@ -103,20 +103,18 @@ void EEPROMInterface::showEepromErrors(QWidget *parent, const QString &title, co
#endif
// TODO: No GUI here, e.g. return string list instead
void EEPROMInterface::showEepromWarnings(QWidget *parent, const QString &title, unsigned long errorsFound)
QString EEPROMInterface::getEepromWarnings(unsigned long errorsFound)
{
std::bitset<NUM_ERRORS> errors((unsigned long long)errorsFound);
QStringList warningsList;
if (errors.test(WARNING_WRONG_FIRMWARE)) { warningsList << tr("- Your radio probably uses a wrong firmware,\n eeprom size is 4096 but only the first 2048 are used"); }
if (errors.test(OLD_VERSION)) { warningsList << tr("- Your eeprom is from an old version of OpenTX, upgrading!\n To keep your original file as a backup, please choose File -> Save As specifying a different name."); }
if (errors.test(WARNING_WRONG_FIRMWARE)) {
warningsList << tr("- Your radio probably uses a wrong firmware,\n eeprom size is 4096 but only the first 2048 are used");
}
if (errors.test(OLD_VERSION)) {
warningsList << tr("- Your eeprom is from an old version of OpenTX, upgrading!\n To keep your original file as a backup, please choose File -> Save As specifying a different name.");
}
QMessageBox msgBox(parent);
msgBox.setWindowTitle(title);
msgBox.setIcon(QMessageBox::Warning);
msgBox.setText(tr("Warnings!"));
msgBox.setInformativeText(warningsList.join("\n"));
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.exec();
return warningsList.join("\n");
}
+1 -1
View File
@@ -178,7 +178,7 @@ class EEPROMInterface
virtual int getSize(const GeneralSettings &) = 0;
//static void showEepromErrors(QWidget *parent, const QString &title, const QString &mainMessage, unsigned long errorsFound);
static void showEepromWarnings(QWidget *parent, const QString &title, unsigned long errorsFound);
static QString getEepromWarnings(unsigned long errorsFound);
protected:
@@ -71,7 +71,15 @@ inline int MAX_SWITCH_SLOTS(Board::Type board, int version)
return 8;
}
#define MAX_SWITCHES_POSITION(board, version) (Boards::getCapability(board, Board::SwitchPositions))
inline int MAX_SWITCHES_POSITION(Board::Type board, int version)
{
if (version < 219 && IS_TARANIS_X7(board))
return Boards::getCapability(board, Board::SwitchPositions) - 2*3;
return Boards::getCapability(board, Board::SwitchPositions);
}
//#define MAX_SWITCHES_POSITION(board, version) (Boards::getCapability(board, Board::SwitchPositions))
#define MAX_ROTARY_ENCODERS(board) (IS_SKY9X(board) ? 1 : 0)
#define MAX_FLIGHT_MODES(board, version) 9
#define MAX_TIMERS(board, version) 3
@@ -1892,7 +1900,7 @@ class SensorField: public TransformedField {
if (sensor.type == SensorData::TELEM_TYPE_CUSTOM) {
sensor.id = _id;
sensor.subid = _subid;
sensor.instance = _instance & 0x1F; // 5 bits instance
sensor.instance = (_instance & 0x1F) + (version <= 218 ? -1 : 0); // 5 bits instance
sensor.rxIdx = (_instance >> 5) & 0x03; // 2 bits Rx idx
sensor.moduleIdx = (_instance >> 7) & 0x1; // 1 bit module idx
sensor.ratio = _ratio;
@@ -2112,10 +2120,11 @@ class ModuleUnionField: public UnionField<unsigned int> {
};
public:
ModuleUnionField(DataField * parent, ModuleData & module):
ModuleUnionField(DataField * parent, ModuleData & module, Board::Type board, unsigned int version):
UnionField<unsigned int>(parent, module.protocol)
{
Append(new AccessField(parent, module));
if (version >= 219)
Append(new AccessField(parent, module));
Append(new PxxField(parent, module));
Append(new MultiField(parent, module));
Append(new PPMField(parent, module.ppm));
@@ -2129,7 +2138,7 @@ class ModuleField: public TransformedField {
TransformedField(parent, internalField),
internalField(this, "Module"),
module(module),
protocolsConversionTable(board)
protocolsConversionTable(board, version)
{
internalField.Append(new ConversionField<UnsignedField<4> >(this, module.protocol, &protocolsConversionTable, "Protocol", DataField::tr("OpenTX doesn't accept this radio protocol")));
internalField.Append(new SignedField<4>(this, module.rfProtocol));
@@ -2144,7 +2153,7 @@ class ModuleField: public TransformedField {
}
}
internalField.Append(new ModuleUnionField(parent, module));
internalField.Append(new ModuleUnionField(parent, module, board, version));
}
virtual void beforeExport()
@@ -73,7 +73,7 @@ class OpenTxGeneralData: public TransformedField {
class ProtocolsConversionTable: public ConversionTable
{
public:
ProtocolsConversionTable(Board::Type board)
ProtocolsConversionTable(Board::Type board, int version)
{
int val = 0;
addConversion(PULSES_OFF, val++);
@@ -83,8 +83,10 @@ class ProtocolsConversionTable: public ConversionTable
addConversion(PULSES_PXX_XJT_D8, val);
addConversion(PULSES_PXX_XJT_LR12, val++);
addConversion(PULSES_ACCESS_ISRM, val);
addConversion(PULSES_ACCST_ISRM_D16, val++);
if (version >= 219) {
addConversion(PULSES_ACCESS_ISRM, val);
addConversion(PULSES_ACCST_ISRM_D16, val++);
}
addConversion(PULSES_LP45, val);
addConversion(PULSES_DSM2, val);
+2 -2
View File
@@ -259,11 +259,11 @@ class RawSource {
bool isTimeBased(Board::Type board = Board::BOARD_UNKNOWN) const;
bool isAvailable(const ModelData * const model = NULL, const GeneralSettings * const gs = NULL, Board::Type board = Board::BOARD_UNKNOWN) const;
bool operator == ( const RawSource & other) {
bool operator == ( const RawSource & other) const {
return (this->type == other.type) && (this->index == other.index);
}
bool operator != ( const RawSource & other) {
bool operator != ( const RawSource & other) const {
return (this->type != other.type) || (this->index != other.index);
}
+2 -2
View File
@@ -91,11 +91,11 @@ class RawSwitch {
QString toString(Board::Type board = Board::BOARD_UNKNOWN, const GeneralSettings * const generalSettings = NULL, const ModelData * const modelData = NULL) const;
bool isAvailable(const ModelData * const model = NULL, const GeneralSettings * const gs = NULL, Board::Type board = Board::BOARD_UNKNOWN) const;
bool operator== ( const RawSwitch& other) {
bool operator== ( const RawSwitch& other) const {
return (this->type == other.type) && (this->index == other.index);
}
bool operator!= ( const RawSwitch& other) {
bool operator!= ( const RawSwitch& other) const {
return (this->type != other.type) || (this->index != other.index);
}
+1 -1
View File
@@ -1334,7 +1334,7 @@ bool MdiChild::loadFile(const QString & filename, bool resetCurrentFile)
QString warning = storage.warning();
if (!warning.isEmpty()) {
// TODO EEPROMInterface::showEepromWarnings(this, CPN_STR_TTL_WARNING, warning);
QMessageBox::warning(this, CPN_STR_TTL_WARNING, warning);
}
if (resetCurrentFile) {
+2 -1
View File
@@ -43,6 +43,7 @@ bool BinEepromFormat::load(RadioData & radioData)
return false;
}
//TODO: show warnings if any
return extract(radioData, eeprom);
}
@@ -94,7 +95,7 @@ bool BinEepromFormat::extract(RadioData & radioData, const QByteArray & eeprom)
std::bitset<NUM_ERRORS> result((unsigned long long)eepromInterface->load(radioData, (uint8_t *)eeprom.data(), eeprom.size()));
if (result.test(ALL_OK)) {
if (errors.test(HAS_WARNINGS)) {
EEPROMInterface::showEepromWarnings(NULL, CPN_STR_TTL_WARNING, errors.to_ulong());
setWarning(EEPROMInterface::getEepromWarnings(errors.to_ulong()));
}
board = eepromInterface->getBoard();
return true;
+34
View File
@@ -0,0 +1,34 @@
set(GTEST_ROOT /usr CACHE STRING "Base path to Google Test headers and source.")
find_path(GTEST_INCDIR gtest/gtest.h HINTS "${GTEST_ROOT}/include" DOC "Path to Google Test header files folder ('gtest/gtest.h').")
find_path(GTEST_SRCDIR src/gtest-all.cc HINTS "${GTEST_ROOT}" "${GTEST_ROOT}/src/gtest" DOC "Path of Google Test 'src' folder.")
if(GTEST_INCDIR AND GTEST_SRCDIR AND Qt5Widgets_FOUND)
add_library(gtests-cpn-lib STATIC EXCLUDE_FROM_ALL ${GTEST_SRCDIR}/src/gtest-all.cc )
target_include_directories(gtests-cpn-lib PUBLIC ${GTEST_INCDIR} ${GTEST_INCDIR}/gtest ${GTEST_SRCDIR})
add_definitions(-DGTESTS)
set(TESTS_PATH ${COMPANION_SRC_DIRECTORY}/tests)
set(RADIO_TESTS_PATH ${RADIO_SRC_DIRECTORY}/tests)
configure_file(${TESTS_PATH}/location.h.in ${CMAKE_CURRENT_BINARY_DIR}/location.h @ONLY)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
if(WIN32)
target_include_directories(gtests-cpn-lib PUBLIC ${WIN_INCLUDE_DIRS})
target_link_libraries(gtests-cpn-lib PRIVATE ${WIN_LINK_LIBRARIES})
endif(WIN32)
file(GLOB TEST_SRC_FILES ${TESTS_PATH}/*.cpp)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 ${WARNING_FLAGS}")
use_cxx11() # ensure gnu++11 in CXX_FLAGS with CMake < 3.1
add_executable(gtests-cpn EXCLUDE_FROM_ALL ${TEST_SRC_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/location.h)
add_dependencies(gtests-cpn gtests-cpn-lib)
target_link_libraries(gtests-cpn gtests-cpn-lib pthread Qt5::Core Qt5::Widgets ${CPN_COMMON_LIB})
message(STATUS "Added optional gtests-cpn target")
else()
message(WARNING "WARNING: gtests target will not be available (check that GTEST_INCDIR, GTEST_SRCDIR, and Qt5Widgets are configured).")
endif()
+70
View File
@@ -0,0 +1,70 @@
#include "gtests.h"
#include "location.h"
#include "storage/storage.h"
TEST(Conversions, ConversionX9DPFrom22)
{
RadioData radioData;
Storage store = Storage(RADIO_TESTS_PATH "/eeprom_22_x9d+.bin");
EXPECT_EQ(true, store.load(radioData));
const ModelData& model = radioData.models[0];
EXPECT_STREQ("Test", model.name);
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH,SWITCH_SA0), model.timers[0].mode);
EXPECT_EQ(80, model.mixData[0].weight);
EXPECT_EQ(900, model.limitData[0].max); // -100
EXPECT_EQ(80, model.expoData[0].weight);
EXPECT_EQ(HELI_SWASH_TYPE_120X, model.swashRingData.type);
EXPECT_EQ(10, model.flightModeData[0].gvars[0]);
EXPECT_STREQ("Tes", model.gvarData[0].name);
EXPECT_EQ(PULSES_PXX_XJT_X16, model.moduleData[0].protocol);
EXPECT_EQ(0, model.moduleData[0].subType);
EXPECT_EQ(PULSES_PXX_R9M, model.moduleData[1].protocol);
EXPECT_EQ(MODULE_SUBTYPE_R9M_FCC, model.moduleData[1].subType);
EXPECT_STREQ("Thr", model.inputNames[0]);
EXPECT_STREQ("Tes", model.sensorData[0].label);
EXPECT_EQ(10, model.sensorData[0].id);
EXPECT_EQ(9, model.sensorData[0].instance);
EXPECT_EQ(RawSource(SOURCE_TYPE_TELEMETRY,0).toValue(), model.logicalSw[0].val1);
}
TEST(Conversions, ConversionX7From22)
{
RadioData radioData;
Storage store = Storage(RADIO_TESTS_PATH "/eeprom_22_x7.bin");
EXPECT_EQ(true, store.load(radioData));
const ModelData& model = radioData.models[0];
EXPECT_STREQ("Test", model.name);
EXPECT_EQ(PULSES_PXX_R9M, model.moduleData[1].protocol);
EXPECT_EQ(MODULE_SUBTYPE_R9M_EU, model.moduleData[1].subType);
EXPECT_EQ(80, model.mixData[0].weight);
EXPECT_EQ(80, model.expoData[0].weight);
EXPECT_EQ(RawSource(SOURCE_TYPE_PPM,0), model.mixData[4].srcRaw);
EXPECT_EQ(RawSwitch(SWITCH_TYPE_TELEMETRY,1), model.mixData[4].swtch);
EXPECT_EQ(HELI_SWASH_TYPE_120X, model.swashRingData.type);
EXPECT_STREQ("Thr", model.inputNames[0]);
EXPECT_STREQ("Tes", model.sensorData[0].label);
EXPECT_EQ(10, model.sensorData[0].id);
EXPECT_EQ(9, model.sensorData[0].instance);
EXPECT_EQ(900, model.limitData[0].max); // -100
EXPECT_EQ(10, model.flightModeData[0].gvars[0]);
EXPECT_STREQ("FMtest", model.flightModeData[1].name);
EXPECT_EQ(RawSwitch(SWITCH_TYPE_VIRTUAL,1), model.flightModeData[1].swtch);
EXPECT_STREQ("Tes", model.gvarData[0].name);
EXPECT_EQ(LS_FN_VPOS, model.logicalSw[0].func);
EXPECT_EQ(RawSource(SOURCE_TYPE_PPM,0).toValue(), model.logicalSw[0].val1);
EXPECT_EQ(0, model.logicalSw[0].val2);
const FrSkyScreenData& screen = model.frsky.screens[0];
EXPECT_EQ(TELEMETRY_SCREEN_NUMBERS, screen.type);
EXPECT_EQ(RawSource(SOURCE_TYPE_PPM,0), screen.body.lines[0].source[0]);
EXPECT_EQ(RawSource(SOURCE_TYPE_SPECIAL,4), screen.body.lines[0].source[1]);
}
+149
View File
@@ -0,0 +1,149 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <QApplication>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory>
#include "gtests.h"
#include "storage.h"
#include "opentxinterface.h"
#include "customdebug.h"
using ::testing::TestEventListener;
using ::testing::EmptyTestEventListener;
using ::testing::Test;
using ::testing::TestCase;
using ::testing::TestEventListeners;
using ::testing::TestInfo;
using ::testing::TestPartResult;
using ::testing::UnitTest;
using ::testing::InitGoogleTest;
namespace {
void printColorCode(int color)
{
#if !GTEST_OS_WINDOWS
if (isatty(fileno(stdout)))
fprintf(stdout, "\033[0;%dm", color);
#endif
}
/**
* Replaces gtest's PrettyUnitTestResultPrinter with something less verbose.
* Uses the default printer if/when the messages might be interesting,
* e.g. for full error info or tests summary.
*/
class TersePrinter : public EmptyTestEventListener {
public:
explicit TersePrinter(TestEventListener * prettyPrinter)
: prettyPrinter(prettyPrinter), currentTestInfo(NULL)
{
}
void OnTestProgramStart(const UnitTest & unit_test)
{
prettyPrinter->OnTestProgramStart(unit_test);
}
void OnTestIterationStart(const UnitTest & unit_test, int iteration)
{
printColorCode(36);
fprintf(stdout, "############ ");
fprintf(stdout, "Running %d test(s) from %d test case(s), iteration: %d.\n", unit_test.test_to_run_count(), unit_test.test_case_to_run_count(), iteration + 1);
printColorCode(0);
fflush(stdout);
}
void OnTestCaseStart(const TestCase & test_case)
{
prettyPrinter->OnTestCaseStart(test_case);
}
void OnTestStart(const TestInfo & test_info)
{
currentTestInfo = &test_info;
}
void OnTestPartResult(const TestPartResult & test_part_result)
{
if (test_part_result.failed()) {
prettyPrinter->OnTestStart(*currentTestInfo);
prettyPrinter->OnTestPartResult(test_part_result);
}
}
void OnTestEnd(const TestInfo & test_info)
{
currentTestInfo = NULL;
if (test_info.result()->Failed()) {
prettyPrinter->OnTestEnd(test_info);
}
else {
printColorCode(32);
fprintf(stdout, "+");
printColorCode(0);
fflush(stdout);
}
}
void OnTestCaseEnd(const TestCase & test_case)
{
fprintf(stdout, "\n");
fflush(stdout);
}
void OnTestIterationEnd(const UnitTest & unit_test, int iteration)
{
prettyPrinter->OnTestIterationEnd(unit_test, iteration);
}
void OnTestProgramEnd(const UnitTest & unit_test)
{
prettyPrinter->OnTestProgramEnd(unit_test);
}
private:
// gtest's default unit test result printer.
std::unique_ptr<TestEventListener> prettyPrinter;
// The currently running TestInfo, if any.
const TestInfo * currentTestInfo;
};
} // anonymous namespace
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
CustomDebug::setFilterRules();
registerStorageFactories();
registerOpenTxFirmwares();
InitGoogleTest(&argc, argv);
// use --verbose option to revert to gtest's default output format
bool verbose = false;
if (argc > 1 && !strcmp(argv[1], "--verbose"))
verbose = true;
if (!verbose) {
TestEventListeners & listeners = UnitTest::GetInstance()->listeners();
TestEventListener * defaultPrinter = listeners.Release(listeners.default_result_printer());
listeners.Append(new TersePrinter(defaultPrinter));
}
return RUN_ALL_TESTS();
}
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _GTESTS_H_
#define _GTESTS_H_
#include <QtCore/QString>
#include <math.h>
#include <gtest/gtest.h>
class OpenTxTest : public testing::Test
{
protected: // You should make the members protected s.t. they can be
// accessed from sub-classes.
// virtual void SetUp() will be called before each test is run. You
// should define it if you need to initialize the varaibles.
// Otherwise, this can be skipped.
virtual void SetUp()
{
}
};
#endif // _GTESTS_H_
+2
View File
@@ -0,0 +1,2 @@
#define TESTS_PATH "@TESTS_PATH@"
#define RADIO_TESTS_PATH "@RADIO_TESTS_PATH@"