From 024aef47119bb9258d5b855506865344e6da2b62 Mon Sep 17 00:00:00 2001 From: Steven Murray <Steven.Murray@cern.ch> Date: Fri, 13 Dec 2019 17:58:08 +0100 Subject: [PATCH] Added cta-immutable-file-test --- cta.spec.in | 1 + tests/CMakeLists.txt | 20 +- tests/ImmutableFileTest.cpp | 284 +++++++++++++++++++++++++ tests/ImmutableFileTest.hpp | 118 ++++++++++ tests/ImmutableFileTestCmdLineArgs.cpp | 116 ++++++++++ tests/ImmutableFileTestCmdLineArgs.hpp | 58 +++++ tests/ImmutableFileTestMain.cpp | 30 +++ 7 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 tests/ImmutableFileTest.cpp create mode 100644 tests/ImmutableFileTest.hpp create mode 100644 tests/ImmutableFileTestCmdLineArgs.cpp create mode 100644 tests/ImmutableFileTestCmdLineArgs.hpp create mode 100644 tests/ImmutableFileTestMain.cpp diff --git a/cta.spec.in b/cta.spec.in index a5c2e9ceb1..5324abe3e5 100644 --- a/cta.spec.in +++ b/cta.spec.in @@ -245,6 +245,7 @@ Unit tests and system tests with virtual tape drives %defattr(0755,root,root,-) %{_libdir}/libsystemTestHelperTests.so* %{_libdir}/libcta-tapedSystemTests.so* +%{_bindir}/cta-immutable-file-test %{_bindir}/cta-rdbmsUnitTests %{_bindir}/cta-rdbmsUnitTests-oracle.sh %{_bindir}/cta-unitTests diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9b9ceae41d..3feef2cdbb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -114,7 +114,25 @@ target_link_libraries(cta-systemTests pthread ${PROTOBUF3_LIBRARIES}) -install(TARGETS cta-rdbmsUnitTests cta-unitTests cta-unitTests-multiProcess cta-systemTests DESTINATION usr/bin) +add_executable(cta-immutable-file-test + ImmutableFileTest.cpp + ImmutableFileTestMain.cpp + ImmutableFileTestCmdLineArgs.cpp) + +set_property (TARGET cta-immutable-file-test APPEND PROPERTY INCLUDE_DIRECTORIES "/usr/include/xrootd") + +target_link_libraries(cta-immutable-file-test + ctacommon) + +install( + TARGETS + cta-rdbmsUnitTests + cta-unitTests + cta-unitTests-multiProcess + cta-systemTests + cta-immutable-file-test + DESTINATION + usr/bin) install(TARGETS systemTestHelperTests DESTINATION usr/${CMAKE_INSTALL_LIBDIR}) diff --git a/tests/ImmutableFileTest.cpp b/tests/ImmutableFileTest.cpp new file mode 100644 index 0000000000..87e18e691a --- /dev/null +++ b/tests/ImmutableFileTest.cpp @@ -0,0 +1,284 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2019 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "common/exception/CommandLineNotParsed.hpp" +#include "common/exception/Exception.hpp" +#include "tests/ImmutableFileTest.hpp" +#include "tests/ImmutableFileTestCmdLineArgs.hpp" + + +#include <iostream> +#include <list> +#include <sstream> +#include <utility> +#include <XrdCl/XrdClFile.hh> + +namespace cta { + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +ImmutableFileTest::ImmutableFileTest( + std::istream &inStream, + std::ostream &outStream, + std::ostream &errStream): + m_in(inStream), + m_out(outStream), + m_err(errStream) { +} + +//------------------------------------------------------------------------------ +// main +//------------------------------------------------------------------------------ +int ImmutableFileTest::main(const int argc, char *const *const argv) { + bool cmdLineNotParsed = false; + std::string errorMessage; + + try { + return exceptionThrowingMain(argc, argv); + } catch(exception::CommandLineNotParsed &ue) { + errorMessage = ue.getMessage().str(); + cmdLineNotParsed = true; + } catch(exception::Exception &ex) { + errorMessage = ex.getMessage().str(); + } catch(std::exception &se) { + errorMessage = se.what(); + } catch(...) { + errorMessage = "An unknown exception was thrown"; + } + + // Reaching this point means the command has failed, an exception was throw + // and errorMessage has been set accordingly + + m_err << errorMessage << std::endl; + if(cmdLineNotParsed) { + m_err << std::endl; + ImmutableFileTestCmdLineArgs::printUsage(m_err); + } + return 1; +} + +//------------------------------------------------------------------------------ +// exceptionThrowingMain +//------------------------------------------------------------------------------ +int ImmutableFileTest::exceptionThrowingMain(const int argc, char *const *const argv) { + const ImmutableFileTestCmdLineArgs cmdLine(argc, argv); + + if(cmdLine.help) { + ImmutableFileTestCmdLineArgs::printUsage(m_out); + return 0; + } + + if(cmdLine.fileUrl.IsLocalFile()) { + exception::Exception ex; + ex.getMessage() << cmdLine.fileUrl.GetURL() << " is local"; + throw ex; + } + if(fileExists(cmdLine.fileUrl)) { + m_out << cmdLine.fileUrl.GetURL() << " already exists" << std::endl; + deleteFile(cmdLine.fileUrl); + m_out << "Deleted " << cmdLine.fileUrl.GetURL() << std::endl; + } else { + m_out << cmdLine.fileUrl.GetURL() << " does not exist yet" << std::endl; + } + + m_out << "Ready to create " << cmdLine.fileUrl.GetURL() << std::endl; + m_out << std::endl; + try { + testOpenCloseFile(cmdLine.fileUrl, XrdCl::OpenFlags::New, 0); + testOpenCloseFile(cmdLine.fileUrl, XrdCl::OpenFlags::Delete | XrdCl::OpenFlags::Write, 0); + testOpenCloseFile(cmdLine.fileUrl, XrdCl::OpenFlags::Write, kXR_NotAuthorized); + testOpenCloseFile(cmdLine.fileUrl, XrdCl::OpenFlags::Update, kXR_NotAuthorized); + testOpenCloseFile(cmdLine.fileUrl, XrdCl::OpenFlags::Append, kXR_NotAuthorized); + } catch(...) { + try { + deleteFile(cmdLine.fileUrl); + } catch(...) { + // Do nothing + } + throw; + } + + return 0; +} + +//------------------------------------------------------------------------------ +// fileExists +//------------------------------------------------------------------------------ +bool ImmutableFileTest::fileExists(const XrdCl::URL &url) { + XrdCl::FileSystem fs(url); + XrdCl::StatInfo *info = 0; + const XrdCl::XRootDStatus statStatus = fs.Stat(url.GetPath(), info); + if(!statStatus.IsOK()) m_out << statStatus.ToStr() << std::endl; + return statStatus.IsOK(); +} + +//------------------------------------------------------------------------------ +// deleteFile +//------------------------------------------------------------------------------ +void ImmutableFileTest::deleteFile(const XrdCl::URL &url) { + XrdCl::FileSystem fs(url); + const XrdCl::XRootDStatus rmStatus = fs.Rm(url.GetPath()); + + if (!rmStatus.IsOK()) { + exception::Exception ex; + ex.getMessage() << "Failed to rm file: URL=" << url.GetURL() << " :" << rmStatus.ToStr(); + throw ex; + } +} + +//------------------------------------------------------------------------------ +// testOpenCloseFile +//------------------------------------------------------------------------------ +void ImmutableFileTest::testOpenCloseFile(const XrdCl::URL &url, + const XrdCl::OpenFlags::Flags openFlags, const uint32_t expectedOpenErrNo) { + bool testPassed = false; + std::ostringstream msg; + const bool expectedSuccess = 0 == expectedOpenErrNo; + XrdCl::File file; + + m_out << "START OF TEST: Opening " << url.GetURL() << " with flags \"" << openFlagsToString(openFlags) << "\"" << + " expecting " << xErrorCodeToString(expectedOpenErrNo) << std::endl; + + const XrdCl::Access::Mode openMode = XrdCl::Access::UR | XrdCl::Access::UW; + const XrdCl::XRootDStatus openStatus = file.Open(url.GetURL(), openFlags, openMode); + { + if (expectedSuccess) { + if(openStatus.IsOK()) { + testPassed = true; + msg << "Succeeded to open file as expected"; + } else { + testPassed = false; + msg << "Failure when success was expected: Failed to open file: " << openStatus.ToStr(); + } + } else { + if (openStatus.IsOK()) { + testPassed = false; + msg << "Success when failure was expected: Successfully opened file"; + } else { + if (expectedOpenErrNo == openStatus.errNo) { + testPassed = true; + msg << "Successfully got " << xErrorCodeToString(expectedOpenErrNo); + } else { + testPassed = false; + msg << "Unexpectedly got " << xErrorCodeToString(openStatus.errNo); + } + } + } + } + + if (openStatus.IsOK()){ + const XrdCl::XRootDStatus closeStatus = file.Close(); + if (!closeStatus.IsOK()) { + exception::Exception ex; + ex.getMessage() << "Failed to close \"" << url.GetURL() << "\" :" << closeStatus.ToStr(); + throw ex; + } + } + + if(testPassed) { + m_out << "END OF TEST: PASSED: " << msg.str() << std::endl; + } else { + exception::Exception ex; + ex.getMessage() << "END OF TEST: FAILED: " << msg.str(); + throw ex; + } +} + +//------------------------------------------------------------------------------ +// openFlagsToString +//------------------------------------------------------------------------------ +std::string ImmutableFileTest::openFlagsToString(XrdCl::OpenFlags::Flags flags) { + typedef std::pair<XrdCl::OpenFlags::Flags, std::string> FlagAndName; + std::list<FlagAndName> allFlags; + allFlags.emplace_back(XrdCl::OpenFlags::Compress, "Compress"); + allFlags.emplace_back(XrdCl::OpenFlags::Delete, "Delete"); + allFlags.emplace_back(XrdCl::OpenFlags::Force, "Force"); + allFlags.emplace_back(XrdCl::OpenFlags::MakePath, "MakePath"); + allFlags.emplace_back(XrdCl::OpenFlags::New, "New"); + allFlags.emplace_back(XrdCl::OpenFlags::NoWait, "NoWait"); + allFlags.emplace_back(XrdCl::OpenFlags::Append, "Append"); + allFlags.emplace_back(XrdCl::OpenFlags::Read, "Read"); + allFlags.emplace_back(XrdCl::OpenFlags::Update, "Update"); + allFlags.emplace_back(XrdCl::OpenFlags::Write, "Write"); + allFlags.emplace_back(XrdCl::OpenFlags::POSC, "POSC"); + allFlags.emplace_back(XrdCl::OpenFlags::Refresh, "Refresh"); + allFlags.emplace_back(XrdCl::OpenFlags::Replica, "Replica"); + allFlags.emplace_back(XrdCl::OpenFlags::SeqIO, "SeqIO"); + allFlags.emplace_back(XrdCl::OpenFlags::PrefName, "PrefName"); + + std::ostringstream result; + for(const auto &flagAndName : allFlags) { + const XrdCl::OpenFlags::Flags flag = flagAndName.first; + const std::string &name = flagAndName.second; + + if(flags & flag) { + if(!result.str().empty()) { + result << " | "; + } + result << name; + flags &= ~flag; + } + } + + if(0 != flags) { + if(!result.str().empty()) { + result << " | "; + } + result << "ONE OR MORE UNKNOWN FLAGS"; + } + + return result.str(); +} + +//------------------------------------------------------------------------------ +// xErrorCodeToString +//------------------------------------------------------------------------------ +std::string ImmutableFileTest::xErrorCodeToString(const uint32_t code) { + switch(code) { + case 0: return "SUCCESS"; + case kXR_ArgInvalid: return "kXR_ArgInvalid"; + case kXR_ArgMissing: return "kXR_ArgMissing"; + case kXR_ArgTooLong: return "kXR_ArgTooLong"; + case kXR_FileLocked: return "kXR_FileLocked"; + case kXR_FileNotOpen: return "kXR_FileNotOpen"; + case kXR_FSError: return "kXR_FSError"; + case kXR_InvalidRequest: return "kXR_InvalidRequest"; + case kXR_IOError: return "kXR_IOError"; + case kXR_NoMemory: return "kXR_NoMemory"; + case kXR_NoSpace: return "kXR_NoSpace"; + case kXR_NotAuthorized: return "kXR_NotAuthorized"; + case kXR_NotFound: return "kXR_NotFound"; + case kXR_ServerError: return "kXR_ServerError"; + case kXR_Unsupported: return "kXR_Unsupported"; + case kXR_noserver: return "kXR_noserver"; + case kXR_NotFile: return "kXR_NotFile"; + case kXR_isDirectory: return "kXR_isDirectory"; + case kXR_Cancelled: return "kXR_Cancelled"; + case kXR_ChkLenErr: return "kXR_ChkLenErr"; + case kXR_ChkSumErr: return "kXR_ChkSumErr"; + case kXR_inProgress: return "kXR_inProgress"; + case kXR_overQuota: return "kXR_overQuota"; + case kXR_SigVerErr: return "kXR_SigVerErr"; + case kXR_DecryptErr: return "kXR_DecryptErr"; + case kXR_Overloaded: return "kXR_Overloaded"; + default: return "UNKNOWN"; + } +} + +} // namespace cta diff --git a/tests/ImmutableFileTest.hpp b/tests/ImmutableFileTest.hpp new file mode 100644 index 0000000000..425e9a85e1 --- /dev/null +++ b/tests/ImmutableFileTest.hpp @@ -0,0 +1,118 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <iostream> +#include <XrdCl/XrdClFile.hh> + +namespace cta { + +/** + * Command-line tool that modifies a file using the XRootD client API. + */ +class ImmutableFileTest { +public: + + /** + * Constructor. + * + * @param inStream Standard input stream. + * @param outStream Standard output stream. + * @param errStream Standard error stream. + */ + ImmutableFileTest(std::istream &inStream, std::ostream &outStream, std::ostream &errStream); + + /** + * The object's implementation of main() that should be called from the main() + * of the program. + * + * @param argc The number of command-line arguments including the program name. + * @param argv The command-line arguments. + * @return The exit value of the program. + */ + int main(const int argc, char *const *const argv); + +private: + + /** + * Standard input stream. + */ + std::istream &m_in; + + /** + * Standard output stream. + */ + std::ostream &m_out; + + /** + * Standard error stream. + */ + std::ostream &m_err; + + /** + * An exception throwing version of main(). + * + * @param argc The number of command-line arguments including the program name. + * @param argv The command-line arguments. + * @return The exit value of the program. + */ + int exceptionThrowingMain(const int argc, char *const *const argv); + + /** + * @return True if the specified file exists + * @param url The XRootD URL of the file to be tested. + */ + bool fileExists(const XrdCl::URL &url); + + /** + * Deletes the specified file. + * + * @param url The XRootD URL of the file to be deleted. + */ + void deleteFile(const XrdCl::URL &url); + + /** + * Tests the opening and closing of the specified file. + * + * WARNING: The specified file can be destroyed depending on the flags used to + * open it. + * + * @param url The XRootD URL of the file to be tested. + * @param openFlags The XRootD flags to be used when opening the file. + * @param expectecErrNo The expected errNo result of opening the file. + */ + void testOpenCloseFile(const XrdCl::URL &url, const XrdCl::OpenFlags::Flags openFlags, + const uint32_t expectedOpenErrNo); + + /** + * @return The string representation of the specified XRootD "open file" + * flags. + * @param flags The XRootD "open file" flags + */ + std::string openFlagsToString(XrdCl::OpenFlags::Flags flags); + + /** + * @return The string representation of the specified XErrorCode. + * @param code The XErrorCode. + */ + std::string xErrorCodeToString(uint32_t code); + +}; // class ImmutableFileTest + +} // namespace cta diff --git a/tests/ImmutableFileTestCmdLineArgs.cpp b/tests/ImmutableFileTestCmdLineArgs.cpp new file mode 100644 index 0000000000..5b69526c81 --- /dev/null +++ b/tests/ImmutableFileTestCmdLineArgs.cpp @@ -0,0 +1,116 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "common/exception/CommandLineNotParsed.hpp" +#include "tests/ImmutableFileTestCmdLineArgs.hpp" + +#include <getopt.h> +#include <ostream> + +namespace cta { + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +ImmutableFileTestCmdLineArgs::ImmutableFileTestCmdLineArgs(const int argc, char *const *const argv): + help(false) { + + static struct option longopts[] = { + {"help", no_argument, NULL, 'h'}, + {NULL , 0, NULL, 0} + }; + + // Prevent getopt() from printing an error message if it does not recognize + // an option character + opterr = 0; + + int opt = 0; + while((opt = getopt_long(argc, argv, ":h", longopts, NULL)) != -1) { + switch(opt) { + case 'h': + help = true; + break; + case ':': // Missing parameter + { + exception::CommandLineNotParsed ex; + ex.getMessage() << "The -" << (char)opt << " option requires a parameter"; + throw ex; + } + case '?': // Unknown option + { + exception::CommandLineNotParsed ex; + if(0 == optopt) { + ex.getMessage() << "Unknown command-line option"; + } else { + ex.getMessage() << "Unknown command-line option: -" << (char)optopt; + } + throw ex; + } + default: + { + exception::CommandLineNotParsed ex; + ex.getMessage() << + "getopt_long returned the following unknown value: 0x" << + std::hex << (int)opt; + throw ex; + } + } // switch(opt) + } // while getopt_long() + + // There is no need to continue parsing when the help option is set + if(help) { + return; + } + + // Calculate the number of non-option ARGV-elements + const int nbArgs = argc - optind; + + // Check the number of arguments + if(nbArgs != 1) { + exception::CommandLineNotParsed ex; + ex.getMessage() << "Wrong number of command-line arguments: expected=1 actual=" << nbArgs; + throw ex; + } + + const std::string fileUrlString = argv[optind]; + + fileUrl.FromString(fileUrlString); + + if(!fileUrl.IsValid()) { + throw exception::Exception(std::string("File URL is not a valid XRootD URL: URL=") + fileUrlString); + } +} + +//------------------------------------------------------------------------------ +// printUsage +//------------------------------------------------------------------------------ +void ImmutableFileTestCmdLineArgs::printUsage(std::ostream &os) { + os << + "Usage:" "\n" + " cta-immutable-file-test URL" "\n" + "Where:" "\n" + " URL is the XRootD URL of the destination file" "\n" + "Options:" "\n" + " -h,--help" "\n" + " Prints this usage message" "\n" + "WARNING:" "\n" + " This command will destroy the destination file!"; + os << std::endl; +} + +} // namespace cta diff --git a/tests/ImmutableFileTestCmdLineArgs.hpp b/tests/ImmutableFileTestCmdLineArgs.hpp new file mode 100644 index 0000000000..e546b74a93 --- /dev/null +++ b/tests/ImmutableFileTestCmdLineArgs.hpp @@ -0,0 +1,58 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <ostream> +#include <string> +#include <XrdCl/XrdClURL.hh> + +namespace cta { + +/** + * Structure to store command-line arguments. + */ +struct ImmutableFileTestCmdLineArgs { + /** + * True if the usage message should be printed. + */ + bool help; + + /** + * The XRootd URL of the file to be modified. + */ + XrdCl::URL fileUrl; + + /** + * Constructor that parses the specified command-line arguments. + * + * @param argc The number of command-line arguments including the name of the + * executable. + * @param argv The vector of command-line arguments. + */ + ImmutableFileTestCmdLineArgs(const int argc, char *const *const argv); + + /** + * Prints the usage message of the command-line tool. + * + * @param os The output stream to which the usage message is to be printed. + */ + static void printUsage(std::ostream &os); +}; + +} // namespace cta diff --git a/tests/ImmutableFileTestMain.cpp b/tests/ImmutableFileTestMain.cpp new file mode 100644 index 0000000000..f7d2c973f7 --- /dev/null +++ b/tests/ImmutableFileTestMain.cpp @@ -0,0 +1,30 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "tests/ImmutableFileTest.hpp" + +#include <iostream> + +//------------------------------------------------------------------------------ +// main +//------------------------------------------------------------------------------ +int main(const int argc, char *const *const argv) { + cta::ImmutableFileTest cmd(std::cin, std::cout, std::cerr); + + return cmd.main(argc, argv); +} -- GitLab