libuav original

Dependents:   UAVCAN UAVCAN_Subscriber

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers array.cpp Source File

array.cpp

00001 /*
00002  * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
00003  */
00004 
00005 #if __GNUC__
00006 # pragma GCC diagnostic ignored "-Wfloat-equal"
00007 # pragma GCC diagnostic ignored "-Wdouble-promotion"
00008 #endif
00009 
00010 #include <gtest/gtest.h>
00011 #include <uavcan/marshal/types.hpp>
00012 #include <uavcan/transport/transfer_buffer.hpp>
00013 
00014 using uavcan::Array;
00015 using uavcan::ArrayModeDynamic;
00016 using uavcan::ArrayModeStatic;
00017 using uavcan::IntegerSpec;
00018 using uavcan::FloatSpec;
00019 using uavcan::SignednessSigned;
00020 using uavcan::SignednessUnsigned;
00021 using uavcan::CastModeSaturate;
00022 using uavcan::CastModeTruncate;
00023 
00024 struct CustomType
00025 {
00026     typedef uavcan::IntegerSpec<8, uavcan::SignednessSigned, uavcan::CastModeTruncate> A;
00027     typedef uavcan::FloatSpec<16, uavcan::CastModeSaturate> B;
00028     // Dynamic array of max len 5 --> 3 bits for len, 5 bits for data --> 1 byte max len
00029     typedef uavcan::Array<uavcan::IntegerSpec<1, uavcan::SignednessUnsigned, uavcan::CastModeSaturate>,
00030                           uavcan::ArrayModeDynamic, 5> C;
00031 
00032     enum { MinBitLen = A::MinBitLen + B::MinBitLen + C::MinBitLen };
00033     enum { MaxBitLen = A::MaxBitLen + B::MaxBitLen + C::MaxBitLen };
00034 
00035     typename uavcan::StorageType<A>::Type a;
00036     typename uavcan::StorageType<B>::Type b;
00037     typename uavcan::StorageType<C>::Type c;
00038 
00039     CustomType()
00040         : a()
00041         , b()
00042         , c()
00043     { }
00044 
00045     bool operator==(const CustomType& rhs) const
00046     {
00047         return a == rhs.a &&
00048                uavcan::areFloatsExactlyEqual(b, rhs.b) &&
00049                c == rhs.c;
00050     }
00051 
00052     static int encode(const CustomType& obj, uavcan::ScalarCodec& codec,
00053                       uavcan::TailArrayOptimizationMode tao_mode = uavcan::TailArrayOptEnabled)
00054     {
00055         int res = 0;
00056         res = A::encode(obj.a, codec, uavcan::TailArrayOptDisabled);
00057         if (res <= 0)
00058         {
00059             return res;
00060         }
00061         res = B::encode(obj.b, codec, uavcan::TailArrayOptDisabled);
00062         if (res <= 0)
00063         {
00064             return res;
00065         }
00066         res = C::encode(obj.c, codec, tao_mode);
00067         if (res <= 0)
00068         {
00069             return res;
00070         }
00071         return 1;
00072     }
00073 
00074     static int decode(CustomType& obj, uavcan::ScalarCodec& codec,
00075                       uavcan::TailArrayOptimizationMode tao_mode = uavcan::TailArrayOptEnabled)
00076     {
00077         int res = 0;
00078         res = A::decode(obj.a, codec, uavcan::TailArrayOptDisabled);
00079         if (res <= 0)
00080         {
00081             return res;
00082         }
00083         res = B::decode(obj.b, codec, uavcan::TailArrayOptDisabled);
00084         if (res <= 0)
00085         {
00086             return res;
00087         }
00088         res = C::decode(obj.c, codec, tao_mode);
00089         if (res <= 0)
00090         {
00091             return res;
00092         }
00093         return 1;
00094     }
00095 };
00096 
00097 
00098 TEST(Array, Basic)
00099 {
00100     typedef Array<IntegerSpec<8, SignednessSigned, CastModeTruncate>, ArrayModeStatic, 4> A1;
00101     typedef Array<FloatSpec<16, CastModeSaturate>, ArrayModeStatic, 2> A2;
00102     typedef Array<CustomType, ArrayModeStatic, 2> A3;
00103 
00104     A1 a1;
00105     A2 a2;
00106     A3 a3;
00107 
00108     ASSERT_EQ(1, A3::ValueType::C::RawValueType::BitLen);
00109 
00110     ASSERT_EQ(8 * 4, A1::MaxBitLen);
00111     ASSERT_EQ(16 * 2, A2::MaxBitLen);
00112     ASSERT_EQ((8 + 16 + 5 + 3) * 2, A3::MaxBitLen);
00113 
00114     /*
00115      * Zero initialization check
00116      */
00117     ASSERT_FALSE(a1.empty());
00118     for (A1::const_iterator it = a1.begin(); it != a1.end(); ++it)
00119     {
00120         ASSERT_EQ(0, *it);
00121     }
00122 
00123     ASSERT_FALSE(a2.empty());
00124     for (A2::const_iterator it = a2.begin(); it != a2.end(); ++it)
00125     {
00126         ASSERT_EQ(0, *it);
00127     }
00128 
00129     for (A3::const_iterator it = a3.begin(); it != a3.end(); ++it)
00130     {
00131         ASSERT_EQ(0, it->a);
00132         ASSERT_EQ(0, it->b);
00133         ASSERT_EQ(0, it->c.size());
00134         ASSERT_TRUE(it->c.empty());
00135     }
00136 
00137     /*
00138      * Modification with known values; array lengths are hard coded.
00139      */
00140     for (uint8_t i = 0; i < 4; i++)
00141     {
00142         a1.at(i) = int8_t(i);
00143     }
00144     for (uint8_t i = 0; i < 2; i++)
00145     {
00146         a2.at(i) = i;
00147     }
00148     for (uint8_t i = 0; i < 2; i++)
00149     {
00150         a3[i].a = int8_t(i);
00151         a3[i].b = i;
00152         for (uint8_t i2 = 0; i2 < 5; i2++)
00153         {
00154             a3[i].c.push_back(i2 & 1);
00155         }
00156         ASSERT_EQ(5, a3[i].c.size());
00157         ASSERT_FALSE(a3[i].c.empty());
00158     }
00159 
00160     /*
00161      * Representation check
00162      * Note that TAO in A3 is not possible because A3::C has less than one byte per item
00163      */
00164     uavcan::StaticTransferBuffer<16> buf;
00165     uavcan::BitStream bs_wr(buf);
00166     uavcan::ScalarCodec sc_wr(bs_wr);
00167 
00168     ASSERT_EQ(1, A1::encode(a1, sc_wr, uavcan::TailArrayOptDisabled));
00169     ASSERT_EQ(1, A2::encode(a2, sc_wr, uavcan::TailArrayOptDisabled));
00170     ASSERT_EQ(1, A3::encode(a3, sc_wr, uavcan::TailArrayOptEnabled));
00171 
00172     ASSERT_EQ(0, A3::encode(a3, sc_wr, uavcan::TailArrayOptEnabled));  // Out of buffer space
00173 
00174     static const std::string Reference =
00175         "00000000 00000001 00000010 00000011 " // A1 (0, 1, 2, 3)
00176         "00000000 00000000 00000000 00111100 " // A2 (0, 1)
00177         "00000000 00000000 00000000 10101010 " // A3[0] (0, 0, bool[5])
00178         "00000001 00000000 00111100 10101010"; // A3[1] (1, 1, bool[5])
00179 
00180     ASSERT_EQ(Reference, bs_wr.toString());
00181 
00182     /*
00183      * Read back
00184      */
00185     uavcan::BitStream bs_rd(buf);
00186     uavcan::ScalarCodec sc_rd(bs_rd);
00187 
00188     A1 a1_;
00189     A2 a2_;
00190     A3 a3_;
00191 
00192     ASSERT_EQ(1, A1::decode(a1_, sc_rd, uavcan::TailArrayOptDisabled));
00193     ASSERT_EQ(1, A2::decode(a2_, sc_rd, uavcan::TailArrayOptDisabled));
00194     ASSERT_EQ(1, A3::decode(a3_, sc_rd, uavcan::TailArrayOptEnabled));
00195 
00196     ASSERT_EQ(a1_, a1);
00197     ASSERT_EQ(a2_, a2);
00198     ASSERT_EQ(a3_, a3);
00199 
00200     for (uint8_t i = 0; i < 4; i++)
00201     {
00202         ASSERT_EQ(a1[i], a1_[i]);
00203     }
00204     for (uint8_t i = 0; i < 2; i++)
00205     {
00206         ASSERT_EQ(a2[i], a2_[i]);
00207     }
00208     for (uint8_t i = 0; i < 2; i++)
00209     {
00210         ASSERT_EQ(a3[i].a, a3_[i].a);
00211         ASSERT_EQ(a3[i].b, a3_[i].b);
00212         ASSERT_EQ(a3[i].c, a3_[i].c);
00213     }
00214 
00215     ASSERT_EQ(0, A3::decode(a3_, sc_rd, uavcan::TailArrayOptEnabled));  // Out of buffer space
00216 
00217     /*
00218      * STL compatibility
00219      */
00220     std::vector<int> v1;
00221     v1.push_back(0);
00222     v1.push_back(1);
00223     v1.push_back(2);
00224     v1.push_back(3);
00225 
00226     ASSERT_TRUE(a1 == v1);
00227     ASSERT_FALSE(a1 != v1);
00228     ASSERT_TRUE(v1 == a1);
00229     ASSERT_FALSE(v1 != a1);
00230     ASSERT_FALSE(a1 < v1);
00231 
00232     v1[0] = 9000;
00233     ASSERT_FALSE(a1 == v1);
00234     ASSERT_TRUE(a1 != v1);
00235     ASSERT_TRUE(a1 < v1);
00236 
00237     ASSERT_EQ(0, a1.front());
00238     ASSERT_EQ(3, a1.back());
00239 
00240     // Boolean vector
00241     std::vector<bool> v2;
00242     v2.push_back(false);
00243     v2.push_back(true);
00244     v2.push_back(false);
00245     v2.push_back(true);
00246     v2.push_back(false);
00247 
00248     ASSERT_TRUE(a3[0].c == v2);
00249     ASSERT_FALSE(a3[0].c == v1);
00250     ASSERT_FALSE(a3[0].c != v2);
00251     ASSERT_TRUE(a3[0].c != v1);
00252 
00253     v2[0] = true;
00254     ASSERT_TRUE(a3[0].c != v2);
00255     ASSERT_FALSE(a3[0].c == v2);
00256 }
00257 
00258 
00259 TEST(Array, Dynamic)
00260 {
00261     typedef Array<IntegerSpec<1, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, 5> A;
00262     typedef Array<IntegerSpec<8, SignednessSigned, CastModeSaturate>, ArrayModeDynamic, 255> B;
00263 
00264     A a;
00265     B b;
00266     B b2;
00267 
00268     ASSERT_EQ(3 + 5, A::MaxBitLen);
00269     ASSERT_EQ(8 + 255 * 8, B::MaxBitLen);
00270 
00271     ASSERT_TRUE(a.empty());
00272     ASSERT_TRUE(b.empty());
00273     ASSERT_TRUE(b2.empty());
00274 
00275     {
00276         uavcan::StaticTransferBuffer<16> buf;
00277         uavcan::BitStream bs_wr(buf);
00278         uavcan::ScalarCodec sc_wr(bs_wr);
00279 
00280         ASSERT_EQ(1, A::encode(a, sc_wr, uavcan::TailArrayOptDisabled));
00281         ASSERT_EQ(1, B::encode(b, sc_wr, uavcan::TailArrayOptDisabled));
00282         ASSERT_EQ(1, B::encode(b2, sc_wr, uavcan::TailArrayOptEnabled));
00283 
00284         ASSERT_EQ("000" "00000 000" "00000", bs_wr.toString()); // Last array was optimized away completely
00285 
00286         uavcan::BitStream bs_rd(buf);
00287         uavcan::ScalarCodec sc_rd(bs_rd);
00288 
00289         ASSERT_EQ(1, A::decode(a, sc_rd, uavcan::TailArrayOptDisabled));
00290         ASSERT_EQ(1, B::decode(b, sc_rd, uavcan::TailArrayOptDisabled));
00291         ASSERT_EQ(1, B::decode(b2, sc_rd, uavcan::TailArrayOptEnabled));
00292 
00293         ASSERT_TRUE(a.empty());
00294         ASSERT_TRUE(b.empty());
00295         ASSERT_TRUE(b2.empty());
00296     }
00297 
00298     a.push_back(true);
00299     a.push_back(false);
00300     a.push_back(true);
00301     a.push_back(false);
00302     a.push_back(true);
00303 
00304     b.push_back(42);
00305     b.push_back(-42);
00306 
00307     b2.push_back(123);
00308     b2.push_back(72);
00309 
00310     {
00311         uavcan::StaticTransferBuffer<16> buf;
00312         uavcan::BitStream bs_wr(buf);
00313         uavcan::ScalarCodec sc_wr(bs_wr);
00314 
00315         ASSERT_EQ(1, A::encode(a, sc_wr, uavcan::TailArrayOptDisabled));
00316         ASSERT_EQ(1, B::encode(b, sc_wr, uavcan::TailArrayOptDisabled));
00317         ASSERT_EQ(1, B::encode(b2, sc_wr, uavcan::TailArrayOptEnabled));  // No length field
00318 
00319         //         A        B len    B[0]     B[1]     B2[0]    B2[1]
00320         ASSERT_EQ("10110101 00000010 00101010 11010110 01111011 01001000", bs_wr.toString());
00321 
00322         uavcan::BitStream bs_rd(buf);
00323         uavcan::ScalarCodec sc_rd(bs_rd);
00324 
00325         a.clear();
00326         b.clear();
00327         b2.clear();
00328         ASSERT_TRUE(a.empty());
00329         ASSERT_TRUE(b.empty());
00330         ASSERT_TRUE(b2.empty());
00331 
00332         ASSERT_EQ(1, A::decode(a, sc_rd, uavcan::TailArrayOptDisabled));
00333         ASSERT_EQ(1, B::decode(b, sc_rd, uavcan::TailArrayOptDisabled));
00334         ASSERT_EQ(1, B::decode(b2, sc_rd, uavcan::TailArrayOptEnabled));
00335 
00336         ASSERT_EQ(5, a.size());
00337         ASSERT_EQ(2, b.size());
00338         ASSERT_EQ(2, b2.size());
00339 
00340         ASSERT_TRUE(a[0]);
00341         ASSERT_FALSE(a[1]);
00342         ASSERT_TRUE(a[2]);
00343         ASSERT_FALSE(a[3]);
00344         ASSERT_TRUE(a[4]);
00345 
00346         ASSERT_EQ(42, b[0]);
00347         ASSERT_EQ(-42, b[1]);
00348 
00349         ASSERT_EQ(123, b2[0]);
00350         ASSERT_EQ(72, b2[1]);
00351     }
00352 
00353     ASSERT_FALSE(a == b);
00354     ASSERT_FALSE(b == a);
00355     ASSERT_TRUE(a != b);
00356     ASSERT_TRUE(b != a);
00357 
00358     a.resize(0);
00359     b.resize(0);
00360     ASSERT_TRUE(a.empty());
00361     ASSERT_TRUE(b.empty());
00362 
00363     a.resize(5, true);
00364     b.resize(255, 72);
00365     ASSERT_EQ(5, a.size());
00366     ASSERT_EQ(255, b.size());
00367 
00368     for (uint8_t i = 0; i < 5; i++)
00369     {
00370         ASSERT_TRUE(a[i]);
00371     }
00372     for (uint8_t i = 0; i < 255; i++)
00373     {
00374         ASSERT_EQ(72, b[i]);
00375     }
00376 }
00377 
00378 
00379 template <typename B>
00380 struct CustomType2
00381 {
00382     typedef uavcan::FloatSpec<16, uavcan::CastModeSaturate> A;
00383 
00384     enum { MinBitLen = A::MinBitLen + B::MinBitLen };
00385     enum { MaxBitLen = A::MaxBitLen + B::MaxBitLen };
00386 
00387     typename uavcan::StorageType<A>::Type a;
00388     typename uavcan::StorageType<B>::Type b;
00389 
00390     CustomType2()
00391         : a()
00392         , b()
00393     { }
00394 
00395     bool operator==(const CustomType2& rhs) const
00396     {
00397         return uavcan::areFloatsExactlyEqual(a, rhs.a) &&
00398                b == rhs.b;
00399     }
00400 
00401     static int encode(const CustomType2& obj, uavcan::ScalarCodec& codec,
00402                       uavcan::TailArrayOptimizationMode tao_mode = uavcan::TailArrayOptEnabled)
00403     {
00404         int res = 0;
00405         res = A::encode(obj.a, codec, uavcan::TailArrayOptDisabled);
00406         if (res <= 0)
00407         {
00408             return res;
00409         }
00410         res = B::encode(obj.b, codec, tao_mode);
00411         if (res <= 0)
00412         {
00413             return res;
00414         }
00415         return 1;
00416     }
00417 
00418     static int decode(CustomType2& obj, uavcan::ScalarCodec& codec,
00419                       uavcan::TailArrayOptimizationMode tao_mode = uavcan::TailArrayOptEnabled)
00420     {
00421         int res = 0;
00422         res = A::decode(obj.a, codec, uavcan::TailArrayOptDisabled);
00423         if (res <= 0)
00424         {
00425             return res;
00426         }
00427         res = B::decode(obj.b, codec, tao_mode);
00428         if (res <= 0)
00429         {
00430             return res;
00431         }
00432         return 1;
00433     }
00434 };
00435 
00436 
00437 template <typename T>
00438 static std::string runEncodeDecode(const typename uavcan::StorageType<T>::Type& value,
00439                                    const uavcan::TailArrayOptimizationMode tao_mode)
00440 {
00441     uavcan::StaticTransferBuffer<(T::MaxBitLen + 7) / 8> buf;
00442     uavcan::BitStream bs_wr(buf);
00443     uavcan::ScalarCodec sc_wr(bs_wr);
00444     EXPECT_EQ(1, T::encode(value, sc_wr, tao_mode));
00445 
00446     typename uavcan::StorageType<T>::Type value2 = typename uavcan::StorageType<T>::Type();
00447     // Decode multiple times to make sure that the decoded type is being correctly de-initialized
00448     for (int i = 0; i < 3; i++)
00449     {
00450         uavcan::BitStream bs_rd(buf);
00451         uavcan::ScalarCodec sc_rd(bs_rd);
00452         EXPECT_EQ(1, T::decode(value2, sc_rd, tao_mode));
00453         EXPECT_EQ(value, value2);
00454     }
00455     return bs_wr.toString();
00456 }
00457 
00458 
00459 TEST(Array, TailArrayOptimization)
00460 {
00461     typedef Array<IntegerSpec<1, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, 5>   OneBitArray;
00462     typedef Array<IntegerSpec<8, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, 255> EightBitArray;
00463     typedef CustomType2<Array<OneBitArray,   ArrayModeDynamic, 255> > A;
00464     typedef CustomType2<Array<EightBitArray, ArrayModeDynamic, 255> > B;
00465     typedef CustomType2<EightBitArray> C;
00466 
00467     A a;
00468     B b;
00469     C c;
00470 
00471     /*
00472      * Empty
00473      */
00474     //         a LSB    a MSB    b len
00475     ASSERT_EQ("00000000 00000000 00000000", runEncodeDecode<A>(a, uavcan::TailArrayOptEnabled));
00476     ASSERT_EQ("00000000 00000000 00000000", runEncodeDecode<A>(a, uavcan::TailArrayOptDisabled));
00477 
00478     //         a LSB    a MSB    b len
00479     ASSERT_EQ("00000000 00000000 00000000", runEncodeDecode<B>(b, uavcan::TailArrayOptEnabled));
00480     ASSERT_EQ("00000000 00000000 00000000", runEncodeDecode<B>(b, uavcan::TailArrayOptDisabled));
00481 
00482     //         a LSB    a MSB
00483     ASSERT_EQ("00000000 00000000",          runEncodeDecode<C>(c, uavcan::TailArrayOptEnabled));
00484     ASSERT_EQ("00000000 00000000 00000000", runEncodeDecode<C>(c, uavcan::TailArrayOptDisabled));
00485 
00486     /*
00487      * A
00488      */
00489     a.b.resize(2);
00490     a.b[0].push_back(true);
00491     a.b[0].push_back(false);
00492     // a.b[1] remains empty
00493     //         a LSB    a MSB    b len    b: len(2), 1, 0, len(0)
00494     ASSERT_EQ("00000000 00000000 00000010 01010000", runEncodeDecode<A>(a, uavcan::TailArrayOptEnabled));
00495     ASSERT_EQ("00000000 00000000 00000010 01010000", runEncodeDecode<A>(a, uavcan::TailArrayOptDisabled));
00496 
00497     /*
00498      * B
00499      */
00500     b.b.resize(3);
00501     b.b[0].push_back(42);
00502     b.b[0].push_back(72);
00503     // b.b[1] remains empty
00504     b.b[2].push_back(123);
00505     b.b[2].push_back(99);
00506     //         a LSB    a MSB    b len    b[0]len  42       72       b[1]len  123      99      (b[2] len optimized out)
00507     ASSERT_EQ("00000000 00000000 00000011 00000010 00101010 01001000 00000000 01111011 01100011",
00508               runEncodeDecode<B>(b, uavcan::TailArrayOptEnabled));
00509     // Same as above, but b[2] len is present                                 v here v
00510     ASSERT_EQ("00000000 00000000 00000011 00000010 00101010 01001000 00000000 00000010 01111011 01100011",
00511               runEncodeDecode<B>(b, uavcan::TailArrayOptDisabled));
00512 
00513     /*
00514      * C
00515      */
00516     c.a = 1;
00517     c.b.push_back(1);
00518     c.b.push_back(2);
00519     c.b.push_back(3);
00520     //         a LSB    a MSB    1        2        3
00521     ASSERT_EQ("00000000 00111100 00000001 00000010 00000011",
00522               runEncodeDecode<C>(c, uavcan::TailArrayOptEnabled));
00523     //         a LSB    a MSB    b len    1        2        3
00524     ASSERT_EQ("00000000 00111100 00000011 00000001 00000010 00000011",
00525               runEncodeDecode<C>(c, uavcan::TailArrayOptDisabled));
00526 }
00527 
00528 
00529 TEST(Array, TailArrayOptimizationErrors)
00530 {
00531     typedef Array<IntegerSpec<8, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, 5> A;
00532 
00533     A a;
00534     ASSERT_TRUE(a.empty());
00535     ASSERT_EQ("",         runEncodeDecode<A>(a, uavcan::TailArrayOptEnabled));
00536     ASSERT_EQ("00000000", runEncodeDecode<A>(a, uavcan::TailArrayOptDisabled));
00537 
00538     // Correct decode/encode
00539     a.push_back(1);
00540     a.push_back(126);
00541     a.push_back(5);
00542     ASSERT_FALSE(a.empty());
00543     ASSERT_EQ("00000001 01111110 00000101",          runEncodeDecode<A>(a, uavcan::TailArrayOptEnabled));
00544     ASSERT_EQ("01100000 00101111 11000000 10100000", runEncodeDecode<A>(a, uavcan::TailArrayOptDisabled));
00545 
00546     // Invalid decode - length field is out of range
00547     uavcan::StaticTransferBuffer<7> buf;
00548     uavcan::BitStream bs_wr(buf);
00549     uavcan::ScalarCodec sc_wr(bs_wr);
00550 
00551     ASSERT_EQ(1, sc_wr.encode<3>(uint8_t(6)));  // Length - more than 5 items, error
00552     ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(42)));
00553     ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(72)));
00554     ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(126)));
00555     ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(1)));
00556     ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(2)));
00557     ASSERT_EQ(1, sc_wr.encode<8>(uint8_t(3)));  // Out of range - only 5 items allowed
00558 
00559     //         197      73       15       192      32       ...
00560     ASSERT_EQ("11000101 01001001 00001111 11000000 00100000 01000000 01100000", bs_wr.toString());
00561 
00562     {
00563         uavcan::BitStream bs_rd(buf);
00564         uavcan::ScalarCodec sc_rd(bs_rd);
00565         A a2;
00566         a2.push_back(56);   // Garbage
00567         ASSERT_EQ(1, a2.size());
00568         // Will fail - declared length is more than 5 items
00569         ASSERT_GT(0, A::decode(a2, sc_rd, uavcan::TailArrayOptDisabled));
00570         // Must be cleared
00571         ASSERT_TRUE(a2.empty());
00572     }
00573     {
00574         uavcan::BitStream bs_rd(buf);
00575         uavcan::ScalarCodec sc_rd(bs_rd);
00576         A a2;
00577         a2.push_back(56);   // Garbage
00578         ASSERT_EQ(1, a2.size());
00579         // Will fail - no length field, but the stream is too long
00580         ASSERT_GT(0, A::decode(a2, sc_rd, uavcan::TailArrayOptEnabled));
00581         // Will contain some garbage
00582         ASSERT_EQ(5, a2.size());
00583         // Interpreted stream - see the values above
00584         ASSERT_EQ(197, a2[0]);
00585         ASSERT_EQ(73,  a2[1]);
00586         ASSERT_EQ(15,  a2[2]);
00587         ASSERT_EQ(192, a2[3]);
00588         ASSERT_EQ(32,  a2[4]);
00589     }
00590 }
00591 
00592 
00593 TEST(Array, DynamicEncodeDecodeErrors)
00594 {
00595     typedef CustomType2<Array<Array<IntegerSpec<8, SignednessUnsigned, CastModeSaturate>,
00596                                     ArrayModeDynamic, 255>,
00597                               ArrayModeDynamic, 255> > A;
00598     A a;
00599     a.b.resize(2);
00600     a.b[0].push_back(55);
00601     a.b[0].push_back(66);
00602     {
00603         uavcan::StaticTransferBuffer<4> buf;
00604         uavcan::BitStream bs_wr(buf);
00605         uavcan::ScalarCodec sc_wr(bs_wr);
00606         ASSERT_EQ(0, A::encode(a, sc_wr, uavcan::TailArrayOptEnabled));  // Not enough buffer space
00607 
00608         uavcan::BitStream bs_rd(buf);
00609         uavcan::ScalarCodec sc_rd(bs_rd);
00610         ASSERT_EQ(0, A::decode(a, sc_rd, uavcan::TailArrayOptEnabled));
00611     }
00612     {
00613         uavcan::StaticTransferBuffer<4> buf;
00614         uavcan::BitStream bs_wr(buf);
00615         uavcan::ScalarCodec sc_wr(bs_wr);
00616         ASSERT_EQ(0, A::encode(a, sc_wr, uavcan::TailArrayOptDisabled));  // Not enough buffer space
00617 
00618         uavcan::BitStream bs_rd(buf);
00619         uavcan::ScalarCodec sc_rd(bs_rd);
00620         ASSERT_EQ(0, A::decode(a, sc_rd, uavcan::TailArrayOptDisabled));
00621     }
00622 }
00623 
00624 
00625 TEST(Array, StaticEncodeDecodeErrors)
00626 {
00627     typedef CustomType2<Array<Array<IntegerSpec<8, SignednessUnsigned, CastModeSaturate>,
00628                                     ArrayModeStatic, 2>,
00629                               ArrayModeStatic, 2> > A;
00630     A a;
00631     a.a = 1.0;
00632     a.b[0][0] = 0x11;
00633     a.b[0][1] = 0x22;
00634     a.b[1][0] = 0x33;
00635     a.b[1][1] = 0x44;
00636     {   // Just enough buffer space - 6 bytes
00637         uavcan::StaticTransferBuffer<6> buf;
00638         uavcan::BitStream bs_wr(buf);
00639         uavcan::ScalarCodec sc_wr(bs_wr);
00640         ASSERT_EQ(1, A::encode(a, sc_wr, uavcan::TailArrayOptDisabled));
00641 
00642         ASSERT_EQ("00000000 00111100 00010001 00100010 00110011 01000100", bs_wr.toString());
00643 
00644         uavcan::BitStream bs_rd(buf);
00645         uavcan::ScalarCodec sc_rd(bs_rd);
00646         ASSERT_EQ(1, A::decode(a, sc_rd, uavcan::TailArrayOptEnabled));
00647     }
00648     {   // Not enough space
00649         uavcan::StaticTransferBuffer<5> buf;
00650         uavcan::BitStream bs_wr(buf);
00651         uavcan::ScalarCodec sc_wr(bs_wr);
00652         ASSERT_EQ(0, A::encode(a, sc_wr, uavcan::TailArrayOptDisabled));
00653 
00654         ASSERT_EQ("00000000 00111100 00010001 00100010 00110011", bs_wr.toString());
00655 
00656         uavcan::BitStream bs_rd(buf);
00657         uavcan::ScalarCodec sc_rd(bs_rd);
00658         ASSERT_EQ(0, A::decode(a, sc_rd, uavcan::TailArrayOptEnabled));
00659     }
00660 }
00661 
00662 
00663 TEST(Array, Copyability)
00664 {
00665     typedef Array<IntegerSpec<1, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, 5>   OneBitArray;
00666     typedef Array<IntegerSpec<8, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, 255> EightBitArray;
00667     typedef Array<OneBitArray,   ArrayModeDynamic, 255> A;
00668     typedef Array<EightBitArray, ArrayModeDynamic, 255> B;
00669     typedef EightBitArray C;
00670 
00671     A a;
00672     B b;
00673     C c;
00674 
00675     A a2 = a;
00676     B b2 = b;
00677     C c2 = c;
00678 
00679     ASSERT_TRUE(a == a2);
00680     ASSERT_TRUE(b == b2);
00681     ASSERT_TRUE(c == c2);
00682 
00683     a.push_back(OneBitArray());
00684     b.push_back(EightBitArray());
00685     c.push_back(42);
00686 
00687     ASSERT_TRUE(a != a2);
00688     ASSERT_TRUE(b != b2);
00689     ASSERT_TRUE(c != c2);
00690 
00691     a2 = a;
00692     b2 = b;
00693     c2 = c;
00694 
00695     ASSERT_TRUE(a2 == a);
00696     ASSERT_TRUE(b2 == b);
00697     ASSERT_TRUE(c2 == c);
00698 }
00699 
00700 
00701 TEST(Array, Appending)
00702 {
00703     typedef Array<FloatSpec<16, CastModeSaturate>, ArrayModeDynamic, 2> A;
00704     typedef Array<FloatSpec<16, CastModeSaturate>, ArrayModeDynamic, 257> B;
00705     A a;
00706     B b;
00707 
00708     a.push_back(1);
00709     a.push_back(2);
00710     a += b;
00711     ASSERT_EQ(2, a.size());
00712     ASSERT_EQ(1, a[0]);
00713     ASSERT_EQ(2, a[1]);
00714 
00715     b += a;
00716     ASSERT_TRUE(b == a);
00717     b += a;
00718     ASSERT_EQ(4, b.size());
00719     ASSERT_EQ(1, b[0]);
00720     ASSERT_EQ(2, b[1]);
00721     ASSERT_EQ(1, b[2]);
00722     ASSERT_EQ(2, b[3]);
00723 }
00724 
00725 
00726 TEST(Array, Strings)
00727 {
00728     typedef Array<IntegerSpec<8, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, 32> A8;
00729     typedef Array<IntegerSpec<7, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, 32> A7;
00730 
00731     A8 a8;
00732     A8 a8_2;
00733     A7 a7;
00734 
00735     ASSERT_TRUE(a8 == a7);
00736     // cppcheck-suppress duplicateExpression
00737     ASSERT_TRUE(a8 == a8);
00738     // cppcheck-suppress duplicateExpression
00739     ASSERT_TRUE(a7 == a7);
00740     ASSERT_TRUE(a8 == "");
00741     ASSERT_TRUE(a7 == "");
00742 
00743     a8 = "Hello world!";
00744     a7 = "123";
00745     ASSERT_TRUE(a8 == "Hello world!");
00746     ASSERT_TRUE(a7 == "123");
00747 
00748     a8 = "Our sun is dying.";
00749     a7 = "456";
00750     ASSERT_TRUE("Our sun is dying." == a8);
00751     ASSERT_TRUE("456" == a7);
00752 
00753     a8 += " 123456";
00754     a8 += "-789";
00755     ASSERT_TRUE("Our sun is dying. 123456-789" == a8);
00756 
00757     ASSERT_TRUE(a8_2 == "");
00758     ASSERT_TRUE(a8_2.empty());
00759     ASSERT_TRUE(a8_2 != a8);
00760     a8_2 = a8;
00761     ASSERT_TRUE(a8_2 == "Our sun is dying. 123456-789");
00762     ASSERT_TRUE(a8_2 == a8);
00763 
00764     /*
00765      * c_str()
00766      */
00767     ASSERT_STREQ("", A8().c_str());
00768     ASSERT_STREQ("", A7().c_str());
00769     ASSERT_STREQ("Our sun is dying. 123456-789", a8_2.c_str());
00770     ASSERT_STREQ("Our sun is dying. 123456-789", a8.c_str());
00771     ASSERT_STREQ("456", a7.c_str());
00772 
00773     /*
00774      * String constructor
00775      */
00776     A8 a8_3("123");
00777     A7 a7_3 = "456";
00778     ASSERT_EQ(3, a8_3.size());
00779     ASSERT_EQ(3, a7_3.size());
00780     ASSERT_STREQ("123", a8_3.c_str());
00781     ASSERT_STREQ("456", a7_3.c_str());
00782 }
00783 
00784 
00785 TEST(Array, AppendFormatted)
00786 {
00787     typedef Array<IntegerSpec<8, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, 45> A8;
00788 
00789     A8 a;
00790 
00791     ASSERT_TRUE("" == a);
00792 
00793     a.appendFormatted("%4.1f", 12.3);                      // 4
00794     a += " ";                                             // 1
00795     a.appendFormatted("%li", -123456789L);                 // 10
00796     a.appendFormatted("%s", " TOTAL PERSPECTIVE VORTEX "); // 26
00797     a.appendFormatted("0x%X", 0xDEADBEEF);                 // 10 --> 4
00798 
00799     ASSERT_STREQ("12.3 -123456789 TOTAL PERSPECTIVE VORTEX 0xDE", a.c_str());
00800 }
00801 
00802 
00803 TEST(Array, FlatStreaming)
00804 {
00805     typedef Array<IntegerSpec<8, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, 32> A8D;
00806     typedef Array<FloatSpec<16, CastModeSaturate>, ArrayModeDynamic, 16> AF16D;
00807     typedef Array<FloatSpec<16, CastModeSaturate>, ArrayModeStatic, 3> AF16S;
00808 
00809     A8D a1;
00810     a1 = "12\n3\x44\xa5\xde\xad\x79";
00811     uavcan::YamlStreamer<A8D>::stream(std::cout, a1, 0);
00812     std::cout << std::endl;
00813 
00814     A8D a2;
00815     a2 = "Hello";
00816     uavcan::YamlStreamer<A8D>::stream(std::cout, a2, 0);
00817     std::cout << std::endl;
00818 
00819     AF16D af16d1;
00820     af16d1.push_back(1.23F);
00821     af16d1.push_back(4.56F);
00822     uavcan::YamlStreamer<AF16D>::stream(std::cout, af16d1, 0);
00823     std::cout << std::endl;
00824 
00825     AF16D af16d2;
00826     uavcan::YamlStreamer<AF16D>::stream(std::cout, af16d2, 0);
00827     std::cout << std::endl;
00828 
00829     AF16S af16s;
00830     uavcan::YamlStreamer<AF16S>::stream(std::cout, af16s, 0);
00831     std::cout << std::endl;
00832 }
00833 
00834 
00835 TEST(Array, MultidimensionalStreaming)
00836 {
00837     typedef Array<FloatSpec<16, CastModeSaturate>, ArrayModeDynamic, 16> Float16Array;
00838     typedef Array<Float16Array, ArrayModeDynamic, 8> TwoDimensional;
00839     typedef Array<TwoDimensional, ArrayModeDynamic, 4> ThreeDimensional;
00840 
00841     ThreeDimensional threedee;
00842     threedee.resize(3);
00843     for (uint8_t x = 0; x < threedee.size(); x++)
00844     {
00845         threedee[x].resize(3);
00846         for (uint8_t y = 0; y < threedee[x].size(); y++)
00847         {
00848             threedee[x][y].resize(3);
00849             for (uint8_t z = 0; z < threedee[x][y].size(); z++)
00850             {
00851                 threedee[x][y][z] = 1.0F / (float(x + y + z) + 1.0F);
00852             }
00853         }
00854     }
00855 
00856     uavcan::YamlStreamer<ThreeDimensional>::stream(std::cout, threedee, 0);
00857     std::cout << std::endl;
00858 }
00859 
00860 
00861 TEST(Array, SquareMatrixPacking)
00862 {
00863     Array<FloatSpec<16, CastModeSaturate>, ArrayModeDynamic, 9> m3x3s;
00864     Array<FloatSpec<32, CastModeSaturate>, ArrayModeDynamic, 4> m2x2f;
00865     Array<FloatSpec<64, CastModeSaturate>, ArrayModeDynamic, 36> m6x6d;
00866 
00867     // NAN will be reduced to empty array
00868     {
00869         const double nans3x3[] =
00870         {
00871             NAN, NAN, NAN,
00872             NAN, NAN, NAN,
00873             NAN, NAN, NAN
00874         };
00875         m3x3s.packSquareMatrix(nans3x3);
00876         ASSERT_EQ(0, m3x3s.size());
00877 
00878         // Empty array will be decoded as zero matrix
00879         double nans3x3_out[9];
00880         m3x3s.unpackSquareMatrix(nans3x3_out);
00881         for (uint8_t i = 0; i < 9; i++)
00882         {
00883             ASSERT_DOUBLE_EQ(0, nans3x3_out[i]);
00884         }
00885     }
00886     {
00887         std::vector<double> empty;
00888         m3x3s.packSquareMatrix(empty);
00889         ASSERT_EQ(0, m3x3s.size());
00890 
00891         empty.resize(9);
00892         m3x3s.unpackSquareMatrix(empty);
00893         for (uint8_t i = 0; i < 9; i++)
00894         {
00895             ASSERT_DOUBLE_EQ(0, empty.at(i));
00896         }
00897     }
00898 
00899     // Scalar matrix will be reduced to a single value
00900     {
00901         std::vector<float> scalar2x2(4);
00902         scalar2x2[0] = scalar2x2[3] = 3.14F;
00903         m2x2f.packSquareMatrix(scalar2x2);
00904         ASSERT_EQ(1, m2x2f.size());
00905         ASSERT_FLOAT_EQ(3.14F, m2x2f[0]);
00906 
00907         m2x2f.unpackSquareMatrix(scalar2x2);
00908         const float reference[] =
00909         {
00910             3.14F, 0.0F,
00911             0.0F, 3.14F
00912         };
00913         ASSERT_TRUE(std::equal(scalar2x2.begin(), scalar2x2.end(), reference));
00914     }
00915     {
00916         const float scalar6x6[] =
00917         {
00918             -18, 0, 0, 0, 0, 0,
00919             0, -18, 0, 0, 0, 0,
00920             0, 0, -18, 0, 0, 0,
00921             0, 0, 0, -18, 0, 0,
00922             0, 0, 0, 0, -18, 0,
00923             0, 0, 0, 0, 0, -18
00924         };
00925         m6x6d.packSquareMatrix(scalar6x6);
00926         ASSERT_EQ(1, m6x6d.size());
00927         ASSERT_DOUBLE_EQ(-18, m6x6d[0]);
00928 
00929         std::vector<long double> output(36);
00930         m6x6d.unpackSquareMatrix(output);
00931         ASSERT_TRUE(std::equal(output.begin(), output.end(), scalar6x6));
00932     }
00933 
00934     // Diagonal matrix will be reduced to an array of length Width
00935     {
00936         const float diagonal6x6[] =
00937         {
00938             1,  0,  0,  0,  0,  0,
00939             0, -2,  0,  0,  0,  0,
00940             0,  0,  3,  0,  0,  0,
00941             0,  0,  0, -4,  0,  0,
00942             0,  0,  0,  0,  5,  0,
00943             0,  0,  0,  0,  0, -6
00944         };
00945         m6x6d.packSquareMatrix(diagonal6x6);
00946         ASSERT_EQ(6, m6x6d.size());
00947         ASSERT_DOUBLE_EQ(1,  m6x6d[0]);
00948         ASSERT_DOUBLE_EQ(-2, m6x6d[1]);
00949         ASSERT_DOUBLE_EQ(3,  m6x6d[2]);
00950         ASSERT_DOUBLE_EQ(-4, m6x6d[3]);
00951         ASSERT_DOUBLE_EQ(5,  m6x6d[4]);
00952         ASSERT_DOUBLE_EQ(-6, m6x6d[5]);
00953 
00954         std::vector<long double> output(36);
00955         m6x6d.unpackSquareMatrix(output);
00956         ASSERT_TRUE(std::equal(output.begin(), output.end(), diagonal6x6));
00957     }
00958 
00959     // A matrix filled with random values will not be compressed
00960     {
00961         std::vector<float> full3x3(9);
00962         for (uint8_t i = 0; i < 9; i++)
00963         {
00964             full3x3[i] = float(i);
00965         }
00966         m3x3s.packSquareMatrix(full3x3);
00967         ASSERT_EQ(9, m3x3s.size());
00968         for (uint8_t i = 0; i < 9; i++)
00969         {
00970             ASSERT_FLOAT_EQ(float(i), m3x3s[i]);
00971         }
00972 
00973         long output[9];
00974         m3x3s.unpackSquareMatrix(output);
00975         ASSERT_TRUE(std::equal(full3x3.begin(), full3x3.end(), output));
00976     }
00977 
00978     // This will be represented as diagonal - NANs are exceptional
00979     {
00980         const double scalarnan3x3[] =
00981         {
00982             NAN, 0, 0,
00983             0, NAN, 0,
00984             0, 0, NAN
00985         };
00986         m3x3s.packSquareMatrix(scalarnan3x3);
00987         ASSERT_EQ(3, m3x3s.size());
00988         ASSERT_FALSE(m3x3s[0] <= m3x3s[0]);   // NAN
00989         ASSERT_FALSE(m3x3s[1] <= m3x3s[1]);   // NAN
00990         ASSERT_FALSE(m3x3s[2] <= m3x3s[2]);   // NAN
00991 
00992         float output[9];
00993         m3x3s.unpackSquareMatrix(output);
00994         ASSERT_FALSE(output[0] <= output[0]);   // NAN
00995         ASSERT_EQ(0, output[1]);
00996         ASSERT_EQ(0, output[2]);
00997         ASSERT_EQ(0, output[3]);
00998         ASSERT_FALSE(output[4] <= output[4]);   // NAN
00999         ASSERT_EQ(0, output[5]);
01000         ASSERT_EQ(0, output[6]);
01001         ASSERT_EQ(0, output[7]);
01002         ASSERT_FALSE(output[8] <= output[8]);   // NAN
01003     }
01004 
01005     // This is a full matrix too (notice the NAN)
01006     {
01007         const float full2x2[] =
01008         {
01009             1,  NAN,
01010             0, -2
01011         };
01012         m2x2f.packSquareMatrix(full2x2);
01013         ASSERT_EQ(4, m2x2f.size());
01014         ASSERT_FLOAT_EQ(1, m2x2f[0]);
01015         ASSERT_FALSE(m2x2f[1] <= m2x2f[1]);   // NAN
01016         ASSERT_FLOAT_EQ(0, m2x2f[2]);
01017         ASSERT_FLOAT_EQ(-2, m2x2f[3]);
01018 
01019         float output[4];
01020         m2x2f.unpackSquareMatrix(output);
01021         ASSERT_EQ(1, output[0]);
01022         ASSERT_FALSE(output[1] <= output[1]);   // NAN
01023         ASSERT_EQ(0, output[2]);
01024         ASSERT_EQ(-2, output[3]);
01025     }
01026 
01027     // Zero matrix will be represented as scalar matrix
01028     {
01029         const float zero2x2[] =
01030         {
01031             0, 0,
01032             0, 0
01033         };
01034         m2x2f.packSquareMatrix(zero2x2);
01035         ASSERT_EQ(1, m2x2f.size());
01036         ASSERT_FLOAT_EQ(0, m2x2f[0]);
01037     }
01038 
01039     // Symmetric matrix will contain only upper-right triangle
01040     {
01041         const float sym2x2[] =
01042         {
01043             1, 2,
01044             2, 1
01045         };
01046         m2x2f.packSquareMatrix(sym2x2);
01047         ASSERT_EQ(3, m2x2f.size());
01048 
01049         float sym2x2_out[4];
01050         m2x2f.unpackSquareMatrix(sym2x2_out);
01051         ASSERT_FLOAT_EQ(1, sym2x2_out[0]);
01052         ASSERT_FLOAT_EQ(2, sym2x2_out[1]);
01053         ASSERT_FLOAT_EQ(2, sym2x2_out[2]);
01054         ASSERT_FLOAT_EQ(1, sym2x2_out[3]);
01055     }
01056     {
01057         const float sym3x3[] =
01058         {
01059             1, 2, 3,
01060             2, 4, 5,
01061             3, 5, 6
01062         };
01063         m3x3s.packSquareMatrix(sym3x3);
01064         ASSERT_EQ(6, m3x3s.size());
01065         ASSERT_EQ(1, m3x3s[0]);
01066         ASSERT_EQ(2, m3x3s[1]);
01067         ASSERT_EQ(3, m3x3s[2]);
01068         ASSERT_EQ(4, m3x3s[3]);
01069         ASSERT_EQ(5, m3x3s[4]);
01070         ASSERT_EQ(6, m3x3s[5]);
01071 
01072         float sym3x3_out[9];
01073         m3x3s.unpackSquareMatrix(sym3x3_out);
01074 
01075         for (int i = 0; i < 9; i++)
01076         {
01077             ASSERT_FLOAT_EQ(sym3x3[i], sym3x3_out[i]);
01078         }
01079     }
01080     {
01081         const double sym6x6[] =
01082         {
01083             1,  2,  3,  4,  5,  6,
01084             2,  7,  8,  9, 10, 11,
01085             3,  8, 12, 13, 14, 15,
01086             4,  9, 13, 16, 17, 18,
01087             5, 10, 14, 17, 19, 20,
01088             6, 11, 15, 18, 20, 21
01089         };
01090         m6x6d.packSquareMatrix(sym6x6);
01091         ASSERT_EQ(21, m6x6d.size());
01092         for (uavcan::uint8_t i = 0; i < 21; i++)
01093         {
01094             ASSERT_DOUBLE_EQ(double(i + 1), m6x6d[i]);
01095         }
01096 
01097         double sym6x6_out[36];
01098         m6x6d.unpackSquareMatrix(sym6x6_out);
01099 
01100         for (int i = 0; i < 36; i++)
01101         {
01102             ASSERT_DOUBLE_EQ(sym6x6[i], sym6x6_out[i]);
01103         }
01104     }
01105 }
01106 
01107 
01108 TEST(Array, FuzzySquareMatrixPacking)
01109 {
01110     Array<FloatSpec<64, CastModeSaturate>, ArrayModeDynamic, 36> m6x6d;
01111 
01112     // Diagonal matrix will be reduced to an array of length Width
01113     {
01114         float diagonal6x6[] =
01115         {
01116             1,  0,  0,  0,  0,  0,
01117             0, -2,  0,  0,  0,  0,
01118             0,  0,  3,  0,  0,  0,
01119             0,  0,  0, -4,  0,  0,
01120             0,  0,  0,  0,  5,  0,
01121             0,  0,  0,  0,  0, -6
01122         };
01123 
01124         // Some almost-zeroes
01125         diagonal6x6[1]  =  std::numeric_limits<float>::epsilon();
01126         diagonal6x6[4]  = -std::numeric_limits<float>::epsilon();
01127         diagonal6x6[34] = -std::numeric_limits<float>::epsilon();
01128 
01129         m6x6d.packSquareMatrix(diagonal6x6);
01130         ASSERT_EQ(6, m6x6d.size());
01131         ASSERT_DOUBLE_EQ(1,  m6x6d[0]);
01132         ASSERT_DOUBLE_EQ(-2, m6x6d[1]);
01133         ASSERT_DOUBLE_EQ(3,  m6x6d[2]);
01134         ASSERT_DOUBLE_EQ(-4, m6x6d[3]);
01135         ASSERT_DOUBLE_EQ(5,  m6x6d[4]);
01136         ASSERT_DOUBLE_EQ(-6, m6x6d[5]);
01137 
01138         std::vector<long double> output(36);
01139         m6x6d.unpackSquareMatrix(output);
01140 
01141         // This comparison will fail because epsilons
01142         ASSERT_FALSE(std::equal(output.begin(), output.end(), diagonal6x6));
01143 
01144         // This comparison will be ok
01145         ASSERT_TRUE(std::equal(output.begin(), output.end(), diagonal6x6, &uavcan::areClose<float, float>));
01146     }
01147 }
01148 
01149 
01150 TEST(Array, SquareMatrixPackingIntegers)
01151 {
01152     Array<IntegerSpec<30, SignednessSigned, CastModeSaturate>, ArrayModeDynamic, 9> m3x3int;
01153     {
01154         const long scalar[] =
01155         {
01156             42, 0, 0,
01157             0, 42, 0,
01158             0, 0, 42
01159         };
01160         m3x3int.packSquareMatrix(scalar);
01161         ASSERT_EQ(1, m3x3int.size());
01162         ASSERT_EQ(42, m3x3int[0]);
01163 
01164         std::vector<int> output(9);
01165         m3x3int.unpackSquareMatrix(output);
01166         ASSERT_TRUE(std::equal(output.begin(), output.end(), scalar));
01167     }
01168     {
01169         std::vector<short> diagonal(9);
01170         diagonal[0] = 6;
01171         diagonal[4] = -57;
01172         diagonal[8] = 1139;
01173         m3x3int.packSquareMatrix(diagonal);
01174         ASSERT_EQ(3, m3x3int.size());
01175         ASSERT_EQ(6, m3x3int[0]);
01176         ASSERT_EQ(-57, m3x3int[1]);
01177         ASSERT_EQ(1139, m3x3int[2]);
01178     }
01179     {
01180         std::vector<long double> full(9);
01181         for (uint8_t i = 0; i < 9; i++)
01182         {
01183             full[i] = i;
01184         }
01185         m3x3int.packSquareMatrix(full);
01186         ASSERT_EQ(9, m3x3int.size());
01187         for (uint8_t i = 0; i < 9; i++)
01188         {
01189             ASSERT_EQ(i, m3x3int[i]);
01190         }
01191     }
01192 }
01193 
01194 #if UAVCAN_EXCEPTIONS
01195 TEST(Array, SquareMatrixPackingErrors)
01196 {
01197     Array<FloatSpec<16, CastModeSaturate>, ArrayModeDynamic, 9> m3x3s;
01198 
01199     std::vector<float> ill_formed_row_major(8);
01200     ASSERT_THROW(m3x3s.packSquareMatrix(ill_formed_row_major), std::out_of_range);
01201 
01202     ASSERT_THROW(m3x3s.unpackSquareMatrix(ill_formed_row_major), std::out_of_range);
01203 }
01204 #endif
01205 
01206 TEST(Array, SquareMatrixPackingInPlace)
01207 {
01208     Array<FloatSpec<16, CastModeSaturate>, ArrayModeDynamic, 9> m3x3s;
01209 
01210     // Will do nothing - matrix is empty
01211     m3x3s.packSquareMatrix();
01212     ASSERT_TRUE(m3x3s.empty());
01213 
01214     // Will fill with zeros - matrix is empty
01215     m3x3s.unpackSquareMatrix();
01216     ASSERT_EQ(9, m3x3s.size());
01217     for (uint8_t i = 0; i < 9; i++)
01218     {
01219         ASSERT_EQ(0, m3x3s[i]);
01220     }
01221 
01222     // Fill an unpackaple matrix
01223     m3x3s.clear();
01224     m3x3s.push_back(11);
01225     m3x3s.push_back(12);
01226     m3x3s.push_back(13);
01227 
01228 #if UAVCAN_EXCEPTIONS
01229     // Shall throw - matrix is ill-formed
01230     ASSERT_THROW(m3x3s.packSquareMatrix(), std::out_of_range);
01231 #endif
01232 
01233     m3x3s.push_back(21);
01234     m3x3s.push_back(22);
01235     m3x3s.push_back(23);
01236     m3x3s.push_back(31);
01237     m3x3s.push_back(32);
01238     m3x3s.push_back(33);
01239 
01240     // Will pack/unpack successfully
01241     ASSERT_EQ(9, m3x3s.size());
01242     m3x3s.packSquareMatrix();
01243     ASSERT_EQ(9, m3x3s.size());
01244     m3x3s.unpackSquareMatrix();
01245 
01246     // Make sure it was unpacked properly
01247     ASSERT_EQ(11, m3x3s[0]);
01248     ASSERT_EQ(12, m3x3s[1]);
01249     ASSERT_EQ(13, m3x3s[2]);
01250     ASSERT_EQ(21, m3x3s[3]);
01251     ASSERT_EQ(22, m3x3s[4]);
01252     ASSERT_EQ(23, m3x3s[5]);
01253     ASSERT_EQ(31, m3x3s[6]);
01254     ASSERT_EQ(32, m3x3s[7]);
01255     ASSERT_EQ(33, m3x3s[8]);
01256 
01257     // Try again with a scalar matrix
01258     m3x3s.clear();
01259     for (unsigned i = 0; i < 9; i++)
01260     {
01261         const bool diagonal = (i == 0) || (i == 4) || (i == 8);
01262         m3x3s.push_back(diagonal ? 123 : 0);
01263     }
01264 
01265     ASSERT_EQ(9, m3x3s.size());
01266     m3x3s.packSquareMatrix();
01267     ASSERT_EQ(1, m3x3s.size());
01268     m3x3s.unpackSquareMatrix();
01269     ASSERT_EQ(9, m3x3s.size());
01270 
01271     for (uint8_t i = 0; i < 9; i++)
01272     {
01273         const bool diagonal = (i == 0) || (i == 4) || (i == 8);
01274         ASSERT_EQ((diagonal ? 123 : 0), m3x3s[i]);
01275     }
01276 
01277     // Try again with symmetric matrix
01278     /*
01279      * Full matrix:
01280      * 1 2 3
01281      * 2 4 5
01282      * 3 5 6
01283      * Compressed triangle:
01284      * 1 2 3
01285      *   4 5
01286      *     6
01287      */
01288     m3x3s.clear();
01289     m3x3s.push_back(1);
01290     m3x3s.push_back(2);
01291     m3x3s.push_back(3);
01292     m3x3s.push_back(4);
01293     m3x3s.push_back(5);
01294     m3x3s.push_back(6);
01295     // Unpacking
01296     ASSERT_EQ(6, m3x3s.size());
01297     m3x3s.unpackSquareMatrix();
01298     ASSERT_EQ(9, m3x3s.size());
01299     // Validating
01300     ASSERT_EQ(1, m3x3s[0]);
01301     ASSERT_EQ(2, m3x3s[1]);
01302     ASSERT_EQ(3, m3x3s[2]);
01303     ASSERT_EQ(2, m3x3s[3]);
01304     ASSERT_EQ(4, m3x3s[4]);
01305     ASSERT_EQ(5, m3x3s[5]);
01306     ASSERT_EQ(3, m3x3s[6]);
01307     ASSERT_EQ(5, m3x3s[7]);
01308     ASSERT_EQ(6, m3x3s[8]);
01309     // Packing back
01310     m3x3s.packSquareMatrix();
01311     ASSERT_EQ(6, m3x3s.size());
01312     // Validating
01313     ASSERT_EQ(1, m3x3s[0]);
01314     ASSERT_EQ(2, m3x3s[1]);
01315     ASSERT_EQ(3, m3x3s[2]);
01316     ASSERT_EQ(4, m3x3s[3]);
01317     ASSERT_EQ(5, m3x3s[4]);
01318     ASSERT_EQ(6, m3x3s[5]);
01319 }
01320 
01321 TEST(Array, FuzzyComparison)
01322 {
01323     typedef Array<Array<Array<FloatSpec<32, CastModeSaturate>, ArrayModeStatic, 2>,
01324                 ArrayModeStatic, 2>,
01325           ArrayModeStatic, 2> ArrayStatic32;
01326 
01327     typedef Array<Array<Array<FloatSpec<64, CastModeSaturate>, ArrayModeDynamic, 2>,
01328                 ArrayModeDynamic, 2>,
01329           ArrayModeDynamic, 2> ArrayDynamic64;
01330 
01331     ArrayStatic32 array_s32;
01332 
01333     ArrayDynamic64 array_d64;
01334 
01335     array_d64.resize(2);
01336     array_d64[0].resize(2);
01337     array_d64[1].resize(2);
01338     array_d64[0][0].resize(2);
01339     array_d64[0][1].resize(2);
01340     array_d64[1][0].resize(2);
01341     array_d64[1][1].resize(2);
01342 
01343     std::cout << "One:";
01344     uavcan::YamlStreamer<ArrayStatic32>::stream(std::cout, array_s32, 0);
01345     std::cout << std::endl << "------";
01346     uavcan::YamlStreamer<ArrayDynamic64>::stream(std::cout, array_d64, 0);
01347     std::cout << std::endl;
01348 
01349     // Both are equal right now
01350     ASSERT_TRUE(array_d64 == array_s32);
01351     ASSERT_TRUE(array_d64.isClose(array_s32));
01352     ASSERT_TRUE(array_s32.isClose(array_d64));
01353 
01354     // Slightly modifying - still close enough
01355     array_s32[0][0][0] = 123.456F + uavcan::NumericTraits<float>::epsilon() * 123.0F;
01356     array_s32[0][0][1] = uavcan::NumericTraits<float>::infinity();
01357     array_s32[0][1][0] = uavcan::NumericTraits<float>::epsilon();
01358     array_s32[0][1][1] = -uavcan::NumericTraits<float>::epsilon();
01359 
01360     array_d64[0][0][0] = 123.456;
01361     array_d64[0][0][1] = uavcan::NumericTraits<double>::infinity();
01362     array_d64[0][1][0] = -uavcan::NumericTraits<double>::epsilon();  // Note that the sign is inverted
01363     array_d64[0][1][1] = uavcan::NumericTraits<double>::epsilon();
01364 
01365     std::cout << "Two:";
01366     uavcan::YamlStreamer<ArrayStatic32>::stream(std::cout, array_s32, 0);
01367     std::cout << std::endl << "------";
01368     uavcan::YamlStreamer<ArrayDynamic64>::stream(std::cout, array_d64, 0);
01369     std::cout << std::endl;
01370 
01371     // They are close bot not exactly equal
01372     ASSERT_FALSE(array_d64 == array_s32);
01373     ASSERT_TRUE(array_d64.isClose(array_s32));
01374     ASSERT_TRUE(array_s32.isClose(array_d64));
01375 
01376     // Not close
01377     array_d64[0][0][0] = 123.457;
01378 
01379     ASSERT_FALSE(array_d64 == array_s32);
01380     ASSERT_FALSE(array_d64.isClose(array_s32));
01381     ASSERT_FALSE(array_s32.isClose(array_d64));
01382 
01383     // Values are close, but lengths differ
01384     array_d64[0][0][0] = 123.456;
01385 
01386     ASSERT_FALSE(array_d64 == array_s32);
01387     ASSERT_TRUE(array_d64.isClose(array_s32));
01388     ASSERT_TRUE(array_s32.isClose(array_d64));
01389 
01390     array_d64[0][0].resize(1);
01391 
01392     ASSERT_FALSE(array_d64 == array_s32);
01393     ASSERT_FALSE(array_d64.isClose(array_s32));
01394     ASSERT_FALSE(array_s32.isClose(array_d64));
01395 
01396     std::cout << "Three:";
01397     uavcan::YamlStreamer<ArrayStatic32>::stream(std::cout, array_s32, 0);
01398     std::cout << std::endl << "------";
01399     uavcan::YamlStreamer<ArrayDynamic64>::stream(std::cout, array_d64, 0);
01400     std::cout << std::endl;
01401 }
01402 
01403 TEST(Array, CaseConversion)
01404 {
01405     Array<IntegerSpec<8, SignednessUnsigned, CastModeTruncate>, ArrayModeDynamic, 30> str;
01406 
01407     str.convertToLowerCaseASCII();
01408     str.convertToUpperCaseASCII();
01409 
01410     ASSERT_STREQ("", str.c_str());
01411 
01412     str = "Hello World!";
01413 
01414     ASSERT_STREQ("Hello World!", str.c_str());
01415     str.convertToLowerCaseASCII();
01416     ASSERT_STREQ("hello world!", str.c_str());
01417     str.convertToUpperCaseASCII();
01418     ASSERT_STREQ("HELLO WORLD!", str.c_str());
01419 }