---
 configure.ac                    |    1 +
 src/Makefile.am                 |  118 ++++++++++++++++
 src/fs/rock/RockSwapDir.cc      |   23 +---
 src/fs/rock/RockSwapDir.h       |   18 ++-
 src/tests/stub_store_rebuild.cc |    4 +-
 src/tests/stub_tools.cc         |    5 +-
 src/tests/testRock.cc           |  288 +++++++++++++++++++++++++++++++++++++++
 src/tests/testRock.h            |   37 +++++
 8 files changed, 470 insertions(+), 24 deletions(-)
 create mode 100644 src/tests/testRock.cc
 create mode 100644 src/tests/testRock.h

diff --git configure.ac configure.ac
index 273d89c..586864c 100644
--- configure.ac
+++ configure.ac
@@ -769,40 +769,41 @@ for fs in $squid_storeio_module_candidates none; do
     aufs)
       if test "x$squid_disk_module_candidates_Blocking" != "xyes" -a \
         "$squid_disk_module_candidates_DiskThreads" != "yes" ; then
         AC_MSG_ERROR([Storage module aufs requires DiskIO module: Blocking or DiskThreads])
       fi
       ;;
     coss)
       AC_MSG_WARN([COSS Support is not stable yet in Squid-3. Please do not use.])
       if ! test "x$squid_disk_module_candidates_AIO" = "xyes"; then
         AC_MSG_ERROR([COSS requires POSIX AIO which is not available.])
       fi
       # Automake on MinGW needs explicit exe extension
       # for STORE_TESTS substition
       STORE_TESTS="$STORE_TESTS tests/testCoss$EXEEXT"
       ;;
     rock)
 	if test "x$squid_disk_module_candidates_IpcIo" != "xyes" -a \
 	  "x$squid_disk_module_candidates_Blocking" != "xyes"; then
 	  AC_MSG_ERROR([Storage module Rock requires IpcIo or Blocking DiskIO module])
 	fi
+	STORE_TESTS="$STORE_TESTS tests/testRock$EXEEXT"
 	;;
     ufs)
       STORE_TESTS="$STORE_TESTS tests/testUfs$EXEEXT"
     esac
 done
 
 dnl hack: need to define those even if not used in the build system to
 dnl make sure that global FS objects are linked to the squid binary.
 AH_TEMPLATE(HAVE_FS_UFS, "Define to 1 if ufs filesystem module is build")
 AH_TEMPLATE(HAVE_FS_AUFS, "Define to 1 if aufs filesystem module is build")
 AH_TEMPLATE(HAVE_FS_DISKD, "Define to 1 if diskd filesystem module is build")
 AH_TEMPLATE(HAVE_FS_COSS, "Define to 1 if coss filesystem module is build")
 AH_TEMPLATE(HAVE_FS_ROCK, "Define to 1 if rock filesystem module is build")
 
 
 dnl got final squid_storeio_module_candidates, build library lists
 dnl This list will not be needed when each fs library has its own Makefile
 STORE_LIBS_TO_BUILD=
 dnl File system libraries to link executables with.
 dnl These are the same as STORE_LIBS_TO_BUILD, but with a path
diff --git src/Makefile.am src/Makefile.am
index 8d80a0e..96072e2 100644
--- src/Makefile.am
+++ src/Makefile.am
@@ -169,40 +169,41 @@ AIO_WIN32_SOURCES =
 endif
 
 if USE_AIOPS_WIN32
 AIOPS_SOURCE = DiskIO/DiskThreads/aiops_win32.cc
 else
 AIOPS_SOURCE = DiskIO/DiskThreads/aiops.cc
 endif
 
 EXTRA_LIBRARIES = libAIO.a libBlocking.a libDiskDaemon.a libDiskThreads.a \
 	libMmapped.a libIpcIo.a
 noinst_LIBRARIES = $(DISK_LIBS)
 noinst_LTLIBRARIES = libsquid.la
 
 EXTRA_PROGRAMS = \
 	DiskIO/DiskDaemon/diskd \
 	unlinkd \
 	dnsserver \
 	recv-announce \
 	tests/testUfs \
 	tests/testCoss \
+	tests/testRock \
 	tests/testNull \
 	ufsdump
 
 ## cfgen is used when building squid
 ## ufsdump is a debug utility, it is possibly useful for end users with cache
 ## corruption, but at this point we do not install it.
 noinst_PROGRAMS = \
 	cf_gen
 
 sbin_PROGRAMS = \
 	squid
 
 bin_PROGRAMS = 
 
 
 libexec_PROGRAMS = \
 	$(DNSSERVER) \
 	$(DISK_PROGRAMS) \
 	$(UNLINKD)
 
@@ -2612,40 +2613,157 @@ tests_testUfs_LDADD = \
 	$(DISK_LIBS) \
 	$(DISK_OS_LIBS) \
 	acl/libapi.la \
 	ipc/libipc.la \
 	$(SSL_LIBS) \
 	comm/libcomm.la \
 	base/libbase.la \
 	ip/libip.la \
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
 	$(top_builddir)/lib/libmiscutil.la \
 	$(REGEXLIB) \
 	$(SQUID_CPPUNIT_LIBS) \
 	$(SSLLIB) \
 	$(COMPAT_LIB) \
 	$(XTRA_LIBS)
 tests_testUfs_LDFLAGS = $(LIBADD_DL)
 tests_testUfs_DEPENDENCIES = \
 	$(SWAP_TEST_DS)
 
+tests_testRock_SOURCES = \
+	cbdata.cc \
+	CacheDigest.cc \
+	ConfigOption.cc \
+	ConfigParser.cc \
+	disk.cc \
+	ETag.cc \
+	EventLoop.cc \
+	event.cc \
+	fd.cc \
+	HttpBody.cc \
+	HttpHdrCc.cc \
+	HttpHdrContRange.cc \
+	HttpHdrRange.cc \
+	HttpHdrSc.cc \
+	HttpHdrScTarget.cc \
+	HttpHeader.cc \
+	HttpHeaderTools.cc \
+	HttpMsg.cc \
+	HttpReply.cc \
+	HttpRequestMethod.cc \
+	HttpStatusLine.cc \
+	int.cc \
+	mem.cc \
+	MemBuf.cc \
+	MemObject.cc \
+	mem_node.cc \
+	Packer.cc \
+	Parsing.cc \
+	RemovalPolicy.cc \
+	StatHist.cc \
+	stmem.cc \
+	store.cc \
+	StoreFileSystem.cc \
+	StoreIOState.cc \
+	StoreMeta.cc \
+	StoreMetaMD5.cc \
+	StoreMetaSTD.cc \
+	StoreMetaSTDLFS.cc \
+	StoreMetaURL.cc \
+	StoreMetaUnpacker.cc \
+	StoreMetaVary.cc \
+	store_dir.cc \
+	store_io.cc \
+	store_key_md5.cc \
+	store_swapmeta.cc \
+	store_swapout.cc \
+	String.cc \
+	SwapDir.cc \
+	tests/testRock.cc \
+	tests/testMain.cc \
+	tests/testRock.h \
+	tests/testStoreSupport.cc \
+	tests/testStoreSupport.h \
+	tests/stub_access_log.cc \
+	tests/stub_cache_cf.cc \
+	tests/stub_cache_manager.cc \
+	tests/stub_client_db.cc \
+	tests/stub_client_side_request.cc \
+	tests/stub_debug.cc \
+	tests/stub_errorpage.cc \
+	tests/stub_HelperChildConfig.cc \
+	tests/stub_http.cc \
+	tests/stub_HttpRequest.cc \
+	tests/stub_ipc.cc \
+	tests/stub_MemStore.cc \
+	tests/stub_mime.cc \
+	tests/stub_Port.cc \
+	tests/stub_pconn.cc \
+	tests/stub_store_client.cc \
+	tests/stub_store_rebuild.cc \
+	tests/stub_tools.cc \
+	tests/stub_UdsOp.cc \
+	time.cc \
+	url.cc \
+	URLScheme.cc \
+	wordlist.cc \
+	$(DELAY_POOL_SOURCE) \
+	$(DISKIO_SOURCE) \
+	$(UNLINKDSOURCE)
+nodist_tests_testRock_SOURCES = \
+	$(DISKIO_GEN_SOURCE) \
+	swap_log_op.cc \
+	SquidMath.cc \
+	SquidMath.h \
+	$(TESTSOURCES)
+tests_testRock_LDADD = \
+	anyp/libanyp.la \
+	libsquid.la \
+	comm/libcomm.la \
+	ip/libip.la \
+	fs/libfs.la \
+	$(AUTH_LIBS) \
+	$(COMMON_LIBS) \
+	$(REPL_OBJS) \
+	$(DISK_LIBS) \
+	$(DISK_OS_LIBS) \
+	acl/libacls.la \
+	acl/libapi.la \
+	acl/libstate.la \
+	eui/libeui.la \
+	ipc/libipc.la \
+	mgr/libmgr.la \
+	base/libbase.la \
+	$(SSL_LIBS) \
+	$(top_builddir)/lib/libmisccontainers.la \
+	$(top_builddir)/lib/libmiscencoding.la \
+	$(top_builddir)/lib/libmiscutil.la \
+	$(REGEXLIB) \
+	$(SQUID_CPPUNIT_LIBS) \
+	$(SSLLIB) \
+	$(COMPAT_LIB) \
+	$(XTRA_LIBS)
+tests_testRock_LDFLAGS = $(LIBADD_DL)
+tests_testRock_DEPENDENCIES = \
+	$(SWAP_TEST_DS)
+
 tests_testCoss_SOURCES = \
 	tests/testCoss.cc \
 	tests/testMain.cc \
 	tests/testCoss.h \
 	tests/stub_cache_manager.cc \
 	tests/stub_client_db.cc \
 	tests/stub_debug.cc \
 	tests/stub_HelperChildConfig.cc \
 	tests/stub_internal.cc \
 	tests/stub_ipc.cc \
 	tests/stub_pconn.cc \
 	tests/stub_store_rebuild.cc \
 	fd.cc \
 	disk.cc \
 	filemap.cc \
 	HttpBody.cc \
 	HttpReply.cc \
 	HttpStatusLine.cc \
 	int.cc \
 	list.cc \
diff --git src/fs/rock/RockSwapDir.cc src/fs/rock/RockSwapDir.cc
index 9673712..600f143 100644
--- src/fs/rock/RockSwapDir.cc
+++ src/fs/rock/RockSwapDir.cc
@@ -1,28 +1,27 @@
 /*
  * $Id$
  *
  * DEBUG: section 47    Store Directory Routines
  */
 
 #include "config.h"
-#include "base/RunnersRegistry.h"
 #include "ConfigOption.h"
 #include "DiskIO/DiskIOModule.h"
 #include "DiskIO/DiskIOStrategy.h"
 #include "DiskIO/ReadRequest.h"
 #include "DiskIO/WriteRequest.h"
 #include "fs/rock/RockSwapDir.h"
 #include "fs/rock/RockIoState.h"
 #include "fs/rock/RockIoRequests.h"
 #include "fs/rock/RockRebuild.h"
 #include "ipc/mem/Pages.h"
 #include "MemObject.h"
 #include "Parsing.h"
 #include "SquidMath.h"
 #include <iomanip>
 
 const int64_t Rock::SwapDir::HeaderSize = 16*1024;
 
 Rock::SwapDir::SwapDir(): ::SwapDir("rock"), filePath(NULL), io(NULL), map(NULL)
 {
 }
@@ -753,55 +752,43 @@ Rock::SwapDir::statfs(StoreEntry &e) const
             }
         }
     }
 
     storeAppendPrintf(&e, "Pending operations: %d out of %d\n",
                       store_open_disk_fd, Config.max_open_disk_fds);
 
     storeAppendPrintf(&e, "Flags:");
 
     if (flags.selected)
         storeAppendPrintf(&e, " SELECTED");
 
     if (flags.read_only)
         storeAppendPrintf(&e, " READ-ONLY");
 
     storeAppendPrintf(&e, "\n");
 
 }
 
 
-/// initializes shared memory segments used by Rock::SwapDir
-class RockSwapDirRr: public Ipc::Mem::RegisteredRunner
+namespace Rock
 {
-public:
-    /* RegisteredRunner API */
-    virtual ~RockSwapDirRr();
-
-protected:
-    virtual void create(const RunnerRegistry &);
-
-private:
-    Vector<Rock::SwapDir::DirMap::Owner *> owners;
-};
-
-RunnerRegistrationEntry(rrAfterConfig, RockSwapDirRr);
-
+    RunnerRegistrationEntry(rrAfterConfig, SwapDirRr);
+}
 
-void RockSwapDirRr::create(const RunnerRegistry &)
+void Rock::SwapDirRr::create(const RunnerRegistry &)
 {
     Must(owners.empty());
     for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
         if (const Rock::SwapDir *const sd = dynamic_cast<Rock::SwapDir *>(INDEXSD(i))) {
             // TODO: check whether entryLimitAllowed() has map here
             Rock::SwapDir::DirMap::Owner *const owner =
                 Rock::SwapDir::DirMap::Init(sd->path, sd->entryLimitAllowed());
             owners.push_back(owner);
         }
     }
 }
 
-RockSwapDirRr::~RockSwapDirRr()
+Rock::SwapDirRr::~SwapDirRr()
 {
     for (size_t i = 0; i < owners.size(); ++i)
         delete owners[i];
 }
diff --git src/fs/rock/RockSwapDir.h src/fs/rock/RockSwapDir.h
index 4e56c42..b89927b 100644
--- src/fs/rock/RockSwapDir.h
+++ src/fs/rock/RockSwapDir.h
@@ -16,84 +16,98 @@ namespace Rock
 
 class Rebuild;
 
 /// \ingroup Rock
 class SwapDir: public ::SwapDir, public IORequestor
 {
 public:
     SwapDir();
     virtual ~SwapDir();
 
     /* public ::SwapDir API */
     virtual void reconfigure();
     virtual StoreSearch *search(String const url, HttpRequest *);
     virtual StoreEntry *get(const cache_key *key);
     virtual void get(String const, STOREGETCLIENT, void * cbdata);
     virtual void disconnect(StoreEntry &e);
     virtual uint64_t currentSize() const;
     virtual uint64_t currentCount() const;
     virtual bool doReportStat() const;
     virtual void swappedOut(const StoreEntry &e);
+    virtual void create();
+    virtual void parse(int index, char *path);
 
     int64_t entryLimitHigh() const { return SwapFilenMax; } ///< Core limit
     int64_t entryLimitAllowed() const;
 
     typedef Ipc::StoreMapWithExtras<DbCellHeader> DirMap;
 
 protected:
     /* protected ::SwapDir API */
     virtual bool needsDiskStrand() const;
-    virtual void create();
     virtual void init();
     virtual ConfigOption *getOptionTree() const;
     virtual bool allowOptionReconfigure(const char *const option) const;
     virtual bool canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const;
     virtual StoreIOState::Pointer createStoreIO(StoreEntry &, StoreIOState::STFNCB *, StoreIOState::STIOCB *, void *);
     virtual StoreIOState::Pointer openStoreIO(StoreEntry &, StoreIOState::STFNCB *, StoreIOState::STIOCB *, void *);
     virtual void maintain();
     virtual void diskFull();
     virtual void reference(StoreEntry &e);
     virtual bool dereference(StoreEntry &e);
     virtual void unlink(StoreEntry &e);
     virtual void statfs(StoreEntry &e) const;
 
     /* IORequestor API */
     virtual void ioCompletedNotification();
     virtual void closeCompleted();
     virtual void readCompleted(const char *buf, int len, int errflag, RefCount< ::ReadRequest>);
     virtual void writeCompleted(int errflag, size_t len, RefCount< ::WriteRequest>);
 
-    virtual void parse(int index, char *path);
     void parseSize(const bool reconfiguring); ///< parses anonymous cache_dir size option
     void validateOptions(); ///< warns of configuration problems; may quit
     bool parseTimeOption(char const *option, const char *value, int reconfiguring);
     void dumpTimeOption(StoreEntry * e) const;
 
     void rebuild(); ///< starts loading and validating stored entry metadata
     ///< used to add entries successfully loaded during rebuild
     bool addEntry(const int fileno, const DbCellHeader &header, const StoreEntry &from);
 
     bool full() const; ///< no more entries can be stored without purging
     void trackReferences(StoreEntry &e); ///< add to replacement policy scope
     void ignoreReferences(StoreEntry &e); ///< delete from repl policy scope
 
     int64_t diskOffset(int filen) const;
     int64_t diskOffsetLimit() const;
     int entryLimit() const { return map->entryLimit(); }
 
     friend class Rebuild;
     const char *filePath; ///< location of cache storage file inside path/
 
 private:
     DiskIOStrategy *io;
     RefCount<DiskFile> theFile; ///< cache storage for this cache_dir
     DirMap *map;
 
     /* configurable options */
     DiskFile::Config fileConfig; ///< file-level configuration options
 
     static const int64_t HeaderSize; ///< on-disk db header size
 };
 
+/// initializes shared memory segments used by Rock::SwapDir
+class SwapDirRr: public Ipc::Mem::RegisteredRunner
+{
+public:
+    /* RegisteredRunner API */
+    virtual ~SwapDirRr();
+
+protected:
+    virtual void create(const RunnerRegistry &);
+
+private:
+    Vector<SwapDir::DirMap::Owner *> owners;
+};
+
 } // namespace Rock
 
 #endif /* SQUID_FS_ROCK_SWAP_DIR_H */
diff --git src/tests/stub_store_rebuild.cc src/tests/stub_store_rebuild.cc
index 22c567a..608cbdd 100644
--- src/tests/stub_store_rebuild.cc
+++ src/tests/stub_store_rebuild.cc
@@ -19,43 +19,41 @@
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, 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 General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  *
  */
 
 #include "squid.h"
 
 void
 storeRebuildProgress(int sd_index, int total, int sofar)
-{
-    fatal ("Not implemented");
-}
+{}
 
 void
 
 storeRebuildComplete(struct _store_rebuild_data *dc)
 {}
 
 bool
 storeRebuildLoadEntry(int, int, MemBuf&, _store_rebuild_data&)
 {
     return false;
 }
 
 bool
 storeRebuildKeepEntry(const StoreEntry &tmpe, const cache_key *key,
                       struct _store_rebuild_data &counts)
 {
     return false;
 }
 
 bool
diff --git src/tests/stub_tools.cc src/tests/stub_tools.cc
index f86d0d7..27afd55 100644
--- src/tests/stub_tools.cc
+++ src/tests/stub_tools.cc
@@ -55,41 +55,44 @@ xmemset(void* dst, int val, size_t sz)
 }
 
 bool
 IamWorkerProcess()
 {
     fprintf(stderr, "Not implemented");
     return true;
 }
 
 bool
 IamDiskProcess()
 {
     fprintf(stderr, "Not implemented");
     return false;
 }
 
 bool
 IamMasterProcess()
 {
     fprintf(stderr, "Not implemented");
-    return false;
+    // Since most tests run as a single process, this is the best default.
+    // TODO: If some test case uses multiple processes and cares about
+    // its role, we may need to parameterize or remove this stub.
+    return true;
 }
 
 bool
 InDaemonMode()
 {
     fprintf(stderr, "Not implemented");
     return false;
 }
 
 bool
 UsingSmp()
 {
     fprintf(stderr, "Not implemented");
     return false;
 }
 
 void
 logsFlush(void)
 {
     fatal("tools.cc required");
diff --git src/tests/testRock.cc src/tests/testRock.cc
new file mode 100644
index 0000000..4fbd233
--- /dev/null
+++ src/tests/testRock.cc
@@ -0,0 +1,288 @@
+#define SQUID_UNIT_TEST 1
+#include "config.h"
+
+#include "DiskIO/DiskIOModule.h"
+#include "HttpHeader.h"
+#include "HttpReply.h"
+#include "Mem.h"
+#include "MemObject.h"
+#include "Store.h"
+#include "StoreFileSystem.h"
+#include "StoreSearch.h"
+#include "SwapDir.h"
+#include "fs/rock/RockSwapDir.h"
+#include "testRock.h"
+#include "testStoreSupport.h"
+
+#if HAVE_STDEXCEPT
+#include <stdexcept>
+#endif
+
+#define TESTDIR "testRock__testRockSearch"
+
+CPPUNIT_TEST_SUITE_REGISTRATION( testRock );
+
+extern REMOVALPOLICYCREATE createRemovalPolicy_lru;
+
+static void
+addSwapDir(testRock::SwapDirPointer aStore)
+{
+    allocate_new_swapdir(&Config.cacheSwap);
+    Config.cacheSwap.swapDirs[Config.cacheSwap.n_configured] = aStore.getRaw();
+    ++Config.cacheSwap.n_configured;
+}
+
+void
+testRock::setUp()
+{
+    CPPUNIT_NS::TestFixture::setUp();
+
+    if (0 > system ("rm -rf " TESTDIR))
+        throw std::runtime_error("Failed to clean test work directory");
+
+    Store::Root(new StoreController);
+
+    store = new Rock::SwapDir();
+
+    addSwapDir(store);
+
+    commonInit();
+
+    char *path=xstrdup(TESTDIR);
+
+    char *config_line=xstrdup("foo 100 max-size=16384");
+
+    strtok(config_line, w_space);
+
+    store->parse(0, path);
+
+    safe_free(path);
+
+    safe_free(config_line);
+
+    /* ok, ready to create */
+    store->create();
+
+    rr = new Rock::SwapDirRr;
+    rr->run(rrAfterConfig);
+}
+
+void
+testRock::tearDown()
+{
+    CPPUNIT_NS::TestFixture::tearDown();
+
+    Store::Root(NULL);
+
+    store = NULL;
+
+    free_cachedir(&Config.cacheSwap);
+
+    delete rr;
+
+    // TODO: do this once, or each time.
+    // safe_free(Config.replPolicy->type);
+    // delete Config.replPolicy;
+
+    if (0 > system ("rm -rf " TESTDIR))
+        throw std::runtime_error("Failed to clean test work directory");
+}
+
+void
+testRock::commonInit()
+{
+    static bool inited = false;
+
+    if (inited)
+        return;
+
+    StoreFileSystem::SetupAllFs();
+
+    Config.Store.avgObjectSize = 1024;
+
+    Config.Store.objectsPerBucket = 20;
+
+    Config.Store.maxObjectSize = 2048;
+
+    Config.store_dir_select_algorithm = xstrdup("round-robin");
+
+    Config.replPolicy = new RemovalPolicySettings;
+
+    Config.replPolicy->type = xstrdup ("lru");
+
+    Config.replPolicy->args = NULL;
+
+    /* garh garh */
+    storeReplAdd("lru", createRemovalPolicy_lru);
+
+    visible_appname_string = xstrdup(APP_FULLNAME);
+
+    Mem::Init();
+
+    comm_init();
+
+    httpHeaderInitModule();	/* must go before any header processing (e.g. the one in errorInitialize) */
+
+    httpReplyInitModule();	/* must go before accepting replies */
+
+    mem_policy = createRemovalPolicy(Config.replPolicy);
+
+    inited = true;
+}
+
+void
+testRock::storeInit()
+{
+    /* ok, ready to use */
+    Store::Root().init();
+
+    /* rebuild is a scheduled event */
+    StockEventLoop loop;
+
+    /* our swapdir must be scheduled to rebuild */
+    CPPUNIT_ASSERT_EQUAL(2, StoreController::store_dirs_rebuilding);
+
+    loop.run();
+
+    /* cannot use loop.run(); as the loop will never idle: the store-dir
+     * clean() scheduled event prevents it
+     */
+
+    /* nothing left to rebuild */
+    CPPUNIT_ASSERT_EQUAL(1, StoreController::store_dirs_rebuilding);
+}
+
+StoreEntry *
+testRock::createEntry(const int i)
+{
+    request_flags flags;
+    flags.cachable = 1;
+    char url[64];
+    snprintf(url, sizeof(url), "dummy url %i", i);
+    url[sizeof(url) - 1] = '\0';
+    StoreEntry *const pe =
+        storeCreateEntry(url, "dummy log url", flags, METHOD_GET);
+    HttpReply *const rep = const_cast<HttpReply *>(pe->getReply());
+    rep->setHeaders(HTTP_OK, "dummy test object", "x-squid-internal/test",
+                    -1, -1, squid_curtime + 100000);
+
+    pe->setPublicKey();
+
+    return pe;
+}
+
+StoreEntry *
+testRock::addEntry(const int i)
+{
+    StoreEntry *const pe = createEntry(i);
+
+    pe->buffer();
+    /* TODO: remove this when the metadata is separated */
+    {
+        Packer p;
+        packerToStoreInit(&p, pe);
+        pe->getReply()->packHeadersInto(&p);
+        packerClean(&p);
+    }
+
+    pe->flush();
+    pe->timestampsSet();
+    pe->complete();
+    pe->swapOut();
+
+    return pe;
+}
+
+StoreEntry *
+testRock::getEntry(const int i)
+{
+    StoreEntry *const pe = createEntry(i);
+    return store->get(reinterpret_cast<const cache_key *>(pe->key));
+}
+
+void
+testRock::testRockCreate()
+{
+    struct stat sb;
+
+    CPPUNIT_ASSERT(::stat(TESTDIR, &sb) == 0);
+
+    /* TODO: check the size */
+
+    /* TODO: test rebuild */
+}
+
+void
+testRock::testRockSwapOut()
+{
+    storeInit();
+
+    // add few entries to prime the database
+    for (int i = 0; i < 5; ++i) {
+        CPPUNIT_ASSERT_EQUAL((uint64_t)i, store->currentCount());
+
+        StoreEntry *const pe = addEntry(i);
+
+        CPPUNIT_ASSERT(pe->swap_status == SWAPOUT_WRITING);
+        CPPUNIT_ASSERT(pe->swap_dirn == 0);
+        CPPUNIT_ASSERT(pe->swap_filen >= 0);
+
+        // Rock::IoState::finishedWriting() schedules an AsyncCall
+        // storeSwapOutFileClosed().  Let it fire.
+        StockEventLoop loop;
+        loop.run();
+
+        CPPUNIT_ASSERT(pe->swap_status == SWAPOUT_DONE);
+
+        pe->unlock();
+    }
+
+    CPPUNIT_ASSERT_EQUAL((uint64_t)5, store->currentCount());
+
+    // try to swap out entry to a used unlocked slot
+    {
+        StoreEntry *const pe = addEntry(4);
+
+        CPPUNIT_ASSERT(pe->swap_status == SWAPOUT_WRITING);
+        CPPUNIT_ASSERT(pe->swap_dirn == 0);
+        CPPUNIT_ASSERT(pe->swap_filen >= 0);
+
+        StockEventLoop loop;
+        loop.run();
+
+        CPPUNIT_ASSERT(pe->swap_status == SWAPOUT_DONE);
+    }
+
+    // try to swap out entry to a used locked slot
+    {
+        StoreEntry *const pe = addEntry(5);
+
+        CPPUNIT_ASSERT(pe->swap_status == SWAPOUT_WRITING);
+        CPPUNIT_ASSERT(pe->swap_dirn == 0);
+        CPPUNIT_ASSERT(pe->swap_filen >= 0);
+
+        // the slot is locked here because the async calls have not run yet
+        StoreEntry *const pe2 = addEntry(5);
+        CPPUNIT_ASSERT(pe2->swap_status == SWAPOUT_NONE);
+        CPPUNIT_ASSERT(pe2->mem_obj->swapout.decision ==
+                       MemObject::SwapOut::swImpossible);
+        CPPUNIT_ASSERT(pe2->swap_dirn == -1);
+        CPPUNIT_ASSERT(pe2->swap_filen == -1);
+
+        StockEventLoop loop;
+        loop.run();
+    }
+
+    CPPUNIT_ASSERT_EQUAL((uint64_t)6, store->currentCount());
+
+    // try to get and unlink entries
+    for (int i = 0; i < 6; ++i) {
+        StoreEntry *const pe = getEntry(i);
+        CPPUNIT_ASSERT(pe != NULL);
+
+        pe->unlink();
+
+        StoreEntry *const pe2 = getEntry(i);
+        CPPUNIT_ASSERT(pe2 == NULL);
+    }
+}
diff --git src/tests/testRock.h src/tests/testRock.h
new file mode 100644
index 0000000..2951d8f
--- /dev/null
+++ src/tests/testRock.h
@@ -0,0 +1,37 @@
+#ifndef SQUID_SRC_TEST_TESTROCK_H
+#define SQUID_SRC_TEST_TESTROCK_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+/*
+ * test the store framework
+ */
+
+class testRock : public CPPUNIT_NS::TestFixture
+{
+    CPPUNIT_TEST_SUITE( testRock );
+    CPPUNIT_TEST( testRockCreate );
+    CPPUNIT_TEST( testRockSwapOut );
+    CPPUNIT_TEST_SUITE_END();
+
+public:
+    virtual void setUp();
+    virtual void tearDown();
+
+    typedef RefCount<Rock::SwapDir> SwapDirPointer;
+
+protected:
+    void commonInit();
+    void storeInit();
+    StoreEntry *createEntry(const int i);
+    StoreEntry *addEntry(const int i);
+    StoreEntry *getEntry(const int i);
+    void testRockCreate();
+    void testRockSwapOut();
+
+private:
+    SwapDirPointer store;
+    Rock::SwapDirRr *rr;
+};
+
+#endif /* SQUID_SRC_TEST_TESTROCK_H */

Reply via email to