#include #include #include "Bytes.h" #include "Log.h" void testBytesDefault(const RNS::Bytes& bytes = {}) { TEST_ASSERT_FALSE(bytes); TEST_ASSERT_EQUAL_size_t(0, bytes.size()); TEST_ASSERT_NULL(bytes.data()); } RNS::Bytes ref("Test"); const RNS::Bytes& testBytesReference() { // NOTE: Can NOT return local instance as reference!!! //RNS::Bytes ref("Test"); TRACE("returning..."); return ref; } void testBytesMain() { RNS::Bytes bytes; TEST_ASSERT_FALSE(bytes); TEST_ASSERT_EQUAL_size_t(0, bytes.size()); TEST_ASSERT_TRUE(bytes.empty()); TEST_ASSERT_NULL(bytes.data()); const uint8_t prestr[] = "Hello"; const uint8_t poststr[] = " World"; const RNS::Bytes prebuf(prestr, 5); TEST_ASSERT_TRUE(prebuf); TEST_ASSERT_EQUAL_size_t(5, prebuf.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", prebuf.data(), prebuf.size()); TEST_ASSERT_FALSE(bytes == prebuf); TEST_ASSERT_TRUE(bytes != prebuf); TEST_ASSERT_TRUE(bytes < prebuf); const RNS::Bytes postbuf(poststr, 6); TEST_ASSERT_TRUE(postbuf); TEST_ASSERT_EQUAL_size_t(6, postbuf.size()); TEST_ASSERT_EQUAL_MEMORY(" World", postbuf.data(), postbuf.size()); TEST_ASSERT_FALSE(postbuf == bytes); TEST_ASSERT_TRUE(postbuf != bytes); TEST_ASSERT_TRUE(postbuf > bytes); TEST_ASSERT_FALSE(prebuf == postbuf); TEST_ASSERT_TRUE(prebuf != postbuf); if (prebuf == postbuf) { TRACE("bytess are the same"); } else { TRACE("bytess are different"); } // test string assignment bytes = "Foo"; TEST_ASSERT_EQUAL_size_t(3, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Foo", bytes.data(), bytes.size()); // test string concat with addition bytes += prebuf + postbuf; TEST_ASSERT_EQUAL_size_t(14, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("FooHello World", bytes.data(), bytes.size()); TRACEF("assign bytes: %s", bytes.toString().c_str()); TRACEF("assign prebuf: %s", prebuf.toString().c_str()); TRACEF("assign postbuf: %s", postbuf.toString().c_str()); // test string assignment with addition bytes = prebuf + postbuf; TEST_ASSERT_EQUAL_size_t(11, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", bytes.data(), bytes.size()); // test string constructor { RNS::Bytes str("Foo"); TEST_ASSERT_EQUAL_size_t(3, str.size()); TEST_ASSERT_EQUAL_MEMORY("Foo", str.data(), str.size()); } // test left in range { RNS::Bytes left(bytes.left(5)); TRACEF("left: %s", left.toString().c_str()); TEST_ASSERT_EQUAL_size_t(5, left.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", left.data(), left.size()); } // test left oob { RNS::Bytes left(bytes.left(20)); TRACEF("oob left: %s", left.toString().c_str()); TEST_ASSERT_EQUAL_size_t(11, left.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", left.data(), left.size()); } // test right in range { RNS::Bytes right(bytes.right(5)); TRACEF("right: %s", right.toString().c_str()); TEST_ASSERT_EQUAL_size_t(5, right.size()); TEST_ASSERT_EQUAL_MEMORY("World", right.data(), right.size()); } // test right oob { RNS::Bytes right(bytes.right(20)); TRACEF("oob right: %s", right.toString().c_str()); TEST_ASSERT_EQUAL_size_t(11, right.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", right.data(), right.size()); } // test mid in range { RNS::Bytes mid(bytes.mid(3, 5)); TRACEF("mid: %s", mid.toString().c_str()); TEST_ASSERT_EQUAL_size_t(5, mid.size()); TEST_ASSERT_EQUAL_MEMORY("lo Wo", mid.data(), mid.size()); } // test mid oob pos { RNS::Bytes mid(bytes.mid(20, 5)); TRACEF("oob pos mid: %s", mid.toString().c_str()); TEST_ASSERT_FALSE(mid); TEST_ASSERT_EQUAL_size_t(0, mid.size()); } // test mid oob pos { RNS::Bytes mid(bytes.mid(3, 20)); TRACEF("oob len mid: %s", mid.toString().c_str()); TEST_ASSERT_EQUAL_size_t(8, mid.size()); TEST_ASSERT_EQUAL_MEMORY("lo World", mid.data(), mid.size()); } // test mid to end variant { RNS::Bytes mid(bytes.mid(3)); TRACEF("end mid: %s", mid.toString().c_str()); TEST_ASSERT_EQUAL_size_t(8, mid.size()); TEST_ASSERT_EQUAL_MEMORY("lo World", mid.data(), mid.size()); } // test resize HEAD("TestBytes: resize", RNS::LOG_TRACE); { RNS::Bytes shrink(bytes); shrink.resize(5); TRACEF("shrink: %s", shrink.toString().c_str()); TEST_ASSERT_EQUAL_size_t(5, shrink.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", shrink.data(), shrink.size()); } // test trim HEAD("TestBytes: trim", RNS::LOG_TRACE); { RNS::Bytes bytes("Hello World"); TRACEF("orig: %s", bytes.toString().c_str()); TEST_ASSERT_EQUAL_size_t(11, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", bytes.data(), bytes.size()); bytes = bytes.left(5); TRACEF("trim: %s", bytes.toString().c_str()); TEST_ASSERT_EQUAL_size_t(5, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", bytes.data(), bytes.size()); } // test creating bytes from default { RNS::Bytes bytes; TEST_ASSERT_FALSE(bytes); TEST_ASSERT_EQUAL_size_t(0, bytes.size()); TEST_ASSERT_NULL(bytes.data()); } // test creating bytes from nullptr { RNS::Bytes bytes = nullptr; TEST_ASSERT_FALSE(bytes); TEST_ASSERT_EQUAL_size_t(0, bytes.size()); TEST_ASSERT_NULL(bytes.data()); } // test creating bytes from NONE { RNS::Bytes bytes(RNS::Bytes::NONE); TEST_ASSERT_FALSE(bytes); TEST_ASSERT_EQUAL_size_t(0, bytes.size()); TEST_ASSERT_NULL(bytes.data()); } // test creating bytes from empty { RNS::Bytes bytes = {}; TEST_ASSERT_FALSE(bytes); TEST_ASSERT_EQUAL_size_t(0, bytes.size()); TEST_ASSERT_NULL(bytes.data()); } // function default argument HEAD("TestBytes: function default argument", RNS::LOG_TRACE); testBytesDefault(); // function reference return HEAD("TestBytes: function reference return", RNS::LOG_TRACE); { RNS::Bytes test = testBytesReference(); TRACE("returned"); TEST_ASSERT_TRUE(test); TEST_ASSERT_EQUAL_size_t(4, test.size()); TEST_ASSERT_EQUAL_MEMORY("Test", test.data(), test.size()); } // TODO test comparison } void testCowBytes() { RNS::Bytes bytes1("1"); TEST_ASSERT_EQUAL_size_t(1, bytes1.size()); TEST_ASSERT_EQUAL_MEMORY("1", bytes1.data(), bytes1.size()); RNS::Bytes bytes2(bytes1); TEST_ASSERT_EQUAL_size_t(1, bytes2.size()); TEST_ASSERT_EQUAL_MEMORY("1", bytes2.data(), bytes2.size()); TEST_ASSERT_EQUAL_PTR(bytes1.data(), bytes2.data()); RNS::Bytes bytes3(bytes2); TEST_ASSERT_EQUAL_size_t(1, bytes3.size()); TEST_ASSERT_EQUAL_MEMORY("1", bytes3.data(), bytes3.size()); TEST_ASSERT_EQUAL_PTR(bytes2.data(), bytes3.data()); TRACEF("pre bytes1 ptr: %p data: %s", (void*)bytes1.data(), bytes1.toString().c_str()); TRACEF("pre bytes2 ptr: %p data: %s", (void*)bytes2.data(), bytes2.toString().c_str()); TRACEF("pre bytes3 ptr: %p data: %s", (void*)bytes3.data(), bytes3.toString().c_str()); //bytes1.append("mississippi"); //assert(bytes1.size() == 12); //assert(memcmp(bytes1.data(), "1mississippi", bytes1.size()) == 0); //assert(bytes1.data() != bytes2.data()); bytes2.append("mississippi"); TEST_ASSERT_EQUAL_size_t(12, bytes2.size()); TEST_ASSERT_EQUAL_MEMORY("1mississippi", bytes2.data(), bytes2.size()); TEST_ASSERT_NOT_EQUAL(bytes1.data(), bytes2.data()); bytes3.assign("mississippi"); TEST_ASSERT_EQUAL_size_t(11, bytes3.size()); TEST_ASSERT_EQUAL_MEMORY("mississippi", bytes3.data(), bytes3.size()); TEST_ASSERT_NOT_EQUAL(bytes2.data(), bytes3.data()); TRACEF("post bytes1 ptr: %p data: %s", (void*)bytes1.data(), bytes1.toString().c_str()); TRACEF("post bytes2 ptr: %p data: %s", (void*)bytes2.data(), bytes2.toString().c_str()); TRACEF("post bytes3 ptr: %p data: %s", (void*)bytes3.data(), bytes3.toString().c_str()); } void testBytesConversion() { { RNS::Bytes bytes("Hello World"); std::string hex = bytes.toHex(true); TRACEF("text: \"%s\" upper hex: \"%s\"", bytes.toString().c_str(), hex.c_str()); TEST_ASSERT_EQUAL_size_t(22, hex.length()); TEST_ASSERT_EQUAL_STRING("48656C6C6F20576F726C64", hex.c_str()); } { RNS::Bytes bytes("Hello World"); std::string hex = bytes.toHex(false); TRACEF("text: \"%s\" lower hex: \"%s\"", bytes.toString().c_str(), hex.c_str()); TEST_ASSERT_EQUAL_size_t(22, hex.length()); TEST_ASSERT_EQUAL_STRING("48656c6c6f20576f726c64", hex.c_str()); } { std::string hex("48656C6C6F20576F726C64"); RNS::Bytes bytes; bytes.assignHex(hex.c_str()); std::string text = bytes.toString(); TRACEF("hex: \"%s\" text: \"%s\"", hex.c_str(), text.c_str()); TEST_ASSERT_EQUAL_size_t(11, text.length()); TEST_ASSERT_EQUAL_STRING("Hello World", text.c_str()); } { std::string hex("48656c6c6f20576f726c64"); RNS::Bytes bytes; bytes.assignHex(hex.c_str()); std::string text = bytes.toString(); TRACEF("hex: \"%s\" text: \"%s\"", hex.c_str(), text.c_str()); TEST_ASSERT_EQUAL_size_t(11, text.length()); TEST_ASSERT_EQUAL_STRING("Hello World", text.c_str()); // overwrite bytes.assignHex(hex.c_str()); text = bytes.toString(); TRACEF("hex: \"%s\" text: \"%s\"", hex.c_str(), text.c_str()); TEST_ASSERT_EQUAL_size_t(11, text.length()); TEST_ASSERT_EQUAL_STRING("Hello World", text.c_str()); // apend bytes.appendHex(hex.c_str()); text = bytes.toString(); TRACEF("hex: \"%s\" text: \"%s\"", hex.c_str(), text.c_str()); TEST_ASSERT_EQUAL_size_t(22, text.length()); TEST_ASSERT_EQUAL_STRING("Hello WorldHello World", text.c_str()); } } void testBytesBase64() { // encode "Hello World" → known base64 value { RNS::Bytes bytes("Hello World"); std::string b64 = bytes.toBase64(); TRACEF("text: \"%s\" base64: \"%s\"", bytes.toString().c_str(), b64.c_str()); TEST_ASSERT_EQUAL_STRING("SGVsbG8gV29ybGQ=", b64.c_str()); } // decode known base64 string → "Hello World" { RNS::Bytes bytes; bytes.assignBase64("SGVsbG8gV29ybGQ="); std::string text = bytes.toString(); TRACEF("base64: \"SGVsbG8gV29ybGQ=\" text: \"%s\"", text.c_str()); TEST_ASSERT_EQUAL_size_t(11, text.length()); TEST_ASSERT_EQUAL_STRING("Hello World", text.c_str()); } // round-trip stability { RNS::Bytes original("Hello World"); std::string b64 = original.toBase64(); RNS::Bytes restored; restored.assignBase64(b64.c_str()); TEST_ASSERT_EQUAL_size_t(original.size(), restored.size()); TEST_ASSERT_EQUAL_MEMORY(original.data(), restored.data(), original.size()); } // empty bytes → toBase64 returns "" { RNS::Bytes empty; TEST_ASSERT_EQUAL_STRING("", empty.toBase64().c_str()); } // assignBase64 then appendBase64 produces concatenated result { RNS::Bytes bytes; bytes.assignBase64("SGVsbG8="); // "Hello" bytes.appendBase64("IFdvcmxk"); // " World" TEST_ASSERT_EQUAL_size_t(11, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", bytes.data(), bytes.size()); } } void testBytesResize() { TRACE("Testing downsize of Bytes with initial capacity"); RNS::Bytes bytes(1024); TEST_ASSERT_FALSE(bytes); TEST_ASSERT_EQUAL_size_t(0, bytes.size()); TEST_ASSERT_EQUAL_size_t(1024, bytes.capacity()); TEST_ASSERT_NOT_NULL(bytes.data()); uint8_t* buffer = bytes.writable(bytes.capacity()); TEST_ASSERT_TRUE(bytes); TEST_ASSERT_EQUAL_size_t(1024, bytes.size()); TEST_ASSERT_EQUAL_size_t(1024, bytes.capacity()); TEST_ASSERT_NOT_NULL(buffer); memcpy(buffer, "Hello World", 11); bytes.resize(11); TEST_ASSERT_EQUAL_size_t(11, bytes.size()); TEST_ASSERT_EQUAL_size_t(1024, bytes.capacity()); } void testBytesStream() { const uint8_t prestr[] = "Hello"; const uint8_t poststr[] = " World"; const RNS::Bytes prebuf(prestr, 5); TEST_ASSERT_TRUE(prebuf); TEST_ASSERT_EQUAL_size_t(5, prebuf.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", prebuf.data(), prebuf.size()); const RNS::Bytes postbuf(poststr, 6); TEST_ASSERT_TRUE(postbuf); TEST_ASSERT_EQUAL_size_t(6, postbuf.size()); TEST_ASSERT_EQUAL_MEMORY(" World", postbuf.data(), postbuf.size()); // stream into empty bytes HEAD("TestBytes: stream into empty bytes", RNS::LOG_TRACE); { RNS::Bytes strmbuf; strmbuf << prebuf << postbuf; TRACE("Results:"); TRACEF("stream prebuf: %s", prebuf.toString().c_str()); TRACEF("stream postbuf: %s", postbuf.toString().c_str()); TRACEF("stream strmbuf: %s", strmbuf.toString().c_str()); TEST_ASSERT_EQUAL_size_t(11, strmbuf.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", strmbuf.data(), strmbuf.size()); TEST_ASSERT_EQUAL_size_t(5, prebuf.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", prebuf.data(), prebuf.size()); TEST_ASSERT_EQUAL_size_t(6, postbuf.size()); TEST_ASSERT_EQUAL_MEMORY(" World", postbuf.data(), postbuf.size()); } // stream into reserved empty bytes HEAD("TestBytes: stream into reserved empty bytes", RNS::LOG_TRACE); { RNS::Bytes strmbuf(256); //strmbuf << prebuf << postbuf; strmbuf << prebuf; strmbuf << postbuf; TRACE("Results:"); //TRACEF("stream prebuf: %s", prebuf.toString().c_str()); //TRACEF("stream postbuf: %s", postbuf.toString().c_str()); //TRACEF("stream strmbuf: %s", strmbuf.toString().c_str()); TEST_ASSERT_EQUAL_size_t(11, strmbuf.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", strmbuf.data(), strmbuf.size()); TEST_ASSERT_EQUAL_size_t(5, prebuf.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", prebuf.data(), prebuf.size()); TEST_ASSERT_EQUAL_size_t(6, postbuf.size()); TEST_ASSERT_EQUAL_MEMORY(" World", postbuf.data(), postbuf.size()); } // stream into populated bytes HEAD("TestBytes: stream into populated bytes", RNS::LOG_TRACE); { RNS::Bytes strmbuf("Stream "); TEST_ASSERT_TRUE(strmbuf); TEST_ASSERT_EQUAL_size_t(7, strmbuf.size()); TEST_ASSERT_EQUAL_MEMORY("Stream ", strmbuf.data(), strmbuf.size()); strmbuf << prebuf << postbuf; TRACE("Results:"); TRACEF("stream prebuf: %s", prebuf.toString().c_str()); TRACEF("stream postbuf: %s", postbuf.toString().c_str()); TRACEF("stream strmbuf: %s", strmbuf.toString().c_str()); TEST_ASSERT_EQUAL_size_t(18, strmbuf.size()); TEST_ASSERT_EQUAL_MEMORY("Stream Hello World", strmbuf.data(), strmbuf.size()); TEST_ASSERT_EQUAL_size_t(5, prebuf.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", prebuf.data(), prebuf.size()); TEST_ASSERT_EQUAL_size_t(6, postbuf.size()); TEST_ASSERT_EQUAL_MEMORY(" World", postbuf.data(), postbuf.size()); } // stream with assignment HEAD("TestBytes: stream with assignment", RNS::LOG_TRACE); // NOTE: This test demonstrates a side-effect of the stream insertion operator!!! // When pre is volatile (non-const) then it gets updated in the process of inserting both pre and post. // This is a known and correct but perhaps unexpected and non-intuitive side-effect of assignment with stream. // To counter this side-effect, intermediate Bytes in a stream insertion chain must be const to avoid being modified. { // NOTE pre must be volatile in order for below stream with assignment to work (since it gets modified) RNS::Bytes pre("Hello"); TEST_ASSERT_EQUAL_size_t(5, pre.size()); const RNS::Bytes post(" World"); TEST_ASSERT_EQUAL_size_t(6, post.size()); RNS::Bytes strmbuf = pre << post; TRACE("Results:"); TRACEF("stream pre: %s", prebuf.toString().c_str()); TRACEF("stream post: %s", postbuf.toString().c_str()); TRACEF("stream strmbuf: %s", strmbuf.toString().c_str()); TEST_ASSERT_EQUAL_size_t(11, strmbuf.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", strmbuf.data(), strmbuf.size()); TEST_ASSERT_EQUAL_size_t(11, pre.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", pre.data(), pre.size()); TEST_ASSERT_EQUAL_size_t(6, post.size()); TEST_ASSERT_EQUAL_MEMORY(" World", post.data(), post.size()); } } void testBytesReserve() { RNS::Bytes src("Hello"); uint8_t hops = 32; { RNS::Bytes dst = src.left(1); dst << hops; dst << src.mid(2); TRACEF("non-reserve: %s", dst.toString().c_str()); TEST_ASSERT_EQUAL_size_t(5, dst.size()); TEST_ASSERT_EQUAL_MEMORY("H llo", dst.data(), dst.size()); } { RNS::Bytes dst(512); dst << src.left(1); dst << hops; dst << src.mid(2); TRACEF("reserve: %s", dst.toString().c_str()); TEST_ASSERT_EQUAL_size_t(5, dst.size()); TEST_ASSERT_EQUAL_MEMORY("H llo", dst.data(), dst.size()); } } void testFind() { HEAD("testFind:", RNS::LOG_TRACE); RNS::Bytes src("Hello"); TEST_ASSERT_EQUAL_INT(0, src.find("H")); TEST_ASSERT_EQUAL_INT(-1, src.find(1, "H")); TEST_ASSERT_EQUAL_INT(2, src.find("ll")); TEST_ASSERT_EQUAL_INT(2, src.find(1, "ll")); TEST_ASSERT_EQUAL_INT(2, src.find(2, "ll")); TEST_ASSERT_EQUAL_INT(-1, src.find(3, "ll")); TEST_ASSERT_EQUAL_INT(-1, src.find(32, "ll")); TEST_ASSERT_EQUAL_INT(-1, src.find("foo")); } void testCompare() { HEAD("testCompare:", RNS::LOG_TRACE); RNS::Bytes bytes("Hello\x20World"); //RNS::Bytes bytes("Hello World"); TRACEF("testCompare: %s", bytes.toString().c_str()); TEST_ASSERT_TRUE(bytes == RNS::Bytes("Hello World")); TEST_ASSERT_TRUE(bytes == RNS::Bytes("Hello\x20World")); TEST_ASSERT_TRUE(bytes == "Hello World"); TEST_ASSERT_TRUE(bytes == "Hello\x20World"); } void testConcat() { HEAD("testConcat:", RNS::LOG_TRACE); const RNS::Bytes bytes1("Hello"); const RNS::Bytes bytes2(" World"); { TRACE("testConcat: prefix"); RNS::Bytes bytes("Prefix "); } // CBA NOTE: Addition-assignment operator is the only case in this test that involves // an extra/intermediate object creation and should therfore be avoided. { TRACE("testConcat: prefix addition-assignment with addition"); RNS::Bytes bytes("Prefix "); bytes += bytes1 + bytes2; //TRACEF("testConcat: bytes: %s", bytes.toString().c_str()); TEST_ASSERT_EQUAL_size_t(18, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Prefix Hello World", bytes.data(), bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", bytes1.data(), bytes1.size()); TEST_ASSERT_EQUAL_MEMORY(" World", bytes2.data(), bytes2.size()); } // CBA NOTE: Extreme care must be excercised when using stream insertion since intermediate // Bytes that are volatile (non-const) will be modified in the process!!! { TRACE("testConcat: prefix stream"); RNS::Bytes bytes("Prefix "); bytes << bytes1 << bytes2; //TRACEF("testConcat: bytes: %s", bytes.toString().c_str()); TEST_ASSERT_EQUAL_size_t(18, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Prefix Hello World", bytes.data(), bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", bytes1.data(), bytes1.size()); TEST_ASSERT_EQUAL_MEMORY(" World", bytes2.data(), bytes2.size()); } { TRACE("testConcat: assignment with addition"); const RNS::Bytes bytes = bytes1 + bytes2; //TRACEF("testConcat: bytes: %s", bytes.toString().c_str()); TEST_ASSERT_EQUAL_size_t(11, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", bytes.data(), bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", bytes1.data(), bytes1.size()); TEST_ASSERT_EQUAL_MEMORY(" World", bytes2.data(), bytes2.size()); } // CBA NOTE: Following construct with assignment using stream insertion is NOT WORKING!!! // Using volatile (non-const) Bytes instead does work in this fashion, but that // has side-effects as stated above. // CBA NOTE: Also curious why Bytes constructor with capacity is being invoked here. /* { TRACE("testConcat: assignment with stream"); const RNS::Bytes bytes = bytes1 << bytes2; //TRACEF("testConcat: bytes: %s", bytes.toString().c_str()); TEST_ASSERT_EQUAL_size_t(11, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", bytes.data(), bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", bytes1.data(), bytes1.size()); TEST_ASSERT_EQUAL_MEMORY(" World", bytes2.data(), bytes2.size()); } */ { TRACE("testConcat: empty with separate stream"); RNS::Bytes bytes; bytes << bytes1; bytes << bytes2; //TRACEF("testConcat: bytes: %s", bytes.toString().c_str()); TEST_ASSERT_EQUAL_size_t(11, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", bytes.data(), bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", bytes1.data(), bytes1.size()); TEST_ASSERT_EQUAL_MEMORY(" World", bytes2.data(), bytes2.size()); } { TRACE("testConcat: construct with addition"); const RNS::Bytes bytes(bytes1 + bytes2); //TRACEF("testConcat: bytes: %s", bytes.toString().c_str()); TEST_ASSERT_EQUAL_size_t(11, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello World", bytes.data(), bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", bytes1.data(), bytes1.size()); TEST_ASSERT_EQUAL_MEMORY(" World", bytes2.data(), bytes2.size()); } } void testIndex() { HEAD("testConcat:", RNS::LOG_TRACE); const RNS::Bytes bytes("Hello"); TEST_ASSERT_EQUAL_UINT8('H', bytes[0]); TEST_ASSERT_EQUAL_UINT8('e', bytes[1]); TEST_ASSERT_EQUAL_UINT8('l', bytes[2]); TEST_ASSERT_EQUAL_UINT8('l', bytes[3]); TEST_ASSERT_EQUAL_UINT8('o', bytes[4]); } // ============================================================================ // Bytes edge case tests // ============================================================================ void test_assignHex_even_length() { // Normal case: even-length hex string RNS::Bytes bytes; bytes.assignHex("48656C6C6F"); // "Hello" TEST_ASSERT_EQUAL_size_t(5, bytes.size()); TEST_ASSERT_EQUAL_MEMORY("Hello", bytes.data(), 5); } void test_assignHex_odd_length() { // Odd-length hex should be truncated to even (drop trailing nibble) RNS::Bytes bytes; bytes.assignHex("ABC"); // 3 chars - only "AB" should be decoded TEST_ASSERT_EQUAL_size_t(1, bytes.size()); TEST_ASSERT_EQUAL_UINT8(0xAB, bytes.data()[0]); } void test_assignHex_single_char() { // Single char hex can't form a complete byte - should produce empty RNS::Bytes bytes; bytes.assignHex("A"); TEST_ASSERT_EQUAL_size_t(0, bytes.size()); } void test_assignHex_empty() { RNS::Bytes bytes; bytes.assignHex(""); TEST_ASSERT_EQUAL_size_t(0, bytes.size()); } void test_appendHex_odd_length() { // appendHex with odd length should also truncate to even RNS::Bytes bytes; bytes.assignHex("4142"); // "AB" TEST_ASSERT_EQUAL_size_t(2, bytes.size()); bytes.appendHex("434"); // 3 chars - only "43" ('C') should be appended TEST_ASSERT_EQUAL_size_t(3, bytes.size()); TEST_ASSERT_EQUAL_UINT8(0x43, bytes.data()[2]); } void test_hex_roundtrip_stability() { // toHex always produces even length, so roundtrip should be safe RNS::Bytes original("Hello World"); std::string hex = original.toHex(); TEST_ASSERT_TRUE(hex.length() % 2 == 0); // Must be even RNS::Bytes restored; restored.assignHex(hex.c_str()); TEST_ASSERT_EQUAL_size_t(original.size(), restored.size()); TEST_ASSERT_EQUAL_MEMORY(original.data(), restored.data(), original.size()); } void test_mid_large_len() { // mid() with len that extends past end should clamp RNS::Bytes bytes("Hello World"); RNS::Bytes mid = bytes.mid(3, 100); TEST_ASSERT_EQUAL_size_t(8, mid.size()); TEST_ASSERT_EQUAL_MEMORY("lo World", mid.data(), 8); // SIZE_MAX len should also clamp, not integer-overflow and crash RNS::Bytes mid2 = bytes.mid(3, SIZE_MAX); TEST_ASSERT_EQUAL_size_t(8, mid2.size()); TEST_ASSERT_EQUAL_MEMORY("lo World", mid2.data(), 8); } void test_mid_zero_size_bytes() { RNS::Bytes empty; RNS::Bytes mid = empty.mid(0, 5); TEST_ASSERT_FALSE(mid); TEST_ASSERT_EQUAL_size_t(0, mid.size()); } void setUp(void) { // set stuff up here before each test } void tearDown(void) { // clean stuff up here after each test } int runUnityTests(void) { UNITY_BEGIN(); // Suite-level setup RNS::Utilities::Memory::dump_heap_stats(); size_t pre_memory = RNS::Utilities::Memory::heap_available(); TRACEF("testBytes: pre-mem: %u", pre_memory); // Run tests RUN_TEST(testBytesMain); RUN_TEST(testCowBytes); RUN_TEST(testBytesConversion); RUN_TEST(testBytesBase64); RUN_TEST(testBytesResize); RUN_TEST(testBytesStream); RUN_TEST(testBytesReserve); RUN_TEST(testFind); RUN_TEST(testCompare); RUN_TEST(testConcat); RUN_TEST(testIndex); // Bytes edge cases RUN_TEST(test_assignHex_even_length); RUN_TEST(test_assignHex_odd_length); RUN_TEST(test_assignHex_single_char); RUN_TEST(test_assignHex_empty); RUN_TEST(test_appendHex_odd_length); RUN_TEST(test_hex_roundtrip_stability); RUN_TEST(test_mid_large_len); RUN_TEST(test_mid_zero_size_bytes); // Suite-level teardown size_t post_memory = RNS::Utilities::Memory::heap_available(); size_t diff_memory = (int)pre_memory - (int)post_memory; TRACEF("testBytes: post-mem: %u", post_memory); TRACEF("testBytes: diff-mem: %u", diff_memory); TEST_ASSERT_EQUAL_size_t(0, diff_memory); return UNITY_END(); } // For native dev-platform or for some embedded frameworks int main(void) { return runUnityTests(); } #ifdef ARDUINO // For Arduino framework void setup() { // Wait ~2 seconds before the Unity test runner // establishes connection with a board Serial interface delay(2000); runUnityTests(); } void loop() {} #endif // For ESP-IDF framework void app_main() { runUnityTests(); }