]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
unit tests for PGLog::merge_log
authorLoic Dachary <loic@dachary.org>
Fri, 7 Jun 2013 13:51:28 +0000 (15:51 +0200)
committerLoic Dachary <loic@dachary.org>
Mon, 10 Jun 2013 12:08:51 +0000 (14:08 +0200)
The tests covers 100% of the LOC of merge_log. It is broken down
in 7 cases to enumerate all the situations it must address. Each case
is isolated in a independant code block where the conditions are
reproduced. Where possible and sensible to read, a code block covers
as much lines as possible. For instance:

  The log entry (1,3) deletes the object x9 but the olog entry (2,3)
  modifies it and is authoritative : the log entry (1,3) is divergent.

is the only test case covering a dozen "if" statements and half a
dozen "while/for" loops. It covers all the lines but it would be
useful to create others scenarii in the future.

Each test is made of a comment describing the test case, the
definition of the data structures to create the desired conditons, a
sequence of EXPECT_* checking that they are met, a single call to
merge_log and another sequence of EXPECT_* ( ordered to be easy to
compare with the first sequence ) checking all the desired side
effects.

The TestPGLog.cc file was untabified to improve the display of ascii
art when it is output as part of a diff.

http://tracker.ceph.com/issues/5213 refs #5213

Signed-off-by: Loic Dachary <loic@dachary.org>
src/test/osd/TestPGLog.cc

index 10d32db22e2e9ec1a94bfc0745b4747b17b2c5b3..3cb90e94a1d4670fec1e31033c068f6a1be97dbb 100644 (file)
@@ -70,7 +70,7 @@ TEST_F(PGLogTest, merge_old_entry) {
   // a clone with no non-divergent log entry is deleted
   {
     clear();
-    
+
     ObjectStore::Transaction t;
     pg_log_entry_t oe;
     pg_info_t info;
@@ -106,7 +106,7 @@ TEST_F(PGLogTest, merge_old_entry) {
   // given in argument) have the same version : do nothing and return true.
   {
     clear();
-    
+
     ObjectStore::Transaction t;
     pg_log_entry_t oe;
     pg_info_t info;
@@ -134,11 +134,11 @@ TEST_F(PGLogTest, merge_old_entry) {
   }
 
   // the new entry (from the logs) has a version that is higher than
-  // the old entry (from the log entry given in argument) : do 
+  // the old entry (from the log entry given in argument) : do
   // nothing and return false
   {
     clear();
-    
+
     ObjectStore::Transaction t;
     pg_info_t info;
     list<hobject_t> remove_snap;
@@ -203,7 +203,7 @@ TEST_F(PGLogTest, merge_old_entry) {
   // old and new are delete : do nothing and return false
   {
     clear();
-    
+
     ObjectStore::Transaction t;
     pg_log_entry_t oe;
     pg_info_t info;
@@ -237,7 +237,7 @@ TEST_F(PGLogTest, merge_old_entry) {
 
   // the new entry (from the logs) has a version that is lower than
   // the old entry (from the log entry given in argument) and
-  // new is update : 
+  // new is update :
   // if the object is not already in missing, add it
   // if the object is already in missing, revise the version it needs
   // return false
@@ -247,7 +247,7 @@ TEST_F(PGLogTest, merge_old_entry) {
       __s32 oe_op = ops[i];
 
       clear();
-    
+
       ObjectStore::Transaction t;
       pg_log_entry_t oe;
       pg_info_t info;
@@ -272,20 +272,20 @@ TEST_F(PGLogTest, merge_old_entry) {
       eversion_t old_version(0, 0);
       // if the object is not already in missing, add it
       {
-       EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log));
+        EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log));
 
-       EXPECT_TRUE(missing.is_missing(ne.soid, ne.version));
-       EXPECT_FALSE(missing.is_missing(ne.soid, old_version));
+        EXPECT_TRUE(missing.is_missing(ne.soid, ne.version));
+        EXPECT_FALSE(missing.is_missing(ne.soid, old_version));
       }
       // if the object is already in missing, revise the version it needs
       {
-       missing.revise_need(ne.soid, old_version);
-       EXPECT_TRUE(missing.is_missing(ne.soid, old_version));
+        missing.revise_need(ne.soid, old_version);
+        EXPECT_TRUE(missing.is_missing(ne.soid, old_version));
 
-       EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log));
+        EXPECT_FALSE(merge_old_entry(t, oe, info, remove_snap, dirty_log));
 
-       EXPECT_TRUE(missing.is_missing(ne.soid, ne.version));
-       EXPECT_FALSE(missing.is_missing(ne.soid, old_version));
+        EXPECT_TRUE(missing.is_missing(ne.soid, ne.version));
+        EXPECT_FALSE(missing.is_missing(ne.soid, old_version));
       }
 
       EXPECT_FALSE(dirty_log);
@@ -299,11 +299,11 @@ TEST_F(PGLogTest, merge_old_entry) {
 
   // the new entry (from the logs) has a version that is lower than
   // the old entry (from the log entry given in argument) and
-  // old is update and new is DELETE : 
+  // old is update and new is DELETE :
   // if the object is in missing, it is removed
   {
     clear();
-    
+
     ObjectStore::Transaction t;
     pg_log_entry_t oe;
     pg_info_t info;
@@ -342,7 +342,7 @@ TEST_F(PGLogTest, merge_old_entry) {
   // do nothing and return false
   {
     clear();
-    
+
     ObjectStore::Transaction t;
     pg_log_entry_t oe;
     pg_info_t info;
@@ -374,14 +374,14 @@ TEST_F(PGLogTest, merge_old_entry) {
   // the old entry (from the log entry given in argument) is not a CLONE and
   // the old entry (from the log entry given in argument) is not a DELETE and
   // the old entry prior_version is lower than the tail of the log :
-  //   add the old object to the remove_snap list and 
+  //   add the old object to the remove_snap list and
   //   add the old object to ondisklog divergent priors and
   //   set dirty_log to true and
   //   add or update the prior_version of the object to missing and
   //   return false
   {
     clear();
-    
+
     ObjectStore::Transaction t;
     pg_log_entry_t oe;
     pg_info_t info;
@@ -420,7 +420,7 @@ TEST_F(PGLogTest, merge_old_entry) {
   //   return false
   {
     clear();
-    
+
     ObjectStore::Transaction t;
     pg_log_entry_t oe;
     pg_info_t info;
@@ -457,7 +457,7 @@ TEST_F(PGLogTest, merge_old_entry) {
   //   return false
   {
     clear();
-    
+
     ObjectStore::Transaction t;
     pg_log_entry_t oe;
     pg_info_t info;
@@ -468,7 +468,7 @@ TEST_F(PGLogTest, merge_old_entry) {
     oe.soid.hash = 1;
     oe.op = pg_log_entry_t::DELETE;
     oe.prior_version = eversion_t();
-    
+
     missing.add(oe.soid, eversion_t(1,1), eversion_t());
 
     EXPECT_FALSE(dirty_log);
@@ -492,12 +492,12 @@ TEST_F(PGLogTest, merge_old_entry) {
   // the old entry (from the log entry given in argument) is not a CLONE and
   // the old entry (from the log entry given in argument) is not a DELETE and
   // the old entry prior_version is eversion_t() :
-  //   add the old object to the remove_snap list and 
+  //   add the old object to the remove_snap list and
   //   remove the prior_version of the object from missing, if any and
   //   return false
   {
     clear();
-    
+
     ObjectStore::Transaction t;
     pg_log_entry_t oe;
     pg_info_t info;
@@ -508,7 +508,7 @@ TEST_F(PGLogTest, merge_old_entry) {
     oe.soid.hash = 1;
     oe.op = pg_log_entry_t::MODIFY;
     oe.prior_version = eversion_t();
-    
+
     missing.add(oe.soid, eversion_t(1,1), eversion_t());
 
     EXPECT_FALSE(dirty_log);
@@ -530,6 +530,514 @@ TEST_F(PGLogTest, merge_old_entry) {
 
 }
 
+TEST_F(PGLogTest, merge_log) {
+  // head and tail match, last_backfill is set:
+  // noop
+  {
+    clear();
+
+    ObjectStore::Transaction t;
+    pg_log_t olog;
+    pg_info_t oinfo;
+    int fromosd = -1;
+    pg_info_t info;
+    list<hobject_t> remove_snap;
+    bool dirty_log = false;
+    bool dirty_info = false;
+    bool dirty_big_info = false;
+
+    hobject_t last_backfill(object_t("oname"), string("key"), 1, 234, 1);
+    info.last_backfill = last_backfill;
+    eversion_t stat_version(10, 1);
+    info.stats.version = stat_version;
+    log.tail = olog.tail = eversion_t(1, 1);
+    log.head = olog.head = eversion_t(2, 1);
+
+    EXPECT_FALSE(missing.have_missing());
+    EXPECT_EQ(0U, log.log.size());
+    EXPECT_EQ(0U, ondisklog.length());
+    EXPECT_EQ(stat_version, info.stats.version);
+    EXPECT_TRUE(remove_snap.empty());
+    EXPECT_TRUE(t.empty());
+    EXPECT_EQ(last_backfill, info.last_backfill);
+    EXPECT_TRUE(info.purged_snaps.empty());
+    EXPECT_FALSE(dirty_log);
+    EXPECT_FALSE(dirty_info);
+    EXPECT_FALSE(dirty_big_info);
+
+    merge_log(t, oinfo, olog, fromosd, info, remove_snap,
+              dirty_log, dirty_info, dirty_big_info);
+
+    EXPECT_FALSE(missing.have_missing());
+    EXPECT_EQ(0U, log.log.size());
+    EXPECT_EQ(0U, ondisklog.length());
+    EXPECT_EQ(stat_version, info.stats.version);
+    EXPECT_TRUE(remove_snap.empty());
+    EXPECT_TRUE(t.empty());
+    EXPECT_TRUE(info.purged_snaps.empty());
+    EXPECT_FALSE(dirty_log);
+    EXPECT_FALSE(dirty_info);
+    EXPECT_FALSE(dirty_big_info);
+  }
+
+  // head and tail match, last_backfill is not set: info.stats is
+  // copied from oinfo.stats but info.stats.reported is guaranteed to
+  // never be replaced by a lower version
+  {
+    clear();
+
+    ObjectStore::Transaction t;
+    pg_log_t olog;
+    pg_info_t oinfo;
+    int fromosd = -1;
+    pg_info_t info;
+    list<hobject_t> remove_snap;
+    bool dirty_log = false;
+    bool dirty_info = false;
+    bool dirty_big_info = false;
+
+    eversion_t stat_version(10, 1);
+    oinfo.stats.version = stat_version;
+    eversion_t stat_reported(10, 1);
+    info.stats.reported = stat_reported;
+    oinfo.stats.reported = eversion_t(1, 1);
+    log.tail = olog.tail = eversion_t(1, 1);
+    log.head = olog.head = eversion_t(2, 1);
+
+    EXPECT_FALSE(missing.have_missing());
+    EXPECT_EQ(0U, log.log.size());
+    EXPECT_EQ(0U, ondisklog.length());
+    EXPECT_EQ(eversion_t(), info.stats.version);
+    EXPECT_EQ(stat_reported, info.stats.reported);
+    EXPECT_TRUE(remove_snap.empty());
+    EXPECT_TRUE(t.empty());
+    EXPECT_TRUE(info.last_backfill.is_max());
+    EXPECT_TRUE(info.purged_snaps.empty());
+    EXPECT_FALSE(dirty_log);
+    EXPECT_FALSE(dirty_info);
+    EXPECT_FALSE(dirty_big_info);
+
+    merge_log(t, oinfo, olog, fromosd, info, remove_snap,
+              dirty_log, dirty_info, dirty_big_info);
+
+    EXPECT_FALSE(missing.have_missing());
+    EXPECT_EQ(0U, log.log.size());
+    EXPECT_EQ(0U, ondisklog.length());
+    EXPECT_EQ(stat_version, info.stats.version);
+    EXPECT_EQ(stat_reported, info.stats.reported);
+    EXPECT_TRUE(remove_snap.empty());
+    EXPECT_TRUE(t.empty());
+    EXPECT_TRUE(info.purged_snaps.empty());
+    EXPECT_FALSE(dirty_log);
+    EXPECT_FALSE(dirty_info);
+    EXPECT_FALSE(dirty_big_info);
+  }
+
+  /*         Before
+            +--------------------------+
+            |  log              olog   |
+            +--------+-------+---------+
+            |        |object |         |
+            |version | hash  | version |
+            |        |       |         |
+            |        |  x5   |  (1,1)  < tail
+            |        |       |         |
+            |        |       |         |
+       tail > (1,4)  |  x7   |         |     
+            |        |       |         |
+            |        |       |         |
+       head > (1,5)  |  x9   |  (1,5)  < head
+            |        |       |         |
+            |        |       |         |
+            +--------+-------+---------+
+
+             After
+            +-----------------
+            |  log           |
+            +--------+-------+
+            |        |object |
+            |version | hash  |
+            |        |       |
+       tail > (1,1)  |  x5   |
+            |        |       |
+            |        |       |
+            | (1,4)  |  x7   |
+            |        |       |
+            |        |       |
+       head > (1,5)  |  x9   |
+            |        |       |
+            |        |       |
+            +--------+-------+
+  */
+  {
+    clear();
+
+    ObjectStore::Transaction t;
+    pg_log_t olog;
+    pg_info_t oinfo;
+    int fromosd = -1;
+    pg_info_t info;
+    list<hobject_t> remove_snap;
+    bool dirty_log = false;
+    bool dirty_info = false;
+    bool dirty_big_info = false;
+
+    {
+      pg_log_entry_t e;
+
+      e.version = eversion_t(1, 4);
+      e.soid.hash = 0x5;
+      log.tail = e.version;
+      log.log.push_back(e);
+      e.version = eversion_t(1, 5);
+      e.soid.hash = 0x9;
+      log.log.push_back(e);
+      log.head = e.version;
+      log.index();
+
+      info.last_update = log.head;
+
+      e.version = eversion_t(1, 1);
+      e.soid.hash = 0x5;
+      olog.tail = e.version;
+      olog.log.push_back(e);
+      e.version = eversion_t(1, 5);
+      e.soid.hash = 0x9;
+      olog.log.push_back(e);
+      olog.head = e.version;
+    }
+
+    hobject_t last_backfill(object_t("oname"), string("key"), 1, 234, 1);
+    info.last_backfill = last_backfill;
+    eversion_t stat_version(10, 1);
+    info.stats.version = stat_version;
+
+    EXPECT_FALSE(missing.have_missing());
+    EXPECT_EQ(2U, log.log.size());
+    EXPECT_EQ(0U, ondisklog.length());
+    EXPECT_EQ(stat_version, info.stats.version);
+    EXPECT_TRUE(remove_snap.empty());
+    EXPECT_TRUE(t.empty());
+    EXPECT_EQ(last_backfill, info.last_backfill);
+    EXPECT_TRUE(info.purged_snaps.empty());
+    EXPECT_FALSE(dirty_log);
+    EXPECT_FALSE(dirty_info);
+    EXPECT_FALSE(dirty_big_info);
+
+    merge_log(t, oinfo, olog, fromosd, info, remove_snap,
+              dirty_log, dirty_info, dirty_big_info);
+
+    EXPECT_FALSE(missing.have_missing());
+    EXPECT_EQ(3U, log.log.size());
+    EXPECT_EQ(0U, ondisklog.length());
+    EXPECT_EQ(stat_version, info.stats.version);
+    EXPECT_TRUE(remove_snap.empty());
+    EXPECT_TRUE(t.empty());
+    EXPECT_TRUE(info.purged_snaps.empty());
+    EXPECT_TRUE(dirty_log);
+    EXPECT_TRUE(dirty_info);
+    EXPECT_TRUE(dirty_big_info);
+  }
+
+  /*        +--------------------------+
+            |  log              olog   |
+            +--------+-------+---------+
+            |        |object |         |
+            |version | hash  | version |
+            |        |       |         |
+       tail > (1,1)  |  x5   |  (1,1)  < tail
+            |        |       |         |
+            |        |       |         |
+            | (1,2)  |  x3   |  (1,2)  < lower_bound
+            |        |       |         |
+            |        |       |         |
+       head > (1,3)  |  x9   |         |
+            | DELETE |       |         |
+            |        |       |         |
+            |        |  x9   |  (2,3)  |
+            |        |       |  MODIFY |
+            |        |       |         |
+            |        |  x7   |  (2,4)  < head
+            |        |       |  DELETE |
+            +--------+-------+---------+
+
+      The log entry (1,3) deletes the object x9 but the olog entry (2,3) modifies
+      it and is authoritative : the log entry (1,3) is divergent.
+
+  */
+  {
+    clear();
+
+    ObjectStore::Transaction t;
+    pg_log_t olog;
+    pg_info_t oinfo;
+    int fromosd = -1;
+    pg_info_t info;
+    list<hobject_t> remove_snap;
+    bool dirty_log = false;
+    bool dirty_info = false;
+    bool dirty_big_info = false;
+
+    hobject_t divergent_object;
+
+    {
+      pg_log_entry_t e;
+
+      e.version = eversion_t(1, 1);
+      e.soid.hash = 0x5;
+      log.tail = e.version;
+      log.log.push_back(e);
+      e.version = eversion_t(1, 2);
+      e.soid.hash = 0x3;
+      log.log.push_back(e);
+      e.version = eversion_t(1,3);
+      e.soid.hash = 0x9;
+      divergent_object = e.soid;
+      e.op = pg_log_entry_t::DELETE;
+      log.log.push_back(e);
+      log.head = e.version;
+      log.index();
+
+      info.last_update = log.head;
+
+      e.version = eversion_t(1, 1);
+      e.soid.hash = 0x5;
+      olog.tail = e.version;
+      olog.log.push_back(e);
+      e.version = eversion_t(1, 2);
+      e.soid.hash = 0x3;
+      olog.log.push_back(e);
+      e.version = eversion_t(2, 3);
+      e.soid.hash = 0x9;
+      e.op = pg_log_entry_t::MODIFY;
+      olog.log.push_back(e);
+      e.version = eversion_t(2, 4);
+      e.soid.hash = 0x7;
+      e.op = pg_log_entry_t::DELETE;
+      olog.log.push_back(e);
+      olog.head = e.version;
+    }
+
+    snapid_t purged_snap(1);
+    {
+      oinfo.last_update = olog.head;
+      oinfo.purged_snaps.insert(purged_snap);
+    }
+
+    EXPECT_FALSE(missing.have_missing());
+    EXPECT_EQ(1U, log.objects.count(divergent_object));
+    EXPECT_EQ(3U, log.log.size());
+    EXPECT_EQ(0U, ondisklog.length());
+    EXPECT_TRUE(remove_snap.empty());
+    EXPECT_TRUE(t.empty());
+    EXPECT_EQ(log.head, info.last_update);
+    EXPECT_TRUE(info.purged_snaps.empty());
+    EXPECT_FALSE(dirty_log);
+    EXPECT_FALSE(dirty_info);
+    EXPECT_FALSE(dirty_big_info);
+
+    merge_log(t, oinfo, olog, fromosd, info, remove_snap,
+              dirty_log, dirty_info, dirty_big_info);
+
+    /* When the divergent entry is a DELETE and the authoritative
+       entry is a MODIFY, the object will be added to missing : it is
+       a verifiable side effect proving the entry was identified
+       to be divergent.
+    */
+    EXPECT_TRUE(missing.is_missing(divergent_object));
+    EXPECT_EQ(1U, log.objects.count(divergent_object));
+    EXPECT_EQ(4U, log.log.size());
+    EXPECT_EQ(0U, ondisklog.length());
+    /* DELETE entries from olog that are appended to the hed of the
+       log are also added to remove_snap.
+     */
+    EXPECT_EQ(0x7U, remove_snap.front().hash);
+    EXPECT_TRUE(t.empty());
+    EXPECT_EQ(log.head, info.last_update);
+    EXPECT_TRUE(info.purged_snaps.contains(purged_snap));
+    EXPECT_TRUE(dirty_log);
+    EXPECT_TRUE(dirty_info);
+    EXPECT_TRUE(dirty_big_info);
+  }
+
+  /*        +--------------------------+
+            |  log              olog   |
+            +--------+-------+---------+
+            |        |object |         |
+            |version | hash  | version |
+            |        |       |         |
+       tail > (1,1)  |  x5   |  (1,1)  < tail
+            |        |       |         |
+            |        |       |         |
+            | (1,4)  |  x7   |  (1,4)  < head
+            |        |       |         |
+            |        |       |         |
+       head > (1,5)  |  x9   |         |
+            |        |       |         |
+            |        |       |         |
+            +--------+-------+---------+
+
+      The head of the log entry (1,5) is divergent because it is greater than the
+      head of olog.
+
+  */
+  {
+    clear();
+
+    ObjectStore::Transaction t;
+    pg_log_t olog;
+    pg_info_t oinfo;
+    int fromosd = -1;
+    pg_info_t info;
+    list<hobject_t> remove_snap;
+    bool dirty_log = false;
+    bool dirty_info = false;
+    bool dirty_big_info = false;
+
+    {
+      pg_log_entry_t e;
+
+      e.version = eversion_t(1, 1);
+      e.soid.hash = 0x5;
+      log.tail = e.version;
+      log.log.push_back(e);
+      e.version = eversion_t(1, 4);
+      e.soid.hash = 0x7;
+      log.log.push_back(e);
+      e.version = eversion_t(1, 5);
+      e.soid.hash = 0x9;
+      log.log.push_back(e);
+      log.head = e.version;
+      log.index();
+
+      info.last_update = log.head;
+
+      e.version = eversion_t(1, 1);
+      e.soid.hash = 0x5;
+      olog.tail = e.version;
+      olog.log.push_back(e);
+      e.version = eversion_t(1, 4);
+      e.soid.hash = 0x7;
+      olog.log.push_back(e);
+      olog.head = e.version;
+    }
+
+    hobject_t last_backfill(object_t("oname"), string("key"), 1, 234, 1);
+    info.last_backfill = last_backfill;
+    eversion_t stat_version(10, 1);
+    info.stats.version = stat_version;
+
+    EXPECT_FALSE(missing.have_missing());
+    EXPECT_EQ(3U, log.log.size());
+    EXPECT_EQ(0U, ondisklog.length());
+    EXPECT_EQ(stat_version, info.stats.version);
+    EXPECT_TRUE(remove_snap.empty());
+    EXPECT_TRUE(t.empty());
+    EXPECT_EQ(last_backfill, info.last_backfill);
+    EXPECT_TRUE(info.purged_snaps.empty());
+    EXPECT_FALSE(dirty_log);
+    EXPECT_FALSE(dirty_info);
+    EXPECT_FALSE(dirty_big_info);
+
+    merge_log(t, oinfo, olog, fromosd, info, remove_snap,
+              dirty_log, dirty_info, dirty_big_info);
+
+    EXPECT_FALSE(missing.have_missing());
+    EXPECT_EQ(2U, log.log.size());
+    EXPECT_EQ(0U, ondisklog.length());
+    EXPECT_EQ(stat_version, info.stats.version);
+    EXPECT_EQ(0x9U, remove_snap.front().hash);
+    EXPECT_TRUE(t.empty());
+    EXPECT_TRUE(info.purged_snaps.empty());
+    EXPECT_TRUE(dirty_log);
+    EXPECT_TRUE(dirty_info);
+    EXPECT_TRUE(dirty_big_info);
+  }
+
+  // If our log is empty, the incoming log needs to have not been trimmed.
+  {
+    clear();
+
+    ObjectStore::Transaction t;
+    pg_log_t olog;
+    pg_info_t oinfo;
+    int fromosd = -1;
+    pg_info_t info;
+    list<hobject_t> remove_snap;
+    bool dirty_log = false;
+    bool dirty_info = false;
+    bool dirty_big_info = false;
+
+    // olog has been trimmed
+    olog.tail = eversion_t(1, 1);
+
+    ASSERT_THROW(merge_log(t, oinfo, olog, fromosd, info, remove_snap,
+                           dirty_log, dirty_info, dirty_big_info), FailedAssertion);
+  }
+
+  /*        +--------------------------+
+            |  log              olog   |
+            +--------+-------+---------+
+            |        |object |         |
+            |version | hash  | version |
+            |        |       |         |
+       tail > (1,1)  |  x5   |         |       
+            |        |       |         |
+            |        |       |         |
+       head > (1,2)  |  x3   |         |            
+            |        |       |         |
+            |        |  x9   |  (2,3)  < tail
+            |        |       |         |
+            |        |       |         |
+            |        |  x5   |  (2,4)  < head
+            |        |       |         |
+            +--------+-------+---------+
+
+      If the logs do not overlap, throw an exception.
+
+  */
+  {
+    clear();
+
+    ObjectStore::Transaction t;
+    pg_log_t olog;
+    pg_info_t oinfo;
+    int fromosd = -1;
+    pg_info_t info;
+    list<hobject_t> remove_snap;
+    bool dirty_log = false;
+    bool dirty_info = false;
+    bool dirty_big_info = false;
+
+    {
+      pg_log_entry_t e;
+
+      e.version = eversion_t(1, 1);
+      e.soid.hash = 0x5;
+      log.tail = e.version;
+      log.log.push_back(e);
+      e.version = eversion_t(1, 2);
+      e.soid.hash = 0x3;
+      log.log.push_back(e);
+      log.head = e.version;
+      log.index();
+
+      info.last_update = log.head;
+
+      e.version = eversion_t(2, 3);
+      e.soid.hash = 0x9;
+      olog.log.push_back(e);
+      e.version = eversion_t(2, 4);
+      e.soid.hash = 0x5;
+      olog.log.push_back(e);
+      olog.head = e.version;
+    }
+
+    EXPECT_THROW(merge_log(t, oinfo, olog, fromosd, info, remove_snap,
+                           dirty_log, dirty_info, dirty_big_info), FailedAssertion);
+  }
+}
+
 int main(int argc, char **argv) {
   vector<const char*> args;
   argv_to_vec(argc, (const char **)argv, args);