You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by sc...@apache.org on 2019/04/05 07:20:50 UTC

[trafficserver] branch master updated: Adds Cache test suits

This is an automated email from the ASF dual-hosted git repository.

scw00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 768bcf8  Adds Cache test suits
768bcf8 is described below

commit 768bcf8cf9b7d6fc36ec9145aed0ae1d9879e056
Author: scw00 <sc...@apache.org>
AuthorDate: Fri Mar 15 22:21:12 2019 +0800

    Adds Cache test suits
---
 .gitignore                                         |   3 +
 iocore/cache/Makefile.am                           | 124 ++++++
 iocore/cache/test/CacheTestHandler.cc              |  76 ++++
 iocore/cache/test/CacheTestHandler.h               |  99 +++++
 iocore/cache/test/main.cc                          | 273 +++++++++++++
 iocore/cache/test/main.h                           | 232 +++++++++++
 iocore/cache/test/storage.config                   | Bin 0 -> 24 bytes
 iocore/cache/test/stub.cc                          | 264 +++++++++++++
 iocore/cache/test/test_Alternate_L_to_S.cc         | 191 +++++++++
 .../cache/test/test_Alternate_L_to_S_remove_L.cc   | 260 +++++++++++++
 .../cache/test/test_Alternate_L_to_S_remove_S.cc   | 261 +++++++++++++
 iocore/cache/test/test_Alternate_S_to_L.cc         | 191 +++++++++
 .../cache/test/test_Alternate_S_to_L_remove_L.cc   | 264 +++++++++++++
 .../cache/test/test_Alternate_S_to_L_remove_S.cc   | 262 +++++++++++++
 iocore/cache/test/test_Cache.cc                    |  55 +++
 iocore/cache/test/test_RWW.cc                      | 429 +++++++++++++++++++++
 iocore/cache/test/var/trafficserver/guard.txt      |  24 ++
 17 files changed, 3008 insertions(+)

diff --git a/.gitignore b/.gitignore
index 0c7f285..3b85d10 100644
--- a/.gitignore
+++ b/.gitignore
@@ -178,3 +178,6 @@ RELEASE
 
 # autest
 tests/env-test/
+
+iocore/cache/test_*
+iocore/cache/test/var/trafficserver/cache.db
diff --git a/iocore/cache/Makefile.am b/iocore/cache/Makefile.am
index ed05579..a9993a3 100644
--- a/iocore/cache/Makefile.am
+++ b/iocore/cache/Makefile.am
@@ -66,6 +66,130 @@ libinkcache_a_SOURCES += \
 	P_CacheTest.h
 endif
 
+TESTS = $(check_PROGRAMS)
+
+test_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	$(iocore_include_dirs) \
+	-I$(abs_top_srcdir)/include \
+	-I$(abs_top_srcdir)/lib \
+	-I$(abs_top_srcdir)/proxy \
+	-I$(abs_top_srcdir)/proxy/http \
+	-I$(abs_top_srcdir)/proxy/http2 \
+	-I$(abs_top_srcdir)/proxy/http3 \
+	-I$(abs_top_srcdir)/proxy/logging \
+	-I$(abs_top_srcdir)/proxy/http/remap \
+	-I$(abs_top_srcdir)/proxy/hdrs \
+	-I$(abs_top_srcdir)/proxy/shared \
+	-I$(abs_top_srcdir)/mgmt \
+	-I$(abs_top_srcdir)/mgmt/utils \
+	-I$(abs_top_srcdir)/tests/include \
+	$(TS_INCLUDES) \
+	@OPENSSL_INCLUDES@
+
+test_LDADD = \
+	$(top_builddir)/src/tscpp/util/libtscpputil.la \
+	$(top_builddir)/iocore/cache/libinkcache.a \
+	$(top_builddir)/proxy/libproxy.a \
+	$(top_builddir)/proxy/http/libhttp.a \
+	$(top_builddir)/proxy/http/remap/libhttp_remap.a \
+	$(top_builddir)/proxy/libproxy.a \
+	$(top_builddir)/iocore/net/libinknet.a \
+	$(top_builddir)/iocore/dns/libinkdns.a \
+	$(top_builddir)/iocore/hostdb/libinkhostdb.a \
+	$(top_builddir)/proxy/logging/liblogging.a \
+	$(top_builddir)/proxy/hdrs/libhdrs.a \
+	$(top_builddir)/proxy/logging/liblogcollation.a \
+	$(top_builddir)/proxy/shared/libdiagsconfig.a \
+	$(top_builddir)/mgmt/libmgmt_p.la \
+	$(top_builddir)/iocore/utils/libinkutils.a \
+	$(top_builddir)/iocore/aio/libinkaio.a \
+	$(top_builddir)/src/tscore/libtscore.la \
+	$(top_builddir)/lib/records/librecords_p.a \
+	$(top_builddir)/iocore/eventsystem/libinkevent.a \
+	$(top_builddir)/lib/tsconfig/libtsconfig.la \
+	@HWLOC_LIBS@ \
+	@LIBPCRE@ \
+	@LIBRESOLV@ \
+	@LIBZ@ \
+	@LIBLZMA@ \
+	@LIBPROFILER@ \
+	@OPENSSL_LIBS@ \
+	@YAMLCPP_LIBS@ \
+	-lm
+
+
+check_PROGRAMS = \
+  test_Cache \
+  test_RWW \
+  test_Alternate_L_to_S \
+  test_Alternate_S_to_L \
+  test_Alternate_L_to_S_remove_L \
+  test_Alternate_L_to_S_remove_S \
+  test_Alternate_S_to_L_remove_S \
+  test_Alternate_S_to_L_remove_L
+
+test_main_SOURCES = \
+  ./test/main.cc \
+  ./test/stub.cc \
+  ./test/CacheTestHandler.cc
+
+test_Cache_CPPFLAGS = $(test_CPPFLAGS)
+test_Cache_LDFLAGS = @AM_LDFLAGS@
+test_Cache_LDADD = $(test_LDADD)
+test_Cache_SOURCES = \
+  $(test_main_SOURCES) \
+  ./test/test_Cache.cc
+
+test_RWW_CPPFLAGS = $(test_CPPFLAGS)
+test_RWW_LDFLAGS = @AM_LDFLAGS@
+test_RWW_LDADD = $(test_LDADD)
+test_RWW_SOURCES = \
+  $(test_main_SOURCES) \
+  ./test/test_RWW.cc
+
+test_Alternate_L_to_S_CPPFLAGS = $(test_CPPFLAGS)
+test_Alternate_L_to_S_LDFLAGS = @AM_LDFLAGS@
+test_Alternate_L_to_S_LDADD = $(test_LDADD)
+test_Alternate_L_to_S_SOURCES = \
+  $(test_main_SOURCES) \
+  ./test/test_Alternate_L_to_S.cc
+
+test_Alternate_S_to_L_CPPFLAGS = $(test_CPPFLAGS)
+test_Alternate_S_to_L_LDFLAGS = @AM_LDFLAGS@
+test_Alternate_S_to_L_LDADD = $(test_LDADD)
+test_Alternate_S_to_L_SOURCES = \
+  $(test_main_SOURCES) \
+  ./test/test_Alternate_S_to_L.cc
+
+test_Alternate_L_to_S_remove_L_CPPFLAGS = $(test_CPPFLAGS)
+test_Alternate_L_to_S_remove_L_LDFLAGS = @AM_LDFLAGS@
+test_Alternate_L_to_S_remove_L_LDADD = $(test_LDADD)
+test_Alternate_L_to_S_remove_L_SOURCES = \
+  $(test_main_SOURCES) \
+  ./test/test_Alternate_L_to_S_remove_L.cc
+
+test_Alternate_L_to_S_remove_S_CPPFLAGS = $(test_CPPFLAGS)
+test_Alternate_L_to_S_remove_S_LDFLAGS = @AM_LDFLAGS@
+test_Alternate_L_to_S_remove_S_LDADD = $(test_LDADD)
+test_Alternate_L_to_S_remove_S_SOURCES = \
+  $(test_main_SOURCES) \
+  ./test/test_Alternate_L_to_S_remove_S.cc
+
+test_Alternate_S_to_L_remove_S_CPPFLAGS = $(test_CPPFLAGS)
+test_Alternate_S_to_L_remove_S_LDFLAGS = @AM_LDFLAGS@
+test_Alternate_S_to_L_remove_S_LDADD = $(test_LDADD)
+test_Alternate_S_to_L_remove_S_SOURCES = \
+  $(test_main_SOURCES) \
+  ./test/test_Alternate_S_to_L_remove_S.cc
+
+test_Alternate_S_to_L_remove_L_CPPFLAGS = $(test_CPPFLAGS)
+test_Alternate_S_to_L_remove_L_LDFLAGS = @AM_LDFLAGS@
+test_Alternate_S_to_L_remove_L_LDADD = $(test_LDADD)
+test_Alternate_S_to_L_remove_L_SOURCES = \
+  $(test_main_SOURCES) \
+  ./test/test_Alternate_S_to_L_remove_L.cc
+
 include $(top_srcdir)/build/tidy.mk
 
 clang-tidy-local: $(DIST_SOURCES)
diff --git a/iocore/cache/test/CacheTestHandler.cc b/iocore/cache/test/CacheTestHandler.cc
new file mode 100644
index 0000000..72cecea
--- /dev/null
+++ b/iocore/cache/test/CacheTestHandler.cc
@@ -0,0 +1,76 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+ 1 test_Cache.cc + X distributed with this work for additional information regarding copyright ownership.  The ASF licenses this
+ file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#include "main.h"
+#include "CacheTestHandler.h"
+
+TestContChain::TestContChain() : Continuation(new_ProxyMutex()) {}
+
+CacheTestHandler::CacheTestHandler(size_t size, const char *url)
+{
+  this->_wt = new CacheWriteTest(size, this, url);
+  this->_rt = new CacheReadTest(size, this, url);
+
+  this->_wt->mutex = this->mutex;
+  this->_rt->mutex = this->mutex;
+  SET_HANDLER(&CacheTestHandler::start_test);
+}
+
+void
+CacheTestHandler::handle_cache_event(int event, CacheTestBase *base)
+{
+  REQUIRE(base != nullptr);
+  switch (event) {
+  case CACHE_EVENT_OPEN_READ:
+    base->do_io_read();
+    break;
+  case CACHE_EVENT_OPEN_WRITE:
+    base->do_io_write();
+    break;
+  case VC_EVENT_READ_READY:
+  case VC_EVENT_WRITE_READY:
+    REQUIRE(base->vc != nullptr);
+    REQUIRE(base->vio != nullptr);
+    base->reenable();
+    break;
+  case VC_EVENT_WRITE_COMPLETE:
+    this_ethread()->schedule_imm(this->_rt);
+    base->close();
+    break;
+  case VC_EVENT_READ_COMPLETE:
+    base->close();
+    delete this;
+    break;
+  default:
+    REQUIRE(false);
+    base->close();
+    delete this;
+    break;
+  }
+  return;
+}
+
+int
+CacheTestHandler::start_test(int event, void *e)
+{
+  this_ethread()->schedule_imm(this->_wt);
+  return 0;
+}
diff --git a/iocore/cache/test/CacheTestHandler.h b/iocore/cache/test/CacheTestHandler.h
new file mode 100644
index 0000000..3d2c1a0
--- /dev/null
+++ b/iocore/cache/test/CacheTestHandler.h
@@ -0,0 +1,99 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#pragma once
+
+void test_done();
+
+#define TEST_DONE() test_done();
+#define T_DONE 1
+#define T_CONT 1
+
+#define DEFAULT_URL "http://www.scw00.com/"
+
+#include <cstddef>
+
+class CacheTestBase;
+
+struct TestContChain : public Continuation {
+  TestContChain();
+  virtual ~TestContChain() { this->next_test(); }
+
+  void
+  add(TestContChain *n)
+  {
+    TestContChain *p = this;
+    while (p->next) {
+      p = p->next;
+    }
+    p->next = n;
+  }
+
+  bool
+  next_test()
+  {
+    if (!this->next) {
+      return false;
+    }
+
+    this_ethread()->schedule_imm(this->next);
+    return true;
+  }
+
+  TestContChain *next = nullptr;
+};
+
+class CacheTestHandler : public TestContChain
+{
+public:
+  CacheTestHandler() = default;
+  CacheTestHandler(size_t size, const char *url = DEFAULT_URL);
+
+  int start_test(int event, void *e);
+
+  virtual void handle_cache_event(int event, CacheTestBase *base);
+
+protected:
+  CacheTestBase *_rt = nullptr;
+  CacheTestBase *_wt = nullptr;
+};
+
+class TerminalTest : public CacheTestHandler
+{
+public:
+  TerminalTest() { SET_HANDLER(&TerminalTest::terminal_event); }
+  ~TerminalTest() { TEST_DONE(); }
+
+  int
+  terminal_event(int event, void *e)
+  {
+    delete this;
+    return 0;
+  }
+
+  void
+  handle_cache_event(int event, CacheTestBase *e) override
+  {
+    delete this;
+  }
+};
diff --git a/iocore/cache/test/main.cc b/iocore/cache/test/main.cc
new file mode 100644
index 0000000..c62cd3f
--- /dev/null
+++ b/iocore/cache/test/main.cc
@@ -0,0 +1,273 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#define CATCH_CONFIG_MAIN
+#include "main.h"
+
+#define THREADS 1
+#define DIAGS_LOG_FILE "diags.log"
+
+char working_dir[1024] = {0};
+void
+test_done()
+{
+  shutdown_event_system = true;
+}
+
+const char *GLOBAL_DATA = (char *)ats_malloc(10 * 1024 * 1024 + 3); // 10M
+
+struct EventProcessorListener : Catch::TestEventListenerBase {
+  using TestEventListenerBase::TestEventListenerBase; // inherit constructor
+
+  virtual void
+  testRunStarting(Catch::TestRunInfo const &testRunInfo) override
+  {
+    getcwd(working_dir, 1024);
+    BaseLogFile *base_log_file = new BaseLogFile("stderr");
+    diags                      = new Diags(testRunInfo.name.c_str(), "*" /* tags */, "" /* actions */, base_log_file);
+    diags->activate_taglist("cache.*|agg.*|locks", DiagsTagType_Debug);
+    diags->config.enabled[DiagsTagType_Debug] = true;
+    diags->show_location                      = SHOW_LOCATION_DEBUG;
+
+    mime_init();
+    Layout::create();
+    RecProcessInit(RECM_STAND_ALONE);
+    LibRecordsConfigInit();
+    ink_net_init(ts::ModuleVersion(1, 0, ts::ModuleVersion::PRIVATE));
+    ink_assert(GLOBAL_DATA != nullptr);
+
+    netProcessor.init();
+    eventProcessor.start(THREADS);
+
+    ink_aio_init(AIO_MODULE_PUBLIC_VERSION);
+
+    EThread *thread = new EThread();
+    thread->set_specific();
+    init_buffer_allocators(0);
+
+    Layout::get()->sysconfdir = std::string_view("./test");
+    Layout::get()->prefix     = std::string_view("./test");
+    ::remove("./test/var/trafficserver/cache.db");
+  }
+};
+CATCH_REGISTER_LISTENER(EventProcessorListener);
+
+extern Store theCacheStore;
+
+void
+init_cache(size_t size, const char *name)
+{
+  ink_cache_init(ts::ModuleVersion(1, 0, ts::ModuleVersion::PRIVATE));
+  cacheProcessor.start();
+}
+
+void
+build_hdrs(HTTPInfo &info, const char *url, const char *content_type)
+{
+  HTTPHdr req;
+  HTTPHdr resp;
+  HTTPParser parser;
+  int err           = -1;
+  char buf[1024]    = {0};
+  const char *start = buf;
+  char *p           = buf;
+
+  REQUIRE(url != nullptr);
+
+  p += sprintf(p, "GET %s HTTP/1.1\n", url);
+  p += sprintf(p, "User-Agent: curl/7.47.0\n");
+  p += sprintf(p, "Accept: %s\n", content_type);
+  p += sprintf(p, "Vary: Content-type\n");
+  p += sprintf(p, "Proxy-Connection: Keep-Alive\n\n");
+
+  req.create(HTTP_TYPE_REQUEST);
+  http_parser_init(&parser);
+
+  while (true) {
+    err = req.parse_req(&parser, &start, p, true);
+    if (err != PARSE_RESULT_CONT) {
+      break;
+    }
+  }
+
+  ink_assert(err == PARSE_RESULT_DONE);
+
+  memset(buf, 0, sizeof(buf));
+  p = buf;
+
+  if (content_type == nullptr) {
+    content_type = "application/octet-stream";
+  }
+
+  p = buf;
+  p += sprintf(p, "HTTP/1.1 200 OK\n");
+  p += sprintf(p, "Content-Type: %s\n", content_type);
+  p += sprintf(p, "Expires: Fri, 15 Mar 2219 08:55:45 GMT\n");
+  p += sprintf(p, "Last-Modified: Thu, 14 Mar 2019 08:47:40 GMT\n\n");
+
+  resp.create(HTTP_TYPE_RESPONSE);
+  http_parser_init(&parser);
+  start = buf;
+
+  while (true) {
+    err = resp.parse_resp(&parser, &start, p, true);
+    if (err != PARSE_RESULT_CONT) {
+      break;
+    }
+  }
+  ink_assert(err == PARSE_RESULT_DONE);
+
+  info.request_set(&req);
+  info.response_set(&resp);
+
+  req.destroy();
+  resp.destroy();
+}
+
+HttpCacheKey
+generate_key(HTTPInfo &info)
+{
+  HttpCacheKey key;
+  Cache::generate_key(&key, info.request_get()->url_get(), 1);
+  return key;
+}
+
+void
+CacheWriteTest::fill_data()
+{
+  size_t size = std::min(WRITE_LIMIT, this->_size);
+  auto n      = this->_write_buffer->write(this->_cursor, size);
+  this->_size -= n;
+  this->_cursor += n;
+}
+
+int
+CacheWriteTest::write_event(int event, void *e)
+{
+  switch (event) {
+  case CACHE_EVENT_OPEN_WRITE:
+    this->vc = (CacheVC *)e;
+    /* fall through */
+  case CACHE_EVENT_OPEN_WRITE_FAILED:
+    this->process_event(event);
+    break;
+  case VC_EVENT_WRITE_READY:
+    this->process_event(event);
+    this->fill_data();
+    break;
+  case VC_EVENT_WRITE_COMPLETE:
+    this->process_event(event);
+    break;
+  default:
+    this->close();
+    CHECK(false);
+    break;
+  }
+  return 0;
+}
+
+void
+CacheWriteTest::do_io_write(size_t size)
+{
+  if (size == 0) {
+    size = this->_size;
+  }
+  this->vc->set_http_info(&this->info);
+  this->vio = this->vc->do_io_write(this, size, this->_write_buffer->alloc_reader());
+}
+
+int
+CacheWriteTest::start_test(int event, void *e)
+{
+  Debug("cache test", "start write test");
+
+  HttpCacheKey key;
+  key = generate_key(this->info);
+
+  SET_HANDLER(&CacheWriteTest::write_event);
+  cacheProcessor.open_write(this, 0, &key, (CacheHTTPHdr *)this->info.request_get(), nullptr);
+  return 0;
+}
+
+int
+CacheReadTest::read_event(int event, void *e)
+{
+  switch (event) {
+  case CACHE_EVENT_OPEN_READ:
+    this->vc = (CacheVC *)e;
+    /* fall through */
+  case CACHE_EVENT_OPEN_READ_FAILED:
+    this->process_event(event);
+    break;
+  case VC_EVENT_READ_READY: {
+    while (this->_reader->block_read_avail()) {
+      auto str = this->_reader->block_read_view();
+      if (memcmp(str.data(), this->_cursor, str.size()) == 0) {
+        this->_reader->consume(str.size());
+        this->_cursor += str.size();
+        this->process_event(event);
+      } else {
+        CHECK(false);
+        this->close();
+        TEST_DONE();
+        break;
+      }
+    }
+    break;
+  }
+  case VC_EVENT_ERROR:
+  case VC_EVENT_EOS:
+  case VC_EVENT_READ_COMPLETE:
+    this->process_event(event);
+    break;
+  default:
+    CHECK(false);
+    this->close();
+    break;
+  }
+  return 0;
+}
+
+void
+CacheReadTest::do_io_read(size_t size)
+{
+  if (size == 0) {
+    size = this->_size;
+  }
+  this->vc->get_http_info(&this->read_http_info);
+  this->vio = this->vc->do_io_read(this, size, this->_read_buffer);
+}
+
+int
+CacheReadTest::start_test(int event, void *e)
+{
+  Debug("cache test", "start read test");
+  HttpCacheKey key;
+  key = generate_key(this->info);
+
+  SET_HANDLER(&CacheReadTest::read_event);
+  cacheProcessor.open_read(this, &key, (CacheHTTPHdr *)this->info.request_get(), &this->params);
+  return 0;
+}
+
+constexpr size_t WRITE_LIMIT = 1024 * 3;
diff --git a/iocore/cache/test/main.h b/iocore/cache/test/main.h
new file mode 100644
index 0000000..b4dc4ee
--- /dev/null
+++ b/iocore/cache/test/main.h
@@ -0,0 +1,232 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+#include "catch.hpp"
+
+#include "tscore/I_Layout.h"
+#include "tscore/Diags.h"
+
+#include "RecordsConfig.h"
+#include "records/I_RecProcess.h"
+#include "P_AIO.h"
+#include "P_CacheDisk.h"
+#include "P_Net.h"
+#include "test/CacheTestHandler.h"
+#include "P_Cache.h"
+
+#include <queue>
+
+// redefine BUILD PREFIX
+#ifdef TS_BUILD_PREFIX
+#undef TS_BUILD_PREFIX
+#endif
+#define TS_BUILD_PREFIX "./test"
+
+#ifdef TS_BUILD_EXEC_PREFIX
+#undef TS_BUILD_EXEC_PREFIX
+#endif
+#define TS_BUILD_EXEC_PREFIX "./test"
+
+#ifdef TS_BUILD_SYSCONFDIR
+#undef TS_BUILD_SYSCONFDIR
+#endif
+#define TS_BUILD_SYSCONFDIR "./test"
+
+#define SLEEP_TIME 20000
+
+void init_cache(size_t size, const char *name = "cache.db");
+void build_hdrs(HTTPInfo &info, const char *url, const char *content_type = "text/html;charset=utf-8");
+
+HttpCacheKey generate_key(HTTPInfo &info);
+
+extern const char *GLOBAL_DATA;
+extern size_t const WRITE_LIMIT;
+
+class CacheInit : public Continuation
+{
+public:
+  CacheInit() : Continuation(new_ProxyMutex()) { SET_HANDLER(&CacheInit::init_event); }
+
+  int
+  start_event(int event, void *e)
+  {
+    Debug("cache_test", "cache init successfully");
+    this->cache_init_success_callback(event, e);
+    return 0;
+  }
+
+  int
+  init_event(int event, void *e)
+  {
+    switch (event) {
+    case EVENT_INTERVAL:
+    case EVENT_IMMEDIATE:
+      if (!CacheProcessor::IsCacheReady(CACHE_FRAG_TYPE_HTTP)) {
+        this_ethread()->schedule_in(this, SLEEP_TIME);
+      } else {
+        SET_HANDLER(&CacheInit::start_event);
+        this->handleEvent(event, e);
+      }
+      return 0;
+    default:
+      CHECK(false);
+      TEST_DONE();
+      return 0;
+    }
+
+    return 0;
+  }
+
+  virtual int cache_init_success_callback(int event, void *e) = 0;
+
+  virtual ~CacheInit() {}
+};
+
+class CacheTestBase : public Continuation
+{
+public:
+  CacheTestBase(CacheTestHandler *test_handler) : Continuation(new_ProxyMutex()), test_handler(test_handler)
+  {
+    SET_HANDLER(&CacheTestBase::init_handler);
+  }
+
+  int
+  init_handler(int event, void *e)
+  {
+    this->start_test(event, e);
+    return 0;
+  }
+
+  // test entrance
+  virtual int start_test(int event, void *e) = 0;
+
+  void
+  process_event(int event)
+  {
+    this->test_handler->handle_cache_event(event, this);
+  }
+
+  virtual void
+  reenable()
+  {
+    if (this->vio) {
+      this->vio->reenable();
+    }
+  }
+
+  int
+  terminal_event(int event, void *e)
+  {
+    delete this;
+    return 0;
+  }
+
+  void
+  close(int error = -1)
+  {
+    if (this->vc) {
+      this->vc->do_io_close(error);
+      this->vc  = nullptr;
+      this->vio = nullptr;
+    }
+
+    SET_HANDLER(&CacheTestBase::terminal_event);
+    if (!this->terminal) {
+      this->terminal = this_ethread()->schedule_imm(this);
+    }
+  }
+
+  virtual void
+  do_io_read(size_t size = 0)
+  {
+    REQUIRE(!"should not be called");
+  }
+
+  virtual void
+  do_io_write(size_t size = 0)
+  {
+    REQUIRE(!"should not be called");
+  }
+
+  Event *terminal                = nullptr;
+  CacheVC *vc                    = nullptr;
+  VIO *vio                       = nullptr;
+  CacheTestHandler *test_handler = nullptr;
+};
+
+class CacheWriteTest : public CacheTestBase
+{
+public:
+  CacheWriteTest(size_t size, CacheTestHandler *cont, const char *url = "http://www.scw00.com/") : CacheTestBase(cont), _size(size)
+  {
+    this->_cursor       = (char *)GLOBAL_DATA;
+    this->_write_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_4K);
+
+    this->info.create();
+    build_hdrs(this->info, url);
+  }
+
+  int start_test(int event, void *e) override;
+  int write_event(int event, void *e);
+  void fill_data();
+  void do_io_write(size_t size = 0) override;
+
+  HTTPInfo info;
+
+private:
+  size_t _size             = 0;
+  char *_cursor            = nullptr;
+  MIOBuffer *_write_buffer = nullptr;
+};
+
+class CacheReadTest : public CacheTestBase
+{
+public:
+  CacheReadTest(size_t size, CacheTestHandler *cont, const char *url = "http://www.scw00.com/") : CacheTestBase(cont), _size(size)
+  {
+    this->_cursor      = (char *)GLOBAL_DATA;
+    this->_read_buffer = new_MIOBuffer(BUFFER_SIZE_INDEX_4K);
+    this->_reader      = this->_read_buffer->alloc_reader();
+
+    this->info.create();
+    build_hdrs(this->info, url);
+  }
+
+  int start_test(int event, void *e) override;
+  int read_event(int event, void *e);
+  void do_io_read(size_t size = 0) override;
+
+  HTTPInfo info;
+  HTTPInfo *read_http_info = nullptr;
+
+private:
+  size_t _size            = 0;
+  char *_cursor           = nullptr;
+  MIOBuffer *_read_buffer = nullptr;
+  IOBufferReader *_reader = nullptr;
+  OverridableHttpConfigParams params;
+};
diff --git a/iocore/cache/test/storage.config b/iocore/cache/test/storage.config
new file mode 100644
index 0000000..d86334e
Binary files /dev/null and b/iocore/cache/test/storage.config differ
diff --git a/iocore/cache/test/stub.cc b/iocore/cache/test/stub.cc
new file mode 100644
index 0000000..d6710ec
--- /dev/null
+++ b/iocore/cache/test/stub.cc
@@ -0,0 +1,264 @@
+/** @file
+
+  Stub file for linking libinknet.a from unit tests
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#include "HttpSessionManager.h"
+#include "HttpBodyFactory.h"
+#include "DiagsConfig.h"
+#include "ts/InkAPIPrivateIOCore.h"
+
+void
+initialize_thread_for_http_sessions(EThread *, int)
+{
+  ink_assert(false);
+}
+
+#include "InkAPIInternal.h"
+void
+APIHooks::append(INKContInternal *cont)
+{
+}
+
+int
+APIHook::invoke(int, void *)
+{
+  ink_assert(false);
+  return 0;
+}
+
+APIHook *
+APIHook::next() const
+{
+  ink_assert(false);
+  return nullptr;
+}
+
+APIHook *
+APIHooks::get() const
+{
+  return nullptr;
+}
+
+void
+APIHooks::prepend(INKContInternal *cont)
+{
+}
+
+void
+APIHooks::clear()
+{
+}
+
+void
+ConfigUpdateCbTable::invoke(const char * /* name ATS_UNUSED */)
+{
+  ink_release_assert(false);
+}
+
+HttpAPIHooks *http_global_hooks        = nullptr;
+SslAPIHooks *ssl_hooks                 = nullptr;
+LifecycleAPIHooks *lifecycle_hooks     = nullptr;
+ConfigUpdateCbTable *global_config_cbs = nullptr;
+
+HttpBodyFactory *body_factory = nullptr;
+
+intmax_t
+ts::svtoi(TextView src, TextView *out, int base)
+{
+  intmax_t zret = 0;
+
+  if (out) {
+    out->clear();
+  }
+  if (!(0 <= base && base <= 36)) {
+    return 0;
+  }
+  if (src.ltrim_if(&isspace) && src) {
+    const char *start = src.data();
+    int8_t v;
+    bool neg = false;
+    if ('-' == *src) {
+      ++src;
+      neg = true;
+    }
+    // If base is 0, it wasn't specified - check for standard base prefixes
+    if (0 == base) {
+      base = 10;
+      if ('0' == *src) {
+        ++src;
+        base = 8;
+        if (src && ('x' == *src || 'X' == *src)) {
+          ++src;
+          base = 16;
+        }
+      }
+    }
+
+    // For performance in common cases, use the templated conversion.
+    switch (base) {
+    case 8:
+      zret = svto_radix<8>(src);
+      break;
+    case 10:
+      zret = svto_radix<10>(src);
+      break;
+    case 16:
+      zret = svto_radix<16>(src);
+      break;
+    default:
+      while (src.size() && (0 <= (v = svtoi_convert[static_cast<unsigned char>(*src)])) && v < base) {
+        auto n = zret * base + v;
+        if (n < zret) {
+          zret = std::numeric_limits<uintmax_t>::max();
+          break; // overflow, stop parsing.
+        }
+        zret = n;
+        ++src;
+      }
+      break;
+    }
+
+    if (out && (src.data() > (neg ? start + 1 : start))) {
+      out->assign(start, src.data());
+    }
+
+    if (neg) {
+      zret = -zret;
+    }
+  }
+  return zret;
+}
+
+void
+HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned int down_time, const char *reason)
+{
+}
+
+HostStatus_t
+HostStatus::getHostStatus(const char *name)
+{
+  return (HostStatus_t)0;
+}
+
+void
+HostStatus::createHostStat(const char *name)
+{
+}
+
+HostStatus::HostStatus() {}
+
+HostStatus::~HostStatus() {}
+
+int auto_clear_hostdb_flag = 0;
+bool ts_is_draining        = false;
+
+void
+INKVConnInternal::do_io_close(int error)
+{
+}
+
+void
+INKVConnInternal::do_io_shutdown(ShutdownHowTo_t howto)
+{
+}
+
+VIO *
+INKVConnInternal::do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *buf, bool owner)
+{
+  return nullptr;
+}
+
+VIO *
+INKVConnInternal::do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf)
+{
+  return nullptr;
+}
+
+void
+INKVConnInternal::destroy()
+{
+}
+
+void
+INKVConnInternal::free()
+{
+}
+
+void
+INKVConnInternal::clear()
+{
+}
+
+void
+INKVConnInternal::reenable(VIO * /* vio ATS_UNUSED */)
+{
+}
+
+bool
+INKVConnInternal::get_data(int id, void *data)
+{
+  return false;
+}
+
+bool
+INKVConnInternal::set_data(int id, void *data)
+{
+  return false;
+}
+
+void
+INKVConnInternal::do_io_transform(VConnection *vc)
+{
+}
+
+void
+INKContInternal::handle_event_count(int event)
+{
+}
+
+void
+INKVConnInternal::retry(unsigned int delay)
+{
+}
+
+INKContInternal::INKContInternal(TSEventFunc funcp, TSMutex mutexp) : DummyVConnection((ProxyMutex *)mutexp) {}
+
+INKContInternal::INKContInternal() : DummyVConnection(nullptr) {}
+
+void
+INKContInternal::destroy()
+{
+}
+
+void
+INKContInternal::clear()
+{
+}
+
+void
+INKContInternal::free()
+{
+}
+
+INKVConnInternal::INKVConnInternal() : INKContInternal() {}
+
+INKVConnInternal::INKVConnInternal(TSEventFunc funcp, TSMutex mutexp) : INKContInternal(funcp, mutexp) {}
diff --git a/iocore/cache/test/test_Alternate_L_to_S.cc b/iocore/cache/test/test_Alternate_L_to_S.cc
new file mode 100644
index 0000000..6d4656a
--- /dev/null
+++ b/iocore/cache/test/test_Alternate_L_to_S.cc
@@ -0,0 +1,191 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#define LARGE_FILE 10 * 1024 * 1024
+#define SMALL_FILE 10 * 1024
+
+#include "main.h"
+
+class CacheAltReadAgain : public CacheTestHandler
+{
+public:
+  CacheAltReadAgain(size_t size, const char *url) : CacheTestHandler()
+  {
+    this->_rt        = new CacheReadTest(size, this, url);
+    this->_rt->mutex = this->mutex;
+
+    SET_HANDLER(&CacheAltReadAgain::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_rt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "text/html;charset=utf-8", len) == 0);
+  }
+};
+
+class CacheAltTest_L_to_S : public CacheTestHandler
+{
+public:
+  CacheAltTest_L_to_S(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+    auto wt = new CacheWriteTest(size, this, url);
+
+    rt->info.destroy();
+    wt->info.destroy();
+
+    rt->info.create();
+    wt->info.create();
+
+    build_hdrs(rt->info, url, "application/x-javascript");
+    build_hdrs(wt->info, url, "application/x-javascript");
+
+    this->_rt = rt;
+    this->_wt = wt;
+
+    this->_rt->mutex = this->mutex;
+    this->_wt->mutex = this->mutex;
+
+    SET_HANDLER(&CacheAltTest_L_to_S::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_wt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_WRITE:
+      base->do_io_write();
+      break;
+    case VC_EVENT_WRITE_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_WRITE_COMPLETE:
+      this->_wt->close();
+      this->_wt = nullptr;
+      this_ethread()->schedule_imm(this->_rt);
+      break;
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+private:
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "application/x-javascript", len) == 0);
+  }
+};
+
+class CacheAltInit : public CacheInit
+{
+public:
+  CacheAltInit() {}
+  int
+  cache_init_success_callback(int event, void *e) override
+  {
+    CacheTestHandler *h     = new CacheTestHandler(LARGE_FILE, "http://www.scw11.com");
+    CacheAltTest_L_to_S *ls = new CacheAltTest_L_to_S(SMALL_FILE, "http://www.scw11.com");
+    CacheAltReadAgain *read = new CacheAltReadAgain(LARGE_FILE, "http://www.scw11.com");
+    TerminalTest *tt        = new TerminalTest;
+
+    h->add(ls);
+    h->add(read); // read again
+    h->add(tt);
+    this_ethread()->schedule_imm(h);
+    delete this;
+    return 0;
+  }
+};
+
+TEST_CASE("cache write -> read", "cache")
+{
+  init_cache(256 * 1024 * 1024);
+  // large write test
+  CacheAltInit *init = new CacheAltInit;
+
+  this_ethread()->schedule_imm(init);
+  this_thread()->execute();
+}
diff --git a/iocore/cache/test/test_Alternate_L_to_S_remove_L.cc b/iocore/cache/test/test_Alternate_L_to_S_remove_L.cc
new file mode 100644
index 0000000..f1167c7
--- /dev/null
+++ b/iocore/cache/test/test_Alternate_L_to_S_remove_L.cc
@@ -0,0 +1,260 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#define LARGE_FILE 10 * 1024 * 1024
+#define SMALL_FILE 10 * 1024
+
+#include "main.h"
+
+// delete dir
+Dir dir = {};
+
+class CacheAltReadAgain2 : public CacheTestHandler
+{
+public:
+  CacheAltReadAgain2(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+
+    rt->mutex = this->mutex;
+
+    rt->info.destroy();
+
+    rt->info.create();
+    build_hdrs(rt->info, url, "application/x-javascript");
+
+    this->_rt = rt;
+
+    SET_HANDLER(&CacheAltReadAgain2::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_rt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "application/x-javascript", len) == 0);
+  }
+};
+
+class CacheAltReadAgain : public CacheTestHandler
+{
+public:
+  CacheAltReadAgain(size_t size, const char *url) : CacheTestHandler()
+  {
+    this->_rt        = new CacheReadTest(size, this, url);
+    this->_rt->mutex = this->mutex;
+
+    SET_HANDLER(&CacheAltReadAgain::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_rt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ_FAILED:
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "text/html;charset=utf-8", len) == 0);
+  }
+};
+
+class CacheAltTest_L_to_S_remove_L : public CacheTestHandler
+{
+public:
+  CacheAltTest_L_to_S_remove_L(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+    auto wt = new CacheWriteTest(size, this, url);
+
+    rt->info.destroy();
+    wt->info.destroy();
+
+    rt->info.create();
+    wt->info.create();
+
+    build_hdrs(rt->info, url, "application/x-javascript");
+    build_hdrs(wt->info, url, "application/x-javascript");
+
+    this->_rt = rt;
+    this->_wt = wt;
+
+    this->_rt->mutex = this->mutex;
+    this->_wt->mutex = this->mutex;
+
+    SET_HANDLER(&CacheAltTest_L_to_S_remove_L::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_wt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_WRITE:
+      base->do_io_write();
+      break;
+    case VC_EVENT_WRITE_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_WRITE_COMPLETE:
+      this->_wt->close();
+      this->_wt = nullptr;
+      this_ethread()->schedule_imm(this->_rt);
+      break;
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      delete_earliest_dir(base->vc);
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "application/x-javascript", len) == 0);
+  }
+
+  void
+  delete_earliest_dir(CacheVC *vc)
+  {
+    CacheKey key        = {};
+    Dir *last_collision = nullptr;
+    SCOPED_MUTEX_LOCK(lock, vc->vol->mutex, this->mutex->thread_holding);
+    vc->vector.data[0].alternate.object_key_get(&key);
+    REQUIRE(dir_probe(&key, vc->vol, &dir, &last_collision) != 0);
+    REQUIRE(dir_delete(&key, vc->vol, &dir));
+  }
+};
+
+class CacheAltInit : public CacheInit
+{
+public:
+  CacheAltInit() {}
+  int
+  cache_init_success_callback(int event, void *e) override
+  {
+    CacheTestHandler *h              = new CacheTestHandler(LARGE_FILE, "http://www.scw11.com");
+    CacheAltTest_L_to_S_remove_L *ls = new CacheAltTest_L_to_S_remove_L(SMALL_FILE, "http://www.scw11.com");
+    CacheAltReadAgain *read          = new CacheAltReadAgain(LARGE_FILE, "http://www.scw11.com");
+    CacheAltReadAgain2 *read2        = new CacheAltReadAgain2(SMALL_FILE, "http://www.scw11.com");
+    TerminalTest *tt                 = new TerminalTest;
+
+    h->add(ls);
+    h->add(read); // read again
+    h->add(read2);
+    h->add(tt);
+    this_ethread()->schedule_imm(h);
+    delete this;
+    return 0;
+  }
+};
+
+TEST_CASE("cache write -> read", "cache")
+{
+  init_cache(256 * 1024 * 1024);
+  // large write test
+  CacheAltInit *init = new CacheAltInit;
+
+  this_ethread()->schedule_imm(init);
+  this_thread()->execute();
+}
diff --git a/iocore/cache/test/test_Alternate_L_to_S_remove_S.cc b/iocore/cache/test/test_Alternate_L_to_S_remove_S.cc
new file mode 100644
index 0000000..f2a4844
--- /dev/null
+++ b/iocore/cache/test/test_Alternate_L_to_S_remove_S.cc
@@ -0,0 +1,261 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#define LARGE_FILE 10 * 1024 * 1024
+#define SMALL_FILE 10 * 1024
+
+#include "main.h"
+
+// delete dir
+Dir dir = {};
+
+class CacheAltReadAgain2 : public CacheTestHandler
+{
+public:
+  CacheAltReadAgain2(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+
+    rt->mutex = this->mutex;
+    this->_rt = rt;
+    SET_HANDLER(&CacheAltReadAgain2::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_rt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "text/html;charset=utf-8", len) == 0);
+  }
+};
+
+class CacheAltReadAgain : public CacheTestHandler
+{
+public:
+  CacheAltReadAgain(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+
+    rt->mutex = this->mutex;
+
+    rt->info.destroy();
+
+    rt->info.create();
+    build_hdrs(rt->info, url, "application/x-javascript");
+
+    this->_rt = rt;
+
+    SET_HANDLER(&CacheAltReadAgain::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_rt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ_FAILED:
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "text/html;charset=utf-8", len) == 0);
+  }
+};
+
+class CacheAltTest_L_to_S_remove_S : public CacheTestHandler
+{
+public:
+  CacheAltTest_L_to_S_remove_S(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+    auto wt = new CacheWriteTest(size, this, url);
+
+    rt->info.destroy();
+    wt->info.destroy();
+
+    rt->info.create();
+    wt->info.create();
+
+    build_hdrs(rt->info, url, "application/x-javascript");
+    build_hdrs(wt->info, url, "application/x-javascript");
+
+    this->_rt = rt;
+    this->_wt = wt;
+
+    this->_rt->mutex = this->mutex;
+    this->_wt->mutex = this->mutex;
+
+    SET_HANDLER(&CacheAltTest_L_to_S_remove_S::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_wt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_WRITE:
+      base->do_io_write();
+      break;
+    case VC_EVENT_WRITE_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_WRITE_COMPLETE:
+      this->_wt->close();
+      this->_wt = nullptr;
+      this_ethread()->schedule_imm(this->_rt);
+      break;
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      delete_earliest_dir(base->vc);
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "application/x-javascript", len) == 0);
+  }
+
+  void
+  delete_earliest_dir(CacheVC *vc)
+  {
+    CacheKey key        = {};
+    Dir *last_collision = nullptr;
+    SCOPED_MUTEX_LOCK(lock, vc->vol->mutex, this->mutex->thread_holding);
+    vc->vector.data[1].alternate.object_key_get(&key);
+    REQUIRE(dir_probe(&key, vc->vol, &dir, &last_collision) != 0);
+    REQUIRE(dir_delete(&key, vc->vol, &dir));
+  }
+};
+
+class CacheAltInit : public CacheInit
+{
+public:
+  CacheAltInit() {}
+  int
+  cache_init_success_callback(int event, void *e) override
+  {
+    CacheTestHandler *h              = new CacheTestHandler(LARGE_FILE, "http://www.scw11.com");
+    CacheAltTest_L_to_S_remove_S *ls = new CacheAltTest_L_to_S_remove_S(SMALL_FILE, "http://www.scw11.com");
+    CacheAltReadAgain *read          = new CacheAltReadAgain(SMALL_FILE, "http://www.scw11.com");
+    CacheAltReadAgain2 *read2        = new CacheAltReadAgain2(LARGE_FILE, "http://www.scw11.com");
+    TerminalTest *tt                 = new TerminalTest;
+
+    h->add(ls);
+    h->add(read); // read again
+    h->add(read2);
+    h->add(tt);
+    this_ethread()->schedule_imm(h);
+    delete this;
+    return 0;
+  }
+};
+
+TEST_CASE("cache write -> read", "cache")
+{
+  init_cache(256 * 1024 * 1024);
+  // large write test
+  CacheAltInit *init = new CacheAltInit;
+
+  this_ethread()->schedule_imm(init);
+  this_thread()->execute();
+}
diff --git a/iocore/cache/test/test_Alternate_S_to_L.cc b/iocore/cache/test/test_Alternate_S_to_L.cc
new file mode 100644
index 0000000..097b61b
--- /dev/null
+++ b/iocore/cache/test/test_Alternate_S_to_L.cc
@@ -0,0 +1,191 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#define LARGE_FILE 10 * 1024 * 1024
+#define SMALL_FILE 10 * 1024
+
+#include "main.h"
+
+class CacheAltReadAgain : public CacheTestHandler
+{
+public:
+  CacheAltReadAgain(size_t size, const char *url) : CacheTestHandler()
+  {
+    this->_rt        = new CacheReadTest(size, this, url);
+    this->_rt->mutex = this->mutex;
+
+    SET_HANDLER(&CacheAltReadAgain::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_rt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "text/html;charset=utf-8", len) == 0);
+  }
+};
+
+class CacheAltTest_L_to_S : public CacheTestHandler
+{
+public:
+  CacheAltTest_L_to_S(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+    auto wt = new CacheWriteTest(size, this, url);
+
+    rt->info.destroy();
+    wt->info.destroy();
+
+    rt->info.create();
+    wt->info.create();
+
+    build_hdrs(rt->info, url, "application/x-javascript");
+    build_hdrs(wt->info, url, "application/x-javascript");
+
+    this->_rt = rt;
+    this->_wt = wt;
+
+    this->_rt->mutex = this->mutex;
+    this->_wt->mutex = this->mutex;
+
+    SET_HANDLER(&CacheAltTest_L_to_S::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_wt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_WRITE:
+      base->do_io_write();
+      break;
+    case VC_EVENT_WRITE_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_WRITE_COMPLETE:
+      this->_wt->close();
+      this->_wt = nullptr;
+      this_ethread()->schedule_imm(this->_rt);
+      break;
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+private:
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "application/x-javascript", len) == 0);
+  }
+};
+
+class CacheAltInit : public CacheInit
+{
+public:
+  CacheAltInit() {}
+  int
+  cache_init_success_callback(int event, void *e) override
+  {
+    CacheTestHandler *h     = new CacheTestHandler(SMALL_FILE, "http://www.scw11.com");
+    CacheAltTest_L_to_S *ls = new CacheAltTest_L_to_S(LARGE_FILE, "http://www.scw11.com");
+    CacheAltReadAgain *read = new CacheAltReadAgain(SMALL_FILE, "http://www.scw11.com");
+    TerminalTest *tt        = new TerminalTest;
+
+    h->add(ls);
+    h->add(read); // read again
+    h->add(tt);
+    this_ethread()->schedule_imm(h);
+    delete this;
+    return 0;
+  }
+};
+
+TEST_CASE("cache write -> read", "cache")
+{
+  init_cache(256 * 1024 * 1024);
+  // large write test
+  CacheAltInit *init = new CacheAltInit;
+
+  this_ethread()->schedule_imm(init);
+  this_thread()->execute();
+}
diff --git a/iocore/cache/test/test_Alternate_S_to_L_remove_L.cc b/iocore/cache/test/test_Alternate_S_to_L_remove_L.cc
new file mode 100644
index 0000000..282224b
--- /dev/null
+++ b/iocore/cache/test/test_Alternate_S_to_L_remove_L.cc
@@ -0,0 +1,264 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#define LARGE_FILE 10 * 1024 * 1024
+#define SMALL_FILE 10 * 1024
+
+#include "main.h"
+
+// delete dir
+Dir dir = {};
+
+class CacheAltReadAgain2 : public CacheTestHandler
+{
+public:
+  CacheAltReadAgain2(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+
+    rt->mutex = this->mutex;
+    this->_rt = rt;
+    SET_HANDLER(&CacheAltReadAgain2::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_rt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "text/html;charset=utf-8", len) == 0);
+  }
+};
+
+class CacheAltReadAgain : public CacheTestHandler
+{
+public:
+  CacheAltReadAgain(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+
+    rt->mutex = this->mutex;
+
+    rt->info.destroy();
+
+    rt->info.create();
+    build_hdrs(rt->info, url, "application/x-javascript");
+
+    this->_rt = rt;
+
+    SET_HANDLER(&CacheAltReadAgain::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    // sleep for a while to wait for writer close
+    this_ethread()->schedule_imm(this->_rt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ_FAILED:
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "text/html;charset=utf-8", len) == 0);
+  }
+};
+
+class CacheAltTest_S_to_L_remove_L : public CacheTestHandler
+{
+public:
+  CacheAltTest_S_to_L_remove_L(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+    auto wt = new CacheWriteTest(size, this, url);
+
+    rt->info.destroy();
+    wt->info.destroy();
+
+    rt->info.create();
+    wt->info.create();
+
+    build_hdrs(rt->info, url, "application/x-javascript");
+    build_hdrs(wt->info, url, "application/x-javascript");
+
+    this->_rt = rt;
+    this->_wt = wt;
+
+    this->_rt->mutex = this->mutex;
+    this->_wt->mutex = this->mutex;
+
+    SET_HANDLER(&CacheAltTest_S_to_L_remove_L::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_wt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_WRITE:
+      base->do_io_write();
+      break;
+    case VC_EVENT_WRITE_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_WRITE_COMPLETE:
+      this->_wt->close();
+      this->_wt = nullptr;
+      // to make sure writer successfully write the final doc done. we need to schedule
+      // to wait for some while. This time should be large than cache_config_mutex_retry_delay
+      this_ethread()->schedule_in(this->_rt, HRTIME_SECONDS(1));
+      break;
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      delete_earliest_dir(base->vc);
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "application/x-javascript", len) == 0);
+  }
+
+  void
+  delete_earliest_dir(CacheVC *vc)
+  {
+    CacheKey key        = {};
+    Dir *last_collision = nullptr;
+    SCOPED_MUTEX_LOCK(lock, vc->vol->mutex, this->mutex->thread_holding);
+    vc->vector.data[1].alternate.object_key_get(&key);
+    REQUIRE(dir_probe(&key, vc->vol, &dir, &last_collision) != 0);
+    REQUIRE(dir_delete(&key, vc->vol, &dir));
+  }
+};
+
+class CacheAltInit : public CacheInit
+{
+public:
+  CacheAltInit() {}
+  int
+  cache_init_success_callback(int event, void *e) override
+  {
+    CacheTestHandler *h              = new CacheTestHandler(SMALL_FILE, "http://www.scw11.com");
+    CacheAltTest_S_to_L_remove_L *ls = new CacheAltTest_S_to_L_remove_L(LARGE_FILE, "http://www.scw11.com");
+    CacheAltReadAgain *read          = new CacheAltReadAgain(LARGE_FILE, "http://www.scw11.com");
+    CacheAltReadAgain2 *read2        = new CacheAltReadAgain2(SMALL_FILE, "http://www.scw11.com");
+    TerminalTest *tt                 = new TerminalTest;
+
+    h->add(ls);
+    h->add(read); // read again
+    h->add(read2);
+    h->add(tt);
+    this_ethread()->schedule_imm(h);
+    delete this;
+    return 0;
+  }
+};
+
+TEST_CASE("cache write -> read", "cache")
+{
+  init_cache(256 * 1024 * 1024);
+  // large write test
+  CacheAltInit *init = new CacheAltInit;
+
+  this_ethread()->schedule_imm(init);
+  this_thread()->execute();
+}
diff --git a/iocore/cache/test/test_Alternate_S_to_L_remove_S.cc b/iocore/cache/test/test_Alternate_S_to_L_remove_S.cc
new file mode 100644
index 0000000..96a1e7c
--- /dev/null
+++ b/iocore/cache/test/test_Alternate_S_to_L_remove_S.cc
@@ -0,0 +1,262 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#define LARGE_FILE 10 * 1024 * 1024
+#define SMALL_FILE 10 * 1024
+
+#include "main.h"
+
+// delete dir
+Dir dir = {};
+
+class CacheAltReadAgain2 : public CacheTestHandler
+{
+public:
+  CacheAltReadAgain2(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+
+    rt->mutex = this->mutex;
+
+    rt->info.destroy();
+
+    rt->info.create();
+    build_hdrs(rt->info, url, "application/x-javascript");
+
+    this->_rt = rt;
+
+    SET_HANDLER(&CacheAltReadAgain2::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_rt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "application/x-javascript", len) == 0);
+  }
+};
+
+class CacheAltReadAgain : public CacheTestHandler
+{
+public:
+  CacheAltReadAgain(size_t size, const char *url) : CacheTestHandler()
+  {
+    this->_rt        = new CacheReadTest(size, this, url);
+    this->_rt->mutex = this->mutex;
+
+    SET_HANDLER(&CacheAltReadAgain::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_rt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ_FAILED:
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "text/html;charset=utf-8", len) == 0);
+  }
+};
+
+class test_Alternate_S_to_L_remove_S : public CacheTestHandler
+{
+public:
+  test_Alternate_S_to_L_remove_S(size_t size, const char *url) : CacheTestHandler()
+  {
+    auto rt = new CacheReadTest(size, this, url);
+    auto wt = new CacheWriteTest(size, this, url);
+
+    rt->info.destroy();
+    wt->info.destroy();
+
+    rt->info.create();
+    wt->info.create();
+
+    build_hdrs(rt->info, url, "application/x-javascript");
+    build_hdrs(wt->info, url, "application/x-javascript");
+
+    this->_rt = rt;
+    this->_wt = wt;
+
+    this->_rt->mutex = this->mutex;
+    this->_wt->mutex = this->mutex;
+
+    SET_HANDLER(&test_Alternate_S_to_L_remove_S::start_test);
+  }
+
+  int
+  start_test(int event, void *e)
+  {
+    REQUIRE(event == EVENT_IMMEDIATE);
+    this_ethread()->schedule_imm(this->_wt);
+    return 0;
+  }
+
+  virtual void
+  handle_cache_event(int event, CacheTestBase *base)
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_WRITE:
+      base->do_io_write();
+      break;
+    case VC_EVENT_WRITE_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_WRITE_COMPLETE:
+      this->_wt->close();
+      this->_wt = nullptr;
+      // to make sure writer successfully write the final doc done. we need to schedule
+      // to wait for some while. This time should be large than cache_config_mutex_retry_delay
+      this_ethread()->schedule_in(this->_rt, HRTIME_SECONDS(1));
+      break;
+    case CACHE_EVENT_OPEN_READ:
+      base->do_io_read();
+      validate_content_type(base);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      break;
+    case VC_EVENT_READ_COMPLETE:
+      delete_earliest_dir(base->vc);
+      base->close();
+      delete this;
+      break;
+    default:
+      REQUIRE(false);
+      break;
+    }
+  }
+
+  void
+  validate_content_type(CacheTestBase *base)
+  {
+    auto rt = dynamic_cast<CacheReadTest *>(base);
+    REQUIRE(rt);
+    MIMEField *field = rt->read_http_info->m_alt->m_response_hdr.field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
+    REQUIRE(field);
+    int len;
+    const char *value = field->value_get(&len);
+    REQUIRE(memcmp(value, "application/x-javascript", len) == 0);
+  }
+
+  void
+  delete_earliest_dir(CacheVC *vc)
+  {
+    CacheKey key        = {};
+    Dir *last_collision = nullptr;
+    SCOPED_MUTEX_LOCK(lock, vc->vol->mutex, this->mutex->thread_holding);
+    vc->vector.data[0].alternate.object_key_get(&key);
+    REQUIRE(dir_probe(&key, vc->vol, &dir, &last_collision) != 0);
+    REQUIRE(dir_delete(&key, vc->vol, &dir));
+  }
+};
+
+class CacheAltInit : public CacheInit
+{
+public:
+  CacheAltInit() {}
+  int
+  cache_init_success_callback(int event, void *e) override
+  {
+    CacheTestHandler *h                = new CacheTestHandler(SMALL_FILE, "http://www.scw11.com");
+    test_Alternate_S_to_L_remove_S *ls = new test_Alternate_S_to_L_remove_S(LARGE_FILE, "http://www.scw11.com");
+    CacheAltReadAgain *read            = new CacheAltReadAgain(SMALL_FILE, "http://www.scw11.com");
+    CacheAltReadAgain2 *read2          = new CacheAltReadAgain2(LARGE_FILE, "http://www.scw11.com");
+    TerminalTest *tt                   = new TerminalTest;
+
+    h->add(ls);
+    h->add(read); // read again
+    h->add(read2);
+    h->add(tt);
+    this_ethread()->schedule_imm(h);
+    delete this;
+    return 0;
+  }
+};
+
+TEST_CASE("cache write -> read", "cache")
+{
+  init_cache(256 * 1024 * 1024);
+  // large write test
+  CacheAltInit *init = new CacheAltInit;
+
+  this_ethread()->schedule_imm(init);
+  this_thread()->execute();
+}
diff --git a/iocore/cache/test/test_Cache.cc b/iocore/cache/test/test_Cache.cc
new file mode 100644
index 0000000..63284f5
--- /dev/null
+++ b/iocore/cache/test/test_Cache.cc
@@ -0,0 +1,55 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#include "main.h"
+
+#define LARGE_FILE 10 * 1024 * 1024
+#define SMALL_FILE 10 * 1024
+
+class CacheCommInit : public CacheInit
+{
+public:
+  CacheCommInit() {}
+  int
+  cache_init_success_callback(int event, void *e) override
+  {
+    CacheTestHandler *h  = new CacheTestHandler(LARGE_FILE);
+    CacheTestHandler *h2 = new CacheTestHandler(SMALL_FILE, "http://www.scw11.com");
+    TerminalTest *tt     = new TerminalTest;
+    h->add(h2);
+    h->add(tt);
+    this_ethread()->schedule_imm(h);
+    delete this;
+    return 0;
+  }
+};
+
+TEST_CASE("cache write -> read", "cache")
+{
+  init_cache(256 * 1024 * 1024);
+  // large write test
+  CacheCommInit *init = new CacheCommInit;
+
+  this_ethread()->schedule_imm(init);
+  this_thread()->execute();
+}
diff --git a/iocore/cache/test/test_RWW.cc b/iocore/cache/test/test_RWW.cc
new file mode 100644
index 0000000..167cf1e
--- /dev/null
+++ b/iocore/cache/test/test_RWW.cc
@@ -0,0 +1,429 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#define LARGE_FILE 10 * 1024 * 1024
+#define SMALL_FILE 10 * 1024
+
+#define DEFAULT_URL "http://www.scw00.com/"
+
+#include "main.h"
+
+class CacheRWWTest;
+
+struct SimpleCont : public Continuation {
+  SimpleCont(CacheTestBase *base) : base(base)
+  {
+    REQUIRE(base != nullptr);
+    SET_HANDLER(&SimpleCont::handle_event);
+    this->mutex = base->mutex;
+  }
+
+  int
+  handle_event(int event, void *data)
+  {
+    Debug("cache_rww_test", "cache write reenable");
+    base->reenable();
+    delete this;
+    return 0;
+  }
+
+  CacheTestBase *base = nullptr;
+};
+
+class CacheRWWTest : public CacheTestHandler
+{
+public:
+  CacheRWWTest(size_t size, const char *url = DEFAULT_URL) : CacheTestHandler(), _size(size)
+  {
+    if (size != LARGE_FILE && size != SMALL_FILE) {
+      REQUIRE(!"size should be LARGE_FILE or SMALL_FILE");
+    }
+
+    this->_rt = new CacheReadTest(size, this, url);
+    this->_wt = new CacheWriteTest(size, this, url);
+
+    this->_rt->mutex = this->mutex;
+    this->_wt->mutex = this->mutex;
+
+    SET_HANDLER(&CacheRWWTest::start_test);
+  }
+
+  void handle_cache_event(int event, CacheTestBase *e) override;
+  int start_test(int event, void *e);
+
+  virtual void process_read_event(int event, CacheTestBase *base);
+  virtual void process_write_event(int event, CacheTestBase *base);
+
+  void
+  close_write(int error = -1)
+  {
+    if (!this->_wt) {
+      return;
+    }
+
+    this->_wt->close(error);
+    this->_wt = nullptr;
+  }
+
+  void
+  close_read(int error = -1)
+  {
+    if (!this->_rt) {
+      return;
+    }
+
+    this->_rt->close(error);
+    this->_rt = nullptr;
+  }
+
+protected:
+  size_t _size        = 0;
+  Event *_read_event  = nullptr;
+  bool _is_read_start = false;
+  CacheTestBase *_rt  = nullptr;
+  CacheTestBase *_wt  = nullptr;
+};
+
+int
+CacheRWWTest::start_test(int event, void *e)
+{
+  REQUIRE(event == EVENT_IMMEDIATE);
+  this_ethread()->schedule_imm(this->_wt);
+  return 0;
+}
+
+void
+CacheRWWTest::process_write_event(int event, CacheTestBase *base)
+{
+  switch (event) {
+  case CACHE_EVENT_OPEN_WRITE:
+    base->do_io_write();
+    break;
+  case VC_EVENT_WRITE_READY:
+    // schedule read test imm
+    if (this->_size != SMALL_FILE && !this->_wt->vc->fragment) {
+      Debug("cache_rww_test", "cache write reenable");
+      base->reenable();
+      return;
+    }
+
+    if (!this->_is_read_start) {
+      if (!this->_read_event) {
+        this->_read_event = this_ethread()->schedule_imm(this->_rt);
+      }
+      return;
+    }
+
+    // stop writing for a while and wait for reading
+    // data->vio->reenable();
+    this_ethread()->schedule_imm(new SimpleCont(base));
+    break;
+  case VC_EVENT_WRITE_COMPLETE:
+    this->close_write();
+
+    break;
+  default:
+    REQUIRE(event == 0);
+    REQUIRE(false);
+    this->close_write();
+    this->close_read();
+    return;
+  }
+
+  if (this->_rt) {
+    this->_rt->reenable();
+  }
+}
+
+void
+CacheRWWTest::process_read_event(int event, CacheTestBase *base)
+{
+  switch (event) {
+  case CACHE_EVENT_OPEN_READ:
+    base->do_io_read();
+    break;
+  case VC_EVENT_READ_READY:
+    Debug("cache_rww_test", "cache read reenable");
+    this->_read_event    = nullptr;
+    this->_is_read_start = true;
+    base->reenable();
+    break;
+  case VC_EVENT_READ_COMPLETE:
+    this->close_read();
+    return;
+
+  default:
+    REQUIRE(false);
+    this->close_write();
+    this->close_read();
+    return;
+  }
+
+  if (this->_wt) {
+    this->_wt->reenable();
+  }
+}
+
+void
+CacheRWWTest::handle_cache_event(int event, CacheTestBase *base)
+{
+  REQUIRE(base != nullptr);
+
+  switch (event) {
+  case CACHE_EVENT_OPEN_WRITE_FAILED:
+  case CACHE_EVENT_OPEN_WRITE:
+  case VC_EVENT_WRITE_READY:
+  case VC_EVENT_WRITE_COMPLETE:
+    this->process_write_event(event, base);
+    break;
+  case CACHE_EVENT_OPEN_READ:
+  case CACHE_EVENT_OPEN_READ_FAILED:
+  case VC_EVENT_ERROR:
+  case VC_EVENT_EOS:
+  case VC_EVENT_READ_READY:
+  case VC_EVENT_READ_COMPLETE:
+    this->process_read_event(event, base);
+    break;
+  default:
+    REQUIRE(false);
+    this->close_write();
+    this->close_read();
+    break;
+  }
+
+  if (this->_wt == nullptr && this->_rt == nullptr) {
+    delete this;
+  }
+
+  return;
+}
+
+class CacheRWWErrorTest : public CacheRWWTest
+{
+public:
+  CacheRWWErrorTest(size_t size, const char *url = DEFAULT_URL) : CacheRWWTest(size, url) {}
+  void
+  process_write_event(int event, CacheTestBase *base) override
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_WRITE:
+      base->do_io_write();
+      break;
+    case VC_EVENT_WRITE_READY:
+      if (this->_size != SMALL_FILE && !this->_wt->vc->fragment) {
+        Debug("cache_rww_test", "cache write reenable");
+        base->reenable();
+        return;
+      }
+
+      if (!this->_is_read_start) {
+        if (!this->_read_event) {
+          this->_read_event = this_ethread()->schedule_imm(this->_rt);
+        }
+        return;
+      } else {
+        this->close_write(100);
+        return;
+      }
+      this_ethread()->schedule_imm(new SimpleCont(base));
+      break;
+
+    case VC_EVENT_WRITE_COMPLETE:
+      REQUIRE(!"should not happen because the writter aborted");
+      this->close_read();
+      this->close_write();
+      break;
+    default:
+      REQUIRE(false);
+      delete this;
+      return;
+    }
+  }
+
+  void
+  process_read_event(int event, CacheTestBase *base) override
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ:
+      this->_read_event    = nullptr;
+      this->_is_read_start = true;
+      base->do_io_read();
+      break;
+    case CACHE_EVENT_OPEN_READ_FAILED:
+      REQUIRE(this->_size == SMALL_FILE);
+      this->close_read();
+      return;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      if (this->_wt) {
+        this->_wt->reenable();
+      }
+      return;
+
+    case VC_EVENT_READ_COMPLETE:
+      REQUIRE(!"should not happen because the writter aborted");
+      this->close_read();
+      this->close_write();
+      break;
+    case VC_EVENT_ERROR:
+    case VC_EVENT_EOS:
+      if (this->_size == LARGE_FILE) {
+        REQUIRE(base->vio->ndone >= 1 * 1024 * 1024 - sizeof(Doc));
+      } else {
+        REQUIRE(base->vio->ndone == 0);
+      }
+      this->close_read();
+      break;
+    default:
+      REQUIRE(event == 0);
+      this->close_read();
+      this->close_write();
+      break;
+    }
+  }
+
+private:
+  bool _is_read_start = false;
+};
+
+class CacheRWWEOSTest : public CacheRWWTest
+{
+public:
+  CacheRWWEOSTest(size_t size, const char *url = DEFAULT_URL) : CacheRWWTest(size, url) {}
+  /*
+   * test this code in openReadMain
+   * if (writer_done()) {
+      last_collision = nullptr;
+      while (dir_probe(&earliest_key, vol, &dir, &last_collision)) {
+        // write complete. this could be reached the size we set in do_io_write or someone call do_io_close with -1 (-1 means write
+   success) flag if (dir_offset(&dir) == dir_offset(&earliest_dir)) { DDebug("cache_read_agg", "%p: key: %X ReadMain complete: %d",
+   this, first_key.slice32(1), (int)vio.ndone); doc_len = vio.ndone; goto Leos;
+        }
+      }
+      // writer abort. server crash. someone call do_io_close() with error flag
+      DDebug("cache_read_agg", "%p: key: %X ReadMain writer aborted: %d", this, first_key.slice32(1), (int)vio.ndone);
+      goto Lerror;
+    }
+   *
+   */
+
+  void
+  process_write_event(int event, CacheTestBase *base) override
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_WRITE:
+      base->do_io_write();
+      break;
+    case VC_EVENT_WRITE_READY:
+      if (this->_size != SMALL_FILE && !this->_wt->vc->fragment) {
+        Debug("cache_rww_test", "cache write reenable");
+        base->reenable();
+        return;
+      }
+
+      if (!this->_is_read_start) {
+        if (!this->_read_event) {
+          this->_read_event = this_ethread()->schedule_imm(this->_rt);
+        }
+        return;
+      }
+      this_ethread()->schedule_imm(new SimpleCont(base));
+      break;
+
+    case VC_EVENT_WRITE_COMPLETE:
+      this->close_write();
+      break;
+    default:
+      REQUIRE(false);
+      delete this;
+      return;
+    }
+  }
+
+  void
+  process_read_event(int event, CacheTestBase *base) override
+  {
+    switch (event) {
+    case CACHE_EVENT_OPEN_READ:
+      this->_read_event    = nullptr;
+      this->_is_read_start = true;
+      base->do_io_read(UINT32_MAX);
+      break;
+    case VC_EVENT_READ_READY:
+      base->reenable();
+      if (this->_wt) {
+        this->_wt->reenable();
+      }
+      return;
+
+    case VC_EVENT_READ_COMPLETE:
+      REQUIRE(!"should not happen because the writter aborted");
+      this->close_read();
+      this->close_write();
+      break;
+    case VC_EVENT_EOS:
+      this->close_write();
+      this->close_read();
+      break;
+    default:
+      REQUIRE(event == 0);
+      this->close_read();
+      this->close_write();
+      break;
+    }
+  }
+
+private:
+  bool _is_read_start = false;
+};
+
+class CacheRWWCacheInit : public CacheInit
+{
+public:
+  CacheRWWCacheInit() {}
+  int
+  cache_init_success_callback(int event, void *e) override
+  {
+    CacheRWWTest *crww        = new CacheRWWTest(LARGE_FILE);
+    CacheRWWErrorTest *crww_l = new CacheRWWErrorTest(LARGE_FILE, "http://www.scw22.com/");
+    CacheRWWEOSTest *crww_eos = new CacheRWWEOSTest(LARGE_FILE, "ttp://www.scw44.com/");
+    TerminalTest *tt          = new TerminalTest();
+
+    crww->add(crww_l);
+    crww->add(crww_eos);
+    crww->add(tt);
+    this_ethread()->schedule_imm(crww);
+    delete this;
+    return 0;
+  }
+};
+
+TEST_CASE("cache rww", "cache")
+{
+  init_cache(256 * 1024 * 1024);
+  cache_config_target_fragment_size = 1 * 1024 * 1024;
+  CacheRWWCacheInit *init           = new CacheRWWCacheInit();
+
+  this_ethread()->schedule_imm(init);
+  this_ethread()->execute();
+}
diff --git a/iocore/cache/test/var/trafficserver/guard.txt b/iocore/cache/test/var/trafficserver/guard.txt
new file mode 100644
index 0000000..8fe3629
--- /dev/null
+++ b/iocore/cache/test/var/trafficserver/guard.txt
@@ -0,0 +1,24 @@
+/** @file
+
+  A brief file description
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+This is guard file for this empty dir