File indexing completed on 2024-05-12 05:55:44

0001 /*
0002  *    This file is part of the Okteta Kasten module, made within the KDE community.
0003  *
0004  *    SPDX-FileCopyrightText: 2012 Alex Richardson <alex.richardson@gmx.de>
0005  *
0006  *    SPDX-License-Identifier: LGPL-2.1-or-later
0007  */
0008 
0009 #include "view/structures/datatypes/array/arraydatainformation.hpp"
0010 #include "view/structures/datatypes/array/primitivearraydata.hpp"
0011 #include "view/structures/datatypes/topleveldatainformation.hpp"
0012 #include "view/structures/datatypes/primitive/primitivetemplateinfo.hpp"
0013 #include "view/structures/datatypes/primitivefactory.hpp"
0014 #include "view/structures/script/scriptengineinitializer.hpp"
0015 // Okteta
0016 #include <Okteta/ByteArrayModel>
0017 // Qt
0018 #include <QTest>
0019 #include <QScriptEngine>
0020 #include <QRandomGenerator>
0021 // Std
0022 #include <memory>
0023 #include <limits>
0024 
0025 class PrimitiveArrayTest : public QObject
0026 {
0027     Q_OBJECT
0028 
0029 private:
0030     /** Tests user defined overrides of byteOrder, typeName, and toStringFunc. */
0031     template <PrimitiveDataType primType, typename T> void testReadCustomizedPrimitiveInternal();
0032     template <PrimitiveDataType primType, typename T> void testReadPrimitiveInternal();
0033     template <PrimitiveDataType primType> void testReadPrimitive();
0034     template <typename T> bool compareItems(T first, T second, uint index);
0035 
0036 private Q_SLOTS:
0037     void initTestCase();
0038     void testReadFloat();
0039     void testReadDouble();
0040     void testReadChar();
0041     void testReadInt8();
0042     void testReadInt16();
0043     void testReadInt32();
0044     void testReadInt64();
0045     void testReadUInt8();
0046     void testReadUInt16();
0047     void testReadUInt32();
0048     void testReadUInt64();
0049     void testReadBool8();
0050     void testReadBool16();
0051     void testReadBool32();
0052     void testReadBool64();
0053 
0054 private:
0055     QScopedArrayPointer<Okteta::Byte> data;
0056     std::unique_ptr<Okteta::ByteArrayModel> model;
0057     QScopedArrayPointer<Okteta::Byte> endianData;
0058     std::unique_ptr<Okteta::ByteArrayModel> endianModel;
0059 };
0060 
0061 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
0062 #define CURRENT_BYTE_ORDER (DataInformation::DataInformationEndianess::EndianessLittle)
0063 #define NON_CURRENT_BYTE_ORDER (DataInformation::DataInformationEndianess::EndianessBig)
0064 #elif Q_BYTE_ORDER == Q_BIG_ENDIAN
0065 #define CURRENT_BYTE_ORDER (DataInformation::DataInformationEndianess::EndianessBig)
0066 #define NON_CURRENT_BYTE_ORDER (DataInformation::DataInformationEndianess::EndianessLittle)
0067 #else
0068 #error unknown byte order
0069 #endif
0070 
0071 static constexpr uint SIZE = 8192;
0072 static constexpr uint ENDIAN_SIZE = 16;
0073 
0074 void PrimitiveArrayTest::initTestCase()
0075 {
0076     data.reset(new Okteta::Byte[SIZE]);
0077     // ensure that we have at least one NaN (quiet + signalling)
0078     AllPrimitiveTypes quietDouble(std::numeric_limits<double>::quiet_NaN());
0079     AllPrimitiveTypes signallingDouble(std::numeric_limits<double>::signaling_NaN());
0080     for (int i = 0; i < 8; ++i) {
0081         data[i] = quietDouble.allBytes[i];
0082         data[8 + i] = signallingDouble.allBytes[i];
0083     }
0084 
0085     AllPrimitiveTypes quietFloat(std::numeric_limits<float>::quiet_NaN());
0086     AllPrimitiveTypes signallingFloat(std::numeric_limits<float>::signaling_NaN());
0087     for (int i = 0; i < 4; ++i) {
0088         data[16 + i] = quietFloat.allBytes[i];
0089         data[20 + i] = signallingFloat.allBytes[i];
0090     }
0091 
0092     auto* randomGenerator = QRandomGenerator::global();
0093     for (uint i = 24; i < SIZE; ++i) {
0094         data[i] = static_cast<Okteta::Byte>(randomGenerator->bounded(256));
0095     }
0096 
0097     auto* copy = new Okteta::Byte[SIZE];
0098     memcpy(copy, data.data(), SIZE);
0099     model.reset(new Okteta::ByteArrayModel(copy, SIZE));
0100     model->setAutoDelete(true);
0101     QCOMPARE(model->size(), Okteta::Size(SIZE));
0102 
0103     endianData.reset(new Okteta::Byte[ENDIAN_SIZE]);
0104     for (uint i = 0; i < ENDIAN_SIZE; ++i) {
0105         endianData[i] = i;
0106     }
0107 
0108     auto* endianCopy = new Okteta::Byte[SIZE];
0109     memcpy(endianCopy, endianData.data(), ENDIAN_SIZE);
0110     endianModel.reset(new Okteta::ByteArrayModel(endianCopy, ENDIAN_SIZE));
0111     endianModel->setAutoDelete(true);
0112     QCOMPARE(endianModel->size(), Okteta::Size(ENDIAN_SIZE));
0113 }
0114 
0115 template <typename T>
0116 bool PrimitiveArrayTest::compareItems(T first, T second, uint index)
0117 {
0118     Q_UNUSED(index);
0119     return first == second;
0120 }
0121 
0122 template <>
0123 bool PrimitiveArrayTest::compareItems(float first, float second, uint index)
0124 {
0125     if (first != first) {
0126         qDebug() << "first was NaN at index" << index;
0127         // NaN
0128         if (second != second) {
0129             // second is NaN too;
0130             union
0131             {
0132                 float floating;
0133                 QIntegerForSizeof<float>::Unsigned integer;
0134             } firstUn, secondUn;
0135             firstUn.floating = first;
0136             secondUn.floating = second;
0137             return firstUn.integer == secondUn.integer;
0138         }
0139 
0140         return false;
0141     }
0142     return first == second;
0143 }
0144 
0145 template <>
0146 bool PrimitiveArrayTest::compareItems(double first, double second, uint index)
0147 {
0148     if (first != first) {
0149         qDebug() << "first was NaN at index" << index;
0150         // NaN
0151         if (second != second) {
0152             // second is NaN too;
0153             union
0154             {
0155                 double floating;
0156                 QIntegerForSizeof<double>::Unsigned integer;
0157             } firstUn, secondUn;
0158             firstUn.floating = first;
0159             secondUn.floating = second;
0160             return firstUn.integer == secondUn.integer;
0161         }
0162 
0163         return false;
0164     }
0165     return first == second;
0166 }
0167 
0168 template <PrimitiveDataType primType>
0169 inline void PrimitiveArrayTest::testReadPrimitive()
0170 {
0171     testReadPrimitiveInternal<primType, typename PrimitiveInfo<primType>::valueType>();
0172     testReadCustomizedPrimitiveInternal<primType, typename PrimitiveInfo<primType>::valueType>();
0173 }
0174 
0175 QScriptValue customToStringFunc(QScriptContext* context, QScriptEngine* engine)
0176 {
0177     Q_UNUSED(context);
0178     Q_UNUSED(engine);
0179     return QStringLiteral("myvalue");
0180 }
0181 
0182 template <PrimitiveDataType primType, typename T>
0183 void PrimitiveArrayTest::testReadCustomizedPrimitiveInternal()
0184 {
0185     LoggerWithContext lwc(nullptr, QString());
0186     QScriptEngine* engine = ScriptEngineInitializer::newEngine();
0187 
0188     PrimitiveDataInformation* primInfo(PrimitiveFactory::newInstance(QStringLiteral("value"), primType, lwc));
0189     primInfo->setByteOrder(NON_CURRENT_BYTE_ORDER);
0190     primInfo->setCustomTypeName(QStringLiteral("mytype"));
0191     primInfo->setToStringFunction(engine->newFunction(customToStringFunc));
0192 
0193     auto* dataInf = new ArrayDataInformation(QStringLiteral("values"),
0194                                              endianModel->size() / sizeof(T),
0195                                              primInfo);
0196     std::unique_ptr<TopLevelDataInformation> top(new TopLevelDataInformation(dataInf, nullptr, engine));
0197 
0198     QCOMPARE(dataInf->childCount(), uint(ENDIAN_SIZE / sizeof(T)));
0199     quint8 bitOffs = 0;
0200     qint64 result = dataInf->readData(endianModel.get(), 0, endianModel->size() * 8, &bitOffs);
0201     QCOMPARE(Okteta::Size(result), endianModel->size() * 8);
0202     T* dataAsT = reinterpret_cast<T*>(endianData.data());
0203     QVERIFY(!dataInf->mData->isComplex());
0204     auto* arrayData = static_cast<PrimitiveArrayData<primType>*>(dataInf->mData.get());
0205 
0206     // Verify byteOrder of values. The data is set up without palindromes.
0207     if (sizeof(T) > 1) {
0208         for (uint i = 0; i < dataInf->childCount(); ++i) {
0209             AllPrimitiveTypes childDataAll = arrayData->valueAt(i);
0210             T childData = childDataAll.value<T>();
0211             T expected = dataAsT[i];
0212             // TODO comparison for float and double: nan != nan
0213             if (compareItems<T>(childData, expected, i)) {
0214                 QByteArray desc = "i=" + QByteArray::number(i) + ", model[i]="
0215                                   + QByteArray::number(childData)
0216                                   + ", data[i]=" + QByteArray::number(expected);
0217                 QVERIFY2(!compareItems<T>(childData, expected, i), desc.constData());
0218             }
0219         }
0220     }
0221     // Verify typeName as the user will see it.
0222     QCOMPARE(arrayData->dataAt(0, DataInformation::ColumnType, Qt::DisplayRole).toString(),
0223              QStringLiteral("mytype"));
0224     // Verify value string as the user will see it.
0225     QCOMPARE(arrayData->dataAt(0, DataInformation::ColumnValue, Qt::DisplayRole).toString(),
0226              QStringLiteral("myvalue"));
0227 }
0228 
0229 template <PrimitiveDataType primType, typename T>
0230 void PrimitiveArrayTest::testReadPrimitiveInternal()
0231 {
0232     LoggerWithContext lwc(nullptr, QString());
0233     auto* dataInf = new ArrayDataInformation(QStringLiteral("values"),
0234                                                              model->size() / sizeof(T),
0235                                                              PrimitiveFactory::newInstance(QStringLiteral("value"), primType, lwc));
0236     dataInf->setByteOrder(CURRENT_BYTE_ORDER);
0237     std::unique_ptr<TopLevelDataInformation> top(new TopLevelDataInformation(dataInf, nullptr,
0238                                                                             ScriptEngineInitializer::newEngine()));
0239     QCOMPARE(dataInf->childCount(), uint(SIZE / sizeof(T)));
0240     quint8 bitOffs = 0;
0241     qint64 result = dataInf->readData(model.get(), 0, model->size() * 8, &bitOffs);
0242     QCOMPARE(Okteta::Size(result), model->size() * 8);
0243     T* dataAsT = reinterpret_cast<T*>(data.get());
0244     QVERIFY(!dataInf->mData->isComplex());
0245     auto* arrayData = static_cast<PrimitiveArrayData<primType>*>(dataInf->mData.get());
0246     for (uint i = 0; i < dataInf->childCount(); ++i) {
0247         AllPrimitiveTypes childDataAll = arrayData->valueAt(i);
0248         T childData = childDataAll.value<T>();
0249         T expected = dataAsT[i];
0250         // TODO comparison for float and double: nan != nan
0251         if (!compareItems<T>(childData, expected, i)) {
0252             QByteArray desc = "i=" + QByteArray::number(i) + ", model[i]="
0253                               + QByteArray::number(childData)
0254                               + ", data[i]=" + QByteArray::number(expected);
0255             QVERIFY2(compareItems<T>(childData, expected, i), desc.constData());
0256         }
0257     }
0258 }
0259 
0260 void PrimitiveArrayTest::testReadFloat()
0261 {
0262     testReadPrimitive<PrimitiveDataType::Float>();
0263 }
0264 
0265 void PrimitiveArrayTest::testReadDouble()
0266 {
0267     testReadPrimitive<PrimitiveDataType::Double>();
0268 }
0269 
0270 void PrimitiveArrayTest::testReadChar()
0271 {
0272     testReadPrimitive<PrimitiveDataType::Char>();
0273 }
0274 
0275 void PrimitiveArrayTest::testReadInt8()
0276 {
0277     testReadPrimitive<PrimitiveDataType::Int8>();
0278 }
0279 
0280 void PrimitiveArrayTest::testReadInt16()
0281 {
0282     testReadPrimitive<PrimitiveDataType::Int16>();
0283 }
0284 
0285 void PrimitiveArrayTest::testReadInt32()
0286 {
0287     testReadPrimitive<PrimitiveDataType::Int32>();
0288 }
0289 
0290 void PrimitiveArrayTest::testReadInt64()
0291 {
0292     testReadPrimitive<PrimitiveDataType::Int64>();
0293 }
0294 
0295 void PrimitiveArrayTest::testReadUInt8()
0296 {
0297     testReadPrimitive<PrimitiveDataType::UInt8>();
0298 }
0299 
0300 void PrimitiveArrayTest::testReadUInt16()
0301 {
0302     testReadPrimitive<PrimitiveDataType::UInt16>();
0303 }
0304 
0305 void PrimitiveArrayTest::testReadUInt32()
0306 {
0307     testReadPrimitive<PrimitiveDataType::UInt32>();
0308 }
0309 
0310 void PrimitiveArrayTest::testReadUInt64()
0311 {
0312     testReadPrimitive<PrimitiveDataType::UInt64>();
0313 }
0314 
0315 void PrimitiveArrayTest::testReadBool8()
0316 {
0317     testReadPrimitive<PrimitiveDataType::Bool8>();
0318 }
0319 
0320 void PrimitiveArrayTest::testReadBool16()
0321 {
0322     testReadPrimitive<PrimitiveDataType::Bool16>();
0323 }
0324 
0325 void PrimitiveArrayTest::testReadBool32()
0326 {
0327     testReadPrimitive<PrimitiveDataType::Bool32>();
0328 }
0329 
0330 void PrimitiveArrayTest::testReadBool64()
0331 {
0332     testReadPrimitive<PrimitiveDataType::Bool64>();
0333 }
0334 
0335 QTEST_GUILESS_MAIN(PrimitiveArrayTest)
0336 
0337 #include "primitivearraytest.moc"