diff --git a/inc/FileSystem.hpp b/inc/FileSystem.hpp index dd1227f..cc225f0 100644 --- a/inc/FileSystem.hpp +++ b/inc/FileSystem.hpp @@ -28,6 +28,7 @@ public: bool makeLink(const Path& source, const Path& to, bool isDynamic); void log() const; + ui64 size() const; static const std::string& getLastError(); diff --git a/inc/Interpreter.hpp b/inc/Interpreter.hpp index 995c585..c7db2c6 100644 --- a/inc/Interpreter.hpp +++ b/inc/Interpreter.hpp @@ -21,6 +21,9 @@ private: private: std::map mCommands; - FileSystem mFileSystem; std::string mError; + +public: + FileSystem mFileSystem; + bool verbose = true; }; \ No newline at end of file diff --git a/inc/Node.hpp b/inc/Node.hpp index aa696ee..150f252 100644 --- a/inc/Node.hpp +++ b/inc/Node.hpp @@ -37,7 +37,7 @@ public: virtual std::shared_ptr findNode(const std::vector& path, ui32 currentDepth = 0); virtual std::shared_ptr findNode(const Key& path) { return nullptr; } - virtual void clearFlags(std::shared_ptr& directory) {} + virtual void clearFlags(std::shared_ptr& directory); virtual bool isHardNode() const; virtual void removeIncomingDynamicLinks(); virtual void removeOutgoingLinks() {} diff --git a/src/Directory.cpp b/src/Directory.cpp index 2f491f1..14a6917 100644 --- a/src/Directory.cpp +++ b/src/Directory.cpp @@ -55,11 +55,11 @@ void Directory::getMaxDepthUtil(ui32 depth, ui32& maxDepth) const { void Directory::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents) { indents[currentDepth] = true; - indent(ss, currentDepth, indents); + ss << key; - // ss << " [" << mIncomingHardLinks.size() << ":" << mIncomingDynamicLinks.size() << "] " << size(); - ss << " [" << mWorkingNodeFlag.lock().get() << "] "; + ss << " [h" << mIncomingHardLinks.size() << ": d" << mIncomingDynamicLinks.size() << "] "; + // ss << " [" << mWorkingNodeFlag.lock().get() << "] "; ss << "\n"; currentDepth++; @@ -90,14 +90,6 @@ std::shared_ptr Directory::clone() const { member.second->mParent = out; } - for (auto& member : out->mMembers) { - std::shared_ptr& node = member.second; - if (auto linkNode = std::dynamic_pointer_cast(node)) { - auto targetNode = linkNode->getTarget(); - assert(Link::linkNodes(linkNode, targetNode, linkNode->isHardLink())); - } - } - return out; } diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp index 27c9c5d..3b535d3 100644 --- a/src/FileSystem.cpp +++ b/src/FileSystem.cpp @@ -81,6 +81,11 @@ bool FileSystem::makeDirectory(const Path& path) { return true; } + if (parentNode->getType() != Node::DIRECTORY) { + gError = "Target path is not a directory"; + return false; + } + auto directory = std::make_shared(); directory->mParent = parentNode; @@ -110,6 +115,11 @@ bool FileSystem::makeFile(const Path& path) { return true; } + if (parentNode->getType() != Node::DIRECTORY) { + gError = "Target path is not a directory"; + return false; + } + auto newFile = std::make_shared(); newFile->mParent = parentNode; @@ -146,6 +156,11 @@ bool FileSystem::makeLink(const Path& source, const Path& target, bool isDynamic return false; } + if (parentNodeTarget->getType() != Node::DIRECTORY) { + gError = "Target path is not a directory"; + return false; + } + const Key& key = source.getFilename(); if (parentNodeTarget->findNode(key)) { @@ -161,7 +176,11 @@ bool FileSystem::makeLink(const Path& source, const Path& target, bool isDynamic return false; } - assert(parentNodeTarget->attachNode(key, newLink)); + if (!parentNodeTarget->attachNode(key, newLink)) { + log(); + } + + // assert(parentNodeTarget->attachNode(key, newLink)); return true; } @@ -247,6 +266,7 @@ bool FileSystem::removeFileOrLink(const Path &path) { return false; } + existingNode->removeIncomingDynamicLinks(); existingNode->removeOutgoingLinks(); if (!parentNode->detachNode(path.getFilename())) { @@ -347,6 +367,11 @@ bool FileSystem::moveNode(const Path &source, const Path &target) { return false; } + if (parentNodeTarget->getType() != Node::DIRECTORY) { + gError = "Target path is not a directory"; + return false; + } + assert(parentNodeTarget->attachNode(key, sourceNode)); assert(parentNodeSource->detachNode(key)); @@ -395,4 +420,8 @@ bool FileSystem::isPathContains(const std::shared_ptr& node, const std::sh } return true; +} + +ui64 FileSystem::size() const { + return root->size(); } \ No newline at end of file diff --git a/src/Interpreter.cpp b/src/Interpreter.cpp index ea53237..cf03a4a 100644 --- a/src/Interpreter.cpp +++ b/src/Interpreter.cpp @@ -109,10 +109,8 @@ bool Interpreter::interpret(const std::string& command) { std::vector words; getWords(command, words); - std::cout << command << "\n"; - if (words.empty()) { - reportError("Empty command"); + if (verbose && 0) reportError("Empty command"); return false; } @@ -122,24 +120,25 @@ bool Interpreter::interpret(const std::string& command) { auto iter = mCommands.find(commandName); if (iter == mCommands.end()) { - reportError("Command not found"); - printHelp(); + if (verbose && 0) reportError("Command not found"); + if (verbose && 0) printHelp(); return false; } if (iter->second.numArguments != words.size() - 1) { - reportError("Invalid number of arguments given for: " + commandName); + if (verbose && 0) reportError("Invalid number of arguments given for: " + commandName); return false; } bool success = iter->second.callback(mFileSystem, words); - mFileSystem.log(); + if (verbose && success) std::cout << command << "\n"; + if (verbose && success) mFileSystem.log(); if (!success) { - reportError(FileSystem::getLastError()); + if (verbose && 0) reportError(FileSystem::getLastError()); return false; } return true; -} \ No newline at end of file +} diff --git a/src/Link.cpp b/src/Link.cpp index 62d0c86..8f18813 100644 --- a/src/Link.cpp +++ b/src/Link.cpp @@ -4,16 +4,18 @@ #include #include #include +#include Link::Link() {} Link::Link(const Link &node) : Node(node) { - mLink = node.mLink; - mIsHard = node.mIsHard; + } std::shared_ptr Link::clone() const { - return std::make_shared(*this); + auto out = std::make_shared(*this); + Link::linkNodes(out, getLink(), isHardLink()); + return out; } Link::~Link() = default; @@ -42,7 +44,9 @@ void Link::removeOutgoingLinks() { auto& links = isHardLink() ? target->mIncomingHardLinks : target->mIncomingDynamicLinks; links.erase(std::remove_if(links.begin(), links.end(), [&](std::weak_ptr& node){ auto targetLink = node.lock(); - return !targetLink || targetLink.get() == this; + assert(targetLink); + auto res = targetLink.get() == this; + return res; }), links.end()); } @@ -62,12 +66,17 @@ std::shared_ptr Link::getTarget() { std::shared_ptr Link::findNode(const std::vector& path, ui32 currentDepth) { assert(path.size() != currentDepth); - return getLink()->findNode(path, currentDepth + 1); + return getLink()->findNode(path, currentDepth); } void Link::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents) { indent(ss, currentDepth, indents); - ss << key << (isHardLink() ? " hlink[" : " dlink["); + + ss << key; + ss << (isHardLink() ? " hlink[" : " dlink["); + + ss << " [h" << mIncomingHardLinks.size() << ": d" << mIncomingDynamicLinks.size() << "] "; + std::vector> path; getNodeStraightPath(getLink(), path); std::reverse(path.begin(), path.end()); diff --git a/src/Node.cpp b/src/Node.cpp index c04adb7..51ae23a 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -31,10 +31,14 @@ ui32 Node::getMaxDepth() const { } std::shared_ptr Node::findNode(const std::vector& path, ui32 currentDepth) { - assert(false); + // assert(false); return nullptr; } +void Node::clearFlags(std::shared_ptr& directory) { + mWorkingNodeFlag = directory; +} + bool Node::isHardNode() const { return std::any_of(mIncomingHardLinks.begin(), mIncomingHardLinks.end(), [this](const std::weak_ptr& hardLink) { auto linkNode = hardLink.lock(); @@ -56,7 +60,7 @@ void Node::removeIncomingDynamicLinks() { auto linkTarget = linkNode->mWorkingNodeFlag.lock(); if (linkTarget && ownLink == linkTarget) { - continue; + // continue; } auto parentNode = linkNode->mParent.lock(); @@ -85,7 +89,7 @@ void Node::dump(std::stringstream& ss) { void Node::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents) { indent(ss, currentDepth, indents); ss << key; - // ss << " [file]"; + ss << " [h" << mIncomingHardLinks.size() << ": d" << mIncomingDynamicLinks.size() << "] "; ss << "\n"; } diff --git a/test/Tests.cpp b/test/Tests.cpp index ea64817..1fb62ee 100644 --- a/test/Tests.cpp +++ b/test/Tests.cpp @@ -1,37 +1,91 @@ -#include "Interpreter.hpp" -#include "UnitTest++/UnitTest++.h" +#include +#include "Tests.hpp" + +SUITE(FSE) { + TEST (Simple) { + Interpreter interpreter; + auto interp = [&](auto name) { + interpreter.interpret(name); + }; + + interp("md a"); + interp("md a/b"); + interp("mdl a a/b"); + interp("copy a C:/"); + interp("deltree a"); + interp("deltree a_copy"); + + interp("md a"); + interp("md a/b"); + interp("mhl a a/b"); + interp("copy a C:/"); + interp("deltree a"); + interp("deltree a_copy"); + interp("del a/b/a"); + interp("deltree a"); + + interp("md a"); + interp("md a/b"); + interp("mdl a a/b"); + interp("copy a C:/"); + interp("md c"); + interp("move a c"); + interp("move a_copy c"); + + interp("copy c C:/"); + + interp("deltree c"); + } + + TEST (CommandNoise) { + Interpreter interpreter; + + StringDistribution commands({ + { "md", 10 }, + { "mf", 10 }, + { "mhl", 10 }, + { "mdl", 10 }, + { "cd", 5 }, + { "rd", 1 }, + { "del", 10 }, + { "deltree", 10 }, + { "move", 20 }, + { "copy", 20 }, + }); + + StringDistribution names({ + { "C:", 1 }, + { "a", 1 }, + { "b", 1 }, + { "c", 1 }, + { "d", 1 }, + { "e", 1 }, + }); + + std::discrete_distribution pathLenDist({ 0.1, 0.2, 0.4, 0.5, 0.1 }); + + auto generatePath = [&](std::stringstream& ss) { + ss << " "; + auto len = pathLenDist(rng); + if (len) ss << names.get(); + for (auto i = 1; i < len; i++) { + ss <<"/" << names.get(); + } + }; + + runFor(interpreter, timeLimitSec, itemLimit, [&]() { + std::stringstream ss; + ss << commands.get(); + generatePath(ss); + if (pathLenDist(rng) % 2) generatePath(ss); + return ss.str(); + }); + + interpreter.mFileSystem.log(); + } +} int main() { - Interpreter interpreter; - interpreter.interpret("md a"); - interpreter.interpret("md a/b"); - interpreter.interpret("mdl a a/b"); - interpreter.interpret("copy a C:/"); - interpreter.interpret("deltree a"); - interpreter.interpret("deltree a_copy"); - - - interpreter.interpret("md a"); - interpreter.interpret("md a/b"); - interpreter.interpret("mhl a a/b"); - interpreter.interpret("copy a C:/"); - interpreter.interpret("deltree a"); - interpreter.interpret("deltree a_copy"); - interpreter.interpret("del a/b/a"); - interpreter.interpret("deltree a"); - - interpreter.interpret("md a"); - interpreter.interpret("md a/b"); - interpreter.interpret("mdl a a/b"); - interpreter.interpret("copy a C:/"); - interpreter.interpret("md c"); - interpreter.interpret("move a c"); - interpreter.interpret("move a_copy c"); - - interpreter.interpret("copy c C:/"); - - interpreter.interpret("deltree c"); - return UnitTest::RunAllTests(); } \ No newline at end of file diff --git a/test/Tests.hpp b/test/Tests.hpp new file mode 100644 index 0000000..b3176d3 --- /dev/null +++ b/test/Tests.hpp @@ -0,0 +1,47 @@ + +#pragma once + +#include "Interpreter.hpp" +#include "UnitTest++/UnitTest++.h" + +#include +#include + +const ui64 timeLimitSec = 5; +const ui64 itemLimit = 1e6; + +std::random_device rd; +std::mt19937 rng; + +class StringDistribution { +private: + std::vector commandList; + std::vector probabilities; + std::discrete_distribution distribution; + +public: + explicit StringDistribution(const std::map& commands) { + for (const auto& command : commands) { + commandList.push_back(command.first); + probabilities.push_back(command.second); + } + double sum = std::accumulate(probabilities.begin(), probabilities.end(), 0.0); + std::for_each(probabilities.begin(), probabilities.end(), [sum](double& element) { element /= sum; }); + distribution = std::discrete_distribution(probabilities.begin(), probabilities.end()); + } + + std::string get() { + return commandList[distribution(rng)]; + } +}; + +template +void runFor(Interpreter& interpreter, ui64 time, ui64 size, tDriver driver) { + auto startTime = std::chrono::steady_clock::now(); + while (true) { + auto currentTime = std::chrono::steady_clock::now(); + double elapsedSeconds = std::chrono::duration(currentTime - startTime).count(); + if (elapsedSeconds >= (double) time || interpreter.mFileSystem.size() > size) break; + interpreter.interpret(driver()); + } +} \ No newline at end of file