diff options
Diffstat (limited to 'searchlib/src/tests/util/ioerrorhandler')
5 files changed, 370 insertions, 0 deletions
diff --git a/searchlib/src/tests/util/ioerrorhandler/.gitignore b/searchlib/src/tests/util/ioerrorhandler/.gitignore new file mode 100644 index 00000000000..2f5c2e77191 --- /dev/null +++ b/searchlib/src/tests/util/ioerrorhandler/.gitignore @@ -0,0 +1 @@ +searchlib_ioerrorhandler_test_app diff --git a/searchlib/src/tests/util/ioerrorhandler/CMakeLists.txt b/searchlib/src/tests/util/ioerrorhandler/CMakeLists.txt new file mode 100644 index 00000000000..92d6ab30d72 --- /dev/null +++ b/searchlib/src/tests/util/ioerrorhandler/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +vespa_add_executable(searchlib_ioerrorhandler_test_app + SOURCES + ioerrorhandler_test.cpp + DEPENDS + searchlib_test + searchlib +) +vespa_add_test(NAME searchlib_ioerrorhandler_test_app COMMAND searchlib_ioerrorhandler_test_app) diff --git a/searchlib/src/tests/util/ioerrorhandler/DESC b/searchlib/src/tests/util/ioerrorhandler/DESC new file mode 100644 index 00000000000..3328798b936 --- /dev/null +++ b/searchlib/src/tests/util/ioerrorhandler/DESC @@ -0,0 +1 @@ +IOErrorHandler test. Take a look at ioerrorhandler_test.cpp for details. diff --git a/searchlib/src/tests/util/ioerrorhandler/FILES b/searchlib/src/tests/util/ioerrorhandler/FILES new file mode 100644 index 00000000000..2447a10e991 --- /dev/null +++ b/searchlib/src/tests/util/ioerrorhandler/FILES @@ -0,0 +1 @@ +ioerrorhandler_test.cpp diff --git a/searchlib/src/tests/util/ioerrorhandler/ioerrorhandler_test.cpp b/searchlib/src/tests/util/ioerrorhandler/ioerrorhandler_test.cpp new file mode 100644 index 00000000000..bcfaa5f8ac8 --- /dev/null +++ b/searchlib/src/tests/util/ioerrorhandler/ioerrorhandler_test.cpp @@ -0,0 +1,358 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +LOG_SETUP("ioerrorhandler_test"); +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/stllike/string.h> +#include <vespa/searchlib/util/statefile.h> +#include <vespa/searchlib/util/ioerrorhandler.h> +#include <atomic> +#include <iostream> +#include <fstream> +#include <string> +#include <setjmp.h> +#include <dlfcn.h> +#include <vespa/searchlib/test/statefile.h> +#include <vespa/searchlib/test/statestring.h> + +extern "C" +{ + +ssize_t read(int fd, void *buf, size_t count); +ssize_t write(int fd, const void *buf, size_t count); +ssize_t pread(int fd, void *buf, size_t count, off_t offset); +ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); + + +} + +using ReadFunc = ssize_t (*)(int fd, void *buf, size_t count); +using WriteFunc = ssize_t (*)(int fd, const void *buf, size_t count); +using PreadFunc = ssize_t (*)(int fd, void *buf, size_t count, off_t offset); +using PwriteFunc = ssize_t (*)(int fd, const void *buf, size_t count, + off_t offset); + +using namespace search::test::statefile; +using namespace search::test::statestring; + +namespace +{ + +ReadFunc libc_read; +WriteFunc libc_write; +PreadFunc libc_pread; +PwriteFunc libc_pwrite; + +} + +int injectErrno; +std::atomic<int> injectreadErrnoTrigger; +std::atomic<int> injectpreadErrnoTrigger; +std::atomic<int> injectwriteErrnoTrigger; +std::atomic<int> injectpwriteErrnoTrigger; + +ssize_t read(int fd, void *buf, size_t count) +{ + if (--injectreadErrnoTrigger == 0) { + errno = injectErrno; + return -1; + } + if (!libc_read) { + libc_read = reinterpret_cast<ReadFunc>(dlsym(RTLD_NEXT, "read")); + } + return libc_read(fd, buf, count); +} + +ssize_t write(int fd, const void *buf, size_t count) +{ + if (--injectwriteErrnoTrigger == 0) { + errno = injectErrno; + return -1; + } + if (!libc_write) { + libc_write = reinterpret_cast<WriteFunc>(dlsym(RTLD_NEXT, "write")); + } + return libc_write(fd, buf, count); +} + +ssize_t pread(int fd, void *buf, size_t count, off_t offset) +{ + if (--injectpreadErrnoTrigger == 0) { + errno = injectErrno; + return -1; + } + if (!libc_pread) { + libc_pread = reinterpret_cast<PreadFunc>(dlsym(RTLD_NEXT, "pread")); + } + return libc_pread(fd, buf, count, offset); +} + + +ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) +{ + if (--injectpwriteErrnoTrigger == 0) { + errno = injectErrno; + return -1; + } + if (!libc_pwrite) { + libc_pwrite = reinterpret_cast<PwriteFunc>(dlsym(RTLD_NEXT, "pwrite")); + } + return libc_pwrite(fd, buf, count, offset); +} + + + +namespace search +{ + +const char *testStringBase = "This is a test\n"; + +using strvec = std::vector<vespalib::string>; + +namespace +{ + +bool +assertHistory(std::vector<vespalib::string> &exp, + std::vector<vespalib::string> &act) +{ + if (!EXPECT_EQUAL(exp.size(), act.size())) { + return false; + } + for (size_t i = 0; i < exp.size(); ++i) { + if (!EXPECT_EQUAL(exp[i], act[i])) { + return false; + } + } + return true; +} + + +class Fixture +{ +public: + + std::unique_ptr<StateFile> sf; + std::unique_ptr<FastOS_File> file; + char buf[8192]; + char *testString; + + Fixture(); + + void openFile(); + + void openFileDIO(); + + void writeTestString(); +}; + + +Fixture::Fixture() + : sf(), + file() +{ + unlink("testfile"); + StateFile::erase("state"); + sf.reset(new StateFile("state")); + testString = &buf[0]; + int off = reinterpret_cast<unsigned long>(testString) & 4095; + if (off != 0) { + testString += 4096 - off; + } + assert(testString + strlen(testStringBase) < &buf[0] + sizeof(buf)); + strcpy(testString, testStringBase); +} + + +void +Fixture::openFile() +{ + file.reset(new FastOS_File); + file->OpenReadWrite("testfile"); +} + +void +Fixture::openFileDIO() +{ + file.reset(new FastOS_File); + file->EnableDirectIO(); + file->OpenReadWrite("testfile"); +} + +void +Fixture::writeTestString() +{ + file->WriteBuf(testString, strlen(testString)); + file->SetPosition(0); +} + + +} + + +TEST("Test that ioerror handler can be instantated") +{ + StateFile::erase("state"); + StateFile sf("state"); + IOErrorHandler ioeh(&sf); + EXPECT_FALSE(ioeh.fired()); +} + + +TEST_F("Test that ioerror handler can process read error", Fixture) +{ + IOErrorHandler ioeh(f.sf.get()); + EXPECT_FALSE(ioeh.fired()); + f.openFile(); + f.writeTestString(); + uint64_t fileSize = f.file->GetSize(); + EXPECT_EQUAL(strlen(f.testString), fileSize); + char buf[1024]; + assert(fileSize <= sizeof(buf)); + try { + f.file->SetPosition(0); + injectErrno = EIO; + injectreadErrnoTrigger = 1; + f.file->ReadBuf(buf, fileSize); + LOG(error, "Should never get here"); + abort(); + } catch (std::runtime_error &e) { + LOG(info, "Caught std::runtime_error exception: %s", e.what()); + EXPECT_TRUE(strstr(e.what(), "Input/output error") != nullptr); + } + EXPECT_TRUE(ioeh.fired()); + { + vespalib::string act = readState(*f.sf); + normalizeTimestamp(act); + vespalib::string exp = "state=down ts=0.0 operation=read " + "file=testfile error=5 offset=0 len=15 " + "rlen=-1\n"; + EXPECT_EQUAL(exp, act); + } + { + strvec exp({ "state=down ts=0.0 operation=read " + "file=testfile error=5 offset=0 len=15 " + "rlen=-1\n"}); + std::vector<vespalib::string> act(readHistory("state.history")); + normalizeTimestamps(act); + TEST_DO(assertHistory(exp, act)); + } +} + +TEST_F("Test that ioerror handler can process pread error", Fixture) +{ + IOErrorHandler ioeh(f.sf.get()); + EXPECT_FALSE(ioeh.fired()); + f.openFile(); + f.writeTestString(); + uint64_t fileSize = f.file->GetSize(); + EXPECT_EQUAL(strlen(f.testString), fileSize); + char buf[1024]; + assert(fileSize <= sizeof(buf)); + try { + f.file->SetPosition(0); + injectErrno = EIO; + injectpreadErrnoTrigger = 1; + f.file->ReadBuf(buf, fileSize, 0); + LOG(error, "Should never get here"); + abort(); + } catch (std::runtime_error &e) { + LOG(info, "Caught std::runtime_error exception: %s", e.what()); + EXPECT_TRUE(strstr(e.what(), "Input/output error") != nullptr); + } + EXPECT_TRUE(ioeh.fired()); + { + vespalib::string act = readState(*f.sf); + normalizeTimestamp(act); + vespalib::string exp = "state=down ts=0.0 operation=read " + "file=testfile error=5 offset=0 len=15 " + "rlen=-1\n"; + EXPECT_EQUAL(exp, act); + } + { + strvec exp({ "state=down ts=0.0 operation=read " + "file=testfile error=5 offset=0 len=15 " + "rlen=-1\n"}); + std::vector<vespalib::string> act(readHistory("state.history")); + normalizeTimestamps(act); + TEST_DO(assertHistory(exp, act)); + } +} + +TEST_F("Test that ioerror handler can process write error", Fixture) +{ + IOErrorHandler ioeh(f.sf.get()); + EXPECT_FALSE(ioeh.fired()); + f.openFile(); + try { + injectErrno = EIO; + injectwriteErrnoTrigger = 1; + f.writeTestString(); + LOG(error, "Should never get here"); + abort(); + } catch (std::runtime_error &e) { + LOG(info, "Caught std::runtime_error exception: %s", e.what()); + EXPECT_TRUE(strstr(e.what(), "Input/output error") != nullptr); + } + EXPECT_TRUE(ioeh.fired()); + { + vespalib::string act = readState(*f.sf); + normalizeTimestamp(act); + vespalib::string exp = "state=down ts=0.0 operation=write " + "file=testfile error=5 offset=0 len=15 " + "rlen=-1\n"; + EXPECT_EQUAL(exp, act); + } + { + strvec exp({ "state=down ts=0.0 operation=write " + "file=testfile error=5 offset=0 len=15 " + "rlen=-1\n"}); + std::vector<vespalib::string> act(readHistory("state.history")); + normalizeTimestamps(act); + TEST_DO(assertHistory(exp, act)); + } +} + + +TEST_F("Test that ioerror handler can process pwrite error", Fixture) +{ + IOErrorHandler ioeh(f.sf.get()); + EXPECT_FALSE(ioeh.fired()); + f.openFileDIO(); + try { + injectErrno = EIO; + injectpwriteErrnoTrigger = 1; + f.writeTestString(); + LOG(error, "Should never get here"); + abort(); + } catch (std::runtime_error &e) { + LOG(info, "Caught std::runtime_error exception: %s", e.what()); + EXPECT_TRUE(strstr(e.what(), "Input/output error") != nullptr); + } + EXPECT_TRUE(ioeh.fired()); + { + vespalib::string act = readState(*f.sf); + normalizeTimestamp(act); + vespalib::string exp = "state=down ts=0.0 operation=write " + "file=testfile error=5 offset=0 len=15 " + "rlen=-1\n"; + EXPECT_EQUAL(exp, act); + } + { + strvec exp({ "state=down ts=0.0 operation=write " + "file=testfile error=5 offset=0 len=15 " + "rlen=-1\n"}); + std::vector<vespalib::string> act(readHistory("state.history")); + normalizeTimestamps(act); + TEST_DO(assertHistory(exp, act)); + } +} + +} + +TEST_MAIN() +{ + TEST_RUN_ALL(); + search::StateFile::erase("state"); + unlink("testfile"); +} |