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