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"