Преглед на файлове

US38.2: Extended status (CLI)

Serdyukov, Denys преди 4 години
родител
ревизия
31892d52a9
променени са 8 файла, в които са добавени 319 реда и са изтрити 4 реда
  1. 2 2
      Client-Server Protocol.md
  2. 1 0
      cli/include/batchioman.h
  3. 4 1
      cli/include/cmdman.h
  4. 1 0
      cli/include/userioman.h
  5. 64 0
      cli/src/batchioman.cpp
  6. 27 0
      cli/src/cmdman.cpp
  7. 62 0
      cli/src/userioman.cpp
  8. 158 1
      cli/test/cmdman_test.cpp

+ 2 - 2
Client-Server Protocol.md

@@ -381,12 +381,12 @@ Server:
     "transfersclientserver": {
         "upload": bool,
         "file": string,
-        "progress": float
+        "progress": int
     }[],
     "transfersserverserver": {
         "type": string,
         "file": string,
-        "progress": float,
+        "progress": int,
         "speed": float,
         "method": string
     }[]

+ 1 - 0
cli/include/batchioman.h

@@ -56,6 +56,7 @@ private:
 	std::string printConnect(Json::Value root);
 	std::string printHelp(Json::Value root);
 	std::string printStatus(Json::Value root);
+	std::string printExtendedstatus(Json::Value root);
 	std::string printDisconnect(Json::Value root);
 	std::string printPut(Json::Value root);
 	std::string printGet(Json::Value root);

+ 4 - 1
cli/include/cmdman.h

@@ -103,8 +103,10 @@ private:
 	/* execute command descriptions and methods go here */
 	const string descHelp = "print available commands";
 	CmdRet cmdHelp(vector<string> args);
-	const string descStatus = "request status from server";
+	const string descStatus = "request basic status information from server";
 	CmdRet cmdStatus(vector<string> args);
+	const string descExtendedstatus = "request detailed status information from server with running transfers";
+	CmdRet cmdExtendedstatus(vector<string> args);
 	const string descDisconnect = "disconnect from server";
 	CmdRet cmdDisconnect(vector<string> args);
 	const string descPut = "upload file to server and add to queue";
@@ -154,6 +156,7 @@ private:
 	 */
 	/* handle commands go here */
 	CmdRet handleStatus(Json::Value);
+	CmdRet handleExtendedstatus(Json::Value);
 	CmdRet handleClose(Json::Value);
 	CmdRet handlePut(Json::Value);
 	CmdRet handleGet(Json::Value);

+ 1 - 0
cli/include/userioman.h

@@ -39,6 +39,7 @@ private:
 	void printConnect(Json::Value root);
 	void printHelp(Json::Value root);
 	void printStatus(Json::Value root);
+	void printExtendedstatus(Json::Value root);
 	void printDisconnect(Json::Value root);
 	void printPut(Json::Value root);
 	void printGet(Json::Value root);

+ 64 - 0
cli/src/batchioman.cpp

@@ -15,6 +15,7 @@ BatchIoMan::BatchIoMan(bool usessl, bool beverbose, std::string batchpath) : IoM
 	printmap["connect"] = &BatchIoMan::printConnect;
 	printmap["help"] = &BatchIoMan::printHelp;
 	printmap["status"] = &BatchIoMan::printStatus;
+	printmap["extendedstatus"] = &BatchIoMan::printExtendedstatus;
 	printmap["disconnect"] = &BatchIoMan::printDisconnect;
 	printmap["put"] = &BatchIoMan::printPut;
 	printmap["get"] = &BatchIoMan::printGet;
@@ -311,6 +312,69 @@ std::string BatchIoMan::printHelp(Json::Value root) {
 
 std::string BatchIoMan::printStatus(Json::Value root) { return std::string("Server reports status: ") + root["response"].asString(); }
 
+std::string BatchIoMan::printExtendedstatus(Json::Value root) {
+	if (!root["accept"].asBool()) {
+		return "Listing transfers failed. Server reports: " + root["error"].asString();
+	} else {
+		std::string retval = "";
+
+		if (!root["transfersclientserver"].empty()) {
+			retval += "\nTransfers between clients and server:\n";
+			/*
+			 * EXAMPLE:
+			 * type      progress  file
+			 * download       99%  foobar.txt
+			 * upload          1%  baz.html
+			 */
+			retval += "type      progress  file\n";
+			for (Json::Value val : root["transfersclientserver"]) {
+				std::string type = val["upload"].asBool() ? "upload" : "download";
+				std::string progress = std::to_string(val["progress"].asInt());
+				std::string file = val["file"].asString();
+
+				char output[21];
+				std::snprintf(output, 21, "%-8s  %7.7s%%  ", type.c_str(), progress.c_str());
+
+				retval += output + file + "\n";
+			}
+		}
+		if (!root["transfersserverserver"].empty()) {
+			retval += "\nTransfers between different servers:\n";
+
+			/*
+			 * EXAMPLE:
+			 * type      progress  method               bytes/sec  file
+			 * download       99%  method0000001          9000.01  foobar.txt
+			 * queued          1%  urgent field             42.00  baz.html
+			 *
+			 * Too long method strings get truncated, unexpectedly high speeds are shown (with less pretty format), e.g.:
+			 *
+			 * download       95%  too long stri  340282346638528859811704183484516925440.00  filename.zip
+			 */
+			retval += "type      progress  method               bytes/sec  file\n";
+			for (Json::Value val : root["transfersclientserver"]) {
+				std::string type = val["type"].asString();
+				std::string progress = std::to_string(val["progress"].asInt());
+				std::string method = val["method"].asString();
+				float speed = val["speed"].asFloat();
+				std::string file = val["file"].asString();
+
+				// size of 80 is just enough for maximum possible float value to fit as string
+				char output[80];
+				std::snprintf(output, 80, "%-8s  %7.7s%%  %-13.13s  %15.2f  ", type.c_str(), progress.c_str(), method.c_str(), speed);
+
+				std::cout << output << file << std::endl;
+
+				retval += output + file + "\n";
+			}
+		}
+		if (root["transfersclientserver"].empty() && root["transfersserverserver"].empty()) {
+			retval += "No transfers running.";
+		}
+		return retval;
+	}
+}
+
 std::string BatchIoMan::printDisconnect(Json::Value root) {
 	if (!root["accept"].asBool()) {
 		return "Disconnect failed.";

+ 27 - 0
cli/src/cmdman.cpp

@@ -19,6 +19,7 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
 	/* initialize execute command map */
 	execmap["help"] = &CmdMan::cmdHelp;
 	execmap["status"] = &CmdMan::cmdStatus;
+	execmap["extendedstatus"] = &CmdMan::cmdExtendedstatus;
 	execmap["disconnect"] = &CmdMan::cmdDisconnect;
 	execmap["put"] = &CmdMan::cmdPut;
 	execmap["get"] = &CmdMan::cmdGet;
@@ -43,6 +44,7 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
 	/* initialize description map */
 	helpmap["help"] = descHelp;
 	helpmap["status"] = descStatus;
+	helpmap["extendedstatus"] = descExtendedstatus;
 	helpmap["disconnect"] = descDisconnect;
 	helpmap["put"] = descPut;
 	helpmap["get"] = descGet;
@@ -62,6 +64,7 @@ CmdMan::CmdMan(FileMan &fm, void (*dpf)(string)) : fileman(fm) {
 
 	/* initialize handle command map */
 	handlemap["status"] = &CmdMan::handleStatus;
+	handlemap["extendedstatus"] = &CmdMan::handleExtendedstatus;
 	handlemap["close"] = &CmdMan::handleClose;
 	handlemap["put"] = &CmdMan::handlePut;
 	handlemap["get"] = &CmdMan::handleGet;
@@ -112,6 +115,17 @@ CmdMan::CmdRet CmdMan::cmdStatus(vector<string> args) {
 	return retval;
 }
 
+CmdMan::CmdRet CmdMan::cmdExtendedstatus(vector<string> args) {
+	CmdRet retval;
+	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
+	Json::Value root;
+	root["command"] = "extendedstatus";
+	retval.type = send;
+	retval.msg = root;
+
+	return retval;
+}
+
 CmdMan::CmdRet CmdMan::cmdDisconnect(vector<string> args) {
 	CmdRet retval;
 	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
@@ -654,6 +668,19 @@ CmdMan::CmdRet CmdMan::handleStatus(Json::Value root) {
 	return retval;
 }
 
+CmdMan::CmdRet CmdMan::handleExtendedstatus(Json::Value root) {
+	CmdRet retval;
+	DEBUGPRINT(string(__PRETTY_FUNCTION__) + " begin");
+
+	if (!root["accept"].asBool()) {
+		retval.type = error;
+	} else {
+		retval.type = print;
+	}
+	retval.msg = root;
+	return retval;
+}
+
 CmdMan::CmdRet CmdMan::handleClose(Json::Value root) {
 	CmdRet retval;
 	Json::Value output;

+ 62 - 0
cli/src/userioman.cpp

@@ -15,6 +15,7 @@ UserIoMan::UserIoMan(bool usessl, bool beverbose) : IoMan(usessl) {
 	printmap["error"] = &UserIoMan::printError;
 	printmap["connect"] = &UserIoMan::printConnect;
 	printmap["help"] = &UserIoMan::printHelp;
+	printmap["extendedstatus"] = &UserIoMan::printExtendedstatus;
 	printmap["status"] = &UserIoMan::printStatus;
 	printmap["disconnect"] = &UserIoMan::printDisconnect;
 	printmap["put"] = &UserIoMan::printPut;
@@ -109,6 +110,67 @@ void UserIoMan::printHelp(Json::Value root) {
 
 void UserIoMan::printStatus(Json::Value root) { std::cout << "Server reports status: " << root["response"].asString() << std::endl; }
 
+void UserIoMan::printExtendedstatus(Json::Value root) {
+
+	if (!root["accept"].asBool()) {
+		std::cout << "Listing transfers failed. Server reports: " << root["error"].asString() << std::endl;
+	} else {
+		if (!root["transfersclientserver"].empty()) {
+			std::cout << std::endl << "Transfers between clients and server:" << std::endl;
+			/*
+			 * EXAMPLE:
+			 * type      progress  file
+			 * download       99%  foobar.txt
+			 * upload          1%  baz.html
+			 */
+			std::cout << "type      progress  file" << std::endl;
+			for (Json::Value val : root["transfersclientserver"]) {
+				std::string type = val["upload"].asBool() ? "upload" : "download";
+				std::string progress = std::to_string(val["progress"].asInt());
+				std::string file = val["file"].asString();
+
+				char output[21];
+				std::snprintf(output, 21, "%-8s  %7.7s%%  ", type.c_str(), progress.c_str());
+
+				std::cout << output << file << std::endl;
+			}
+		}
+		if (!root["transfersserverserver"].empty()) {
+			std::cout << std::endl << "Transfers between different servers:" << std::endl;
+
+			/*
+			 * EXAMPLE:
+			 * type      progress  method               bytes/sec  file
+			 * download       99%  method0000001          9000.01  foobar.txt
+			 * queued          1%  urgent field             42.00  baz.html
+			 *
+			 * Too long method strings get truncated, unexpectedly high speeds are shown (with less pretty format), e.g.:
+			 *
+			 * download       95%  too long stri  340282346638528859811704183484516925440.00  filename.zip
+			 */
+			std::cout << "type      progress  method               bytes/sec  file" << std::endl;
+			for (Json::Value val : root["transfersserverserver"]) {
+				std::string type = val["type"].asString();
+				std::string progress = std::to_string(val["progress"].asInt());
+				std::string method = val["method"].asString();
+				float speed = val["speed"].asFloat();
+				std::string file = val["file"].asString();
+
+				// size of 80 is just enough for maximum possible float value to fit as string
+				char output[80];
+				std::snprintf(output, 80, "%-8s  %7.7s%%  %-13.13s  %15.2f  ", type.c_str(), progress.c_str(), method.c_str(), speed);
+
+				std::cout << output << file << std::endl;
+			}
+		}
+		if (root["transfersclientserver"].empty() && root["transfersserverserver"].empty()) {
+			std::cout << "No transfers running." << std::endl;
+		} else {
+			std::cout << std::endl;
+		}
+	}
+}
+
 void UserIoMan::printDisconnect(Json::Value root) {
 	if (!root["accept"].asBool()) {
 		std::cout << "Disconnect failed." << std::endl;

+ 158 - 1
cli/test/cmdman_test.cpp

@@ -2188,6 +2188,162 @@ TEST(testStatus, Test) {
 	EXPECT_TRUE(cm.isLoggedIn());
 }
 
+/* =====================================
+ * test for extendedstatus
+ */
+
+TEST(testExtendedstatus, Negative) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare cmd/args/root
+	cmd = "extendedstatus";
+	args = {};
+	root["command"] = "extendedstatus";
+	root["accept"] = false;
+	root["error"] = "some foobar went wrong";
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "extendedstatus");
+	EXPECT_EQ(retvalHdl.type, CmdMan::error);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "extendedstatus");
+	EXPECT_FALSE(retvalHdl.msg["accept"].asBool());
+	EXPECT_THAT(retvalHdl.msg["error"].asString(), testing::HasSubstr("some foobar went wrong"));
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testExtendedstatus, Positive) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root, transfersclientserver, tcs1, tcs2, transfersserverserver, tss1, tss2, tss3;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare cmd/args/root
+	cmd = "extendedstatus";
+	args = {};
+	root["command"] = "extendedstatus";
+	root["accept"] = true;
+
+	tcs1["upload"] = true;
+	tcs1["file"] = "green.txt";
+	tcs1["progress"] = 99;
+	transfersclientserver.append(tcs1);
+	tcs2["upload"] = false;
+	tcs2["file"] = "red.txt";
+	tcs2["progress"] = 2;
+	transfersclientserver.append(tcs2);
+	root["transfersclientserver"] = transfersclientserver;
+
+	tss1["type"] = "upload";
+	tss1["file"] = "cyan.txt";
+	tss1["progress"] = 100;
+	tss1["method"] = "urg field";
+	tss1["speed"] = 42.0f;
+	transfersserverserver.append(tss1);
+	tss2["type"] = "download";
+	tss2["file"] = "magenta.txt";
+	tss2["progress"] = 42;
+	tss2["method"] = "fancy thing";
+	tss2["speed"] = 0.1f;
+	transfersserverserver.append(tss2);
+	tss3["type"] = "queued";
+	tss3["file"] = "yellow.txt";
+	tss3["progress"] = 42;
+	tss3["method"] = "cool timing stuff";
+	tss3["speed"] = 9000.001f;
+	transfersserverserver.append(tss3);
+	root["transfersserverserver"] = transfersserverserver;
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "extendedstatus");
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "extendedstatus");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+
+	EXPECT_TRUE(retvalHdl.msg["transfersclientserver"][0]["upload"].asBool());
+	EXPECT_EQ(retvalHdl.msg["transfersclientserver"][0]["file"].asString(), "green.txt");
+	EXPECT_EQ(retvalHdl.msg["transfersclientserver"][0]["progress"].asInt(), 99);
+	EXPECT_FALSE(retvalHdl.msg["transfersclientserver"][1]["upload"].asBool());
+	EXPECT_EQ(retvalHdl.msg["transfersclientserver"][1]["file"].asString(), "red.txt");
+	EXPECT_EQ(retvalHdl.msg["transfersclientserver"][1]["progress"].asInt(), 2);
+
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][0]["type"].asString(), "upload");
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][0]["file"].asString(), "cyan.txt");
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][0]["progress"].asInt(), 100);
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][0]["method"].asString(), "urg field");
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][0]["speed"].asFloat(), 42.0f);
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][1]["type"].asString(), "download");
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][1]["file"].asString(), "magenta.txt");
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][1]["progress"].asInt(), 42);
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][1]["method"].asString(), "fancy thing");
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][1]["speed"].asFloat(), 0.1f);
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][2]["type"].asString(), "queued");
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][2]["file"].asString(), "yellow.txt");
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][2]["progress"].asInt(), 42);
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][2]["method"].asString(), "cool timing stuff");
+	EXPECT_EQ(retvalHdl.msg["transfersserverserver"][2]["speed"].asFloat(), 9000.001f);
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
+TEST(testExtendedstatus, Positive_NoTransfersRunning) {
+	FileManMock fm;
+	CmdManForTest cm(fm, dummyDebugPrint);
+	cm.initLoggedIn();
+
+	std::string cmd;
+	std::vector<std::string> args;
+	Json::Value root, transfersclientserver, transfersserverserver;
+	CmdMan::CmdRet retvalCmd;
+	CmdMan::CmdRet retvalHdl;
+
+	// prepare cmd/args/root
+	cmd = "extendedstatus";
+	args = {};
+	root["command"] = "extendedstatus";
+	root["accept"] = true;
+	root["transfersclientserver"] = transfersclientserver;
+	root["transfersserverserver"] = transfersserverserver;
+
+	// stick into cmdman
+	retvalCmd = cm.execute(cmd, args);
+	retvalHdl = cm.handle(root);
+
+	// check things
+	EXPECT_EQ(retvalCmd.type, CmdMan::send);
+	EXPECT_EQ(retvalCmd.msg["command"].asString(), "extendedstatus");
+	EXPECT_EQ(retvalHdl.type, CmdMan::print);
+	EXPECT_EQ(retvalHdl.msg["command"].asString(), "extendedstatus");
+	EXPECT_TRUE(retvalHdl.msg["accept"].asBool());
+	EXPECT_TRUE(retvalHdl.msg["transfersclientserver"].empty());
+	EXPECT_TRUE(retvalHdl.msg["transfersserverserver"].empty());
+
+	EXPECT_TRUE(cm.isLoggedIn());
+}
+
 /* =====================================
  * tests for help
  */
@@ -2656,7 +2812,8 @@ TEST(testCommandsWithRequiredLogin, NotLoggedInOrNotConnected) {
 	CmdMan::CmdRet retval1, retval2, retval3;
 
 	// prepare cmd/args
-	std::vector<std::string> cmds = {"put", "get", "list", "putdata", "getdata", "listdata", "head", "deletefile", "deleteme", "keyfile", "closekey"};
+	std::vector<std::string> cmds = {"put",  "get",        "list",     "putdata", "getdata",  "listdata",
+	                                 "head", "deletefile", "deleteme", "keyfile", "closekey", "extendedstatus"};
 	// as every command works fine with too many args, we will simply pass three to all of them
 	std::vector<std::string> args = {"arg1", "arg2", "arg3"};