// -*- mode: c++; c-basic-offset:4 -*- // This file is part of libdap, A C++ implementation of the OPeNDAP Data // Access Protocol. // Copyright (c) 2002,2003 OPeNDAP, Inc. // Author: James Gallagher // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. #include // for access stat #include #include #include // for create_cache_root_test #include #include #include #include #include #include #include #include #include "HTTPCache.h" #include "HTTPConnect.h" // Used to generate a response to cache. #ifndef WIN32 // Signals are exquisitely non-portable. #include "SignalHandler.h" // Needed to clean up this singleton. #endif #include "RCReader.h" // ditto #include"GetOpt.h" // #define DODS_DEBUG #include "debug.h" #if defined(DODS_DEBUG) || defined(DODS_DEBUG2) #include #endif using namespace CppUnit; using namespace std; #ifdef WIN32 #define F_OK 0 #define W_OK 2 #endif static bool debug = false; #undef DBG #define DBG(x) do { if (debug) (x); } while(false); namespace libdap { inline static int file_size(string name) { struct stat s; stat(name.c_str(), &s); return s.st_size; } #if 0 inline static void print_entry(HTTPCache *, HTTPCacheTable::CacheEntry **e) { cerr << "Entry: " << (*e)->get_cachename() << endl; } #endif // Note that because this test class uses the fixture 'hc' we must always // force access to the single user/process lock for the cache. This is // because a fixture is always created (by setUp) *before* the body of the // test is run. So by the time we're at the first line of the test, The // persistent store's lock has already been grabbed. 10/14/02 jhrg class HTTPCacheTest: public TestFixture { private: HTTPCache *hc; HTTPConnect *http_conn; string index_file_line; string localhost_url; string expired; int hash_value; vector h; protected: public: HTTPCacheTest() : hc(0), http_conn(0) { putenv((char*) "DODS_CONF=./cache-testsuite/dodsrc"); http_conn = new HTTPConnect(RCReader::instance()); DBG2(cerr << "Entering HTTPCacheTest ctor... "); hash_value = 656; localhost_url = "http://test.opendap.org/test-304.html"; index_file_line = "http://test.opendap.org/test-304.html cache-testsuite/dods_cache/656/dodsKbcD0h \"3f62c-157-139c2680\" 1121283146 -1 343 0 656 1 7351 1121360379 3723 0"; expired = "http://test.opendap.org/cgi-bin/expires.sh"; h.push_back("ETag: jhrgjhrgjhrg"); h.push_back("Last-Modified: Sat, 05 Nov 1994 08:49:37 GMT"); h.push_back("Expires: Mon, 07 Nov 1994 08:49:37 GMT"); h.push_back("Date: Sun, 06 Nov 1994 08:49:37 GMT"); DBG2(cerr << "exiting." << endl); } ~HTTPCacheTest() { delete http_conn; http_conn = 0; DBG2(cerr << "Entering the HTTPCacheTest dtor... ");DBG2(cerr << "exiting." << endl); } #if 0 static inline bool is_hop_by_hop_header(const string &header) { return header.find("Connection") != string::npos || header.find("Keep-Alive") != string::npos || header.find("Proxy-Authenticate") != string::npos || header.find("Proxy-Authorization") != string::npos || header.find("Transfer-Encoding") != string::npos || header.find("Upgrade") != string::npos; } #endif void setUp() { // Called before every test. DBG2(cerr << "Entering HTTPCacheTest::setUp... " << endl); hc = new HTTPCache("cache-testsuite/dods_cache/", true); DBG2(cerr << "exiting setUp" << endl); } void tearDown() { // Called after every test. DBG2(cerr << "Entering HTTPCacheTest::tearDown... " << endl); delete hc; hc = 0; DBG2(cerr << "exiting tearDown" << endl); } CPPUNIT_TEST_SUITE (HTTPCacheTest); CPPUNIT_TEST (constructor_test); CPPUNIT_TEST (cache_index_read_test); CPPUNIT_TEST (cache_index_parse_line_test); CPPUNIT_TEST (get_entry_from_cache_table_test); CPPUNIT_TEST (cache_index_write_test); CPPUNIT_TEST (create_cache_root_test); CPPUNIT_TEST (set_cache_root_test); CPPUNIT_TEST (get_single_user_lock_test); CPPUNIT_TEST (release_single_user_lock_test); CPPUNIT_TEST (create_hash_directory_test); CPPUNIT_TEST (create_location_test); CPPUNIT_TEST (parse_headers_test); CPPUNIT_TEST (calculate_time_test); CPPUNIT_TEST (write_metadata_test); CPPUNIT_TEST (cache_response_test); #if 0 // This test does not seem to work in New Zealand - maybe because // of the dateline??? jhrg 1/31/13 CPPUNIT_TEST(is_url_valid_test); #endif CPPUNIT_TEST (get_cached_response_test); CPPUNIT_TEST (perform_garbage_collection_test); CPPUNIT_TEST (purge_cache_and_release_cached_response_test); CPPUNIT_TEST (get_conditional_response_headers_test); CPPUNIT_TEST (update_response_test); CPPUNIT_TEST (cache_gc_test); // Make this the last test because when distcheck is run, running // it before other tests will break them. CPPUNIT_TEST (instance_test); CPPUNIT_TEST_SUITE_END(); void constructor_test() { DBG(cerr << "hc->cache_index: " << hc->d_http_cache_table->d_cache_index << endl); CPPUNIT_ASSERT(hc->d_http_cache_table->d_cache_index == "cache-testsuite/dods_cache/.index"); CPPUNIT_ASSERT(hc->d_cache_root == "cache-testsuite/dods_cache/"); DBG(cerr << "Current size: " << hc->d_http_cache_table->d_current_size << endl); DBG(cerr << "Block size: " << hc->d_http_cache_table->d_block_size << endl); CPPUNIT_ASSERT(hc->d_http_cache_table->d_current_size == hc->d_http_cache_table->d_block_size); } void cache_index_read_test() { CPPUNIT_ASSERT(hc->d_http_cache_table->cache_index_read()); HTTPCacheTable::CacheEntry *e = hc->d_http_cache_table->get_locked_entry_from_cache_table(localhost_url); CPPUNIT_ASSERT(e); CPPUNIT_ASSERT(e->url == localhost_url); e->unlock_read_response(); } void cache_index_parse_line_test() { HTTPCacheTable::CacheEntry *e = hc->d_http_cache_table->cache_index_parse_line(index_file_line.c_str()); CPPUNIT_ASSERT(e->url == localhost_url); CPPUNIT_ASSERT(e->cachename == "cache-testsuite/dods_cache/656/dodsKbcD0h"); #ifdef WIN32 char *tmpstr = "\"3f62c-157-139c2680\""; CPPUNIT_ASSERT(e->etag == tmpstr); #else CPPUNIT_ASSERT(e->etag == "\"3f62c-157-139c2680\""); #endif CPPUNIT_ASSERT(e->lm == 1121283146); // Skip ahead ... CPPUNIT_ASSERT(e->must_revalidate == false); delete e; e = 0; } // This will also test the add_entry_to_cache_table() method. void get_entry_from_cache_table_test() { HTTPCacheTable::CacheEntry *e = hc->d_http_cache_table->cache_index_parse_line(index_file_line.c_str()); // Test adding an entry and getting it back. hc->d_http_cache_table->add_entry_to_cache_table(e); HTTPCacheTable::CacheEntry *e2 = hc->d_http_cache_table->get_locked_entry_from_cache_table(localhost_url); CPPUNIT_ASSERT(e2); CPPUNIT_ASSERT(e2->url == localhost_url); e2->unlock_read_response(); // Now test what happens when two entries collide. HTTPCacheTable::CacheEntry *e3 = hc->d_http_cache_table->cache_index_parse_line(index_file_line.c_str()); // Change the url so we can tell the difference (the hash is the same) e3->url = "http://new.url.same.hash/test/collisions.gif"; hc->d_http_cache_table->add_entry_to_cache_table(e3); // Use the version of get_entry... that lets us pass in the hash // value (as opposed to the normal version which calculates the hash // from the url. 10/01/02 jhrg HTTPCacheTable::CacheEntry *g = hc->d_http_cache_table->get_locked_entry_from_cache_table(hash_value, e3->url); CPPUNIT_ASSERT(g); CPPUNIT_ASSERT(g->url == e3->url); g->unlock_read_response(); g = hc->d_http_cache_table->get_locked_entry_from_cache_table("http://not.in.table/never.x"); CPPUNIT_ASSERT(g == 0); } void cache_index_write_test() { try { HTTPCache * hc_3 = new HTTPCache("cache-testsuite/dods_cache/", true); hc_3->d_http_cache_table->add_entry_to_cache_table( hc->d_http_cache_table->cache_index_parse_line(index_file_line.c_str())); hc_3->d_http_cache_table->d_cache_index = hc->d_cache_root + "test_index"; hc_3->d_http_cache_table->cache_index_write(); HTTPCache *hc_4 = new HTTPCache("cache-testsuite/dods_cache/", true); hc_4->d_http_cache_table->d_cache_index = hc_3->d_cache_root + "test_index"; hc_4->d_http_cache_table->cache_index_read(); HTTPCacheTable::CacheEntry *e = hc_4->d_http_cache_table->get_locked_entry_from_cache_table(localhost_url); DBG(cerr << "Got locked entry" << endl); CPPUNIT_ASSERT(e); CPPUNIT_ASSERT(e->url == localhost_url); e->unlock_read_response(); delete hc_3; hc_3 = 0; delete hc_4; hc_4 = 0; } catch (Error &e) { //cerr << "Fail: " << e.get_error_message() << endl; CPPUNIT_FAIL(e.get_error_message()); } } void create_cache_root_test() { hc->create_cache_root("/tmp/silly/"); CPPUNIT_ASSERT(access("/tmp/silly/", F_OK) == 0); remove("/tmp/silly"); #if 0 // This test doesn't work on some machines where the build is // run as root or where /root is owned by some other user (as is // the case with OS/X. try { hc->create_cache_root("/root/very_silly/"); access("/root/very_silly/", F_OK); remove("/root/very_silly/"); CPPUNIT_ASSERT(!"Should not be able to do this..."); } catch (Error &e) { CPPUNIT_ASSERT("This is where we want to be"); CPPUNIT_ASSERT(access("/root/very_silly/", F_OK) != 0); } #endif } void set_cache_root_test() { #if 0 // env var support removed 3/22/11 jhrg putenv("DODS_CACHE=/home/jimg"); hc->set_cache_root(); CPPUNIT_ASSERT(hc->d_cache_root == "/home/jimg/dods-cache/"); remove("/home/jimg/w3c-cache/"); #endif hc->set_cache_root("/home/jimg/test_cache"); CPPUNIT_ASSERT(hc->d_cache_root == "/home/jimg/test_cache/"); remove("/home/jimg/test_cache/"); } void get_single_user_lock_test() { hc->set_cache_root("/tmp/dods_test_cache"); hc->release_single_user_lock(); CPPUNIT_ASSERT(hc->get_single_user_lock()); CPPUNIT_ASSERT(access("/tmp/dods_test_cache/.lock", F_OK) == 0); // Second time should fail CPPUNIT_ASSERT(!hc->get_single_user_lock()); } void release_single_user_lock_test() { hc->set_cache_root("/tmp/dods_test_cache"); remove("/tmp/dods_test_cache/.lock"); // in case prev. test fails hc->d_locked_open_file = 0; CPPUNIT_ASSERT(hc->get_single_user_lock()); CPPUNIT_ASSERT(access("/tmp/dods_test_cache/.lock", F_OK) == 0); hc->release_single_user_lock(); CPPUNIT_ASSERT(hc->get_single_user_lock()); CPPUNIT_ASSERT(access("/tmp/dods_test_cache/.lock", F_OK) == 0); CPPUNIT_ASSERT(!hc->get_single_user_lock()); remove("/tmp/dods_test_cache/.lock"); } void create_hash_directory_test() { hc->set_cache_root("/tmp/dods_test_cache"); CPPUNIT_ASSERT(hc->d_http_cache_table->create_hash_directory(391) == "/tmp/dods_test_cache/391"); CPPUNIT_ASSERT(access("/tmp/dods_test_cache/391", W_OK) == 0); #if 0 // This test doesn't work on some machines where the build is // run as root or where /root is owned by some other user (as is // the case with OS/X. hc->set_cache_root("/root/"); try { hc->create_hash_directory(391); CPPUNIT_ASSERT(!"Create in bad directory"); } catch (Error &e) { } #endif remove("/tmp/dods_test_cache/391"); } void create_location_test() { hc->set_cache_root("/tmp/dods_test_cache"); HTTPCacheTable::CacheEntry *e = new HTTPCacheTable::CacheEntry; e->url = localhost_url; e->hash = hash_value; try { hc->d_http_cache_table->create_location(e); CPPUNIT_ASSERT(e->cachename != ""); } catch (Error &e) { CPPUNIT_ASSERT(true && "could not create entry file"); } remove(e->cachename.c_str()); delete e; e = 0; } void parse_headers_test() { HTTPCacheTable::CacheEntry *e = new HTTPCacheTable::CacheEntry; hc->d_http_cache_table->parse_headers(e, hc->d_max_entry_size, h); CPPUNIT_ASSERT(e->lm == 784025377); delete e; e = 0; } void calculate_time_test() { HTTPCacheTable::CacheEntry *e = new HTTPCacheTable::CacheEntry; hc->d_http_cache_table->parse_headers(e, hc->d_max_entry_size, h); hc->d_http_cache_table->calculate_time(e, hc->d_default_expiration, time(0)); CPPUNIT_ASSERT(e->corrected_initial_age > 249300571); CPPUNIT_ASSERT(e->freshness_lifetime == 86400); delete e; e = 0; } void write_metadata_test() { hc->set_cache_root("/tmp/dods_test_cache"); HTTPCacheTable::CacheEntry *e = new HTTPCacheTable::CacheEntry; try { e->hash = 101; hc->d_http_cache_table->create_location(e); CPPUNIT_ASSERT(e->cachename != ""); } catch (Error &e) { CPPUNIT_ASSERT(true && "could not create entry file"); } hc->write_metadata(e->cachename, h); vector headers; hc->read_metadata(e->cachename, headers); vector::iterator i, j; for (i = headers.begin(), j = h.begin(); i != headers.end() && j != h.end(); ++i, ++j) { CPPUNIT_ASSERT(*i == *j); } remove(e->cachename.c_str()); remove(string(e->cachename + ".meta").c_str()); delete e; e = 0; } void cache_response_test() { HTTPResponse *rs = http_conn->fetch_url(localhost_url); try { time_t now = time(0); vector *headers = rs->get_headers(); hc->cache_response(localhost_url, now, *headers, rs->get_stream()); CPPUNIT_ASSERT(hc->is_url_in_cache(localhost_url)); HTTPCacheTable::CacheEntry *e = hc->d_http_cache_table->get_locked_entry_from_cache_table(localhost_url); CPPUNIT_ASSERT(file_size(e->cachename) == 343); e->unlock_read_response(); delete rs; rs = 0; } catch (Error &e) { delete rs; rs = 0; cerr << "Error: " << e.get_error_message() << endl; CPPUNIT_ASSERT(!"Caught unexpected Error/InternalErr"); } } void is_url_valid_test() { cache_response_test(); // This should get a response into the cache. CPPUNIT_ASSERT(hc->is_url_valid(localhost_url)); } void get_cached_response_test() { cache_response_test(); // Get a response into the cache. vector cached_headers; FILE *cached_body = hc->get_cached_response(localhost_url, cached_headers); HTTPResponse *rs = http_conn->fetch_url(localhost_url); vector *headers = rs->get_headers(); // headers and cached_headers should match, except for the values. vector::iterator i, j; for (i = cached_headers.begin(), j = headers->begin(); i != cached_headers.end() && j != headers->end(); ++i, ++j) { string ch = (*i).substr(0, (*i).find(": ")); // Skip over headers that won't be cached. jhrg 7/4/05 while (is_hop_by_hop_header(*j)) ++j; string h = (*j).substr(0, (*j).find(": ")); DBG(cerr << "cached: " << ch << ", header: " << h << endl); CPPUNIT_ASSERT(ch == h); } #ifdef DODS_DEBUG std::ostream_iterator out_it(std::cerr, "\n"); cerr << "Cached headers: "; std::copy(cached_headers.begin(), cached_headers.end(), out_it); cerr << "Headers: "; std::copy(headers->begin(), headers->end(), out_it); #endif CPPUNIT_ASSERT(i == cached_headers.end()); // This may not be true if. For example, keep-alive might appear in the list of headers // received, but not in the list of headers cached. // CPPUNIT_ASSERT(j == headers->end()); // every byte of the cached_body and response body should match. while (!feof(rs->get_stream()) && !feof(cached_body) && !ferror(rs->get_stream()) && !ferror(cached_body)) { char cb, b; int cn = fread(&cb, 1, 1, cached_body); int n = fread(&b, 1, 1, rs->get_stream()); CPPUNIT_ASSERT(cn == n); if (cn == 1) CPPUNIT_ASSERT(cb == b); } CPPUNIT_ASSERT(feof(rs->get_stream()) && feof(cached_body)); hc->release_cached_response(cached_body); delete rs; rs = 0; } void perform_garbage_collection_test() { try { delete hc; hc = 0; auto_ptr gc(new HTTPCache("cache-testsuite/gc_cache", true)); DBG(cerr << "get_cache_root: " << gc->get_cache_root() << endl); HTTPResponse *rs = http_conn->fetch_url(localhost_url); gc->cache_response(localhost_url, time(0), *(rs->get_headers()), rs->get_stream()); CPPUNIT_ASSERT(gc->is_url_in_cache(localhost_url)); delete rs; rs = 0; rs = http_conn->fetch_url(expired); gc->cache_response(expired, time(0), *(rs->get_headers()), rs->get_stream()); CPPUNIT_ASSERT(gc->is_url_in_cache(expired)); delete rs; rs = 0; sleep(2); gc->perform_garbage_collection(); gc->d_http_cache_table->cache_index_write(); CPPUNIT_ASSERT( !gc->is_url_in_cache(expired) && "This may fail if sleep is not long enough before gc above"); } catch (Error &e) { cerr << "Exception: " << e.get_error_message() << endl; CPPUNIT_ASSERT(false); } } void purge_cache_and_release_cached_response_test() { try { auto_ptr pc(new HTTPCache("cache-testsuite/purge_cache", true)); DBG(cerr << "get_cache_root: " << pc->get_cache_root() << endl); time_t now = time(0); HTTPResponse *rs = http_conn->fetch_url(localhost_url); pc->cache_response(localhost_url, now, *(rs->get_headers()), rs->get_stream()); CPPUNIT_ASSERT(pc->is_url_in_cache(localhost_url)); delete rs; rs = 0; string expired = "http://test.opendap.org/cgi-bin/expires.sh"; now = time(0); rs = http_conn->fetch_url(expired); pc->cache_response(expired, now, *(rs->get_headers()), rs->get_stream()); CPPUNIT_ASSERT(pc->is_url_in_cache(expired)); delete rs; rs = 0; HTTPCacheTable::CacheEntry *e1 = pc->d_http_cache_table->get_locked_entry_from_cache_table(expired); HTTPCacheTable::CacheEntry *e2 = pc->d_http_cache_table->get_locked_entry_from_cache_table(localhost_url); string e1_file = e1->cachename; string e2_file = e2->cachename; e1->unlock_read_response(); e2->unlock_read_response(); vector headers; FILE *b = pc->get_cached_response(expired, headers); try { pc->purge_cache(); CPPUNIT_ASSERT(!"This call should throw Error"); } catch (Error &e) { CPPUNIT_ASSERT("Caught Error as expected"); } pc->release_cached_response(b); pc->purge_cache(); CPPUNIT_ASSERT(!pc->is_url_in_cache(localhost_url)); CPPUNIT_ASSERT(!pc->is_url_in_cache(expired)); CPPUNIT_ASSERT(access(e1_file.c_str(), F_OK) != 0); CPPUNIT_ASSERT(access(e2_file.c_str(), F_OK) != 0); CPPUNIT_ASSERT(pc->d_http_cache_table->d_current_size == 0); } catch (Error &e) { cerr << "Exception: " << e.get_error_message() << endl; CPPUNIT_ASSERT(false); } } void instance_test() { try { // FIXME: Explain HTTPCache::delete_instance(); HTTPCache *c = HTTPCache::instance("cache-testsuite/singleton_cache", true); DBG(cerr << "get_cache_root: " << c->get_cache_root() << endl); if (!c->is_url_in_cache(localhost_url)) { HTTPResponse *rs = http_conn->fetch_url(localhost_url); c->cache_response(localhost_url, time(0), *(rs->get_headers()), rs->get_stream()); delete rs; rs = 0; } CPPUNIT_ASSERT(c->is_url_in_cache(localhost_url)); if (!c->is_url_in_cache(expired)) { HTTPResponse *rs = http_conn->fetch_url(expired); c->cache_response(expired, time(0), *(rs->get_headers()), rs->get_stream()); delete rs; rs = 0; } CPPUNIT_ASSERT(c->is_url_in_cache(expired)); HTTPCacheTable::CacheEntry *e1 = c->d_http_cache_table->get_locked_entry_from_cache_table(expired); HTTPCacheTable::CacheEntry *e2 = c->d_http_cache_table->get_locked_entry_from_cache_table(localhost_url); string e1_file = e1->cachename; string e2_file = e2->cachename; e1->unlock_read_response(); e2->unlock_read_response(); c->purge_cache(); CPPUNIT_ASSERT(!c->is_url_in_cache(localhost_url)); CPPUNIT_ASSERT(!c->is_url_in_cache(expired)); CPPUNIT_ASSERT(access(e1_file.c_str(), F_OK) != 0); CPPUNIT_ASSERT(access(e2_file.c_str(), F_OK) != 0); } catch (Error &e) { cerr << "Exception: " << e.get_error_message() << endl; CPPUNIT_ASSERT(false); } // Call this here to simulate exiting the program. This ensures that // the next test's call to instance() gets a fresh cache. The static // method will still be run at exit, but that's OK since it tests the // value of _instance and simply returns with it's zero. HTTPCache::delete_instance(); #ifndef WIN32 SignalHandler::delete_instance(); #endif } void get_conditional_response_headers_test() { try { auto_ptr c(new HTTPCache("cache-testsuite/header_cache", true)); DBG(cerr << "get_cache_root: " << c->get_cache_root() << endl); CPPUNIT_ASSERT(c->get_cache_root() == "cache-testsuite/header_cache/"); if (!c->is_url_in_cache(localhost_url)) { HTTPResponse *rs = http_conn->fetch_url(localhost_url); c->cache_response(localhost_url, time(0), *(rs->get_headers()), rs->get_stream()); delete rs; } CPPUNIT_ASSERT(c->is_url_in_cache(localhost_url)); if (!c->is_url_in_cache(expired)) { HTTPResponse *rs = http_conn->fetch_url(expired); c->cache_response(expired, time(0), *(rs->get_headers()), rs->get_stream()); delete rs; } CPPUNIT_ASSERT(c->is_url_in_cache(expired)); vector h = c->get_conditional_request_headers(localhost_url); DBG(copy(h.begin(), h.end(), ostream_iterator(cout, "\n"))); DBG(cerr << "if none match location: " << h[0].find("If-None-Match: ") << endl); // I know what the strings should start with... CPPUNIT_ASSERT(h[0].find("If-None-Match: ") == 0); h = c->get_conditional_request_headers(expired); DBG(cerr << "Number of headers: " << h.size() << endl); DBG(copy(h.begin(), h.end(), ostream_iterator(cout, "\n"))); CPPUNIT_ASSERT(h[0].find("If-Modified-Since: ") == 0); } catch (Error &e) { CPPUNIT_FAIL(e.get_error_message()); } } void update_response_test() { try { auto_ptr c(new HTTPCache("cache-testsuite/singleton_cache", true)); DBG(cerr << "get_cache_root: " << c->get_cache_root() << endl); if (!c->is_url_in_cache(localhost_url)) { HTTPResponse *rs = http_conn->fetch_url(localhost_url); c->cache_response(localhost_url, time(0), *(rs->get_headers()), rs->get_stream()); delete rs; } if (!c->is_url_in_cache(expired)) { HTTPResponse *rs = http_conn->fetch_url(expired); c->cache_response(expired, time(0), *(rs->get_headers()), rs->get_stream()); delete rs; } // Yes, there's stuff here. CPPUNIT_ASSERT(c->is_url_in_cache(localhost_url)); CPPUNIT_ASSERT(c->is_url_in_cache(expired)); vector orig_h; FILE *cr = c->get_cached_response(localhost_url, orig_h); DBG(copy(orig_h.begin(), orig_h.end(), ostream_iterator(cerr, "\n"))); // Before we merge, et c., check that the headers we're going to // poke in aren't already there. CPPUNIT_ASSERT(find(orig_h.begin(), orig_h.end(), "XHTTPCache: 123456789") == orig_h.end()); CPPUNIT_ASSERT(find(orig_h.begin(), orig_h.end(), "Date: ") == orig_h.end()); // Make up some new headers. vector new_h; new_h.push_back("XHTTPCache: 123456789"); new_h.push_back("Date: "); c->release_cached_response(cr); c->update_response(localhost_url, time(0), new_h); vector updated_h; cr = c->get_cached_response(localhost_url, updated_h); c->release_cached_response(cr); DBG(cerr << endl); DBG(copy(updated_h.begin(), updated_h.end(), ostream_iterator(cerr, "\n"))); // The XHTTPCacheTest header should be new, Date should replace the // existing Date header. // This may not be true when using distcheck and/or when the user // has set USE_CACHE to 1 in their .dodsrc. jhrg 9/29/15 // CPPUNIT_ASSERT(orig_h.size() + 1 == updated_h.size()); CPPUNIT_ASSERT(find(updated_h.begin(), updated_h.end(), "XHTTPCache: 123456789") != updated_h.end()); CPPUNIT_ASSERT(find(updated_h.begin(), updated_h.end(), "Date: ") != updated_h.end()); } catch (Error &e) { CPPUNIT_FAIL(e.get_error_message()); } } // Only run this interactively since you need to hit Ctrl-c to generate // SIGINT while the cache is doing its thing. 02/10/04 jhrg void interrupt_test() { try { auto_ptr c(new HTTPCache("cache-testsuite/singleton_cache", true)); string coads = "http://test.opendap.org/dap/data/nc/coads_climatology.nc"; if (!c->is_url_in_cache(coads)) { HTTPResponse *rs = http_conn->fetch_url(coads); cerr << "In interrupt test, hit ctrl-c now... "; c->cache_response(coads, time(0), *(rs->get_headers()), rs->get_stream()); cerr << "to late."; delete rs; } } catch (Error &e) { CPPUNIT_FAIL(e.get_error_message()); } } void cache_gc_test() { string fnoc1 = "http://test.opendap.org/dap/data/nc/fnoc1.nc.dds"; string jan = "http://test.opendap.org/dap/data/nc/jan.nc.dds"; string feb = "http://test.opendap.org/dap/data/nc/feb.nc.dds"; try { auto_ptr pc(new HTTPCache("cache-testsuite/purge_cache", true)); #if 0 // This broke Fedora ppc64le system with XFS system CPPUNIT_ASSERT(pc->d_http_cache_table->d_block_size == 4096); #endif // Change the size parameters so that we can run some tests pc->d_total_size = 12288; // bytes pc->d_folder_size = pc->d_total_size / 10; pc->d_gc_buffer = pc->d_total_size / 10; // The cache should start empty CPPUNIT_ASSERT(pc->d_http_cache_table->d_current_size == 0); // Get a url HTTPResponse *rs = http_conn->fetch_url(fnoc1); pc->cache_response(fnoc1, time(0), *(rs->get_headers()), rs->get_stream()); CPPUNIT_ASSERT(pc->is_url_in_cache(fnoc1)); delete rs; rs = 0; // trigger a hit for fnoc1 vector h; FILE *f = pc->get_cached_response(fnoc1, h); pc->release_cached_response(f); rs = http_conn->fetch_url(jan); pc->cache_response(jan, time(0), *(rs->get_headers()), rs->get_stream()); CPPUNIT_ASSERT(pc->is_url_in_cache(jan)); delete rs; rs = 0; // trigger two hits for jan f = pc->get_cached_response(jan, h); pc->release_cached_response(f); f = pc->get_cached_response(jan, h); pc->release_cached_response(f); rs = http_conn->fetch_url(feb); pc->cache_response(feb, time(0), *(rs->get_headers()), rs->get_stream()); CPPUNIT_ASSERT(pc->is_url_in_cache(feb)); delete rs; rs = 0; } catch (Error &e) { CPPUNIT_FAIL(e.get_error_message()); } // now that pc is out of scope, its dtor has been run and GC // performed. The feb URL should have been deleted. try { auto_ptr pc(new HTTPCache("cache-testsuite/purge_cache", true)); CPPUNIT_ASSERT(!pc->is_url_in_cache(feb)); } catch (Error &e) { CPPUNIT_FAIL(e.get_error_message()); } } }; CPPUNIT_TEST_SUITE_REGISTRATION (HTTPCacheTest); } // namespace libdap int main(int argc, char*argv[]) { GetOpt getopt(argc, argv, "dh"); int option_char; while ((option_char = getopt()) != -1) switch (option_char) { case 'd': debug = 1; // debug is a static global break; case 'h': { // help - show test names cerr << "Usage: HTTPCacheTest has the following tests:" << endl; const std::vector &tests = libdap::HTTPCacheTest::suite()->getTests(); unsigned int prefix_len = libdap::HTTPCacheTest::suite()->getName().append("::").length(); for (std::vector::const_iterator i = tests.begin(), e = tests.end(); i != e; ++i) { cerr << (*i)->getName().replace(0, prefix_len, "") << endl; } break; } default: break; } // Run cleanup here, so that the first run works (since this code now // sets up the tests). // This gives valgrind fits... system("cd cache-testsuite && ./cleanup.sh"); CppUnit::TextTestRunner runner; runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest()); bool wasSuccessful = true; string test = ""; int i = getopt.optind; if (i == argc) { // run them all wasSuccessful = runner.run(""); } else { for (; i < argc; ++i) { if (debug) cerr << "Running " << argv[i] << endl; test = libdap::HTTPCacheTest::suite()->getName().append("::").append(argv[i]); wasSuccessful = wasSuccessful && runner.run(test); } } return wasSuccessful ? 0 : 1; }