/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include /// Delta unit-tests. class DeltaTests : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(DeltaTests); #if ENABLE_DELTAS CPPUNIT_TEST(testDeltaSequence); CPPUNIT_TEST(testRandomDeltas); #endif CPPUNIT_TEST_SUITE_END(); void testDeltaSequence(); void testRandomDeltas(); std::vector loadPng(const char *relpath, png_uint_32& height, png_uint_32& width, png_uint_32& rowBytes) { std::ifstream file(relpath); std::stringstream buffer; buffer << file.rdbuf(); file.close(); std::vector rows = Png::decodePNG(buffer, height, width, rowBytes); std::vector output; for (png_uint_32 y = 0; y < height; ++y) { for (png_uint_32 i = 0; i < width * 4; ++i) { output.push_back(rows[y][i]); } } return output; } std::vector applyDelta( const std::vector &pixmap, png_uint_32 width, png_uint_32 height, const std::vector &delta, const std::string& testname); void assertEqual(const std::vector &a, const std::vector &b, int width, int height, const std::string& testname); }; // Quick hack for debugging std::vector DeltaTests::applyDelta( const std::vector &pixmap, png_uint_32 width, png_uint_32 height, const std::vector &zDelta, const std::string& testname) { LOK_ASSERT(zDelta.size() >= 4); LOK_ASSERT(zDelta[0] == 'D'); std::vector delta; delta.resize(1024*1024*4); // lots of extra space. size_t compSize = ZSTD_decompress( delta.data(), delta.size(), zDelta.data() + 1, zDelta.size() - 1); LOK_ASSERT_EQUAL(ZSTD_isError(compSize), (unsigned)false); delta.resize(compSize); // start with the same state. std::vector output = pixmap; LOK_ASSERT_EQUAL(output.size(), size_t(pixmap.size())); LOK_ASSERT_EQUAL(output.size(), size_t(width * height * 4)); size_t offset = 0, i; for (i = 0; i < delta.size() && offset < output.size();) { switch (delta[i]) { case 'c': // copy row. { int count = (uint8_t)(delta[i+1]); int srcRow = (uint8_t)(delta[i+2]); int destRow = (uint8_t)(delta[i+3]); // std::cout << "copy " << count <<" row(s) " << srcRow << " to " << destRow << '\n'; for (int cnt = 0; cnt < count; ++cnt) { const char *src = &pixmap[width * (srcRow + cnt) * 4]; char *dest = &output[width * (destRow + cnt) * 4]; for (size_t j = 0; j < width * 4; ++j) *dest++ = *src++; } i += 4; break; } case 'd': // new run { int destRow = (uint8_t)(delta[i+1]); int destCol = (uint8_t)(delta[i+2]); size_t length = (uint8_t)(delta[i+3]); i += 4; // std::cout << "new " << length << " at " << destCol << ", " << destRow << '\n'; LOK_ASSERT(length <= width - destCol); char *dest = &output[width * destRow * 4 + destCol * 4]; for (size_t j = 0; j < length * 4 && i < delta.size(); ++j) *dest++ = delta[i++]; break; } case 't': // termination LOK_ASSERT(i == delta.size() - 1); i++; break; default: std::cout << "Unknown delta code " << delta[i] << '\n'; LOK_ASSERT(false); break; } } LOK_ASSERT_EQUAL(delta.size(), i); return output; } void DeltaTests::assertEqual(const std::vector &a, const std::vector &b, int width, int /* height */, const std::string& testname) { LOK_ASSERT_EQUAL(a.size(), b.size()); for (size_t i = 0; i < a.size(); ++i) { if (a[i] != b[i]) { std::cout << "Differences starting at byte " << i << ' ' << (i/4 % width) << ", " << (i / (width * 4)) << ":\n"; size_t len; for (len = 0; (a[i+len] != b[i+len] || len < 8) && i + len < a.size(); ++len) { std::cout << std::hex << (int)((unsigned char)a[i+len]) << " != "; std::cout << std::hex << (int)((unsigned char)b[i+len]) << " "; if (len > 0 && (len % 16 == 0)) std::cout<< '\n'; } std::cout << " size " << len << '\n'; LOK_ASSERT(false); } } } void DeltaTests::testDeltaSequence() { constexpr auto testname = __func__; DeltaGenerator gen; png_uint_32 height, width, rowBytes; const TileWireId textWid = 1; std::vector text = DeltaTests::loadPng(TDOC "/delta-text.png", height, width, rowBytes); LOK_ASSERT(height == 256 && width == 256 && rowBytes == 256*4); LOK_ASSERT_EQUAL(size_t(256 * 256 * 4), text.size()); const TileWireId text2Wid = 2; std::vector text2 = DeltaTests::loadPng(TDOC "/delta-text2.png", height, width, rowBytes); LOK_ASSERT(height == 256 && width == 256 && rowBytes == 256*4); LOK_ASSERT_EQUAL(size_t(256 * 256 * 4), text2.size()); std::vector delta; // Stash it in the cache LOK_ASSERT(gen.createDelta( reinterpret_cast(&text[0]), 0, 0, width, height, width, height, TileLocation(1, 2, 3, 0, 1), delta, textWid, false) == false); LOK_ASSERT(delta.size() == 0); // Build a delta between text2 & textWid LOK_ASSERT(gen.createDelta( reinterpret_cast(&text2[0]), 0, 0, width, height, width, height, TileLocation(1, 2, 3, 0, 1), delta, text2Wid, false) == true); LOK_ASSERT(delta.size() > 0); // Apply it to move to the second frame std::vector reText2 = applyDelta(text, width, height, delta, testname); assertEqual(reText2, text2, width, height, testname); // Build a delta between text & text2Wid std::vector two2one; LOK_ASSERT(gen.createDelta( reinterpret_cast(&text[0]), 0, 0, width, height, width, height, TileLocation(1, 2, 3, 0, 1), two2one, textWid, false) == true); LOK_ASSERT(two2one.size() > 0); // Apply it to get back to where we started std::vector reText = applyDelta(text2, width, height, two2one, testname); assertEqual(reText, text, width, height, testname); } void DeltaTests::testRandomDeltas() { } CPPUNIT_TEST_SUITE_REGISTRATION(DeltaTests); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */