diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2014-12-31 00:59:02 +0000 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-06-13 17:29:37 +0100 |
commit | 343e7f9689ea4ff50dd1ea8e61636d28deb37179 (patch) | |
tree | ec6636f6b8c791af17b69119a03e43e894932d19 | |
parent | Fix case of SQL (diff) | |
download | p2pvr-343e7f9689ea4ff50dd1ea8e61636d28deb37179.tar.bz2 p2pvr-343e7f9689ea4ff50dd1ea8e61636d28deb37179.tar.xz p2pvr-343e7f9689ea4ff50dd1ea8e61636d28deb37179.zip |
Remove all the mess around symlinks and fancy structure in storage and add some unit tests
-rw-r--r-- | p2pvr/daemon/recorder.cpp | 4 | ||||
-rw-r--r-- | p2pvr/daemon/storage.cpp | 247 | ||||
-rw-r--r-- | p2pvr/daemon/storage.h | 21 | ||||
-rw-r--r-- | p2pvr/daemon/unittests/Jamfile.jam | 19 | ||||
-rw-r--r-- | p2pvr/daemon/unittests/testStorage.cpp | 83 | ||||
-rw-r--r-- | p2pvr/ice/p2pvr.ice | 1 |
6 files changed, 118 insertions, 257 deletions
diff --git a/p2pvr/daemon/recorder.cpp b/p2pvr/daemon/recorder.cpp index 35aeba8..2580e84 100644 --- a/p2pvr/daemon/recorder.cpp +++ b/p2pvr/daemon/recorder.cpp @@ -8,6 +8,8 @@ #include "serviceStreamer.h" #include "storage.h" #include "muxer.h" +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> std::string Recorder::extension; std::string Recorder::muxerCommand; @@ -62,7 +64,7 @@ Recorder::StartRecording(P2PVR::SchedulePtr schedule, DVBSI::ServicePtr service, auto storage = P2PVR::StoragePrx::checkedCast(adapter->createProxy(adapter->getCommunicator()->stringToIdentity("Storage"))); auto recordings = P2PVR::RecordingsPrx::checkedCast(adapter->createProxy(adapter->getCommunicator()->stringToIdentity("Recordings"))); - auto id = storage->CreateForEventRecording(extension, schedule, service, event); + auto id = boost::lexical_cast<std::string>(boost::uuids::random_generator()()); auto store = storage->OpenForWrite(id); ScopeObject _store(NULL, NULL, [this,&store,&storage,&id]() { storage->Close(store); storage->Delete(id); }); auto target = store; diff --git a/p2pvr/daemon/storage.cpp b/p2pvr/daemon/storage.cpp index 2465afe..d43bddc 100644 --- a/p2pvr/daemon/storage.cpp +++ b/p2pvr/daemon/storage.cpp @@ -2,234 +2,30 @@ #include "storage.h" #include "fileSink.h" #include "muxedFileSink.h" -#include <commonHelpers.h> #include <fcntl.h> #include <logger.h> #include <boost/filesystem/operations.hpp> -#include <boost/uuid/uuid_generators.hpp> -#include <boost/uuid/uuid_io.hpp> -#include <boost/lexical_cast.hpp> namespace fs = boost::filesystem; std::string Storage::muxerCommand; fs::path Storage::root; -fs::path Storage::byAll; -fs::path Storage::byTitle; -fs::path Storage::byDate; -fs::path Storage::byService; -fs::path Storage::bySchedule; DECLARE_OPTIONS(Storage, "P2PVR Storage options") ("p2pvr.storage.muxercommand", Options::value(&muxerCommand, "/usr/bin/ffmpeg -i - -f mp4 -y ${TARGET}"), "Command to perform TS->PS muxing (default: '/usr/bin/ffmpeg -i - -f mp4 -y ${TARGET}')") ("p2pvr.storage.root", Options::value(&root, "recordings"), "Root folder in which to store recordings") -("p2pvr.storage.all", Options::value(&byAll, "all"), - "Sub folder in which to store all recordings") -("p2pvr.storage.bytitle", Options::value(&byTitle, "title"), - "Sub folder in which to store by title") -("p2pvr.storage.bydate", Options::value(&byDate, "date"), - "Sub folder in which to store by date") -("p2pvr.storage.byservice", Options::value(&byService, "service"), - "Sub folder in which to store by service") -("p2pvr.storage.byschedule", Options::value(&bySchedule, "schedule"), - "Sub folder in which to store by schedule") END_OPTIONS(Storage); -inline static -fs::path createPath(const fs::path & start) -{ - return start; -} - -template <typename Arg> -inline static -fs::path -operator/(const fs::path & lhs, const IceUtil::Optional<Arg> & rhs) -{ - if (rhs) { - return lhs / *rhs; - } - return lhs; -} - -template <typename Arg, typename ... Args> -inline static -fs::path createPath(const fs::path & start, const Arg & arg, const Args & ... args) -{ - auto path = start / arg; - return createPath(path, args...); -} - -template <typename Arg, typename ... Args> -inline static -std::string firstNotNull(const Arg & arg, const Args & ...) -{ - return boost::lexical_cast<std::string>(arg); -} - -template <typename Arg, typename ... Args> -inline static -std::string firstNotNull(const IceUtil::Optional<Arg> & arg, const Args & ... args) -{ - return arg ? firstNotNull(*arg, args...) : firstNotNull(args...); -} - -template <typename ... Args> -inline static -std::string firstNotNull(const std::string & arg, const Args & ...) -{ - return arg; -} - -inline static -std::string -formatIf(const boost::format & f) -{ - return f.str(); -} - -template <typename Arg, typename ... Args> -inline static -IceUtil::Optional<std::string> -formatIf(boost::format & f, const IceUtil::Optional<Arg> & arg, const Args & ... args) -{ - if (arg) { - f % *arg; - return formatIf(f, args...); - } - else { - return IceUtil::Optional<std::string>(); - } -} - -template <typename Arg, typename ... Args> -inline static -IceUtil::Optional<std::string> -formatIf(boost::format & f, const Arg & arg, const Args & ... args) -{ - f % arg; - return formatIf(f, args...); -} - -template <typename... Args> -inline static -IceUtil::Optional<std::string> -formatIf(const std::string & msgfmt, const Args & ... args) -{ - boost::format fmt(msgfmt); - return formatIf(fmt, args...); -} - -std::string -Storage::CreateForEventRecording(const std::string & ext, const P2PVR::SchedulePtr & schedule, const DVBSI::ServicePtr & service, const DVBSI::EventPtr & event, const Ice::Current &) -{ - fs::create_directories(root / byAll); - auto id = boost::lexical_cast<std::string>(boost::uuids::random_generator()()); - fs::path path = root / byAll / id; - path.replace_extension(ext); - auto fd = open(path.string().c_str(), O_WRONLY | O_CREAT | O_EXCL, 0664); - if (fd < 0) { - Logger()->messagebf(LOG_ERR, "%s: Failed to open file for writing at %s (%d:%s)", __PRETTY_FUNCTION__, - path, errno, strerror(errno)); - throw P2PVR::StorageException(path.string(), strerror(errno)); - } - createSymlinks(ext, id, schedule, service, event); - Logger()->messagebf(LOG_INFO, "%s: Created new recording %s", __PRETTY_FUNCTION__, path); - - close(fd); - return id; -} - -void -Storage::createSymlinks(const std::string & ext, const std::string & target, const P2PVR::SchedulePtr & schedule, const DVBSI::ServicePtr & service, const DVBSI::EventPtr & event) -{ - // By title, with optional season and episode information - createSymlink(ext, target, createPath(root, byTitle, - event->Title, - formatIf("Season %02d", event->Season), - firstNotNull( - formatIf("Part %02d of %02d - %s", event->Episode, event->Episodes, event->Subtitle), - formatIf("Episode %02d - %s", event->Episode, event->Subtitle), - formatIf("Part %02d of %02d - %s", event->Episode, event->Episodes, event->Description), - formatIf("Part %02d of %02d", event->Episode, event->Episodes), - formatIf("Episode %02d - %s", event->Episode, event->Description), - event->Subtitle, - event->Description, - formatIf("Episode %02d", event->Episode), - event->StartTime))); - // By date - createSymlink(ext, target, createPath(root, byDate, - formatIf("%04d-%02d-%02d", event->StartTime.Year, event->StartTime.Month, event->StartTime.Day), - firstNotNull( - formatIf("%s - %s", event->Title, event->Subtitle), - formatIf("%s - %s", event->Title, event->Description), - event->Title))); - // By service - createSymlink(ext, target, createPath(root, byService, - service->Name, - firstNotNull( - formatIf("%s - %s", event->Title, event->Subtitle), - formatIf("%s - %s", event->Title, event->Description), - event->Title))); - // By schedule title - createSymlink(ext, target, createPath(root, bySchedule, - formatIf("Title: %s", schedule->Title), - formatIf("%04d-%02d-%02d", event->StartTime.Year, event->StartTime.Month, event->StartTime.Day), - firstNotNull( - formatIf("%s - %s", event->Title, event->Subtitle), - formatIf("%s - %s", event->Title, event->Description), - event->Title))); - // By schedule search - createSymlink(ext, target, createPath(root, bySchedule, - formatIf("Search: %s", schedule->Search), - firstNotNull( - formatIf("%s - %s", event->Title, event->Subtitle), - formatIf("%s - %s", event->Title, event->Description), - event->Title))); -} -void -Storage::createSymlink(const std::string & ext, const std::string & target, const fs::path & link) -{ - fs::path path = link; - path.replace_extension(ext); - Logger()->messagebf(LOG_DEBUG, "%s: link(%s) -> target(%s)", __PRETTY_FUNCTION__, path, target); - if (fs::exists(path)) { - Logger()->messagebf(LOG_WARNING, "%s: symlink already exists %s", __PRETTY_FUNCTION__, path); - return; - } - fs::create_directories(path.parent_path()); - fs::path relativeTarget; - for (fs::path tmp = path.parent_path(); tmp != root; tmp = tmp.parent_path()) { - relativeTarget /= ".."; - } - relativeTarget /= byAll; - relativeTarget /= target; - relativeTarget.replace_extension(ext); - fs::create_symlink(relativeTarget, path); -} - -std::string -Storage::FindExtension(const std::string & id) -{ - fs::directory_iterator end; - for (fs::directory_iterator itr(root / byAll); itr != end; ++itr) { - if (itr->path().stem() == id) { - return itr->path().extension().string(); - } - } - return ""; -} - P2PVR::RawDataClientPrx Storage::OpenForWrite(const std::string & id, const Ice::Current & ice) { - fs::path path = root / byAll / id; - path.replace_extension(FindExtension(id)); + fs::create_directories(root); + fs::path path = root / id; P2PVR::RawDataClient * target; if (muxerCommand.empty()) { - auto fd = open(path.string().c_str(), O_WRONLY | O_APPEND | O_LARGEFILE); + auto fd = open(path.string().c_str(), O_WRONLY | O_CREAT | O_EXCL, 0664); if (fd < 0) { Logger()->messagebf(LOG_ERR, "%s: Failed to open file for reading at %s (%d:%s)", __PRETTY_FUNCTION__, path, errno, strerror(errno)); @@ -254,41 +50,15 @@ Storage::Close(const P2PVR::RawDataClientPrx & file, const Ice::Current &) void Storage::Delete(const std::string & id, const Ice::Current &) { - fs::path path = root / byAll / id; - path.replace_extension(FindExtension(id)); - Logger()->messagebf(LOG_INFO, "%s: Deleting links to %s", __PRETTY_FUNCTION__, path); - DeleteFrom(path, fs::canonical(root)); -} - -void -Storage::DeleteFrom(const fs::path & path, const fs::path & from) -{ - Logger()->messagebf(LOG_DEBUG, "%s: Deleting links to %s to %s", __PRETTY_FUNCTION__, path, from); - fs::directory_iterator end; - for (fs::directory_iterator itr(from); itr != end; ++itr) { - if (fs::is_directory(*itr)) { - DeleteFrom(path, *itr); - } - else { - boost::system::error_code err; - auto link = fs::canonical(*itr, err); - if (err || link == path) { - Logger()->messagebf(LOG_DEBUG, "%s: deleting %s", __PRETTY_FUNCTION__, *itr); - fs::remove(*itr); - } - } - } - if (from != root && fs::is_empty(from)) { - Logger()->messagebf(LOG_DEBUG, "%s: deleting directory %s", __PRETTY_FUNCTION__, from); - fs::remove(from); - } + fs::path path = root / id; + Logger()->messagebf(LOG_INFO, "%s: Deleting %s", __PRETTY_FUNCTION__, path); + fs::remove(path); } void Storage::Send(const std::string & id, const P2PVR::RawDataClientPrx & target, Ice::Long start, Ice::Long len, const Ice::Current &) { - fs::path path = root / byAll / id; - path.replace_extension(FindExtension(id)); + fs::path path = root / id; auto fd = open(path.string().c_str(), O_RDONLY | O_LARGEFILE); if (fd < 0) { Logger()->messagebf(LOG_ERR, "%s: Failed to open file for reading at %s (%d:%s)", __PRETTY_FUNCTION__, @@ -318,9 +88,8 @@ Storage::Send(const std::string & id, const P2PVR::RawDataClientPrx & target, Ic Ice::Long Storage::FileSize(const std::string & id, const Ice::Current &) { - fs::path path = root / byAll / id; + fs::path path = root / id; try { - path.replace_extension(FindExtension(id)); return fs::file_size(path); } catch (...) { diff --git a/p2pvr/daemon/storage.h b/p2pvr/daemon/storage.h index 89a2836..0655bd2 100644 --- a/p2pvr/daemon/storage.h +++ b/p2pvr/daemon/storage.h @@ -9,21 +9,15 @@ class Storage : public P2PVR::Storage { public: - std::string CreateForEventRecording(const std::string & ext, const P2PVR::SchedulePtr &, const DVBSI::ServicePtr &, const DVBSI::EventPtr &, const Ice::Current &); - P2PVR::RawDataClientPrx OpenForWrite(const std::string &, const Ice::Current &); - void Close(const P2PVR::RawDataClientPrx & file, const Ice::Current &); - void Delete(const std::string &, const Ice::Current &); - Ice::Long FileSize(const std::string &, const Ice::Current &); - void Send(const std::string &, const P2PVR::RawDataClientPrx & target, Ice::Long start, Ice::Long len, const Ice::Current &); + P2PVR::RawDataClientPrx OpenForWrite(const std::string &, const Ice::Current &) override; + void Close(const P2PVR::RawDataClientPrx & file, const Ice::Current &) override; + void Delete(const std::string &, const Ice::Current &) override; + Ice::Long FileSize(const std::string &, const Ice::Current &) override; + void Send(const std::string &, const P2PVR::RawDataClientPrx & target, Ice::Long start, Ice::Long len, const Ice::Current &) override; INITOPTIONS; protected: - static void createSymlinks(const std::string & ext, const std::string &, const P2PVR::SchedulePtr &, const DVBSI::ServicePtr &, const DVBSI::EventPtr &); - static void createSymlink(const std::string & ext, const std::string & target, const boost::filesystem::path & link); - static void DeleteFrom(const boost::filesystem::path &, const boost::filesystem::path &); - static std::string FindExtension(const std::string & id); - typedef TemporarayIceAdapterObject<P2PVR::RawDataClient> OpenFile; typedef boost::shared_ptr<OpenFile> OpenFilePtr; typedef std::set<OpenFilePtr> OpenFiles; @@ -31,11 +25,6 @@ class Storage : public P2PVR::Storage { static std::string muxerCommand; static boost::filesystem::path root; - static boost::filesystem::path byAll; - static boost::filesystem::path byTitle; - static boost::filesystem::path byDate; - static boost::filesystem::path byService; - static boost::filesystem::path bySchedule; }; #endif diff --git a/p2pvr/daemon/unittests/Jamfile.jam b/p2pvr/daemon/unittests/Jamfile.jam index e41b164..f07338f 100644 --- a/p2pvr/daemon/unittests/Jamfile.jam +++ b/p2pvr/daemon/unittests/Jamfile.jam @@ -102,3 +102,22 @@ unit-test testSched : <define>ROOT=\"$(me)\" ; +unit-test testStorage : + testStorage.cpp + : + <define>BOOST_TEST_DYN_LINK + <library>../..//p2common + <library>../..//p2basics + <library>../..//p2lib + <library>../..//p2ice + <library>../..//p2xml + <library>../..//p2ut + <library>..//p2pvrdaemon + <library>IceUtil + <library>Ice + <library>boost_system + <library>boost_filesystem + <library>../..//boost_utf + <define>ROOT=\"$(me)\" + ; + diff --git a/p2pvr/daemon/unittests/testStorage.cpp b/p2pvr/daemon/unittests/testStorage.cpp new file mode 100644 index 0000000..5480566 --- /dev/null +++ b/p2pvr/daemon/unittests/testStorage.cpp @@ -0,0 +1,83 @@ +#define BOOST_TEST_MODULE Storage +#include <boost/test/unit_test.hpp> +#include <boost/filesystem/operations.hpp> +#include <testOptionsSource.h> +#include <Ice/ObjectAdapter.h> +#include <Ice/Service.h> +#include <maintenance.h> +#include <mockTuner.h> +#include <si.h> +#include <storage.h> +#include <boost/lexical_cast.hpp> +#include <commonHelpers.h> + +const boost::filesystem::path rootDir = "/tmp/ut/p2pvr/recordings"; +class Core { + public: + Core() + { + int paramCount = 0; + ic = Ice::initialize(paramCount, NULL); + auto adapter = ic->createObjectAdapterWithEndpoints("Adp", "tcp -p 12004"); + TestOptionsSource::LoadTestOptions({ + { "p2pvr.storage.muxercommand", std::string() }, + { "p2pvr.storage.root", rootDir.string() }, + }); + adapter->add(new Storage(), ic->stringToIdentity("Storage")); + adapter->activate(); + + st = P2PVR::StoragePrx::checkedCast(ic->stringToProxy("Storage")); + BOOST_REQUIRE(st); + st->ice_ping(); + } + + ~Core() + { + ic->destroy(); + } + + P2PVR::StoragePrx st; + + private: + Ice::CommunicatorPtr ic; +}; + +BOOST_FIXTURE_TEST_SUITE( StCore, Core ) + +BOOST_AUTO_TEST_CASE( st_openWriteClose ) +{ + boost::filesystem::remove_all(rootDir); + std::string id = "made-up-storage-id"; + + auto rdc = st->OpenForWrite(id); + + P2PVR::Data data; + data.resize(1024); + + rdc->NewData(data); + + st->Close(rdc); + + auto stSize = st->FileSize(id); + BOOST_REQUIRE_EQUAL(1024, stSize); + BOOST_REQUIRE_EQUAL(1024, boost::filesystem::file_size(rootDir / id)); + + st->Delete(id); + BOOST_REQUIRE(!boost::filesystem::exists(rootDir / id)); +} + +BOOST_AUTO_TEST_CASE( st_notuniqueid ) +{ + boost::filesystem::remove_all(rootDir); + std::string id = "made-up-storage-id"; + + auto rdc = st->OpenForWrite(id); + st->Close(rdc); + + BOOST_REQUIRE_THROW(st->OpenForWrite(id), P2PVR::StorageException); + st->Delete(id); +} + +BOOST_AUTO_TEST_SUITE_END() + + diff --git a/p2pvr/ice/p2pvr.ice b/p2pvr/ice/p2pvr.ice index 7f58539..0d287d5 100644 --- a/p2pvr/ice/p2pvr.ice +++ b/p2pvr/ice/p2pvr.ice @@ -64,7 +64,6 @@ module P2PVR { }; interface Storage { - idempotent string CreateForEventRecording(string ext, Schedule sc, DVBSI::Service se, DVBSI::Event ev) throws StorageException; idempotent RawDataClient * OpenForWrite(string guid) throws StorageException; idempotent void Close(RawDataClient * file) throws StorageException; idempotent void Delete(string guid) throws StorageException; |