]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
test: Refactored testceph.cc into gtest framework
authorSam Lang <sam.lang@inktank.com>
Tue, 9 Oct 2012 14:06:16 +0000 (09:06 -0500)
committerSam Lang <sam.lang@inktank.com>
Tue, 9 Oct 2012 15:33:11 +0000 (10:33 -0500)
Moved all the functionality tests for the libcephfs
API into the gtest framework.  Also adds tests for
directories to improve test coverage of the libcephfs
interfaces.

Signed-off-by: Sam Lang <sam.lang@inktank.com>
src/Makefile.am
src/client/testceph.cc [deleted file]
src/test/libcephfs/test.cc

index 2b4688380a331728ec71865480b7699a979fbb9d..f725617cd12369de1c48643ea8d4f652ed3d05fd 100644 (file)
@@ -283,10 +283,6 @@ libcephfs_la_LDFLAGS =  $(PTHREAD_LIBS) $(CRYPTO_LIBS) $(EXTRALIBS) \
             ${AM_LDFLAGS} -version-info 1:0:0 -export-symbols-regex '^ceph_.*'
 lib_LTLIBRARIES += libcephfs.la
 
-testceph_SOURCES = client/testceph.cc
-testceph_LDADD = libcephfs.la $(PTHREAD_LIBS) -lm $(CRYPTO_LIBS) $(EXTRALIBS)
-bin_DEBUGPROGRAMS += testceph
-
 testtimers_SOURCES = test/TestTimers.cc
 testtimers_LDADD = $(LIBGLOBAL_LDA)
 bin_DEBUGPROGRAMS += testtimers
diff --git a/src/client/testceph.cc b/src/client/testceph.cc
deleted file mode 100644 (file)
index 5e95e90..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-// -*- 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) 2004-2006 Sage Weil <sage@newdream.net>
- *
- * This is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1, as published by the Free Software 
- * Foundation.  See file COPYING.
- * 
- */
-
-#include "common/errno.h"
-#include "include/cephfs/libcephfs.h"
-#include <stdlib.h>
-
-#include <errno.h>
-#include <dirent.h>
-#include <iostream>
-#include <fcntl.h>
-#include <sys/xattr.h>
-#include <string.h>
-
-using std::cout;
-using std::cerr;
-
-int main(int argc, const char **argv)
-{
-  struct ceph_mount_info *cmount;
-    cout << "calling ceph_create..." << std::endl;
-  int my_fd;
-  int ret = ceph_create(&cmount, NULL);
-  if (ret) {
-    cerr << "ceph_create failed with error: " << ret << std::endl;
-    return 1;
-  }
-    cout << "calling ceph_conf_read_file..." << ret << std::endl;
-  ceph_conf_read_file(cmount, NULL);
-    cout << "calling ceph_conf_parse_argv..." << ret << std::endl;
-  ceph_conf_parse_argv(cmount, argc, argv);
-
-  char buf[128];
-    cout << "calling ceph_conf_get..." << ret << std::endl;
-  ret = ceph_conf_get(cmount, "log file", buf, sizeof(buf));
-  if (ret) {
-    cerr << "ceph_conf_get(\"log file\") failed with error " << ret << std::endl;
-  }
-  else {
-    cout << "log_file = \"" << buf << "\"" << std::endl;
-  }
-
-  ret = ceph_mount(cmount, NULL);
-  if (ret) {
-    cerr << "ceph_mount error: " << ret << std::endl;
-    return 1;
-  }
-  cout << "Successfully mounted Ceph!" << std::endl;
-
-  // what if foo is already there???
-  struct ceph_dir_result *foo_dir = NULL;
-  ret = ceph_opendir(cmount, "foo", &foo_dir);
-  if (ret != -ENOENT) {
-    cerr << "ceph_opendir error: unexpected result from trying to open foo: "
-        << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_opendir: success, foo does not exist" << std::endl;
-  }
-
-  // Can't close a dir that is not open!
-  //ret = ceph_closedir(cmount, foo_dir);
-  //if (ret == 0) {
-  //  cerr << "ceph_closedir success" << std::endl;
-  //} else {
-  //  cerr << "ceph_closedir error: " << cpp_strerror(ret) << std::endl;
-  //}
-  //
-  ret = ceph_mkdir(cmount, "foo",  0777);
-  if (ret) {
-    cerr << "ceph_mkdir error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_mkdir: success" << std::endl;
-  }
-
-  struct stat stbuf;
-  ret = ceph_lstat(cmount, "foo", &stbuf);
-  if (ret) {
-    cerr << "ceph_lstat error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_lstat: success" << std::endl;
-  }
-
-  if (!S_ISDIR(stbuf.st_mode)) {
-    cerr << "ceph_lstat(foo): foo is not a directory? st_mode = "
-        << stbuf.st_mode << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_lstat: mode success" << std::endl;
-  }
-
-  ret = ceph_rmdir(cmount, "foo");
-  if (ret) {
-    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_rmdir: success" << std::endl;
-  }
-
-  ret = ceph_mkdirs(cmount, "foo/bar/baz",  0777);
-  if (ret) {
-    cerr << "ceph_mkdirs error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_mkdirs: success" << std::endl;
-  }
-
-  // Should test .. as well!
-
-  cout << "testing chdir on foo/bar/baz";
-  ret = ceph_chdir(cmount, "foo/bar/baz");
-  if (ret) {
-    cerr << ": failed: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  }
-
-  cout << " success!" << std::endl;
-
-  // Now, try absolute
-  cout << "testing chdir to /foo/bar";
-  ret = ceph_chdir(cmount, "/foo/bar");
-  if (ret) {
-    cerr << ": failed: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  }
-
-  cout << " success!" << std::endl;
-
-  // Now, back to the top
-  cout << "testing chdir to /";
-  ret = ceph_chdir(cmount, "/");
-  if (ret) {
-    cerr << ": failed: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  }
-
-  cout << "success!" << std::endl;
-
-  ret = ceph_rmdir(cmount, "foo/bar/baz");
-  if (ret) {
-    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_rmdir: success" << std::endl;
-  }
-  ret = ceph_rmdir(cmount, "foo/bar");
-  if (ret) {
-    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_rmdir: success" << std::endl;
-  }
-  ret = ceph_rmdir(cmount, "foo");
-  if (ret) {
-    cerr << "ceph_rmdir error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_rmdir: success" << std::endl;
-  }
-  my_fd = ret = ceph_open(cmount, "barfile", O_CREAT, 0666);
-  if (ret < 0) {
-    cerr << "ceph_open O_CREAT error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_open: success" << std::endl;
-  }
-  ret = ceph_setxattr(cmount, "barfile", "user.testxattr", (void *) "AYBABTU", 7, XATTR_CREATE);
-  if (ret < 0) {
-    cerr << "ceph_setxattr error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_setxattr: success" << std::endl;
-  }
-  char *outlist;
-  outlist = (char *) malloc(256);
-  ret = ceph_listxattr(cmount, "barfile", outlist, 0);
-  free(outlist);
-  if (ret < 1) {
-    cerr << "ceph_listxattr error (a): should have returned > 0" << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_listxattr: success" << std::endl;
-  }
-  
-  outlist = (char *) malloc(ret);
-  ret = ceph_listxattr(cmount, "barfile", outlist, ret);
-  if (ret < 1) {
-    cerr << "ceph_listxattr error (b): should have returned > 0" << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_listxattr: success" << std::endl;
-  }
-  void *aybabtu;
-  aybabtu = malloc(8);
-  ret = ceph_getxattr(cmount, "barfile", "user.testxattr", aybabtu, 7);
-  if (ret < 1) {
-    cerr << "ceph_getxattr error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_getxattr: success" << std::endl;
-  }
-  char aybabtu_reference[]="AYBABTU";
-  if (strncmp((char *) aybabtu, aybabtu_reference,7)) {
-    cerr << "ceph_getxattr error: no match (" << aybabtu << ") should be (" << aybabtu_reference << cpp_strerror(ret) << std::endl;
-  }
-  ret = ceph_close(cmount,my_fd);
-  if (ret < 0) {
-    cerr << "ceph_close error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_close: success" << std::endl;
-  }
-
-
-  cout << "Attempting lstat on '/.'" << std::endl;
-  ret = ceph_lstat(cmount, "/.", &stbuf);
-  if (ret) {
-    cerr << "ceph_lstat error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_lstat: success" << std::endl;
-  }
-  cout << "Attempting lstat on '.'" << std::endl;
-  ret = ceph_lstat(cmount, ".", &stbuf);
-  if (ret) {
-    cerr << "ceph_lstat error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_lstat: success" << std::endl;
-  }
-  cout << "Setting up readdir test" << std::endl;
-  ret = ceph_mkdir(cmount, "readdir_test",  0777);
-  if (ret) {
-    cerr << "ceph_mkdir error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_mkdir: success" << std::endl;
-  }
-  my_fd = ret = ceph_open(cmount, "readdir_test/opened_file_1", O_CREAT, 0666);
-  if (ret < 0) {
-    cerr << "ceph_open O_CREAT error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_open: success" << std::endl;
-  }
-  ret = ceph_close(cmount, my_fd);
-  if (ret < 0) {
-    cerr << "ceph_close error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_close: success" << std::endl;
-  }
-
-  // test empty name components
-  my_fd = ret = ceph_open(cmount, "readdir_test//opened_file_1", O_RDONLY, 0666);
-  if (ret < 0) {
-    cerr << "ceph_open O_RDONLY error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_open: success" << std::endl;
-  }
-  ret = ceph_close(cmount, my_fd);
-  if (ret < 0) {
-    cerr << "ceph_close error: " << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_close: success" << std::endl;
-  }
-
-  struct ceph_dir_result *readdir_test_dir;
-  ret = ceph_opendir(cmount, "readdir_test", &readdir_test_dir);
-  if (ret != 0) {
-    cerr << "ceph_opendir error: unexpected result from trying to open readdir_test: "
-        << cpp_strerror(ret) << std::endl;
-    return 1;
-  } else {
-    cout << "ceph_opendir: success" << std::endl;
-  }
-  cout << "Attempting readdir on opened directory..." << std::endl;
-  struct dirent * result;
-  //result = (struct dirent *) malloc(sizeof(struct dirent));
-  result = ceph_readdir(cmount, readdir_test_dir);
-  if (result == (dirent *) NULL) {
-    cout << "ceph_readdir: failed to read any entries" << std::endl;
-  }
-  loff_t telldir_result;
-  while ( result != (dirent *) NULL) {
-    cout << "ceph_readdir: dirent->d_name: (" << result->d_name << ")" << std::endl;
-    cout << "ceph_telldir: starting" << std::endl;
-    telldir_result = ceph_telldir(cmount, readdir_test_dir);
-    if (telldir_result > -1) {
-      cout << "ceph_telldir: offset: from return code:" << telldir_result << std::endl;
-    } else {
-      cout << "ceph_telldir: failed" << std::endl;
-    }
-    cout << "ceph_readdir: lookup success: trying for another..." << std::endl;
-    result = ceph_readdir(cmount, readdir_test_dir);
-  }
-  cout << "ceph_readdir: finished" << std::endl;
-
-  // tell us that we're at the end of the directory:
-  cout << "ceph_telldir: starting" << std::endl;
-  telldir_result = ceph_telldir(cmount, readdir_test_dir);
-  if (telldir_result > -1) {
-    cout << "ceph_telldir: offset: from return code:" << telldir_result << std::endl;
-  } else {
-    cout << "ceph_telldir: failed" << std::endl;
-  }
-
-  ret = ceph_closedir(cmount,readdir_test_dir);
-  if (ret == 0) {
-    cerr << "ceph_closedir success" << std::endl;
-  } else {
-    cerr << "ceph_closedir error: " << cpp_strerror(ret) << std::endl;
-  }
-  
-  ceph_shutdown(cmount);
-
-  return 0;
-}
index 382aebe3042bce034fe3da61d3dfef218c41e0a3..0f5d3ddf873cddff39056d7532a7c71c0244277a 100644 (file)
@@ -18,6 +18,8 @@
 #include <sys/fcntl.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <dirent.h>
+#include <sys/xattr.h>
 
 TEST(LibCephFS, Open_empty_component) {
 
@@ -64,3 +66,285 @@ TEST(LibCephFS, Mount_non_exist) {
   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
   ASSERT_NE(0, ceph_mount(cmount, "/non-exist"));
 }
+
+TEST(LibCephFS, Mount) {
+  struct ceph_mount_info *cmount;
+  ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+  ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+  ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+  ceph_shutdown(cmount);
+
+  ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+  ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+  ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+  ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Dir_ls) {
+
+  pid_t mypid = getpid();
+
+  struct ceph_mount_info *cmount;
+  ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+  ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+  ASSERT_EQ(ceph_mount(cmount, "/"), 0);
+
+  struct ceph_dir_result *ls_dir = NULL;
+  char foostr[256];
+  sprintf(foostr, "dir_ls%d", mypid);
+  ASSERT_EQ(ceph_opendir(cmount, foostr, &ls_dir), -ENOENT);
+
+  ASSERT_EQ(ceph_mkdir(cmount, foostr, 0777), 0);
+  struct stat stbuf;
+  ASSERT_EQ(ceph_lstat(cmount, foostr, &stbuf), 0);
+  ASSERT_NE(S_ISDIR(stbuf.st_mode), 0);
+
+  char barstr[256];
+  sprintf(barstr, "dir_ls2%d", mypid);
+  ASSERT_EQ(ceph_lstat(cmount, barstr, &stbuf), -ENOENT);
+
+  // insert files into directory and test open
+  char bazstr[256];
+  int i = 0, r = rand() % 4096;
+  for(; i < r; ++i) {
+
+    sprintf(bazstr, "dir_ls%d/dirf%d", mypid, i);
+    int fd  = ceph_open(cmount, bazstr, O_CREAT|O_RDONLY, 0666);
+    ASSERT_GT(fd, 0);
+    ASSERT_EQ(ceph_close(cmount, fd), 0);
+
+    // set file sizes for readdirplus
+    ceph_truncate(cmount, bazstr, i);
+  }
+
+  ASSERT_EQ(ceph_opendir(cmount, foostr, &ls_dir), 0);
+
+  // not guaranteed to get . and .. first, but its a safe assumption in this case
+  struct dirent *result = ceph_readdir(cmount, ls_dir);
+  ASSERT_TRUE(result != NULL);
+  ASSERT_STREQ(result->d_name, ".");
+  result = ceph_readdir(cmount, ls_dir);
+  ASSERT_TRUE(result != NULL);
+  ASSERT_STREQ(result->d_name, "..");
+
+  std::vector<std::pair<char *, int> > entries;
+  // check readdir and capture stream order for future tests
+  for(i = 0; i < r; ++i) {
+
+    result = ceph_readdir(cmount, ls_dir);
+    ASSERT_TRUE(result != NULL);
+
+    int size;
+    sscanf(result->d_name, "dirf%d", &size);
+    entries.push_back(std::pair<char*,int>(strdup(result->d_name), size));
+  }
+
+  ASSERT_TRUE(ceph_readdir(cmount, ls_dir) == NULL);
+
+  // test rewinddir
+  ceph_rewinddir(cmount, ls_dir);
+
+  result = ceph_readdir(cmount, ls_dir);
+  ASSERT_TRUE(result != NULL);
+  ASSERT_STREQ(result->d_name, ".");
+  result = ceph_readdir(cmount, ls_dir);
+  ASSERT_TRUE(result != NULL);
+  ASSERT_STREQ(result->d_name, "..");
+
+  // check telldir
+  for(i = 0; i < r-1; ++i) {
+    int r = ceph_telldir(cmount, ls_dir);
+    ASSERT_GT(r, -1);
+    ceph_seekdir(cmount, ls_dir, r);
+    result = ceph_readdir(cmount, ls_dir);
+    ASSERT_TRUE(result != NULL);
+    ASSERT_STREQ(result->d_name, entries[i].first);
+  }
+
+  int t = ceph_telldir(cmount, ls_dir);
+  ASSERT_GT(t, -1);
+
+  ceph_rewinddir(cmount, ls_dir);
+
+  // test seekdir - move to end
+  ceph_seekdir(cmount, ls_dir, t);
+
+  // check that we're at the end
+  ASSERT_TRUE(ceph_readdir(cmount, ls_dir) != NULL);
+  ASSERT_TRUE(ceph_readdir(cmount, ls_dir) == NULL);
+
+  ceph_rewinddir(cmount, ls_dir);
+
+  // test getdents
+  struct dirent *getdents_entries;
+  getdents_entries = (struct dirent *)malloc(r * sizeof(*getdents_entries));
+
+  int count = 0;
+  while (count < r) {
+    int len = ceph_getdents(cmount, ls_dir, (char *)getdents_entries, r * sizeof(*getdents_entries));
+    ASSERT_GT(len, 0);
+    ASSERT_TRUE((len % sizeof(*getdents_entries)) == 0);
+    int n = len / sizeof(*getdents_entries);
+    if (count == 0) {
+      ASSERT_STREQ(getdents_entries[0].d_name, ".");
+      ASSERT_STREQ(getdents_entries[1].d_name, "..");
+    }
+    int j;
+    i = count;
+    for(j = 2; j < n; ++i, ++j) {
+      ASSERT_STREQ(getdents_entries[j].d_name, entries[i].first);
+    }
+    count += n;
+  }
+
+  free(getdents_entries);
+
+  // test readdir_r
+  ceph_rewinddir(cmount, ls_dir);
+
+  result = ceph_readdir(cmount, ls_dir);
+  ASSERT_TRUE(result != NULL);
+  ASSERT_STREQ(result->d_name, ".");
+  result = ceph_readdir(cmount, ls_dir);
+  ASSERT_TRUE(result != NULL);
+  ASSERT_STREQ(result->d_name, "..");
+
+  for(i = 0; i < r; ++i) {
+    struct dirent rdent;
+    ASSERT_EQ(ceph_readdir_r(cmount, ls_dir, &rdent), 1);
+    ASSERT_STREQ(rdent.d_name, entries[i].first);
+  }
+
+  // test readdirplus
+  ceph_rewinddir(cmount, ls_dir);
+
+  result = ceph_readdir(cmount, ls_dir);
+  ASSERT_TRUE(result != NULL);
+  ASSERT_STREQ(result->d_name, ".");
+  result = ceph_readdir(cmount, ls_dir);
+  ASSERT_TRUE(result != NULL);
+  ASSERT_STREQ(result->d_name, "..");
+
+  for(i = 0; i < r; ++i) {
+    struct dirent rdent;
+    struct stat st;
+    int stmask;
+    ASSERT_EQ(ceph_readdirplus_r(cmount, ls_dir, &rdent, &st, &stmask), 1);
+    ASSERT_STREQ(rdent.d_name, entries[i].first);
+    ASSERT_EQ(st.st_size, entries[i].second);
+    ASSERT_EQ(st.st_ino, rdent.d_ino);
+    //ASSERT_EQ(st.st_mode, (mode_t)0666);
+  }
+
+  ASSERT_EQ(ceph_closedir(cmount, ls_dir), 0);
+
+  ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Many_nested_dirs) {
+  struct ceph_mount_info *cmount;
+  ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+  ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+  ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+  const char *many_path = "a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a";
+  ASSERT_EQ(ceph_mkdirs(cmount, many_path, 0755), 0);
+
+  int i = 0;
+
+  for(; i < 39; ++i) {
+    ASSERT_EQ(ceph_chdir(cmount, "a"), 0);
+
+    struct ceph_dir_result *dirp;
+    ASSERT_EQ(ceph_opendir(cmount, "a", &dirp), 0);
+    struct dirent *dent = ceph_readdir(cmount, dirp);
+    ASSERT_TRUE(dent != NULL);
+    ASSERT_STREQ(dent->d_name, ".");
+    dent = ceph_readdir(cmount, dirp);
+    ASSERT_TRUE(dent != NULL);
+    ASSERT_STREQ(dent->d_name, "..");
+    dent = ceph_readdir(cmount, dirp);
+    ASSERT_TRUE(dent != NULL);
+    ASSERT_STREQ(dent->d_name, "a");
+    ASSERT_EQ(ceph_closedir(cmount, dirp), 0);
+  }
+
+  ASSERT_STREQ(ceph_getcwd(cmount), "/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a");
+
+  ASSERT_EQ(ceph_chdir(cmount, "a/a/a"), 0);
+
+  for(i = 0; i < 39; ++i) {
+    ASSERT_EQ(ceph_chdir(cmount, ".."), 0);
+    ASSERT_EQ(ceph_rmdir(cmount, "a"), 0);
+  }
+
+  ASSERT_EQ(ceph_chdir(cmount, "/"), 0);
+
+  ASSERT_EQ(ceph_rmdir(cmount, "a/a/a"), 0);
+
+  ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Xattrs) {
+  struct ceph_mount_info *cmount;
+  ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+  ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+  ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+  char test_xattr_file[256];
+  sprintf(test_xattr_file, "test_xattr_%d", getpid());
+  int fd = ceph_open(cmount, test_xattr_file, O_CREAT, 0666);
+  ASSERT_GT(fd, 0);
+
+  char i = 'a';
+  char xattrk[128];
+  char xattrv[128];
+  for(; i < 'a'+26; ++i) {
+    sprintf(xattrk, "user.test_xattr_%c", i);
+    int len = sprintf(xattrv, "testxattr%c", i);
+    ASSERT_EQ(ceph_setxattr(cmount, test_xattr_file, xattrk, (void *) xattrv, len, XATTR_CREATE), 0);
+  }
+
+  char xattrlist[128*26];
+  int len = ceph_listxattr(cmount, test_xattr_file, xattrlist, sizeof(xattrlist));
+  char *p = xattrlist;
+  char *n;
+  i = 'a';
+  while(len > 0) {
+    sprintf(xattrk, "user.test_xattr_%c", i);
+    ASSERT_STREQ(p, xattrk);
+
+    char gxattrv[128];
+    int alen = ceph_getxattr(cmount, test_xattr_file, p, (void *) gxattrv, 128);
+    sprintf(xattrv, "testxattr%c", i);
+    ASSERT_TRUE(!strncmp(xattrv, gxattrv, alen));
+
+    n = index(p, '\0');
+    n++;
+    len -= (n - p);
+    p = n;
+    ++i;
+  }
+
+  i = 'a';
+  for(i = 'a'; i < 'a'+26; ++i) {
+    sprintf(xattrk, "user.test_xattr_%c", i);
+    ASSERT_EQ(ceph_removexattr(cmount, test_xattr_file, xattrk), 0);
+  }
+
+  ceph_close(cmount, fd);
+  ceph_shutdown(cmount);
+}
+
+TEST(LibCephFS, Lstat_slashdot) {
+  struct ceph_mount_info *cmount;
+  ASSERT_EQ(ceph_create(&cmount, NULL), 0);
+  ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
+  ASSERT_EQ(ceph_mount(cmount, NULL), 0);
+
+  struct stat stbuf;
+  ASSERT_EQ(ceph_lstat(cmount, "/.", &stbuf), 0);
+  ASSERT_EQ(ceph_lstat(cmount, ".", &stbuf), 0);
+
+  ceph_shutdown(cmount);
+}