#include // Unit test helper class class _Assert : public Print { public: _Assert() : m_idx(0) {} size_t write(uint8_t b) { m_buf[m_idx++] = b; return 1; } int8_t compare(const char * expected) const { int8_t len_comparison = strlen(expected) - m_idx; if ( len_comparison != 0 ) return len_comparison; return memcmp(m_buf, expected, m_idx ); } int8_t compare(const __FlashStringHelper* expected) const { int8_t len_comparison = strlen_P(reinterpret_cast(expected)) - m_idx; if ( len_comparison != 0) return len_comparison; return memcmp_P(m_buf, expected, m_idx); } void reset(void) { m_idx = 0; } const char *buffer(void) { m_buf[m_idx] = 0; return m_buf; } private: char m_buf[40]; uint8_t m_idx; }; _Assert Assert; uint16_t ran=0; uint16_t passed=0; template inline Print& operator==(Print& stm, T expected) { // Hackery since we know stm is of type _Assert _Assert* assert = reinterpret_cast<_Assert*>(&stm); int8_t cmp = assert->compare(expected); // Would love to use the streaming functions here, but as we're testing them we can't Serial.print("Test "); Serial.print(++ran); if ( cmp == 0 ) { Serial.println(" passed."); passed++; } else { Serial.print(" failed. Expected ["); Serial.print(expected); Serial.print("] but was ["); Serial.print(assert->buffer()); Serial.println("]."); } assert->reset(); return stm; } class PrintableTest : public Printable { size_t printTo(Print& p) const { return p.print("printed"); } }; class NoCopyConstructorType : public Printable { public: NoCopyConstructorType() {} size_t printTo(Print& p) const { return p.print("NoCopyCtr"); } private: // Remove the copy constructor which will ensure that it can't be passed to the streaming template by value NoCopyConstructorType(NoCopyConstructorType&) {} }; void setup() { Serial.begin(115200); } void loop() { ran = passed = 0; // // Check the assertion comparators // Assert.print("one"); if ( Assert.compare("one") != 0 ) { Serial.println(F("const char* comparator failed")); while(true); } if ( Assert.compare(F("one")) != 0) { Serial.println(F("const __FlashStringHelper* comparator failed")); while(true); } Assert.reset(); const char abc[] = "abc"; // // Start by testing that simple types all function correctly // using the generic template // Assert << 1 == F("1"); Assert << 10 == F("10"); Assert << -5 == F("-5"); Assert << 'a' == F("a"); Assert << B1000 == F("8"); Assert << 1.0 == F("1.00"); Assert << 1.005 == F("1.00"); Assert << -10.23 == F("-10.23"); Assert << "abc" == F("abc"); Assert << abc == F("abc"); Assert << abc == abc; Assert << F("abc") == F("abc"); Assert << String("string") == F("string"); Assert << PrintableTest() == F("printed"); // // Check that chaining works // Assert << 1 << '2' << 0x3 << "4" << F("5") == F("12345"); // // Now check that the _BASED ones work too // Assert << _DEC(1) == F("1"); Assert << _DEC(-1) == F("-1"); Assert << _DEC((int8_t)1) == F("1"); Assert << _DEC((int8_t)-1) == F("-1"); Assert << _DEC((uint8_t)1) == F("1"); Assert << _DEC((uint8_t)-1) == F("255"); Assert << _DEC((int16_t)1) == F("1"); Assert << _DEC((int16_t)-1) == F("-1"); Assert << _DEC((uint16_t)1) == F("1"); Assert << _DEC((uint16_t)-1) == F("65535"); Assert << _DEC((int32_t)1) == F("1"); Assert << _DEC((int32_t)-1) == F("-1"); Assert << _DEC((uint32_t)1) == F("1"); Assert << _DEC((uint32_t)-1) == F("4294967295"); Assert << (uint32_t)4294967295 == F("4294967295"); Assert << _HEX(15) == F("F"); Assert << _OCT(15) == F("17"); Assert << _BIN(15) == F("1111"); Assert << _BYTE(64) == F("@"); // // _FLOAT // double v = 1.23456789012345; Assert << _FLOAT(v, 1) == F("1.2"); Assert << _FLOAT(v, 2) == F("1.23"); Assert << _FLOAT(v, 3) == F("1.235"); Assert << _FLOAT(v, 4) == F("1.2346"); Assert << _FLOAT(v, 5) == F("1.23457"); Assert << _FLOAT(v, 6) == F("1.234568"); Assert << _FLOAT(1.7649E22, 5) == F("ovf"); // This really needs work! // // endl // Assert << 1 << endl == F("1\r\n"); // // Padding helper // Assert << _PAD(10,' ') == F(" "); Assert << _PAD(4, '0') == F("0000"); Assert << _PAD(20, '=') == F("===================="); // // Width streaming // Assert << _WIDTH(1, 5) == F(" 1"); Assert << _WIDTH(-1, 5) == F(" -1"); Assert << _WIDTHZ(1, 5) == F("00001"); Assert << _WIDTHZ(128,5) == F("00128"); Assert << _WIDTHZ(_HEX(128), 5) == F("00080"); Assert << _WIDTH(abc, 5) == F(" abc"); Assert << _WIDTH("one", 5) == F(" one"); Assert << _WIDTH(F("one"), 5) == F(" one"); // Check lightweight std::is_signed on AVR platforms Assert << _WIDTH((int8_t)-1, 11) == F(" -1"); Assert << _WIDTH((uint8_t)-1, 11) == F(" 255"); Assert << _WIDTH((int16_t)-1, 11) == F(" -1"); Assert << _WIDTH((uint16_t)-1, 11) == F(" 65535"); Assert << _WIDTH((int32_t)-1, 11) == F(" -1"); Assert << _WIDTH((uint32_t)-1, 11) == F(" 4294967295"); Assert << _WIDTH((int8_t)-20, 11) == F(" -20"); Assert << _WIDTH((int16_t)-32767, 11) ==F(" -32767"); Assert << _WIDTH((int32_t)-65536, 11) ==F(" -65536"); // // Width streaming for floats // Assert << _WIDTH(_FLOAT(-9.99, 1), 11)==F(" -10.0"); Assert << _WIDTH(-3.14159, 11) == F(" -3.14"); // default nb of digits after decimal point is 2 for print(double) Assert << _WIDTH((float)12.566, 11) == F(" 12.57"); Assert << _FLOATW(-1, 2, 11) == F(" -1.00"); Assert << _FLOATW(1.23456e4, 1, 11) == F(" 12345.6"); #ifdef ARDUINO_ARCH_AVR Assert << _FLOATW(-3.1415926, 6, 11) == F(" -3.141592"); #else Assert << _FLOATW(-3.1415926, 6, 11) == F(" -3.141593"); #endif Assert << _FLOATW(-9.999999, 4, 11) == F(" -10.0000"); Assert << _FLOATW(-0.0, 1, 11) == F(" 0.0"); Assert << _FLOATW(-0.01, 1, 11) == F(" -0.0"); Assert << _FLOATW(-0.01, 0, 11) == F(" -0"); Assert << _FLOATW(1.4, 0, 11) == F(" 1"); Assert << _FLOATW(0. / 0., 2, 11) == F(" nan"); Assert << _FLOATW(-1. / 0., 2, 11) == F(" inf"); #ifndef ESP8266 // no ovf on esp8266 Assert << _FLOATW(1.e10, 2, 11) == F(" ovf"); #endif // // Stream formatter // Assert << _FMT("Hello % the time is %:%:%", "gazoodle", _WIDTHZ(1,2), _WIDTHZ(4,2), _WIDTHZ(8,2)) == F("Hello gazoodle the time is 01:04:08"); Assert << _FMT("Too many % % % for the parms", 1) == F("Too many 1 % % for the parms"); Assert << _FMT("Too few % for the parms", 1, 2) == F("Too few 1 for the parms"); Assert << _FMT("Output a \\% sign") == F("Output a % sign"); Assert << _FMT("Handle trailing \\") == F("Handle trailing "); Assert << _FMT(F("Replace % with %"), 1, 2 ) == F("Replace 1 with 2"); Assert << _FMT("Your score is %\\%", 65.3) == F("Your score is 65.30%"); Assert << _FMT("Once % a %%%", F("upon"), 't', 1, "me" ) == F("Once upon a t1me"); Assert << _FMT("This [%][%][%] grid", _WIDTH(10, 4), _WIDTH(_HEX(619),4), _WIDTH(3,4)) == F("This [ 10][ 26B][ 3] grid"); // // Make sure that all the stream operators return the stream // by chaining them together. // Assert << (int8_t)1 // Generic template << _BYTE(50) // _BYTE_CODE << _HEX(3) // _BASED << _FLOAT(4,0) // _FLOAT << _PAD(1,'5') // Padding << _WIDTH(6,1) // Width << _FMT("%",7) // Formatter << endl == F("1234567\r\n"); // // Ensure streaming template overide doesn't pass by value // Assert << NoCopyConstructorType() == F("NoCopyCtr"); // // Final report // Serial.println(); Serial.print(F("Tests complete. ")); Serial.print(ran); Serial.print(F(" tests ran. ")); Serial.print(passed); Serial.print(" passed, "); Serial.print(ran-passed); Serial.println(" failed."); delay(1000); }