summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Deymo <deymo@google.com>2017-09-27 14:28:54 +0200
committerAlex Deymo <deymo@google.com>2017-09-28 19:36:57 +0200
commitfb3b632fdebf18a93e137159c52bc90e4812318f (patch)
tree6d0cb5374559306bbde716084cfe4b12be2ea119
parent538a75d13b8061deb99f33c5766542d286563aad (diff)
downloadbsdiff-fb3b632fdebf18a93e137159c52bc90e4812318f.tar.gz
Add unittest for the PatchWriter.
Split the unittest logic between bsdiff() function and the PatchWriter. This patch now tests individually the PatchWriter logic that writes to disk and the bsdiff() logic to create patches in the same very simple cases. We also update the Android.bp file to only pass -DBSDIFF_TARGET_UNITTEST when building for the target. Bug: 34220646 Test: ran unittests. Change-Id: I9350157ee4a0b5a617f44bb187bd7652f6d6f017
-rw-r--r--Android.bp7
-rw-r--r--Makefile1
-rw-r--r--bsdiff.cc3
-rw-r--r--bsdiff.gyp1
-rw-r--r--bsdiff_unittest.cc86
-rw-r--r--fake_patch_writer.h69
-rw-r--r--patch_writer_unittest.cc110
7 files changed, 243 insertions, 34 deletions
diff --git a/Android.bp b/Android.bp
index fb98989..e0f0f1d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -101,10 +101,10 @@ cc_test {
"bspatch_unittest.cc",
"extents_file_unittest.cc",
"extents_unittest.cc",
+ "patch_writer_unittest.cc",
"test_utils.cc",
"testrunner.cc",
],
- cflags: ["-DBSDIFF_TARGET_UNITTEST"],
static_libs: [
"libbsdiff",
"libbspatch",
@@ -113,4 +113,9 @@ cc_test {
"libdivsufsort",
"libbz",
],
+ target: {
+ android: {
+ cflags: ["-DBSDIFF_TARGET_UNITTEST"],
+ },
+ },
}
diff --git a/Makefile b/Makefile
index 2372b76..efa44c5 100644
--- a/Makefile
+++ b/Makefile
@@ -53,6 +53,7 @@ bsdiff_common_unittests := \
bspatch_unittest.cc \
extents_file_unittest.cc \
extents_unittest.cc \
+ patch_writer_unittest.cc \
test_utils.cc \
testrunner.cc
diff --git a/bsdiff.cc b/bsdiff.cc
index 7c9a2e2..67aeb6c 100644
--- a/bsdiff.cc
+++ b/bsdiff.cc
@@ -143,7 +143,8 @@ int bsdiff(const u_char* old_buf, off_t oldsize, const u_char* new_buf,
if ((I=static_cast<saidx_t*>(malloc((oldsize+1)*sizeof(saidx_t))))==NULL)
err(1,NULL);
- if (divsufsort(old_buf, I, oldsize)) err(1, "divsufsort");
+ // Note: divsufsort() fails when the passed size is 0 and old_buf is NULL.
+ if (oldsize > 0 && divsufsort(old_buf, I, oldsize)) err(1, "divsufsort");
if (I_cache)
*I_cache = I;
}
diff --git a/bsdiff.gyp b/bsdiff.gyp
index 694e9cc..f9d8ddc 100644
--- a/bsdiff.gyp
+++ b/bsdiff.gyp
@@ -128,6 +128,7 @@
'bspatch_unittest.cc',
'extents_file_unittest.cc',
'extents_unittest.cc',
+ 'patch_writer_unittest.cc',
'test_utils.cc',
],
},
diff --git a/bsdiff_unittest.cc b/bsdiff_unittest.cc
index 69e8b1c..6bd128d 100644
--- a/bsdiff_unittest.cc
+++ b/bsdiff_unittest.cc
@@ -5,58 +5,80 @@
#include "bsdiff/bsdiff.h"
#include <gtest/gtest.h>
+#include <algorithm>
+#include <random>
#include <string>
#include <vector>
-#include "bsdiff/test_utils.h"
+#include "bsdiff/fake_patch_writer.h"
-using test_utils::BsdiffPatchFile;
+namespace {
+
+// Generate deterministic random data in the output buffer. The buffer must be
+// already allocated with the desired size. The data generated depends on the
+// selected size.
+void GenerateRandomBuffer(std::vector<uint8_t>* buffer) {
+ std::minstd_rand prng(1234 + buffer->size());
+ std::generate(buffer->begin(), buffer->end(), prng);
+}
+
+} // namespace
namespace bsdiff {
class BsdiffTest : public testing::Test {
protected:
- BsdiffTest()
- : old_file_("bsdiff_oldfile.XXXXXX"),
- new_file_("bsdiff_newfile.XXXXXX"),
- patch_file_("bsdiff_patchfile.XXXXXX") {
+ BsdiffTest() = default;
+ ~BsdiffTest() override = default;
+
+ void RunBsdiff() {
+ EXPECT_EQ(0, bsdiff(old_file_.data(), old_file_.size(), new_file_.data(),
+ new_file_.size(), &patch_writer_, nullptr));
}
- ~BsdiffTest() override {}
- test_utils::ScopedTempFile old_file_;
- test_utils::ScopedTempFile new_file_;
- test_utils::ScopedTempFile patch_file_;
+ std::vector<uint8_t> old_file_;
+ std::vector<uint8_t> new_file_;
+ FakePatchWriter patch_writer_;
};
// Check that a file with no changes has a very small patch (no extra data).
TEST_F(BsdiffTest, EqualEmptyFiles) {
// Empty old and new files.
- EXPECT_EQ(0, bsdiff(old_file_.filename().c_str(),
- new_file_.filename().c_str(),
- patch_file_.filename().c_str()));
- BsdiffPatchFile patch;
- EXPECT_TRUE(patch.LoadFromFile(patch_file_.filename()));
- EXPECT_TRUE(patch.IsValid());
-
- // An empty bz2 file will have 14 bytes.
- EXPECT_EQ(14, patch.diff_len);
- EXPECT_EQ(14U, patch.extra_len);
+ RunBsdiff();
+
+ // No entries should be generated on an empty new file.
+ EXPECT_TRUE(patch_writer_.entries().empty());
}
TEST_F(BsdiffTest, EqualSmallFiles) {
std::string some_text = "Hello world!";
- std::vector<uint8_t> vec_some_text(some_text.begin(), some_text.end());
- test_utils::WriteFile(old_file_.filename(), vec_some_text);
- EXPECT_EQ(0, bsdiff(old_file_.filename().c_str(),
- new_file_.filename().c_str(),
- patch_file_.filename().c_str()));
- BsdiffPatchFile patch;
- EXPECT_TRUE(patch.LoadFromFile(patch_file_.filename()));
- EXPECT_TRUE(patch.IsValid());
-
- // An empty bz2 file will have 14 bytes.
- EXPECT_EQ(14, patch.diff_len);
- EXPECT_EQ(14U, patch.extra_len);
+ old_file_.insert(old_file_.begin(), some_text.begin(), some_text.end());
+ new_file_.insert(new_file_.begin(), some_text.begin(), some_text.end());
+ RunBsdiff();
+
+ EXPECT_EQ(1U, patch_writer_.entries().size());
+ ControlEntry entry = patch_writer_.entries()[0];
+ EXPECT_EQ(some_text.size(), entry.diff_size);
+ EXPECT_EQ(0U, entry.extra_size);
+}
+
+TEST_F(BsdiffTest, FileWithSmallErrorsTest) {
+ old_file_.resize(100);
+ GenerateRandomBuffer(&old_file_);
+ new_file_ = old_file_;
+ // Break a few bytes somewhere in the middle.
+ new_file_[20]++;
+ new_file_[30] += 2;
+ new_file_[31] += 2;
+
+ RunBsdiff();
+
+ // We expect that the result has only one entry with all in the diff stream
+ // since the two files are very similar.
+ EXPECT_EQ(1U, patch_writer_.entries().size());
+ ControlEntry entry = patch_writer_.entries()[0];
+ EXPECT_EQ(100U, entry.diff_size);
+ EXPECT_EQ(0U, entry.extra_size);
}
} // namespace bsdiff
diff --git a/fake_patch_writer.h b/fake_patch_writer.h
new file mode 100644
index 0000000..bfbc1e1
--- /dev/null
+++ b/fake_patch_writer.h
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef _BSDIFF_FAKE_PATCH_WRITER_H_
+#define _BSDIFF_FAKE_PATCH_WRITER_H_
+
+#include <gtest/gtest.h>
+#include <vector>
+
+#include "bsdiff/patch_writer_interface.h"
+
+namespace bsdiff {
+
+// A fake PatchWriterInterface derived class with easy access to the data passed
+// to it.
+class FakePatchWriter : public PatchWriterInterface {
+ public:
+ FakePatchWriter() = default;
+ ~FakePatchWriter() override = default;
+
+ // PatchWriterInterface overrides.
+ bool InitializeBuffers(const uint8_t* old_buf,
+ uint64_t old_size,
+ const uint8_t* new_buf,
+ uint64_t new_size) override {
+ EXPECT_FALSE(initialized_);
+ initialized_ = true;
+ new_size_ = new_size;
+ old_size_ = old_size;
+ return true;
+ }
+
+ bool AddControlEntry(const ControlEntry& entry) override {
+ EXPECT_TRUE(initialized_);
+ EXPECT_FALSE(closed_);
+ entries_.push_back(entry);
+ return true;
+ }
+
+ bool Close() override {
+ EXPECT_FALSE(closed_) << "Close() already called";
+ closed_ = true;
+ return true;
+ }
+
+ // Fake getter methods.
+ const std::vector<ControlEntry>& entries() const { return entries_; }
+
+ uint64_t new_size() const { return new_size_; }
+ uint64_t old_size() const { return old_size_; }
+
+ private:
+ // The list of ControlEntry passed to this class.
+ std::vector<ControlEntry> entries_;
+
+ // Whether this class was initialized.
+ bool initialized_{false};
+
+ // Whether the patch was closed.
+ bool closed_{false};
+
+ uint64_t new_size_;
+ uint64_t old_size_;
+};
+
+} // namespace bsdiff
+
+#endif // _BSDIFF_FAKE_PATCH_WRITER_H_
diff --git a/patch_writer_unittest.cc b/patch_writer_unittest.cc
new file mode 100644
index 0000000..24a8552
--- /dev/null
+++ b/patch_writer_unittest.cc
@@ -0,0 +1,110 @@
+// Copyright 2017 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "bsdiff/patch_writer.h"
+
+#include <gtest/gtest.h>
+
+#include "bsdiff/test_utils.h"
+
+namespace {
+
+// Generated with:
+// echo 'Hello World' | hexdump -v -e '" " 12/1 "0x%02x, " "\n"'
+const uint8_t kHelloWorld[] = {
+ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x0a,
+};
+
+// Compressed empty file.
+// bzip2 -9 </dev/null | hexdump -v -e '" " 7/1 "0x%02x, " "\n"'
+const uint8_t kCompressedEmpty[] = {0x42, 0x5a, 0x68, 0x39, 0x17, 0x72, 0x45,
+ 0x38, 0x50, 0x90, 0x00, 0x00, 0x00, 0x00};
+
+// echo 'Hello World' | bzip2 -9 | hexdump -v -e '" " 12/1 "0x%02x, " "\n"'
+const uint8_t kCompressedHelloWorld[] = {
+ 0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59, 0xd8, 0x72,
+ 0x01, 0x2f, 0x00, 0x00, 0x01, 0x57, 0x80, 0x00, 0x10, 0x40, 0x00, 0x00,
+ 0x40, 0x00, 0x80, 0x06, 0x04, 0x90, 0x00, 0x20, 0x00, 0x22, 0x06, 0x86,
+ 0xd4, 0x20, 0xc9, 0x88, 0xc7, 0x69, 0xe8, 0x28, 0x1f, 0x8b, 0xb9, 0x22,
+ 0x9c, 0x28, 0x48, 0x6c, 0x39, 0x00, 0x97, 0x80,
+};
+
+// Compressed a buffer of zeros of the same length of the kHelloWorld.
+// head -c `echo 'Hello World' | wc -c` /dev/zero | bzip2 -9 |
+// hexdump -v -e '" " 10/1 "0x%02x, " "\n"'
+const uint8_t kCompressedZeros[] = {
+ 0x42, 0x5a, 0x68, 0x39, 0x31, 0x41, 0x59, 0x26, 0x53, 0x59,
+ 0xf6, 0x63, 0xab, 0xde, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40,
+ 0x40, 0x20, 0x00, 0x21, 0x00, 0x82, 0x83, 0x17, 0x72, 0x45,
+ 0x38, 0x50, 0x90, 0xf6, 0x63, 0xab, 0xde,
+};
+
+} // namespace
+
+namespace bsdiff {
+
+class BsdiffPatchWriterTest : public testing::Test {
+ protected:
+ test_utils::ScopedTempFile patch_file_{"bsdiff_newfile.XXXXXX"};
+ BsdiffPatchWriter patch_writer_{patch_file_.filename()};
+};
+
+TEST_F(BsdiffPatchWriterTest, CreateEmptyPatchTest) {
+ EXPECT_TRUE(patch_writer_.InitializeBuffers(nullptr, 0, nullptr, 0));
+ EXPECT_TRUE(patch_writer_.Close());
+
+ test_utils::BsdiffPatchFile patch;
+ EXPECT_TRUE(patch.LoadFromFile(patch_file_.filename()));
+ EXPECT_TRUE(patch.IsValid());
+
+ // An empty bz2 file will have 14 bytes.
+ EXPECT_EQ(sizeof(kCompressedEmpty), static_cast<uint64_t>(patch.diff_len));
+ EXPECT_EQ(sizeof(kCompressedEmpty), patch.extra_len);
+}
+
+TEST_F(BsdiffPatchWriterTest, AllInExtraStreamTest) {
+ EXPECT_TRUE(patch_writer_.InitializeBuffers(
+ nullptr, 0, kHelloWorld, sizeof(kHelloWorld)));
+ // Write to the extra stream in two parts: first 5 bytes, then the rest.
+ EXPECT_TRUE(patch_writer_.AddControlEntry(ControlEntry(0, 5, 0)));
+ EXPECT_TRUE(patch_writer_.AddControlEntry(
+ ControlEntry(0, sizeof(kHelloWorld) - 5, 0)));
+ EXPECT_TRUE(patch_writer_.Close());
+
+ test_utils::BsdiffPatchFile patch;
+ EXPECT_TRUE(patch.LoadFromFile(patch_file_.filename()));
+ EXPECT_TRUE(patch.IsValid());
+ EXPECT_EQ(patch.bz2_diff,
+ std::vector<uint8_t>(kCompressedEmpty,
+ kCompressedEmpty + sizeof(kCompressedEmpty)));
+ EXPECT_EQ(patch.bz2_extra,
+ std::vector<uint8_t>(
+ kCompressedHelloWorld,
+ kCompressedHelloWorld + sizeof(kCompressedHelloWorld)));
+
+ EXPECT_EQ(static_cast<int64_t>(sizeof(kHelloWorld)), patch.new_file_len);
+}
+
+TEST_F(BsdiffPatchWriterTest, AllInDiffStreamTest) {
+ EXPECT_TRUE(patch_writer_.InitializeBuffers(
+ kHelloWorld, sizeof(kHelloWorld), kHelloWorld, sizeof(kHelloWorld)));
+ // Write to the extra stream in two parts: first 5 bytes, then the rest.
+ EXPECT_TRUE(
+ patch_writer_.AddControlEntry(ControlEntry(sizeof(kHelloWorld), 0, 0)));
+ EXPECT_TRUE(patch_writer_.Close());
+
+ test_utils::BsdiffPatchFile patch;
+ EXPECT_TRUE(patch.LoadFromFile(patch_file_.filename()));
+ EXPECT_TRUE(patch.IsValid());
+ EXPECT_EQ(patch.bz2_extra,
+ std::vector<uint8_t>(kCompressedEmpty,
+ kCompressedEmpty + sizeof(kCompressedEmpty)));
+ EXPECT_EQ(patch.bz2_diff,
+ std::vector<uint8_t>(kCompressedZeros,
+ kCompressedZeros + sizeof(kCompressedZeros)));
+
+ EXPECT_EQ(static_cast<int64_t>(sizeof(kHelloWorld)), patch.new_file_len);
+}
+
+} // namespace bsdiff