All of lore.kernel.org
 help / color / mirror / Atom feed
From: Loic Dachary <loic@dachary.org>
To: ceph-devel@vger.kernel.org
Subject: [PATCH] unit tests for src/common/buffer.{cc,h}
Date: Sun, 17 Feb 2013 20:38:52 +0100	[thread overview]
Message-ID: <1361129932-7466-1-git-send-email-loic@dachary.org> (raw)

Implement unit tests covering most lines of code ( > 92% ) and all
methods as show by the output of make check-coverage :
http://dachary.org/wp-uploads/2013/03/ceph-lcov/ .

The following static constructors are implemented by opaque classes
defined in buffer.cc ( buffer::raw_char, buffer::raw_posix_aligned
etc. ). Testing the implementation of these classes is done by
variations of the calls to the static constructors.

    copy(const char *c, unsigned len);
    create(unsigned len);
    claim_char(unsigned len, char *buf);
    create_malloc(unsigned len);
    claim_malloc(unsigned len, char *buf);
    create_static(unsigned len, char *buf);
    create_page_aligned(unsigned len);

The raw_mmap_pages class cannot be tested because it is commented out in
raw_posix_aligned. The raw_hack_aligned class is only tested under Cygwin.
The raw_posix_aligned class is not tested under Cygwin.

The unittest_bufferlist.sh script calls unittest_bufferlist with the
CEPH_BUFFER_TRACK=true environment variable to enable the code
tracking the memory usage. It cannot be done within the bufferlist.cc
file itself because it relies on the initialization of a global
variable  ( buffer_track_alloc ).

When raw_posix_aligned is called on DARWIN, the data is not aligned
on CEPH_PAGE_SIZE because it calls valloc(size) which is the equivalent of
memalign(sysconf(_SC_PAGESIZE),size) and not memalign(CEPH_PAGE_SIZE,size).
For this reason the alignment test is de-activated on DARWIN.

The tests are grouped in

TEST(BufferPtr, ... ) for buffer::ptr
TEST(BufferListIterator, ...) for buffer::list::iterator
TEST(BufferList, ...) for buffer::list
TEST(BufferHash, ...) for buffer::hash

and each method ( and all variations of the prototype ) are
included into a single TEST() function.

Although most aspects of the methods are tested, including exceptions
and border cases, inconsistencies are not highlighted . For
instance

    buffer::list::iterator i;
    i.advance(1);

would dereference a buffer::raw NULL pointer although

    buffer::ptr p;
    p.wasted()

asserts instead of dereferencing the buffer::raw NULL pointer. It
would be better to always assert in case a NULL pointer is about to be
used. But this is a minor inconsistency that is probably not worth a
test.

The following buffer::list methods

    ssize_t read_fd(int fd, size_t len);
    int write_fd(int fd) const;

are not fully tested because the border cases cannot be reliably
reproduced. Going thru a pointer indirection when calling the ::writev
or safe_read functions would allow the test to create mockups to synthetize
the conditions for border cases.

tracker.ceph.com/issues/4066 refs #4066

Signed-off-by: Loic Dachary <loic@dachary.org>
---
 src/Makefile.am            |    5 +-
 src/test/bufferlist.cc     | 1801 +++++++++++++++++++++++++++++++++++++++++---
 src/unittest_bufferlist.sh |   19 +
 3 files changed, 1731 insertions(+), 94 deletions(-)
 create mode 100755 src/unittest_bufferlist.sh

diff --git a/src/Makefile.am b/src/Makefile.am
index 556de51..1725588 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,7 +19,8 @@ EXTRA_DIST = \
 	libs3/libs3.spec \
 	libs3/mswin \
 	libs3/src \
-	libs3/test
+	libs3/test \
+	unittest_bufferlist.sh
 
 CLEANFILES =
 bin_PROGRAMS =
@@ -38,7 +39,7 @@ check_PROGRAMS =
 # tests to actually run on "make check"; if you need extra, non-test,
 # executables built, you need to replace this with manual assignments
 # target by target
-TESTS = $(check_PROGRAMS)
+TESTS = $(check_PROGRAMS) unittest_bufferlist.sh
 
 check-local:
 	$(srcdir)/test/encoding/check-generated.sh
diff --git a/src/test/bufferlist.cc b/src/test/bufferlist.cc
index 7abced1..6f8ba19 100644
--- a/src/test/bufferlist.cc
+++ b/src/test/bufferlist.cc
@@ -1,77 +1,1650 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library Public License for more details.
+ *
+ */
+
 #include <tr1/memory>
+#include <limits.h>
+#include <errno.h>
+#include <sys/uio.h>
 
 #include "include/buffer.h"
 #include "include/encoding.h"
+#include "common/environment.h"
 
 #include "gtest/gtest.h"
 #include "stdlib.h"
-
+#include "fcntl.h"
+#include "sys/stat.h"
 
 #define MAX_TEST 1000000
 
-TEST(BufferPtr, cmp) {
-  bufferptr empty;
-  bufferptr a("A", 1);
-  bufferptr ab("AB", 2);
-  bufferptr af("AF", 2);
-  bufferptr acc("ACC", 3);
-  EXPECT_GE(-1, empty.cmp(a));
-  EXPECT_LE(1, a.cmp(empty));
-  EXPECT_GE(-1, a.cmp(ab));
-  EXPECT_LE(1, ab.cmp(a));
-  EXPECT_EQ(0, ab.cmp(ab));
-  EXPECT_GE(-1, ab.cmp(af));
-  EXPECT_LE(1, af.cmp(ab));
-  EXPECT_GE(-1, acc.cmp(af));
-  EXPECT_LE(1, af.cmp(acc));
+TEST(Buffer, constructors) {
+  bool ceph_buffer_track = get_env_bool("CEPH_BUFFER_TRACK");
+  unsigned len = 17;
+  //
+  // buffer::create
+  //
+  if (ceph_buffer_track)
+    EXPECT_EQ(0, buffer::get_total_alloc());
+  {
+    bufferptr ptr(buffer::create(len));
+    EXPECT_EQ(len, ptr.length());
+    if (ceph_buffer_track)
+      EXPECT_EQ(len, (unsigned)buffer::get_total_alloc());
+  }
+  //
+  // buffer::claim_char
+  //
+  if (ceph_buffer_track)
+    EXPECT_EQ(0, buffer::get_total_alloc());
+  {
+    char* str = new char[len];
+    ::memset(str, 'X', len);
+    bufferptr ptr(buffer::claim_char(len, str));
+    if (ceph_buffer_track)
+      EXPECT_EQ(len, (unsigned)buffer::get_total_alloc());
+    EXPECT_EQ(len, ptr.length());
+    EXPECT_EQ(str, ptr.c_str());
+    bufferptr clone = ptr.clone();
+    EXPECT_EQ(0, ::memcmp(clone.c_str(), ptr.c_str(), len));
+  }
+  //
+  // buffer::create_static
+  //
+  if (ceph_buffer_track)
+    EXPECT_EQ(0, buffer::get_total_alloc());
+  {
+    char* str = new char[len];
+    bufferptr ptr(buffer::create_static(len, str));
+    if (ceph_buffer_track)
+      EXPECT_EQ(0, buffer::get_total_alloc());
+    EXPECT_EQ(len, ptr.length());
+    EXPECT_EQ(str, ptr.c_str());
+    delete [] str;
+  }
+  //
+  // buffer::create_malloc
+  //
+  if (ceph_buffer_track)
+    EXPECT_EQ(0, buffer::get_total_alloc());
+  {
+    bufferptr ptr(buffer::create_malloc(len));
+    if (ceph_buffer_track)
+      EXPECT_EQ(len, (unsigned)buffer::get_total_alloc());
+    EXPECT_EQ(len, ptr.length());
+    EXPECT_THROW(buffer::create_malloc((unsigned)ULLONG_MAX), buffer::bad_alloc);
+  }
+  //
+  // buffer::claim_malloc
+  //
+  if (ceph_buffer_track)
+    EXPECT_EQ(0, buffer::get_total_alloc());
+  {
+    char* str = (char*)malloc(len);
+    ::memset(str, 'X', len);
+    bufferptr ptr(buffer::claim_malloc(len, str));
+    if (ceph_buffer_track)
+      EXPECT_EQ(len, (unsigned)buffer::get_total_alloc());
+    EXPECT_EQ(len, ptr.length());
+    EXPECT_EQ(str, ptr.c_str());
+    bufferptr clone = ptr.clone();
+    EXPECT_EQ(0, ::memcmp(clone.c_str(), ptr.c_str(), len));
+  }
+  //
+  // buffer::copy
+  //
+  if (ceph_buffer_track)
+    EXPECT_EQ(0, buffer::get_total_alloc());
+  {
+    const std::string expected(len, 'X');
+    bufferptr ptr(buffer::copy(expected.c_str(), expected.size()));
+    if (ceph_buffer_track)
+      EXPECT_EQ(len, (unsigned)buffer::get_total_alloc());
+    EXPECT_NE(expected.c_str(), ptr.c_str());
+    EXPECT_EQ(0, ::memcmp(expected.c_str(), ptr.c_str(), len));
+  }
+  //
+  // buffer::create_page_aligned
+  //
+  if (ceph_buffer_track)
+    EXPECT_EQ(0, buffer::get_total_alloc());
+  {
+    bufferptr ptr(buffer::create_page_aligned(len));
+    ::memset(ptr.c_str(), 'X', len);
+    if (ceph_buffer_track)
+      EXPECT_EQ(len, (unsigned)buffer::get_total_alloc());
+    EXPECT_THROW(buffer::create_page_aligned((unsigned)ULLONG_MAX), buffer::bad_alloc);
+#ifndef DARWIN
+    ASSERT_TRUE(ptr.is_page_aligned());
+#endif // DARWIN 
+    bufferptr clone = ptr.clone();
+    EXPECT_EQ(0, ::memcmp(clone.c_str(), ptr.c_str(), len));
+  }
+  if (ceph_buffer_track)
+    EXPECT_EQ(0, buffer::get_total_alloc());
+}
+
+TEST(BufferRaw, ostream) {
+  bufferptr ptr(1);
+  std::ostringstream stream;
+  stream << *ptr.get_raw();
+  EXPECT_GT(stream.str().size(), stream.str().find("buffer::raw("));
+  EXPECT_GT(stream.str().size(), stream.str().find("len 1 nref 1)"));
+}
+
+//                                     
+// +-----------+                +-----+
+// |           |                |     |
+// |  offset   +----------------+     |
+// |           |                |     |
+// |  length   +----            |     |
+// |           |    \-------    |     |
+// +-----------+            \---+     |
+// |   ptr     |                +-----+
+// +-----------+                | raw |
+//                              +-----+
+//
+TEST(BufferPtr, constructors) {
+  unsigned len = 17;
+  //
+  // ptr::ptr()
+  //
+  {
+    buffer::ptr ptr;
+    EXPECT_FALSE(ptr.have_raw());
+    EXPECT_EQ((unsigned)0, ptr.offset());
+    EXPECT_EQ((unsigned)0, ptr.length());
+  }
+  //
+  // ptr::ptr(raw *r)
+  //
+  {
+    bufferptr ptr(buffer::create(len));
+    EXPECT_TRUE(ptr.have_raw());
+    EXPECT_EQ((unsigned)0, ptr.offset());
+    EXPECT_EQ(len, ptr.length());
+    EXPECT_EQ(ptr.raw_length(), ptr.length());
+    EXPECT_EQ(1, ptr.raw_nref());
+  }
+  //
+  // ptr::ptr(unsigned l)
+  //
+  {
+    bufferptr ptr(len);
+    EXPECT_TRUE(ptr.have_raw());
+    EXPECT_EQ((unsigned)0, ptr.offset());
+    EXPECT_EQ(len, ptr.length());
+    EXPECT_EQ(1, ptr.raw_nref());
+  }
+  //
+  // ptr(const char *d, unsigned l)
+  //
+  {
+    const std::string str(len, 'X');
+    bufferptr ptr(str.c_str(), len);
+    EXPECT_TRUE(ptr.have_raw());
+    EXPECT_EQ((unsigned)0, ptr.offset());
+    EXPECT_EQ(len, ptr.length());
+    EXPECT_EQ(1, ptr.raw_nref());
+    EXPECT_EQ(0, ::memcmp(str.c_str(), ptr.c_str(), len));
+  }
+  //
+  // ptr(const ptr& p)
+  //
+  {
+    const std::string str(len, 'X');
+    bufferptr original(str.c_str(), len);
+    bufferptr ptr(original);
+    EXPECT_TRUE(ptr.have_raw());
+    EXPECT_EQ(original.get_raw(), ptr.get_raw());
+    EXPECT_EQ(2, ptr.raw_nref());
+    EXPECT_EQ(0, ::memcmp(original.c_str(), ptr.c_str(), len));
+  }
+  //
+  // ptr(const ptr& p, unsigned o, unsigned l)
+  //
+  {
+    const std::string str(len, 'X');
+    bufferptr original(str.c_str(), len);
+    bufferptr ptr(original, 0, 0);
+    EXPECT_TRUE(ptr.have_raw());
+    EXPECT_EQ(original.get_raw(), ptr.get_raw());
+    EXPECT_EQ(2, ptr.raw_nref());
+    EXPECT_EQ(0, ::memcmp(original.c_str(), ptr.c_str(), len));
+    EXPECT_THROW(bufferptr(original, 0, original.length() + 1), FailedAssertion);
+    EXPECT_THROW(bufferptr(bufferptr(), 0, 0), FailedAssertion);
+  }
+}
+
+TEST(BufferPtr, assignment) {
+  unsigned len = 17;
+  //
+  // override a bufferptr set with the same raw
+  //
+  {
+    bufferptr original(len);
+    bufferptr same_raw(original.get_raw());
+    unsigned offset = 5;
+    unsigned length = len - offset;
+    original.set_offset(offset);
+    original.set_length(length);
+    same_raw = original;
+    ASSERT_EQ(2, original.raw_nref());
+    ASSERT_EQ(same_raw.get_raw(), original.get_raw());
+    ASSERT_EQ(same_raw.offset(), original.offset());
+    ASSERT_EQ(same_raw.length(), original.length());
+  }
+
+  //
+  // self assignment is a noop
+  //
+  {
+    bufferptr original(len);
+    original = original;
+    ASSERT_EQ(1, original.raw_nref());
+    ASSERT_EQ((unsigned)0, original.offset());
+    ASSERT_EQ(len, original.length());
+  }
+  
+  //
+  // a copy points to the same raw
+  //
+  {
+    bufferptr original(len);
+    unsigned offset = 5;
+    unsigned length = len - offset;
+    original.set_offset(offset);
+    original.set_length(length);
+    bufferptr ptr;
+    ptr = original;
+    ASSERT_EQ(2, original.raw_nref());
+    ASSERT_EQ(ptr.get_raw(), original.get_raw());
+    ASSERT_EQ(original.offset(), ptr.offset());
+    ASSERT_EQ(original.length(), ptr.length());
+  }
+}
+
+TEST(BufferPtr, clone) {
+  unsigned len = 17;
+  bufferptr ptr(len);
+  ::memset(ptr.c_str(), 'X', len);
+  bufferptr clone = ptr.clone();
+  EXPECT_EQ(0, ::memcmp(clone.c_str(), ptr.c_str(), len));
+}
+
+TEST(BufferPtr, swap) {
+  unsigned len = 17;
+
+  bufferptr ptr1(len);
+  ::memset(ptr1.c_str(), 'X', len);
+  unsigned ptr1_offset = 4;
+  ptr1.set_offset(ptr1_offset);
+  unsigned ptr1_length = 3;
+  ptr1.set_length(ptr1_length);
+
+  bufferptr ptr2(len);
+  ::memset(ptr2.c_str(), 'Y', len);
+  unsigned ptr2_offset = 5;
+  ptr2.set_offset(ptr2_offset);
+  unsigned ptr2_length = 7;
+  ptr2.set_length(ptr2_length);
+
+  ptr1.swap(ptr2);
+
+  EXPECT_EQ(ptr2_length, ptr1.length());
+  EXPECT_EQ(ptr2_offset, ptr1.offset());
+  EXPECT_EQ('Y', ptr1[0]);
+
+  EXPECT_EQ(ptr1_length, ptr2.length());
+  EXPECT_EQ(ptr1_offset, ptr2.offset());
+  EXPECT_EQ('X', ptr2[0]);
+}
+
+TEST(BufferPtr, release) {
+  unsigned len = 17;
+
+  bufferptr ptr1(len);
+  {
+    bufferptr ptr2(ptr1);
+    EXPECT_EQ(2, ptr1.raw_nref());
+  }
+  EXPECT_EQ(1, ptr1.raw_nref());
+}
+
+TEST(BufferPtr, have_raw) {
+  {
+    bufferptr ptr;
+    EXPECT_FALSE(ptr.have_raw());
+  }
+  {
+    bufferptr ptr(1);
+    EXPECT_TRUE(ptr.have_raw());
+  }
+}
+
+TEST(BufferPtr, at_buffer_head) {
+  bufferptr ptr(2);
+  EXPECT_TRUE(ptr.at_buffer_head());
+  ptr.set_offset(1);
+  EXPECT_FALSE(ptr.at_buffer_head());
+}
+
+TEST(BufferPtr, at_buffer_tail) {
+  bufferptr ptr(2);
+  EXPECT_TRUE(ptr.at_buffer_tail());
+  ptr.set_length(1);
+  EXPECT_FALSE(ptr.at_buffer_tail());
+}
+
+TEST(BufferPtr, is_n_page_sized) {
+  {
+    bufferptr ptr(CEPH_PAGE_SIZE);
+    EXPECT_TRUE(ptr.is_n_page_sized());
+  }
+  {
+    bufferptr ptr(1);
+    EXPECT_FALSE(ptr.is_n_page_sized());
+  }
+}
+
+TEST(BufferPtr, accessors) {
+  unsigned len = 17;
+  bufferptr ptr(len);
+  ptr.c_str()[0] = 'X';
+  ptr[1] = 'Y';
+  const bufferptr const_ptr(ptr);
+
+  EXPECT_NE((void*)NULL, (void*)ptr.get_raw());
+  EXPECT_EQ('X', ptr.c_str()[0]);
+  {
+    bufferptr ptr;
+    EXPECT_THROW(ptr.c_str(), FailedAssertion);
+    EXPECT_THROW(ptr[0], FailedAssertion);
+  }
+  EXPECT_EQ('X', const_ptr.c_str()[0]);
+  {
+    const bufferptr const_ptr;
+    EXPECT_THROW(const_ptr.c_str(), FailedAssertion);
+    EXPECT_THROW(const_ptr[0], FailedAssertion);
+  }
+  EXPECT_EQ(len, const_ptr.length());
+  EXPECT_EQ((unsigned)0, const_ptr.offset());
+  EXPECT_EQ((unsigned)0, const_ptr.start());
+  EXPECT_EQ(len, const_ptr.end());
+  EXPECT_EQ(len, const_ptr.end());
+  {
+    bufferptr ptr(len);
+    unsigned unused = 1;
+    ptr.set_length(ptr.length() - unused);
+    EXPECT_EQ(unused, ptr.unused_tail_length());
+  }
+  {
+    bufferptr ptr;
+    EXPECT_EQ((unsigned)0, ptr.unused_tail_length());
+  }
+  EXPECT_THROW(ptr[len], FailedAssertion);
+  EXPECT_THROW(const_ptr[len], FailedAssertion);
+  {
+    const bufferptr const_ptr;
+    EXPECT_THROW(const_ptr.raw_c_str(), FailedAssertion);
+    EXPECT_THROW(const_ptr.raw_length(), FailedAssertion);
+    EXPECT_THROW(const_ptr.raw_nref(), FailedAssertion);
+  }
+  EXPECT_NE((const char *)NULL, const_ptr.raw_c_str());
+  EXPECT_EQ(len, const_ptr.raw_length());
+  EXPECT_EQ(2, const_ptr.raw_nref());
+  {
+    bufferptr ptr(len);
+    unsigned wasted = 1;
+    ptr.set_length(ptr.length() - wasted * 2);
+    ptr.set_offset(wasted);
+    EXPECT_EQ(wasted * 2, ptr.wasted());
+  }
+}
+
+TEST(BufferPtr, cmp) {
+  bufferptr empty;
+  bufferptr a("A", 1);
+  bufferptr ab("AB", 2);
+  bufferptr af("AF", 2);
+  bufferptr acc("ACC", 3);
+  EXPECT_GE(-1, empty.cmp(a));
+  EXPECT_LE(1, a.cmp(empty));
+  EXPECT_GE(-1, a.cmp(ab));
+  EXPECT_LE(1, ab.cmp(a));
+  EXPECT_EQ(0, ab.cmp(ab));
+  EXPECT_GE(-1, ab.cmp(af));
+  EXPECT_LE(1, af.cmp(ab));
+  EXPECT_GE(-1, acc.cmp(af));
+  EXPECT_LE(1, af.cmp(acc));
+}
+
+TEST(BufferPtr, is_zero) {
+  char str[2] = { '\0', 'X' };
+  {
+    const bufferptr ptr(buffer::create_static(2, str));
+    EXPECT_FALSE(ptr.is_zero());
+  }
+  {
+    const bufferptr ptr(buffer::create_static(1, str));
+    EXPECT_TRUE(ptr.is_zero());
+  }
+}
+
+TEST(BufferPtr, copy_out) {
+  {
+    const bufferptr ptr;
+    EXPECT_THROW(ptr.copy_out((unsigned)0, (unsigned)0, NULL), FailedAssertion);
+  }
+  {
+    char in[] = "ABC";
+    const bufferptr ptr(buffer::create_static(strlen(in), in));
+    EXPECT_THROW(ptr.copy_out((unsigned)0, strlen(in) + 1, NULL), buffer::end_of_buffer);
+    EXPECT_THROW(ptr.copy_out(strlen(in) + 1, (unsigned)0, NULL), buffer::end_of_buffer);
+    char out[1] = { 'X' };
+    ptr.copy_out((unsigned)1, (unsigned)1, out);
+    EXPECT_EQ('B', out[0]);
+  }
+}
+
+TEST(BufferPtr, copy_in) {
+  {
+    bufferptr ptr;
+    EXPECT_THROW(ptr.copy_in((unsigned)0, (unsigned)0, NULL), FailedAssertion);
+  }
+  {
+    char in[] = "ABCD";
+    bufferptr ptr(2);
+    EXPECT_THROW(ptr.copy_in((unsigned)0, strlen(in) + 1, NULL), FailedAssertion);
+    EXPECT_THROW(ptr.copy_in(strlen(in) + 1, (unsigned)0, NULL), FailedAssertion);
+    ptr.copy_in((unsigned)0, (unsigned)2, in);
+    EXPECT_EQ(in[0], ptr[0]);
+    EXPECT_EQ(in[1], ptr[1]);
+  }
+}
+
+TEST(BufferPtr, append) {
+  {
+    bufferptr ptr;
+    EXPECT_THROW(ptr.append('A'), FailedAssertion);
+    EXPECT_THROW(ptr.append("B", (unsigned)1), FailedAssertion);
+  }
+  {
+    bufferptr ptr(2);
+    EXPECT_THROW(ptr.append('A'), FailedAssertion);
+    EXPECT_THROW(ptr.append("B", (unsigned)1), FailedAssertion);
+    ptr.set_length(0);
+    ptr.append('A');
+    EXPECT_EQ((unsigned)1, ptr.length());
+    EXPECT_EQ('A', ptr[0]);
+    ptr.append("B", (unsigned)1);
+    EXPECT_EQ((unsigned)2, ptr.length());
+    EXPECT_EQ('B', ptr[1]);
+  }
+}
+
+TEST(BufferPtr, zero) {
+  char str[] = "XXXX";
+  bufferptr ptr(buffer::create_static(strlen(str), str));
+  EXPECT_THROW(ptr.zero(ptr.length() + 1, 0), FailedAssertion);
+  ptr.zero(1, 1);
+  EXPECT_EQ('X', ptr[0]);
+  EXPECT_EQ('\0', ptr[1]);
+  EXPECT_EQ('X', ptr[2]);
+  ptr.zero();
+  EXPECT_EQ('\0', ptr[0]);
+}
+
+TEST(BufferPtr, ostream) {
+  {
+    bufferptr ptr;
+    std::ostringstream stream;
+    stream << ptr;
+    EXPECT_GT(stream.str().size(), stream.str().find("buffer:ptr(0~0 no raw"));
+  }
+  {
+    char str[] = "XXXX";
+    bufferptr ptr(buffer::create_static(strlen(str), str));
+    std::ostringstream stream;
+    stream << ptr;
+    EXPECT_GT(stream.str().size(), stream.str().find("len 4 nref 1)"));
+  }  
+}
+
+//
+//                                             +---------+
+//                                             | +-----+ |
+//    list              ptr                    | |     | |
+// +----------+       +-----+                  | |     | |
+// | append_  >------->     >-------------------->     | |
+// |  buffer  |       +-----+                  | |     | |
+// +----------+                        ptr     | |     | |
+// |   _len   |      list            +-----+   | |     | |
+// +----------+    +------+     ,--->+     >----->     | |
+// | _buffers >---->      >-----     +-----+   | +-----+ |
+// +----------+    +----^-+     \      ptr     |   raw   |
+// |  last_p  |        /         `-->+-----+   | +-----+ |
+// +--------+-+       /              +     >----->     | |
+//          |       ,-          ,--->+-----+   | |     | |
+//          |      /        ,---               | |     | |
+//          |     /     ,---                   | |     | |
+//        +-v--+-^--+--^+-------+              | |     | |
+//        | bl | ls | p | p_off >--------------->|     | |
+//        +----+----+-----+-----+              | +-----+ |
+//        |               | off >------------->|   raw   |
+//        +---------------+-----+              |         |
+//              iterator                       +---------+
+//
+TEST(BufferListIterator, constructors) {
+  //
+  // iterator()
+  //
+  {
+    buffer::list::iterator i;
+    EXPECT_EQ((unsigned)0, i.get_off());
+  }
+
+  //
+  // iterator(list *l, unsigned o=0)
+  //
+  {
+    bufferlist bl;
+    bl.append("ABC", 3);
+
+    {
+      bufferlist::iterator i(&bl);
+      EXPECT_EQ((unsigned)0, i.get_off());
+      EXPECT_EQ('A', *i);
+    }
+    {
+      bufferlist::iterator i(&bl, 1);
+      EXPECT_EQ('B', *i);
+      EXPECT_EQ((unsigned)2, i.get_remaining());
+    }
+  }
+
+  //
+  // iterator(list *l, unsigned o, std::list<ptr>::iterator ip, unsigned po)
+  // not tested because of http://tracker.ceph.com/issues/4101
+
+  //
+  // iterator(const iterator& other)
+  //
+  {
+    bufferlist bl;
+    bl.append("ABC", 3);
+    bufferlist::iterator i(&bl, 1);
+    bufferlist::iterator j(i);
+    EXPECT_EQ(*i, *j);
+    ++j;
+    EXPECT_NE(*i, *j);
+    EXPECT_EQ('B', *i);
+    EXPECT_EQ('C', *j);
+    bl.c_str()[1] = 'X';
+    j.advance(-1);
+    EXPECT_EQ('X', *j);
+  }
+}
+
+TEST(BufferListIterator, operator_equal) {
+  bufferlist bl;
+  bl.append("ABC", 3);
+  bufferlist::iterator i(&bl, 1);
+
+  i = i;
+  EXPECT_EQ('B', *i);
+  bufferlist::iterator j;
+  j = i;
+  EXPECT_EQ('B', *j);
+}
+
+TEST(BufferListIterator, get_off) {
+  bufferlist bl;
+  bl.append("ABC", 3);
+  bufferlist::iterator i(&bl, 1);
+  EXPECT_EQ((unsigned)1, i.get_off());
+}
+
+TEST(BufferListIterator, get_remaining) {
+  bufferlist bl;
+  bl.append("ABC", 3);
+  bufferlist::iterator i(&bl, 1);
+  EXPECT_EQ((unsigned)2, i.get_remaining());
+}
+
+TEST(BufferListIterator, end) {
+  bufferlist bl;
+  {
+    bufferlist::iterator i(&bl);
+    EXPECT_TRUE(i.end());
+  }
+  bl.append("ABC", 3);
+  {
+    bufferlist::iterator i(&bl);
+    EXPECT_FALSE(i.end());
+  }
+}
+
+TEST(BufferListIterator, advance) {
+  bufferlist bl;
+  const std::string one("ABC");
+  bl.append(bufferptr(one.c_str(), one.size()));
+  const std::string two("DEF");
+  bl.append(bufferptr(two.c_str(), two.size()));
+
+  {
+    bufferlist::iterator i(&bl);
+    EXPECT_THROW(i.advance(200), buffer::end_of_buffer);
+  }
+  {
+    bufferlist::iterator i(&bl);
+    EXPECT_THROW(i.advance(-1), buffer::end_of_buffer);
+  }
+  {
+    bufferlist::iterator i(&bl);
+    EXPECT_EQ('A', *i);
+    i.advance(1);
+    EXPECT_EQ('B', *i);
+    i.advance(3);
+    EXPECT_EQ('E', *i);
+    i.advance(-3);
+    EXPECT_EQ('B', *i);
+    i.advance(-1);
+    EXPECT_EQ('A', *i);
+  }
+}
+
+TEST(BufferListIterator, seek) {
+  bufferlist bl;
+  bl.append("ABC", 3);
+  bufferlist::iterator i(&bl, 1);
+  EXPECT_EQ('B', *i);
+  i.seek(2);
+  EXPECT_EQ('C', *i);
+}
+
+TEST(BufferListIterator, operator_star) {
+  bufferlist bl;
+  {
+    bufferlist::iterator i(&bl);
+    EXPECT_THROW(*i, buffer::end_of_buffer);
+  }
+  bl.append("ABC", 3);
+  {
+    bufferlist::iterator i(&bl);
+    EXPECT_EQ('A', *i);
+    EXPECT_THROW(i.advance(200), buffer::end_of_buffer);
+    EXPECT_THROW(*i, buffer::end_of_buffer);
+  }
+}
+
+TEST(BufferListIterator, operator_plus_plus) {
+  bufferlist bl;
+  {
+    bufferlist::iterator i(&bl);
+    EXPECT_THROW(++i, buffer::end_of_buffer);
+  }
+  bl.append("ABC", 3);
+  {
+    bufferlist::iterator i(&bl);
+    ++i;
+    EXPECT_EQ('B', *i);
+  }  
+}
+
+TEST(BufferListIterator, get_current_ptr) {
+  bufferlist bl;
+  {
+    bufferlist::iterator i(&bl);
+    EXPECT_THROW(++i, buffer::end_of_buffer);
+  }
+  bl.append("ABC", 3);
+  {
+    bufferlist::iterator i(&bl, 1);
+    const buffer::ptr ptr = i.get_current_ptr();
+    EXPECT_EQ('B', ptr[0]);
+    EXPECT_EQ((unsigned)1, ptr.offset());
+    EXPECT_EQ((unsigned)2, ptr.length());
+  }  
+}
+
+TEST(BufferListIterator, copy) {
+  bufferlist bl;
+  const char *expected = "ABC";
+  bl.append(expected, 3);
+  //
+  // void copy(unsigned len, char *dest);
+  //
+  {
+    char* copy = (char*)malloc(3);
+    ::memset(copy, 'X', 3);
+    bufferlist::iterator i(&bl);
+    //
+    // demonstrates that it seeks back to offset if p == ls->end()
+    //
+    EXPECT_THROW(i.advance(200), buffer::end_of_buffer); 
+    i.copy(2, copy);
+    EXPECT_EQ(0, ::memcmp(copy, expected, 2));
+    EXPECT_EQ('X', copy[2]);
+    i.seek(0);
+    i.copy(3, copy);
+    EXPECT_EQ(0, ::memcmp(copy, expected, 3));
+  }
+  //
+  // void buffer::list::iterator::copy(unsigned len, ptr &dest)
+  //
+  {
+    bufferptr ptr;
+    bufferlist::iterator i(&bl);
+    i.copy(2, ptr);
+    EXPECT_EQ((unsigned)2, ptr.length());
+    EXPECT_EQ('A', ptr[0]);
+    EXPECT_EQ('B', ptr[1]);
+  }
+  //
+  // void buffer::list::iterator::copy(unsigned len, list &dest)
+  //
+  {
+    bufferlist copy;
+    bufferlist::iterator i(&bl);
+    //
+    // demonstrates that it seeks back to offset if p == ls->end()
+    //
+    EXPECT_THROW(i.advance(200), buffer::end_of_buffer); 
+    i.copy(2, copy);
+    EXPECT_EQ(0, ::memcmp(copy.c_str(), expected, 2));
+    i.seek(0);
+    i.copy(3, copy);
+    EXPECT_EQ('A', copy[0]);
+    EXPECT_EQ('B', copy[1]);
+    EXPECT_EQ('A', copy[2]);
+    EXPECT_EQ('B', copy[3]);
+    EXPECT_EQ('C', copy[4]);
+    EXPECT_EQ((unsigned)(2 + 3), copy.length());
+  }
+  //
+  // void buffer::list::iterator::copy_all(list &dest)
+  //
+  {
+    bufferlist copy;
+    bufferlist::iterator i(&bl);
+    //
+    // demonstrates that it seeks back to offset if p == ls->end()
+    //
+    EXPECT_THROW(i.advance(200), buffer::end_of_buffer); 
+    i.copy_all(copy);
+    EXPECT_EQ('A', copy[0]);
+    EXPECT_EQ('B', copy[1]);
+    EXPECT_EQ('C', copy[2]);
+    EXPECT_EQ((unsigned)3, copy.length());
+  }
+  //
+  // void copy(unsigned len, std::string &dest)
+  //
+  {
+    std::string copy;
+    bufferlist::iterator i(&bl);
+    //
+    // demonstrates that it seeks back to offset if p == ls->end()
+    //
+    EXPECT_THROW(i.advance(200), buffer::end_of_buffer); 
+    i.copy(2, copy);
+    EXPECT_EQ(0, ::memcmp(copy.c_str(), expected, 2));
+    i.seek(0);
+    i.copy(3, copy);
+    EXPECT_EQ('A', copy[0]);
+    EXPECT_EQ('B', copy[1]);
+    EXPECT_EQ('A', copy[2]);
+    EXPECT_EQ('B', copy[3]);
+    EXPECT_EQ('C', copy[4]);
+    EXPECT_EQ((unsigned)(2 + 3), copy.length());
+  }
+}
+
+TEST(BufferListIterator, copy_in) {
+  bufferlist bl;
+  const char *existing = "XXX";
+  bl.append(existing, 3);
+  //
+  // void buffer::list::iterator::copy_in(unsigned len, const char *src)
+  //
+  {
+    bufferlist::iterator i(&bl);
+    //
+    // demonstrates that it seeks back to offset if p == ls->end()
+    //
+    EXPECT_THROW(i.advance(200), buffer::end_of_buffer); 
+    const char *expected = "ABC";
+    i.copy_in(3, expected);
+    EXPECT_EQ(0, ::memcmp(bl.c_str(), expected, 3));
+    EXPECT_EQ('A', bl[0]);
+    EXPECT_EQ('B', bl[1]);
+    EXPECT_EQ('C', bl[2]);
+    EXPECT_EQ((unsigned)3, bl.length());
+  }
+  //
+  // void buffer::list::iterator::copy_in(unsigned len, const list& otherl)
+  //
+  {
+    bufferlist::iterator i(&bl);
+    //
+    // demonstrates that it seeks back to offset if p == ls->end()
+    //
+    EXPECT_THROW(i.advance(200), buffer::end_of_buffer); 
+    bufferlist expected;
+    expected.append("ABC", 3);
+    i.copy_in(3, expected);
+    EXPECT_EQ(0, ::memcmp(bl.c_str(), expected.c_str(), 3));
+    EXPECT_EQ('A', bl[0]);
+    EXPECT_EQ('B', bl[1]);
+    EXPECT_EQ('C', bl[2]);
+    EXPECT_EQ((unsigned)3, bl.length());
+  }
+}
+
+TEST(BufferList, constructors) {
+  //
+  // list()
+  //
+  {
+    bufferlist bl;
+    ASSERT_EQ((unsigned)0, bl.length());
+  }
+  //
+  // list(unsigned prealloc)
+  //
+  {
+    bufferlist bl(1);
+    ASSERT_EQ((unsigned)0, bl.length());
+    bl.append('A');
+    ASSERT_EQ('A', bl[0]);
+  }
+  //
+  // list(const list& other)
+  //
+  {
+    bufferlist bl(1);
+    bl.append('A');
+    ASSERT_EQ('A', bl[0]);
+    bufferlist copy(bl);
+    ASSERT_EQ('A', copy[0]);
+  }
+}
+
+TEST(BufferList, operator_equal) {
+  bufferlist bl;
+  bl.append("ABC", 3);
+  {
+    std::string dest;
+    bl.copy(1, 1, dest);
+    ASSERT_EQ('B', dest[0]);
+  }
+  bufferlist copy;
+  copy = bl;
+  {
+    std::string dest;
+    copy.copy(1, 1, dest);
+    ASSERT_EQ('B', dest[0]);
+  }
+}
+
+TEST(BufferList, buffers) {
+  bufferlist bl;
+  ASSERT_EQ((unsigned)0, bl.buffers().size());
+  bl.append('A');
+  ASSERT_EQ((unsigned)1, bl.buffers().size());
+}
+
+TEST(BufferList, swap) {
+  bufferlist b1;
+  b1.append('A');
+
+  bufferlist b2;
+  b2.append('B');
+
+  b1.swap(b2);
+
+  std::string s1;
+  b1.copy(0, 1, s1);
+  ASSERT_EQ('B', s1[0]);
+
+  std::string s2;
+  b2.copy(0, 1, s2);
+  ASSERT_EQ('A', s2[0]);
+}
+
+TEST(BufferList, length) {
+  bufferlist bl;
+  ASSERT_EQ((unsigned)0, bl.length());
+  bl.append('A');
+  ASSERT_EQ((unsigned)1, bl.length());
+}
+
+TEST(BufferList, contents_equal) {
+  //
+  // A BB
+  // AB B
+  //
+  bufferlist bl1;
+  bl1.append("A");
+  bl1.append("BB");
+  bufferlist bl2;
+  ASSERT_FALSE(bl1.contents_equal(bl2)); // different length
+  bl2.append("AB");
+  bl2.append("B");
+  ASSERT_TRUE(bl1.contents_equal(bl2)); // same length same content
+  //
+  // ABC
+  //
+  bufferlist bl3;
+  bl3.append("ABC");
+  ASSERT_FALSE(bl1.contents_equal(bl3)); // same length different content
+}
+
+TEST(BufferList, is_page_aligned) {
+  {
+    bufferlist bl;
+    EXPECT_TRUE(bl.is_page_aligned());
+  }
+  {
+    bufferlist bl;
+    bufferptr ptr(2);
+    ptr.set_offset(1);
+    ptr.set_length(1);
+    bl.append(ptr);
+    EXPECT_FALSE(bl.is_page_aligned());
+    bl.rebuild_page_aligned();
+    EXPECT_FALSE(bl.is_page_aligned());
+  }
+  {
+    bufferlist bl;
+    bufferptr ptr(CEPH_PAGE_SIZE + 1);
+    ptr.set_offset(1);
+    ptr.set_length(CEPH_PAGE_SIZE);
+    bl.append(ptr);
+    EXPECT_FALSE(bl.is_page_aligned());
+    bl.rebuild_page_aligned();
+    EXPECT_TRUE(bl.is_page_aligned());
+  }
+}
+
+TEST(BufferList, is_n_page_sized) {
+  {
+    bufferlist bl;
+    EXPECT_TRUE(bl.is_n_page_sized());
+  }
+  {
+    bufferlist bl;
+    bl.append_zero(1);
+    EXPECT_FALSE(bl.is_n_page_sized());
+  }
+  {
+    bufferlist bl;
+    bl.append_zero(CEPH_PAGE_SIZE);
+    EXPECT_TRUE(bl.is_n_page_sized());
+  }
+}
+
+TEST(BufferList, is_zero) {
+  {
+    bufferlist bl;
+    EXPECT_TRUE(bl.is_zero());
+  }
+  {
+    bufferlist bl;
+    bl.append('A');
+    EXPECT_FALSE(bl.is_zero());
+  }
+  {
+    bufferlist bl;
+    bl.append_zero(1);
+    EXPECT_TRUE(bl.is_zero());
+  }
+}
+
+TEST(BufferList, clear) {
+  bufferlist bl;
+  unsigned len = 17;
+  bl.append_zero(len);
+  bl.clear();
+  EXPECT_EQ((unsigned)0, bl.length());
+  EXPECT_EQ((unsigned)0, bl.buffers().size());
+}
+
+TEST(BufferList, push_front) {
+  //
+  // void push_front(ptr& bp)
+  //
+  {
+    bufferlist bl;
+    bufferptr ptr;
+    bl.push_front(ptr);
+    EXPECT_EQ((unsigned)0, bl.length());
+    EXPECT_EQ((unsigned)0, bl.buffers().size());
+  }
+  unsigned len = 17;
+  {
+    bufferlist bl;
+    bl.append('A');
+    bufferptr ptr(len);
+    ptr.c_str()[0] = 'B';
+    bl.push_front(ptr);
+    EXPECT_EQ((unsigned)(1 + len), bl.length());
+    EXPECT_EQ((unsigned)2, bl.buffers().size());
+    EXPECT_EQ('B', bl.buffers().front()[0]);
+    EXPECT_EQ(ptr.get_raw(), bl.buffers().front().get_raw());
+  }
+  //
+  // void push_front(raw *r)
+  //
+  {
+    bufferlist bl;
+    bl.append('A');
+    bufferptr ptr(len);
+    ptr.c_str()[0] = 'B';
+    bl.push_front(ptr.get_raw());
+    EXPECT_EQ((unsigned)(1 + len), bl.length());
+    EXPECT_EQ((unsigned)2, bl.buffers().size());
+    EXPECT_EQ('B', bl.buffers().front()[0]);
+    EXPECT_EQ(ptr.get_raw(), bl.buffers().front().get_raw());
+  }
+}
+
+TEST(BufferList, push_back) {
+  //
+  // void push_back(ptr& bp)
+  //
+  {
+    bufferlist bl;
+    bufferptr ptr;
+    bl.push_back(ptr);
+    EXPECT_EQ((unsigned)0, bl.length());
+    EXPECT_EQ((unsigned)0, bl.buffers().size());
+  }
+  unsigned len = 17;
+  {
+    bufferlist bl;
+    bl.append('A');
+    bufferptr ptr(len);
+    ptr.c_str()[0] = 'B';
+    bl.push_back(ptr);
+    EXPECT_EQ((unsigned)(1 + len), bl.length());
+    EXPECT_EQ((unsigned)2, bl.buffers().size());
+    EXPECT_EQ('B', bl.buffers().back()[0]);
+    EXPECT_EQ(ptr.get_raw(), bl.buffers().back().get_raw());
+  }
+  //
+  // void push_back(raw *r)
+  //
+  {
+    bufferlist bl;
+    bl.append('A');
+    bufferptr ptr(len);
+    ptr.c_str()[0] = 'B';
+    bl.push_back(ptr.get_raw());
+    EXPECT_EQ((unsigned)(1 + len), bl.length());
+    EXPECT_EQ((unsigned)2, bl.buffers().size());
+    EXPECT_EQ('B', bl.buffers().back()[0]);
+    EXPECT_EQ(ptr.get_raw(), bl.buffers().back().get_raw());
+  }
+}
+
+TEST(BufferList, is_contiguous) {
+  bufferlist bl;
+  EXPECT_TRUE(bl.is_contiguous());
+  EXPECT_EQ((unsigned)0, bl.buffers().size());
+  bl.append('A');  
+  EXPECT_TRUE(bl.is_contiguous());
+  EXPECT_EQ((unsigned)1, bl.buffers().size());
+  bufferptr ptr(1);
+  bl.push_back(ptr);
+  EXPECT_FALSE(bl.is_contiguous());
+  EXPECT_EQ((unsigned)2, bl.buffers().size());
+}
+
+TEST(BufferList, rebuild) {
+  {
+    bufferlist bl;
+    bufferptr ptr(2);
+    ptr.set_offset(1);
+    ptr.set_length(1);
+    bl.append(ptr);
+    EXPECT_FALSE(bl.is_page_aligned());
+    bl.rebuild();
+    EXPECT_FALSE(bl.is_page_aligned());
+  }
+  {
+    bufferlist bl;
+    const std::string str(CEPH_PAGE_SIZE, 'X');
+    bl.append(str.c_str(), str.size());
+    bl.append(str.c_str(), str.size());
+    EXPECT_EQ((unsigned)2, bl.buffers().size());
+    EXPECT_TRUE(bl.is_page_aligned());
+    bl.rebuild();
+    EXPECT_TRUE(bl.is_page_aligned());
+    EXPECT_EQ((unsigned)1, bl.buffers().size());
+  }
+}
+
+TEST(BufferList, rebuild_page_aligned) {
+  {
+    bufferlist bl;
+    {
+      bufferptr ptr(CEPH_PAGE_SIZE + 1);
+      ptr.set_offset(1);
+      ptr.set_length(CEPH_PAGE_SIZE);
+      bl.append(ptr);
+    }
+    EXPECT_EQ((unsigned)1, bl.buffers().size());
+    EXPECT_FALSE(bl.is_page_aligned());
+    bl.rebuild_page_aligned();
+    EXPECT_TRUE(bl.is_page_aligned());
+    EXPECT_EQ((unsigned)1, bl.buffers().size());
+  }
+  {
+    bufferlist bl;
+    {
+      bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE));
+      bl.append(ptr);
+    }
+    {
+      bufferptr ptr(CEPH_PAGE_SIZE + 1);
+      bl.append(ptr);
+    }
+    {
+      bufferptr ptr(2);
+      ptr.set_offset(1);
+      ptr.set_length(1);
+      bl.append(ptr);
+    }
+    {
+      bufferptr ptr(CEPH_PAGE_SIZE - 2);
+      bl.append(ptr);
+    }
+    {
+      bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE));
+      bl.append(ptr);
+    }
+    {
+      bufferptr ptr(CEPH_PAGE_SIZE + 1);
+      ptr.set_offset(1);
+      ptr.set_length(CEPH_PAGE_SIZE);
+      bl.append(ptr);
+    }
+    EXPECT_EQ((unsigned)6, bl.buffers().size());
+    EXPECT_TRUE((bl.length() & ~CEPH_PAGE_MASK) == 0);
+    EXPECT_FALSE(bl.is_page_aligned());
+    bl.rebuild_page_aligned();
+    EXPECT_TRUE(bl.is_page_aligned());
+    EXPECT_EQ((unsigned)4, bl.buffers().size());
+  }
+}
+
+TEST(BufferList, claim) {
+  bufferlist from;
+  {
+    bufferptr ptr(2);
+    from.append(ptr);
+  }
+  bufferlist to;
+  {
+    bufferptr ptr(4);
+    to.append(ptr);
+  }
+  EXPECT_EQ((unsigned)4, to.length());
+  EXPECT_EQ((unsigned)1, to.buffers().size());
+  to.claim(from);
+  EXPECT_EQ((unsigned)2, to.length());
+  EXPECT_EQ((unsigned)1, to.buffers().size());
+  EXPECT_EQ((unsigned)0, from.buffers().size());
+  EXPECT_EQ((unsigned)0, from.length());
+}
+
+TEST(BufferList, claim_append) {
+  bufferlist from;
+  {
+    bufferptr ptr(2);
+    from.append(ptr);
+  }
+  bufferlist to;
+  {
+    bufferptr ptr(4);
+    to.append(ptr);
+  }
+  EXPECT_EQ((unsigned)4, to.length());
+  EXPECT_EQ((unsigned)1, to.buffers().size());
+  to.claim_append(from);
+  EXPECT_EQ((unsigned)(4 + 2), to.length());
+  EXPECT_EQ((unsigned)4, to.buffers().front().length());
+  EXPECT_EQ((unsigned)2, to.buffers().back().length());
+  EXPECT_EQ((unsigned)2, to.buffers().size());
+  EXPECT_EQ((unsigned)0, from.buffers().size());
+  EXPECT_EQ((unsigned)0, from.length());
+}
+
+TEST(BufferList, claim_prepend) {
+  bufferlist from;
+  {
+    bufferptr ptr(2);
+    from.append(ptr);
+  }
+  bufferlist to;
+  {
+    bufferptr ptr(4);
+    to.append(ptr);
+  }
+  EXPECT_EQ((unsigned)4, to.length());
+  EXPECT_EQ((unsigned)1, to.buffers().size());
+  to.claim_prepend(from);
+  EXPECT_EQ((unsigned)(2 + 4), to.length());
+  EXPECT_EQ((unsigned)2, to.buffers().front().length());
+  EXPECT_EQ((unsigned)4, to.buffers().back().length());
+  EXPECT_EQ((unsigned)2, to.buffers().size());
+  EXPECT_EQ((unsigned)0, from.buffers().size());
+  EXPECT_EQ((unsigned)0, from.length());
+}
+
+TEST(BufferList, begin) {
+  bufferlist bl;
+  bl.append("ABC");
+  bufferlist::iterator i = bl.begin();
+  EXPECT_EQ('A', *i);
+}
+
+TEST(BufferList, end) {
+  bufferlist bl;
+  bl.append("ABC");
+  bufferlist::iterator i = bl.end();
+  i.advance(-1);
+  EXPECT_EQ('C', *i);
+}
+
+TEST(BufferList, copy) {
+  //
+  // void copy(unsigned off, unsigned len, char *dest) const;
+  //
+  {
+    bufferlist bl;
+    EXPECT_THROW(bl.copy((unsigned)100, (unsigned)100, (char*)0), buffer::end_of_buffer);
+    const char *expected = "ABC";
+    bl.append(expected);
+    char *dest = new char[2];
+    bl.copy(1, 2, dest);
+    EXPECT_EQ(0, ::memcmp(expected + 1, dest, 2));
+    delete [] dest;
+  }
+  //
+  // void copy(unsigned off, unsigned len, list &dest) const;
+  //
+  {
+    bufferlist bl;
+    bufferlist dest;
+    EXPECT_THROW(bl.copy((unsigned)100, (unsigned)100, dest), buffer::end_of_buffer);
+    const char *expected = "ABC";
+    bl.append(expected);
+    bl.copy(1, 2, dest);
+    EXPECT_EQ(0, ::memcmp(expected + 1, dest.c_str(), 2));
+  }
+  //
+  // void copy(unsigned off, unsigned len, std::string &dest) const;
+  //
+  {
+    bufferlist bl;
+    std::string dest;
+    EXPECT_THROW(bl.copy((unsigned)100, (unsigned)100, dest), buffer::end_of_buffer);
+    const char *expected = "ABC";
+    bl.append(expected);
+    bl.copy(1, 2, dest);
+    EXPECT_EQ(0, ::memcmp(expected + 1, dest.c_str(), 2));
+  }
+}
+
+TEST(BufferList, copy_in) {
+  //
+  // void copy_in(unsigned off, unsigned len, const char *src);
+  //
+  {
+    bufferlist bl;
+    bl.append("XXX");
+    EXPECT_THROW(bl.copy_in((unsigned)100, (unsigned)100, (char*)0), buffer::end_of_buffer);
+    bl.copy_in(1, 2, "AB");
+    EXPECT_EQ(0, ::memcmp("XAB", bl.c_str(), 3));
+  }
+  //
+  // void copy_in(unsigned off, unsigned len, const list& src);
+  //
+  {
+    bufferlist bl;
+    bl.append("XXX");
+    bufferlist src;
+    src.append("ABC");
+    EXPECT_THROW(bl.copy_in((unsigned)100, (unsigned)100, src), buffer::end_of_buffer);
+    bl.copy_in(1, 2, src);
+    EXPECT_EQ(0, ::memcmp("XAB", bl.c_str(), 3));    
+  }
 }
 
-TEST(BufferList, zero) {
+TEST(BufferList, append) {
   //
-  // void zero()
+  // void append(char c);
   //
   {
     bufferlist bl;
+    EXPECT_EQ((unsigned)0, bl.buffers().size());
     bl.append('A');
-    EXPECT_EQ('A', bl[0]);
-    bl.zero();
-    EXPECT_EQ('\0', bl[0]);
+    EXPECT_EQ((unsigned)1, bl.buffers().size());
+    EXPECT_TRUE(bl.is_page_aligned());
   }
   //
-  // void zero(unsigned o, unsigned l)
+  // void append(const char *data, unsigned len);
+  //
+  {
+    bufferlist bl(CEPH_PAGE_SIZE);
+    std::string str(CEPH_PAGE_SIZE * 2, 'X');
+    bl.append(str.c_str(), str.size());
+    EXPECT_EQ((unsigned)2, bl.buffers().size());
+    EXPECT_EQ(CEPH_PAGE_SIZE, bl.buffers().front().length());
+    EXPECT_EQ(CEPH_PAGE_SIZE, bl.buffers().back().length());
+  }
+  //
+  // void append(const std::string& s);
+  //
+  {
+    bufferlist bl(CEPH_PAGE_SIZE);
+    std::string str(CEPH_PAGE_SIZE * 2, 'X');
+    bl.append(str);
+    EXPECT_EQ((unsigned)2, bl.buffers().size());
+    EXPECT_EQ(CEPH_PAGE_SIZE, bl.buffers().front().length());
+    EXPECT_EQ(CEPH_PAGE_SIZE, bl.buffers().back().length());
+  }
+  //
+  // void append(const ptr& bp);
+  //
+  {
+    bufferlist bl;
+    EXPECT_EQ((unsigned)0, bl.buffers().size());
+    EXPECT_EQ((unsigned)0, bl.length());
+    {
+      bufferptr ptr;
+      bl.append(ptr);
+      EXPECT_EQ((unsigned)0, bl.buffers().size());
+      EXPECT_EQ((unsigned)0, bl.length());
+    }
+    {
+      bufferptr ptr(3);
+      bl.append(ptr);
+      EXPECT_EQ((unsigned)1, bl.buffers().size());
+      EXPECT_EQ((unsigned)3, bl.length());
+    }
+  }
+  //
+  // void append(const ptr& bp, unsigned off, unsigned len);
+  //
+  {
+    bufferlist bl;
+    bl.append('A');
+    bufferptr back(bl.buffers().back());
+    bufferptr in(back);
+    EXPECT_EQ((unsigned)1, bl.buffers().size());
+    EXPECT_EQ((unsigned)1, bl.length());
+    EXPECT_THROW(bl.append(in, (unsigned)100, (unsigned)100), FailedAssertion);
+    EXPECT_LT((unsigned)0, in.unused_tail_length());
+    in.append('B');
+    bl.append(in, back.end(), 1);
+    EXPECT_EQ((unsigned)1, bl.buffers().size());
+    EXPECT_EQ((unsigned)2, bl.length());
+    EXPECT_EQ('B', bl[1]);
+  }
+  {
+    bufferlist bl;
+    EXPECT_EQ((unsigned)0, bl.buffers().size());
+    EXPECT_EQ((unsigned)0, bl.length());
+    bufferptr ptr(2);
+    ptr.set_length(0);
+    ptr.append("AB", 2);
+    bl.append(ptr, 1, 1);
+    EXPECT_EQ((unsigned)1, bl.buffers().size());
+    EXPECT_EQ((unsigned)1, bl.length());
+  }
+  //
+  // void append(const list& bl);
+  //
+  {
+    bufferlist bl;
+    bl.append('A');
+    bufferlist other;
+    other.append('B');
+    bl.append(other);
+    EXPECT_EQ((unsigned)2, bl.buffers().size());
+    EXPECT_EQ('B', bl[1]);
+  }
+  //
+  // void append(std::istream& in);
   //
+  {
+    bufferlist bl;
+    std::string expected("ABC\n\nDEF\n");
+    std::istringstream is("ABC\n\nDEF");
+    bl.append(is);
+    EXPECT_EQ(0, ::memcmp(expected.c_str(), bl.c_str(), expected.size()));
+    EXPECT_EQ(expected.size(), bl.length());
+  }
+}
+
+TEST(BufferList, append_zero) {
+  bufferlist bl;
+  bl.append('A');
+  EXPECT_EQ((unsigned)1, bl.buffers().size());
+  EXPECT_EQ((unsigned)1, bl.length());
+  bl.append_zero(1);
+  EXPECT_EQ((unsigned)2, bl.buffers().size());
+  EXPECT_EQ((unsigned)2, bl.length());
+  EXPECT_EQ('\0', bl[1]);
+}
+
+TEST(BufferList, operator_brackets) {
+  bufferlist bl;
+  EXPECT_THROW(bl[1], buffer::end_of_buffer);
+  bl.append('A');
+  bufferlist other;
+  other.append('B');
+  bl.append(other);
+  EXPECT_EQ((unsigned)2, bl.buffers().size());
+  EXPECT_EQ('B', bl[1]);
+}
+
+TEST(BufferList, c_str) {
+  bufferlist bl;
+  EXPECT_EQ((const char*)NULL, bl.c_str());
+  bl.append('A');
+  bufferlist other;
+  other.append('B');
+  bl.append(other);
+  EXPECT_EQ((unsigned)2, bl.buffers().size());
+  EXPECT_EQ(0, ::memcmp("AB", bl.c_str(), 2));
+}
+
+TEST(BufferList, substr_of) {
+  bufferlist bl;
+  EXPECT_THROW(bl.substr_of(bl, 1, 1), buffer::end_of_buffer);
   const char *s[] = {
     "ABC",
     "DEF",
     "GHI",
-    "KLM"
+    "JKL"
   };
-  {
-    bufferlist bl;
-    bufferptr ptr(s[0], strlen(s[0]));
+  for (unsigned i = 0; i < 4; i++) {
+    bufferptr ptr(s[i], strlen(s[i]));
     bl.push_back(ptr);
-    bl.zero((unsigned)0, (unsigned)1);
-    EXPECT_EQ(0, ::memcmp("\0BC", bl.c_str(), 3));
   }
-  {
-    bufferlist bl;
-    for (unsigned i = 0; i < 4; i++) {
-      bufferptr ptr(s[i], strlen(s[i]));
-      bl.push_back(ptr);
-    }
-    EXPECT_THROW(bl.zero((unsigned)0, (unsigned)2000), FailedAssertion);
-    bl.zero((unsigned)2, (unsigned)5);
-    EXPECT_EQ(0, ::memcmp("AB\0\0\0\0\0HIKLM", bl.c_str(), 9));
+  EXPECT_EQ((unsigned)4, bl.buffers().size());
+
+  bufferlist other;
+  other.append("TO BE CLEARED");
+  other.substr_of(bl, 4, 4);
+  EXPECT_EQ((unsigned)2, other.buffers().size());
+  EXPECT_EQ((unsigned)4, other.length());
+  EXPECT_EQ(0, ::memcmp("EFGH", other.c_str(), 4));
+}
+
+TEST(BufferList, splice) {
+  bufferlist bl;
+  EXPECT_THROW(bl.splice(1, 1), buffer::end_of_buffer);
+  const char *s[] = {
+    "ABC",
+    "DEF",
+    "GHI",
+    "JKL"
+  };
+  for (unsigned i = 0; i < 4; i++) {
+    bufferptr ptr(s[i], strlen(s[i]));
+    bl.push_back(ptr);
   }
+  EXPECT_EQ((unsigned)4, bl.buffers().size());
+  EXPECT_THROW(bl.splice(0, 0), FailedAssertion);
+
+  bufferlist other;
+  other.append('X');
+  bl.splice(4, 4, &other);
+  EXPECT_EQ((unsigned)3, other.buffers().size());
+  EXPECT_EQ((unsigned)5, other.length());
+  EXPECT_EQ(0, ::memcmp("XEFGH", other.c_str(), other.length()));
+  EXPECT_EQ((unsigned)8, bl.length());
   {
-    bufferlist bl;
-    for (unsigned i = 0; i < 4; i++) {
-      bufferptr ptr(s[i], strlen(s[i]));
-      bl.push_back(ptr);
-    }
-    bl.zero((unsigned)3, (unsigned)3);
-    EXPECT_EQ(0, ::memcmp("ABC\0\0\0GHIKLM", bl.c_str(), 9));
+    bufferlist tmp(bl);
+    EXPECT_EQ(0, ::memcmp("ABCDIJKL", tmp.c_str(), tmp.length()));
+  }
+
+  bl.splice(4, 4);
+  EXPECT_EQ((unsigned)4, bl.length());
+  EXPECT_EQ(0, ::memcmp("ABCD", bl.c_str(), bl.length()));
+}
+
+TEST(BufferList, write) {
+  std::ostringstream stream;
+  bufferlist bl;
+  bl.append("ABC");
+  bl.write(1, 2, stream);
+  EXPECT_EQ("BC", stream.str());
+}
+
+TEST(BufferList, encode_base64) {
+  bufferlist bl;
+  bl.append("ABCD");
+  bufferlist other;
+  bl.encode_base64(other);
+  const char *expected = "QUJDRA==";
+  EXPECT_EQ(0, ::memcmp(expected, other.c_str(), strlen(expected)));
+}
+
+TEST(BufferList, decode_base64) {
+  bufferlist bl;
+  bl.append("QUJDRA==");
+  bufferlist other;
+  other.decode_base64(bl);
+  const char *expected = "ABCD";
+  EXPECT_EQ(0, ::memcmp(expected, other.c_str(), strlen(expected)));
+  bufferlist malformed;
+  malformed.append("QUJDRA");
+  EXPECT_THROW(other.decode_base64(malformed), buffer::malformed_input);
+}
+
+TEST(BufferList, hexdump) {
+  bufferlist bl;
+  std::ostringstream stream;
+  bl.append("013245678901234\0006789012345678901234", 32);
+  bl.hexdump(stream);
+  EXPECT_EQ("0000 : 30 31 33 32 34 35 36 37 38 39 30 31 32 33 34 00 : 013245678901234.\n"
+	    "0010 : 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 : 6789012345678901\n",
+	    stream.str());
+}
+
+TEST(BufferList, read_file) {
+  std::string error;
+  bufferlist bl;
+  ::unlink("testfile");
+  EXPECT_EQ(-ENOENT, bl.read_file("UNLIKELY", &error));
+  ::system("echo ABC > testfile ; chmod 0 testfile");
+  EXPECT_EQ(-EACCES, bl.read_file("testfile", &error));
+  ::system("chmod +r testfile");
+  EXPECT_EQ(0, bl.read_file("testfile", &error));
+  ::unlink("testfile");
+  EXPECT_EQ((unsigned)4, bl.length());
+  std::string actual(bl.c_str(), bl.length());
+  EXPECT_EQ("ABC\n", actual);
+}
+
+TEST(BufferList, read_fd) {
+  unsigned len = 4;
+  ::unlink("testfile");
+  ::system("echo ABC > testfile");
+  int fd = -1;
+  bufferlist bl;
+  EXPECT_EQ(-EBADF, bl.read_fd(fd, len));
+  fd = ::open("testfile", O_RDONLY);
+  EXPECT_EQ(len, bl.read_fd(fd, len));
+  EXPECT_EQ(len, bl.length());
+  EXPECT_EQ(CEPH_PAGE_SIZE - len, bl.buffers().front().unused_tail_length());
+  ::close(fd);
+  ::unlink("testfile");
+}
+
+TEST(BufferList, write_file) {
+  ::unlink("testfile");
+  int mode = 0600;
+  bufferlist bl;
+  EXPECT_EQ(-ENOENT, bl.write_file("un/like/ly", mode));
+  bl.append("ABC");
+  EXPECT_EQ(0, bl.write_file("testfile", mode));
+  struct stat st;
+  memset(&st, 0, sizeof(st));
+  ::stat("testfile", &st);
+  EXPECT_EQ((unsigned)(mode | S_IFREG), st.st_mode);
+  ::unlink("testfile");
+}
+
+TEST(BufferList, write_fd) {
+  ::unlink("testfile");
+  int fd = ::open("testfile", O_WRONLY|O_CREAT|O_TRUNC, 0600);
+  bufferlist bl;
+  for (unsigned i = 0; i < IOV_MAX * 2; i++) {
+    bufferptr ptr("A", 1);
+    bl.push_back(ptr);
   }
+  EXPECT_EQ(0, bl.write_fd(fd));
+  ::close(fd);
+  struct stat st;
+  memset(&st, 0, sizeof(st));
+  ::stat("testfile", &st);
+  EXPECT_EQ(IOV_MAX * 2, st.st_size);
+  ::unlink("testfile");
+}
+
+TEST(BufferList, crc32c) {
+  bufferlist bl;
+  __u32 crc = 0;
+  bl.append("A");
+  crc = bl.crc32c(crc);
+  EXPECT_EQ((unsigned)0xB3109EBF, crc);
+  crc = bl.crc32c(crc);
+  EXPECT_EQ((unsigned)0x5FA5C0CC, crc);
 }
 
 TEST(BufferList, compare) {
@@ -121,6 +1694,72 @@ TEST(BufferList, compare) {
   ASSERT_TRUE(ab == ab);
 }
 
+TEST(BufferList, ostream) {
+  std::ostringstream stream;
+  bufferlist bl;
+  const char *s[] = {
+    "ABC",
+    "DEF"
+  };
+  for (unsigned i = 0; i < 2; i++) {
+    bufferptr ptr(s[i], strlen(s[i]));
+    bl.push_back(ptr);
+  }
+  stream << bl;
+  std::cerr << stream.str() << std::endl;
+  EXPECT_GT(stream.str().size(), stream.str().find("list(len=6,"));
+  EXPECT_GT(stream.str().size(), stream.str().find("len 3 nref 1),\n"));
+  EXPECT_GT(stream.str().size(), stream.str().find("len 3 nref 1)\n"));
+}
+
+TEST(BufferList, zero) {
+  //
+  // void zero()
+  //
+  {
+    bufferlist bl;
+    bl.append('A');
+    EXPECT_EQ('A', bl[0]);
+    bl.zero();
+    EXPECT_EQ('\0', bl[0]);
+  }
+  //
+  // void zero(unsigned o, unsigned l)
+  //
+  const char *s[] = {
+    "ABC",
+    "DEF",
+    "GHI",
+    "KLM"
+  };
+  {
+    bufferlist bl;
+    bufferptr ptr(s[0], strlen(s[0]));
+    bl.push_back(ptr);
+    bl.zero((unsigned)0, (unsigned)1);
+    EXPECT_EQ(0, ::memcmp("\0BC", bl.c_str(), 3));
+  }
+  {
+    bufferlist bl;
+    for (unsigned i = 0; i < 4; i++) {
+      bufferptr ptr(s[i], strlen(s[i]));
+      bl.push_back(ptr);
+    }
+    EXPECT_THROW(bl.zero((unsigned)0, (unsigned)2000), FailedAssertion);
+    bl.zero((unsigned)2, (unsigned)5);
+    EXPECT_EQ(0, ::memcmp("AB\0\0\0\0\0HIKLM", bl.c_str(), 9));
+  }
+  {
+    bufferlist bl;
+    for (unsigned i = 0; i < 4; i++) {
+      bufferptr ptr(s[i], strlen(s[i]));
+      bl.push_back(ptr);
+    }
+    bl.zero((unsigned)3, (unsigned)3);
+    EXPECT_EQ(0, ::memcmp("ABC\0\0\0GHIKLM", bl.c_str(), 9));
+  }
+}
+
 TEST(BufferList, EmptyAppend) {
   bufferlist bl;
   bufferptr ptr;
@@ -151,54 +1790,6 @@ TEST(BufferList, TestPtrAppend) {
   ASSERT_EQ(memcmp(bl.c_str(), correct, curpos), 0);
 }
 
-TEST(BufferList, ptr_assignment) {
-  unsigned len = 17;
-  //
-  // override a bufferptr set with the same raw
-  //
-  {
-    bufferptr original(len);
-    bufferptr same_raw(original.get_raw());
-    unsigned offset = 5;
-    unsigned length = len - offset;
-    original.set_offset(offset);
-    original.set_length(length);
-    same_raw = original;
-    ASSERT_EQ(2, original.raw_nref());
-    ASSERT_EQ(same_raw.get_raw(), original.get_raw());
-    ASSERT_EQ(same_raw.offset(), original.offset());
-    ASSERT_EQ(same_raw.length(), original.length());
-  }
-
-  //
-  // self assignment is a noop
-  //
-  {
-    bufferptr original(len);
-    original = original;
-    ASSERT_EQ(1, original.raw_nref());
-    ASSERT_EQ((unsigned)0, original.offset());
-    ASSERT_EQ(len, original.length());
-  }
-  
-  //
-  // a copy points to the same raw
-  //
-  {
-    bufferptr original(len);
-    unsigned offset = 5;
-    unsigned length = len - offset;
-    original.set_offset(offset);
-    original.set_length(length);
-    bufferptr ptr;
-    ptr = original;
-    ASSERT_EQ(2, original.raw_nref());
-    ASSERT_EQ(ptr.get_raw(), original.get_raw());
-    ASSERT_EQ(original.offset(), ptr.offset());
-    ASSERT_EQ(original.length(), ptr.length());
-  }
-}
-
 TEST(BufferList, TestDirectAppend) {
   bufferlist bl;
   char correct[MAX_TEST];
@@ -234,3 +1825,29 @@ TEST(BufferList, TestCopyAll) {
   bl2.copy(0, BIG_SZ, (char*)big2);
   ASSERT_EQ(memcmp(big.get(), big2, BIG_SZ), 0);
 }
+
+TEST(BufferHash, all) {
+  {
+    bufferlist bl;
+    bl.append("A");
+    bufferhash hash;
+    EXPECT_EQ((unsigned)0, hash.digest());
+    hash.update(bl);
+    EXPECT_EQ((unsigned)0xB3109EBF, hash.digest());
+    hash.update(bl);
+    EXPECT_EQ((unsigned)0x5FA5C0CC, hash.digest());
+  }
+  {
+    bufferlist bl;
+    bl.append("A");
+    bufferhash hash;
+    EXPECT_EQ((unsigned)0, hash.digest());
+    bufferhash& returned_hash =  hash << bl;
+    EXPECT_EQ(&returned_hash, &hash);
+    EXPECT_EQ((unsigned)0xB3109EBF, hash.digest());
+  }
+}
+
+// Local Variables:
+// compile-command: "cd .. ; make unittest_bufferlist ; ulimit -s unlimited ; CEPH_BUFFER_TRACK=true valgrind --max-stackframe=20000000 --tool=memcheck ./unittest_bufferlist # --gtest_filter=BufferList.constructors"
+// End:
diff --git a/src/unittest_bufferlist.sh b/src/unittest_bufferlist.sh
new file mode 100755
index 0000000..0f05afe
--- /dev/null
+++ b/src/unittest_bufferlist.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Ceph - scalable distributed file system
+#
+# Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Library Public License for more details.
+#
+CEPH_BUFFER_TRACK=true ./unittest_bufferlist
-- 
1.7.10.4


             reply	other threads:[~2013-02-17 19:38 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-02-17 19:38 Loic Dachary [this message]
2013-02-18  5:48 ` [PATCH] unit tests for src/common/buffer.{cc,h} Sage Weil
2013-02-18  7:17   ` Loic Dachary

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1361129932-7466-1-git-send-email-loic@dachary.org \
    --to=loic@dachary.org \
    --cc=ceph-devel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.