浏览代码

Merge branch 'us15' into develop

US15.1: List command on server side

See merge request tobias.wach/ccats!21

Closes #20
Jonas Pflanzer 5 年之前
父节点
当前提交
3bb488c750

+ 3 - 1
cli/src/cmdman.cpp

@@ -31,6 +31,7 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
   execmap["signup"] = &CmdMan::cmdSignup;
   execmap["putdata"] = &CmdMan::cmdPutdata;
   execmap["getdata"] = &CmdMan::cmdGetdata;
+  execmap["listdata"] = &CmdMan::cmdListdata;
 
   /* initialize description map */
   helpmap["help"] = descHelp;
@@ -51,6 +52,7 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
   handlemap["version"] = &CmdMan::handleVersion;
   handlemap["login"] = &CmdMan::handleLogin;
   handlemap["signup"] = &CmdMan::handleSignup;
+  handlemap["listdata"] = &CmdMan::handleListdata;
 
   debugprintfunc = dpf;
 }
@@ -561,7 +563,7 @@ CmdMan::CmdRet CmdMan::handleListdata(Json::Value root) {
       toadd.push_back(i.asString());
     fileman.putListData(toadd);
     // loaded successfully
-    if (!fileman.getListRemainingChunks()) {
+    if (fileman.getListRemainingChunks() < 0) {
       // everything sent
       retval.type = print;
       for (string s : fileman.getListData())

+ 1 - 1
cli/src/fileman.cpp

@@ -104,7 +104,7 @@ void FileMan::setGetChunks(int chunks) {
 
 void FileMan::setListChunks(int chunks) {
   listchunks = chunks;
-  getchunksRemaining = chunks - 1;
+  listchunksRemaining = chunks - 1;
 }
 
 std::vector<char> FileMan::readPut() {

+ 45 - 0
daemon/include/FileManager.h

@@ -46,6 +46,19 @@ private:
    */
   std::streamoff getFileSize;
 
+  /**
+   * list vector for list command
+   */
+  std::vector<std::vector<std::string>> list;
+
+  /**
+   * Get vector of all files in current directory.
+   * Ignores directories
+   *
+   * @return vector of all files in the current directory
+   */
+  std::vector<std::string> listFiles();
+
 public:
   enum { max_data_length = 512 };
 
@@ -125,6 +138,38 @@ public:
    * Reads data from get file
    */
   virtual std::vector<char> readGet();
+
+  /**
+   * Open list command. Set list vector and claculate chunks
+   *
+   * @return chunks of the resulting list | if a filename is too long it returns
+   * -1
+   */
+  virtual int openList();
+
+  /**
+   * @return remaining chunks to be send with listdata command
+   */
+  virtual int getRemainingListChunks();
+
+  /**
+   * @return size of the list vector
+   */
+  virtual int getListSize();
+
+  /**
+   * Return next chunk for listdata command.
+   * Increases remainingListChunks. You need to run openList before.
+   *
+   * @return next chnuk vector
+   */
+  virtual std::vector<std::string> getNextChunkFromList();
+
+  /**
+   * Cancel current list command.
+   * Clear list vector and set remainingListChunks zero.
+   */
+  void cancelList();
 };
 
 #endif

+ 5 - 0
daemon/include/JsonCommander.h

@@ -101,6 +101,11 @@ private:
    */
   Response executeList(const Json::Value &message);
 
+  /**
+   * Executes the listdata command
+   */
+  Response executeListData(const Json::Value &message);
+
   /**
    * Executes the put command
    */

+ 1 - 1
daemon/src/CMakeLists.txt

@@ -11,7 +11,7 @@ pkg_check_modules(JSONCPP REQUIRED jsoncpp)
 
 
 find_package(Threads REQUIRED)
-find_package(Boost 1.67 REQUIRED COMPONENTS system)
+find_package(Boost 1.67 REQUIRED COMPONENTS system filesystem)
 # find_package(libtins 4.2 REQUIRED)
 
 include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDEDIR})

+ 64 - 0
daemon/src/FileManager.cpp

@@ -1,5 +1,8 @@
 #include "../include/FileManager.h"
 
+#include <boost/filesystem.hpp>
+#include <boost/range/iterator_range.hpp>
+
 #include "../include/Config.h"
 
 FileManager::FileManager() : fileDirectory(Config::getValue("filedirectory")) {}
@@ -90,3 +93,64 @@ std::vector<char> FileManager::readGet() {
 
   return data;
 }
+
+std::vector<std::string> FileManager::listFiles() {
+  std::vector<std::string> res;
+  for (const auto &entry :
+       boost::filesystem::directory_iterator(this->fileDirectory)) {
+    if (boost::filesystem::is_directory(entry.path()))
+      continue;
+    res.push_back(entry.path().filename().string());
+  }
+  return res;
+}
+
+int FileManager::openList() {
+  std::vector<std::string> fullList = listFiles();
+
+  if (fullList.size() == 0) {
+    return 0;
+  }
+
+  // add empty chunk vector
+  this->list.push_back(std::vector<std::string>());
+
+  int cursize = 0;
+  for (const std::string &s : fullList) {
+    if (s.length() > max_data_length)
+      return -1;
+    cursize += s.length() + 3;
+    if (cursize > max_data_length) {
+      this->list.push_back(std::vector<std::string>());
+      cursize = 0;
+    }
+
+    // add string to its chunk
+    this->list.back().push_back(s);
+  }
+
+  return this->list.size();
+}
+
+int FileManager::getRemainingListChunks() { return this->list.size(); }
+
+int FileManager::getListSize() {
+  int size = 0;
+  for (const std::vector<std::string> &l : this->list) {
+    size += l.size();
+  }
+  return size;
+}
+
+std::vector<std::string> FileManager::getNextChunkFromList() {
+  if (getRemainingListChunks() == 0) { // This should never happen!!!
+    return std::vector<std::string>();
+  }
+
+  std::vector<std::string> ret = this->list.back();
+  this->list.pop_back();
+
+  return ret;
+}
+
+void FileManager::cancelList() { this->list.clear(); }

+ 62 - 6
daemon/src/JsonCommander.cpp

@@ -7,6 +7,7 @@ JsonCommander::JsonCommander(FileManager &fileManager)
   commandsMap["status"] = &JsonCommander::executeStatus;
   commandsMap["close"] = &JsonCommander::executeClose;
   commandsMap["list"] = &JsonCommander::executeList;
+  commandsMap["listdata"] = &JsonCommander::executeListData;
   commandsMap["put"] = &JsonCommander::executePut;
   commandsMap["putdata"] = &JsonCommander::executePutdata;
   commandsMap["get"] = &JsonCommander::executeGet;
@@ -69,14 +70,69 @@ JsonCommander::Response JsonCommander::executeList(const Json::Value &message) {
   response.action = send;
   response.json["command"] = "list";
 
-  // TODO return real file list
+  int chunks;
+  if (fileManager.getRemainingListChunks() > 0) {
+    response.json["accept"] = false;
+    response.json["chunks"] = -1;
+    response.json["items"] = -1;
+    response.json["error"] = "there is already an open list command";
+  } else if ((chunks = fileManager.openList()) ==
+             -1) { // TODO do we need to check? maybe. Think about it
+    response.json["accept"] = false;
+    response.json["chunks"] = -1;
+    response.json["items"] = -1;
+    response.json["error"] = "there is a filename which is too long";
+  } else {
+    response.json["accept"] = true;
+    response.json["chunks"] = chunks;
+    response.json["items"] = fileManager.getListSize();
+    response.json["error"] = "";
+  }
+
+  return response;
+}
 
+JsonCommander::Response
+JsonCommander::executeListData(const Json::Value &message) {
+  JsonCommander::Response response;
+  response.action = send;
+  response.json["command"] = "listdata";
   Json::Value array;
-  array.append("some");
-  array.append("important");
-  array.append("data");
-  response.json["names"] = array;
-  response.json["remaining"] = 0;
+
+  const int remainingListchunks = fileManager.getRemainingListChunks();
+  if (!message["chunk"].isInt() || !message["cancel"].isBool()) {
+    response.action = closeAndSend;
+    response.json["cancel"] = true;
+    response.json["remaining"] = -1;
+    response.json["names"] = Json::arrayValue;
+    response.json["error"] = "incorrect listdata command request";
+  } else if (remainingListchunks == 0) {
+    response.json["cancel"] = true;
+    response.json["remaining"] = -1;
+    response.json["names"] = Json::arrayValue;
+    response.json["error"] = "there are no chunks to send";
+  } else if (message["cancel"].asBool()) {
+    response.json["cancel"] = true;
+    response.json["remaining"] =
+        message["chunk"].asInt(); // so it can be associated to the request
+    response.json["names"] = Json::arrayValue;
+    response.json["error"] = "";
+    fileManager.cancelList();
+  } else if (remainingListchunks - 1 != message["chunk"].asInt()) {
+    response.action = closeAndSend;
+    response.json["cancel"] = true;
+    response.json["remaining"] = -1;
+    response.json["names"] = Json::arrayValue;
+    response.json["error"] = "wrong chunk number";
+  } else {
+    std::vector<std::string> v = fileManager.getNextChunkFromList();
+    for (int i = 0; i < v.size(); i++)
+      array.append(v.at(i));
+    response.json["remaining"] = message["chunk"].asInt();
+    response.json["cancel"] = false;
+    response.json["names"] = array;
+    response.json["error"] = "";
+  }
 
   return response;
 }

+ 2 - 2
daemon/test/CMakeLists.txt

@@ -8,7 +8,7 @@ pkg_check_modules(JSONCPP REQUIRED jsoncpp)
 
 
 find_package(Threads REQUIRED)
-find_package(Boost 1.67 REQUIRED COMPONENTS system)
+find_package(Boost 1.67 REQUIRED COMPONENTS system filesystem)
 
 # Setup testing
 enable_testing()
@@ -20,7 +20,7 @@ find_package(GTest REQUIRED)
 include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDEDIR} ${GMOCK_INCLUDE_DIR} ${GTEST_INCLUDE_DIR})
 
 # Add test cpp file
-add_executable(jsonCommanderTest test/JsonCommanderTest.cpp src/JsonCommander.cpp src/FileManager.cpp src/base64.cpp src/UserManager.cpp src/Config.cpp)
+add_executable(jsonCommanderTest test/JsonCommanderTest.cpp src/JsonCommander.cpp src/FileManager.cpp src/base64.cpp src/UserManager.cpp test/ConfigMock.cpp)
 target_link_libraries(jsonCommanderTest ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${JSONCPP_LIBRARIES} ${GMOCK_LIBRARIES} ${GTEST_LIBRARY} ${GTEST_MAIN_LIBRARY})
 
 add_test(

+ 16 - 0
daemon/test/ConfigMock.cpp

@@ -0,0 +1,16 @@
+#include "../include/Config.h"
+
+namespace Config {
+std::map<std::string, std::string> storage;
+};
+
+bool Config::init(const std::string &filename) { return true; }
+
+std::string Config::getValue(const std::string &key) {
+  auto it = storage.find(key);
+  if (it != storage.end()) {
+    return it->second;
+  }
+
+  return "";
+}

+ 5 - 0
daemon/test/FileManagerMock.h

@@ -24,6 +24,11 @@ public:
 
   MOCK_METHOD(void, writePut, (const std::vector<char> &data), (override));
   MOCK_METHOD(std::vector<char>, readGet, (), (override));
+
+  MOCK_METHOD(int, openList,(), (override));
+  MOCK_METHOD(int, getRemainingListChunks, (), (override));
+  MOCK_METHOD(int, getListSize, (), (override));
+  MOCK_METHOD(std::vector<std::string>, getNextChunkFromList, (), (override));
 };
 
 #endif

+ 272 - 0
daemon/test/JsonCommanderTest.cpp

@@ -628,4 +628,276 @@ TEST(Getdata, WrongChunk) {
   EXPECT_EQ(response.json["data"].asString(), "");
   EXPECT_TRUE(response.json["error"].asString().length() > 0);
 }
+
+/* List tests */
+TEST(List, Positive) {
+  FileManagerMock fileManager;
+
+  JsonCommander jsonCommander(fileManager);
+
+  const std::string command = "list";
+  Json::Value message;
+  message["command"] = command;
+
+  EXPECT_CALL(fileManager, openList()).WillOnce(testing::Return(1));
+  EXPECT_CALL(fileManager, getListSize()).WillOnce(testing::Return(5));
+
+  JsonCommander::Response response = jsonCommander.execute(message);
+
+  EXPECT_TRUE(response.action == JsonCommander::Action::send);
+  EXPECT_EQ(response.json["command"].asString(), command);
+  EXPECT_TRUE(response.json["accept"].asBool());
+  EXPECT_EQ(response.json["chunks"].asInt(), 1);
+  EXPECT_EQ(response.json["items"].asInt(), 5);
+  EXPECT_EQ(response.json["error"].asString(), "");
+}
+
+TEST(List, Negative) {
+  FileManagerMock fileManager;
+
+  JsonCommander jsonCommander(fileManager);
+
+  const std::string command = "list";
+  Json::Value message;
+  message["command"] = command;
+
+  EXPECT_CALL(fileManager, openList()).WillOnce(testing::Return(-1));
+
+  JsonCommander::Response response = jsonCommander.execute(message);
+
+  EXPECT_TRUE(response.action == JsonCommander::Action::send);
+  EXPECT_EQ(response.json["command"].asString(), command);
+  EXPECT_FALSE(response.json["accept"].asBool());
+  EXPECT_EQ(response.json["chunks"].asInt(), -1);
+  EXPECT_EQ(response.json["items"].asInt(), -1);
+  EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
+TEST(List, EmptyList) {
+  FileManagerMock fileManager;
+
+  JsonCommander jsonCommander(fileManager);
+
+  const std::string command = "list";
+  Json::Value message;
+  message["command"] = command;
+
+  EXPECT_CALL(fileManager, openList()).WillOnce(testing::Return(0));
+  EXPECT_CALL(fileManager, getListSize()).WillOnce(testing::Return(0));
+
+  JsonCommander::Response response = jsonCommander.execute(message);
+
+  EXPECT_TRUE(response.action == JsonCommander::Action::send);
+  EXPECT_EQ(response.json["command"].asString(), command);
+  EXPECT_TRUE(response.json["accept"].asBool());
+  EXPECT_EQ(response.json["chunks"].asInt(), 0);
+  EXPECT_EQ(response.json["items"].asInt(), 0);
+  EXPECT_EQ(response.json["error"].asString(), "");
+}
+
+/* Listdata tests */
+
+void fillExampleFileList(std::vector<std::string> (&chunk)[3]) {
+  chunk[0].push_back("file01.txt");
+  chunk[0].push_back("bumdibumps");
+  chunk[0].push_back("1");
+  chunk[0].push_back("Ich habe Hunger.txt");
+  chunk[0].push_back("answerIs42");
+  chunk[0].push_back("123456789456115811");
+  chunk[0].push_back("kek");
+  chunk[1].push_back("1337");
+  chunk[1].push_back("cats.png");
+  chunk[1].push_back("more_cats.png");
+  chunk[1].push_back("ugly dog.tiff");
+  chunk[1].push_back("hello.txt");
+  chunk[1].push_back("bye.exe");
+  chunk[1].push_back("poster.pdf");
+  chunk[2].push_back("headbang.gif");
+  chunk[2].push_back("feelsbad.jpg");
+  chunk[2].push_back("hack.s");
+  chunk[2].push_back("SodiumChloride");
+  chunk[2].push_back(
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst"
+      "uvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN"
+      "OPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
+}
+
+TEST(Listdata, Positive) {
+  FileManagerMock fileManager;
+
+  JsonCommander jsonCommander(fileManager);
+
+  const std::string command = "listdata";
+
+  const int chunks = 3;
+  std::vector<std::string> chunk[chunks];
+  fillExampleFileList(chunk);
+
+  int remaining = chunks - 1;
+
+  for (int k = 0; k < chunks; k++) {
+    Json::Value message;
+    message["command"] = command;
+    message["chunk"] = remaining;
+    message["cancel"] = false;
+
+    EXPECT_CALL(fileManager, getRemainingListChunks())
+        .WillOnce(testing::Return(remaining + 1));
+    EXPECT_CALL(fileManager, getNextChunkFromList())
+        .WillOnce(testing::Return(chunk[k]));
+
+    JsonCommander::Response response = jsonCommander.execute(message);
+
+    EXPECT_TRUE(response.action == JsonCommander::Action::send);
+    EXPECT_EQ(response.json["command"].asString(), command);
+    EXPECT_FALSE(response.json["cancel"].asBool());
+    EXPECT_EQ(response.json["remaining"].asInt(), remaining--);
+    EXPECT_TRUE(response.json["names"].isArray());
+    Json::Value array = response.json["names"];
+    EXPECT_EQ(array.size(), chunk[k].size());
+    for (int i = 0; i < 3; i++) {
+      EXPECT_EQ(array[i].asString(), chunk[k][i]);
+    }
+    EXPECT_EQ(response.json["error"].asString(), "");
+  }
+}
+
+TEST(Listdata, Cancel) {
+  FileManagerMock fileManager;
+
+  JsonCommander jsonCommander(fileManager);
+
+  const std::string command = "listdata";
+
+  const int chunks = 3;
+  std::vector<std::string> chunk[chunks];
+  fillExampleFileList(chunk);
+
+  int remaining = chunks - 1;
+
+  Json::Value message;
+  message["command"] = command;
+  message["chunk"] = remaining;
+  message["cancel"] = false;
+
+  EXPECT_CALL(fileManager, getRemainingListChunks())
+      .WillOnce(testing::Return(remaining + 1));
+  EXPECT_CALL(fileManager, getNextChunkFromList())
+      .WillOnce(testing::Return(chunk[0]));
+
+  JsonCommander::Response response = jsonCommander.execute(message);
+
+  EXPECT_TRUE(response.action == JsonCommander::Action::send);
+  EXPECT_EQ(response.json["command"].asString(), command);
+  EXPECT_FALSE(response.json["cancel"].asBool());
+  EXPECT_EQ(response.json["remaining"].asInt(), remaining--);
+  EXPECT_TRUE(response.json["names"].isArray());
+  Json::Value array = response.json["names"];
+  EXPECT_EQ(array.size(), chunk[0].size());
+  for (int i = 0; i < 3; i++) {
+    EXPECT_EQ(array[i].asString(), chunk[0][i]);
+  }
+  EXPECT_EQ(response.json["error"].asString(), "");
+
+  message = Json::Value();
+  message["command"] = command;
+  message["chunk"] = remaining;
+  message["cancel"] = true;
+
+  EXPECT_CALL(fileManager, getRemainingListChunks())
+      .WillOnce(testing::Return(remaining + 1));
+
+  response = jsonCommander.execute(message);
+  EXPECT_TRUE(response.action == JsonCommander::Action::send);
+  EXPECT_EQ(response.json["command"].asString(), command);
+  EXPECT_TRUE(response.json["cancel"].asBool());
+  EXPECT_EQ(response.json["remaining"].asInt(), remaining--);
+  EXPECT_TRUE(response.json["names"].isArray());
+  EXPECT_EQ(response.json["error"].asString(), "");
+}
+
+TEST(Listdata, WrongChunkNumber) {
+  FileManagerMock fileManager;
+
+  JsonCommander jsonCommander(fileManager);
+
+  const std::string command = "listdata";
+
+  const int chunks = 3;
+  int remaining = chunks - 1;
+
+  Json::Value message;
+  message["command"] = command;
+  message["chunk"] = remaining;
+  message["cancel"] = false;
+
+  // return smaller remaining
+  EXPECT_CALL(fileManager, getRemainingListChunks())
+      .WillOnce(testing::Return(remaining));
+
+  JsonCommander::Response response = jsonCommander.execute(message);
+
+  EXPECT_TRUE(response.action == JsonCommander::Action::closeAndSend);
+  EXPECT_EQ(response.json["command"].asString(), command);
+  EXPECT_TRUE(response.json["cancel"].asBool());
+  EXPECT_EQ(response.json["remaining"].asInt(), -1);
+  EXPECT_TRUE(response.json["names"].isArray());
+  EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
+TEST(Listdata, NoChunksToBeSend) {
+  FileManagerMock fileManager;
+
+  JsonCommander jsonCommander(fileManager);
+
+  const std::string command = "listdata";
+
+  const int chunks = 0;
+
+  Json::Value message;
+  message["command"] = command;
+  message["chunk"] = 1;
+  message["cancel"] = false;
+
+  // return smaller remaining
+  EXPECT_CALL(fileManager, getRemainingListChunks())
+      .WillOnce(testing::Return(chunks));
+
+  JsonCommander::Response response = jsonCommander.execute(message);
+
+  EXPECT_TRUE(response.action == JsonCommander::Action::send);
+  EXPECT_EQ(response.json["command"].asString(), command);
+  EXPECT_TRUE(response.json["cancel"].asBool());
+  EXPECT_EQ(response.json["remaining"].asInt(), -1);
+  EXPECT_TRUE(response.json["names"].isArray());
+  EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
+TEST(Listdata, InvalidRequest) {
+  FileManagerMock fileManager;
+
+  JsonCommander jsonCommander(fileManager);
+
+  const std::string command = "listdata";
+
+  const int chunks = 3;
+
+  Json::Value message;
+  message["command"] = command;
+  message["chunk"] = 1;
+
+  // return smaller remaining
+  EXPECT_CALL(fileManager, getRemainingListChunks())
+      .WillOnce(testing::Return(chunks));
+
+  JsonCommander::Response response = jsonCommander.execute(message);
+
+  EXPECT_TRUE(response.action == JsonCommander::Action::closeAndSend);
+  EXPECT_EQ(response.json["command"].asString(), command);
+  EXPECT_TRUE(response.json["cancel"].asBool());
+  EXPECT_EQ(response.json["remaining"].asInt(), -1);
+  EXPECT_TRUE(response.json["names"].isArray());
+  EXPECT_TRUE(response.json["error"].asString().compare("") != 0);
+}
+
 } // namespace