From eaeaa6474adcad32744e11eee5d3687cc54e058f Mon Sep 17 00:00:00 2001
From: armatusmiles <akurotych@gmail.com>
Date: Fri, 8 Feb 2019 00:02:02 +0700
Subject: [PATCH] [+] Realization dap_http_user_agent with unit-tests

---
 http_server/http_client/dap_http_user_agent.c | 142 +++++++++++++++---
 http_server/http_client/dap_http_user_agent.h |  68 ++++++---
 test/http_server/CMakeLists.txt               |   2 +-
 test/http_server/dap_http_user_agent_test.c   | 133 ++++++++++++++++
 test/http_server/dap_http_user_agent_test.h   |   6 +
 test/http_server/main.c                       |   2 +
 6 files changed, 311 insertions(+), 42 deletions(-)
 create mode 100644 test/http_server/dap_http_user_agent_test.c
 create mode 100644 test/http_server/dap_http_user_agent_test.h

diff --git a/http_server/http_client/dap_http_user_agent.c b/http_server/http_client/dap_http_user_agent.c
index b7e2a6e..92b851a 100644
--- a/http_server/http_client/dap_http_user_agent.c
+++ b/http_server/http_client/dap_http_user_agent.c
@@ -1,41 +1,145 @@
 #include "dap_http_user_agent.h"
+#include "dap_common.h"
 #include <string.h>
+#include <stdio.h>
 
-dap_http_user_agent_t* dap_http_user_agent_new(const char* a_name,
-                                               const char* a_comment,
-                                               unsigned short a_major_version,
-                                               unsigned short a_minor_version)
+#define LOG_TAG "dap_http_user_agent"
+
+struct dap_http_user_agent {
+    char* name; // Ex: "DapVpnClient/2.2
+    char* comment; // text after name
+    unsigned int major_version;
+    unsigned int minor_version;
+    char* string_representation;
+};
+
+static char* _dap_http_user_agent_to_string(dap_http_user_agent_ptr_t a_agent)
 {
-    // TODO
-    return NULL;
+    char * result = calloc(1, sizeof(*a_agent));
+
+    if(a_agent->comment) {
+        sprintf(result, "%s/%d.%d %s", a_agent->name,
+                a_agent->major_version, a_agent->minor_version,
+                a_agent->comment);
+    } else {
+        sprintf(result, "%s/%d.%d", a_agent->name,
+                a_agent->major_version, a_agent->minor_version);
+    }
+
+    return result;
 }
 
-void dap_http_user_agent_delete(dap_http_user_agent_t* a_agent)
+
+dap_http_user_agent_ptr_t dap_http_user_agent_new(const char* a_name,
+                                                  unsigned short a_major_version,
+                                                  unsigned short a_minor_version,
+                                                  const char* a_comment)
 {
-    // TODO
+    if(a_name == NULL) {
+        log_it(L_ERROR, "Name is NULL");
+        return NULL;
+    }
+
+    dap_http_user_agent_ptr_t res = DAP_NEW_Z(struct dap_http_user_agent);
+    res->name = strdup(a_name);
+    res->comment = a_comment ? strdup(a_comment) : NULL;
+    res->major_version = a_major_version;
+    res->minor_version = a_minor_version;
+    res->string_representation = _dap_http_user_agent_to_string(res);
+    return res;
 }
 
-dap_http_user_agent_t* dap_http_user_agent_new_from_str(const char* a_user_agent_str)
+void dap_http_user_agent_delete(dap_http_user_agent_ptr_t a_agent)
 {
-    // TODO
-    return NULL;
+    free(a_agent->name);
+    free(a_agent->comment);
+    free(a_agent->string_representation);
+    free(a_agent);
+}
+
+dap_http_user_agent_ptr_t dap_http_user_agent_new_from_str(const char* a_user_agent_str)
+{
+    dap_http_user_agent_ptr_t result = NULL;
+    /* Parse user agent line */
+    char* user_agent_str_copy = strdup(a_user_agent_str);
+    char* version_line = strtok(user_agent_str_copy, " ");
+    char* comment = strtok(NULL, " ");
+
+    char* l_name = strtok(version_line, "/");
+
+    char* l_version = strtok(NULL, "/");
+    if(l_version == NULL) {
+        log_it(L_ERROR, "Wrong input value %s", a_user_agent_str);
+        goto END;
+    }
+
+    char* l_major = strtok(l_version, ".");
+    char* l_minor = strtok(NULL, ".");
+    if(l_minor == NULL) {
+        log_it(L_ERROR, "Wrong input value %s", a_user_agent_str);
+        goto END;
+    }
+    /* PARSE LINE successful */
+
+    result = DAP_NEW_Z(struct dap_http_user_agent);
+    result->name = strdup(l_name);
+    result->comment = comment ? strdup(comment) : NULL;
+    result->major_version = (unsigned int) atoi(l_major);
+    result->minor_version = (unsigned int) atoi(l_minor);
+
+END:
+    free(user_agent_str_copy);
+    return result;
 }
 
-char* dap_http_user_agent_to_string(dap_http_user_agent_t* a_agent)
+void dap_http_user_agent_add_comment(dap_http_user_agent_ptr_t a_agent, const char* comment)
 {
     // TODO
-    return NULL;
 }
 
+static inline int _compare_versions(unsigned int ver1, unsigned int ver2)
+{
+    if(ver1 > ver2)
+        return 1;
+    if(ver1 < ver2)
+        return -1;
+    return 0;
+}
 
-void dap_http_user_agent_add_comment(dap_http_user_agent_t* a_agent, const char* comment)
+int dap_http_user_agent_versions_compare(dap_http_user_agent_ptr_t a_agent1,
+                                         dap_http_user_agent_ptr_t a_agent2)
 {
-    // TODO
+    if(strcmp(a_agent1->name, a_agent2->name) != 0) {
+        log_it(L_ERROR, "Names not equal");
+        return -3;
+    }
+
+    int result = _compare_versions(a_agent1->major_version, a_agent2->major_version);
+    if(result != 0) return result;
+    return _compare_versions(a_agent1->minor_version, a_agent2->minor_version);
 }
 
-int dap_http_user_agent_versions_compare(dap_http_user_agent_t* a_agent1,
-                                         dap_http_user_agent_t* a_agent2)
+unsigned int dap_http_user_agent_get_major_version(dap_http_user_agent_ptr_t a_agent)
 {
-    // TODO
-    return -2;
+    return a_agent->major_version;
+}
+
+unsigned int dap_http_user_agent_get_minor_version(dap_http_user_agent_ptr_t a_agent)
+{
+    return a_agent->minor_version;
+}
+
+const char* dap_http_user_agent_get_comment(dap_http_user_agent_ptr_t a_agent)
+{
+    return a_agent->comment;
+}
+
+const char* dap_http_user_agent_get_name(dap_http_user_agent_ptr_t a_agent)
+{
+    return a_agent->name;
+}
+
+char* dap_http_user_agent_to_string(dap_http_user_agent_ptr_t a_agent)
+{
+    return a_agent->string_representation;
 }
diff --git a/http_server/http_client/dap_http_user_agent.h b/http_server/http_client/dap_http_user_agent.h
index 8f37fc6..409565c 100644
--- a/http_server/http_client/dap_http_user_agent.h
+++ b/http_server/http_client/dap_http_user_agent.h
@@ -2,7 +2,7 @@
  * Authors:
  * Anatolii Kurotych <akurotych@gmail.com>
  * DeM Labs Inc.   https://demlabs.net
- * DeM Labs Open source community https://github.com/demlabsinc
+ * DeM Labs Open source community https://github.com/kelvinblockchain
  * Copyright  (c) 2017-2019
  * All rights reserved.
 
@@ -25,30 +25,54 @@
 #ifndef _DAP_HTTP_USER_AGENT_H_
 #define _DAP_HTTP_USER_AGENT_H_
 
-typedef struct dap_http_user_agent {
-    char* name; // Ex: "DapVpnClient/2.2
-    char* comment; // text after name
-    unsigned short major_version;
-    unsigned short minor_version;
-} dap_http_user_agent_t;
+typedef struct dap_http_user_agent* dap_http_user_agent_ptr_t;
 
-dap_http_user_agent_t* dap_http_user_agent_new(const char* a_name,
-                                               const char* a_comment,
-                                               unsigned short a_major_version,
-                                               unsigned short a_minor_version);
+/**
+ * @brief dap_http_user_agent_new
+ * @param a_name
+ * @param a_comment - Can be NULL
+ * @param a_major_version
+ * @param a_minor_version
+ * @return
+ */
+dap_http_user_agent_ptr_t dap_http_user_agent_new(const char* a_name,
+                                                  unsigned short a_major_version,
+                                                  unsigned short a_minor_version,
+                                                  const char* a_comment);
 
-void dap_http_user_agent_delete(dap_http_user_agent_t* a_agent);
+/**
+ * @brief dap_http_user_agent_delete
+ * @param a_agent
+ */
+void dap_http_user_agent_delete(dap_http_user_agent_ptr_t a_agent);
 
-// If parsing not successful - returns NULL
-dap_http_user_agent_t* dap_http_user_agent_new_from_str(const char* a_user_agent_str);
+/**
+ * @brief dap_http_user_agent_new_from_str
+ * @param a_user_agent_str
+ * @return If parsing not successful - NULL
+ */
+dap_http_user_agent_ptr_t dap_http_user_agent_new_from_str(const char* a_user_agent_str);
 
-// Allocates memory for string and returns result
-char* dap_http_user_agent_to_string(dap_http_user_agent_t* a_agent);
 
-// returns:
-// 0 - equals
-// 1 - a_agent1 version above then a_agent2
-// -1 - a_agent2 version above then a_agent1
-int dap_http_user_agent_versions_compare(dap_http_user_agent_t* a_agent1,
-                                         dap_http_user_agent_t* a_agent2);
+/**
+ * @brief dap_http_user_agent_to_string
+ * @param a_agent
+ * @details Don't allocates memory. Uses internal buffer
+ * @return
+ */
+char* dap_http_user_agent_to_string(dap_http_user_agent_ptr_t a_agent);
+
+/**
+ * @brief dap_http_user_agent_versions_compare
+ * @param a_agent1
+ * @param a_agent2
+ * @return 0 == equals -1 == a_agent1 < a_agent2 | 1 == a_agent1 > a_agent2 | -2 == Erorr agent names not equals
+ */
+int dap_http_user_agent_versions_compare(dap_http_user_agent_ptr_t a_agent1,
+                                         dap_http_user_agent_ptr_t a_agent2);
+
+unsigned int dap_http_user_agent_get_major_version(dap_http_user_agent_ptr_t);
+unsigned int dap_http_user_agent_get_minor_version(dap_http_user_agent_ptr_t);
+const char* dap_http_user_agent_get_name(dap_http_user_agent_ptr_t);
+const char* dap_http_user_agent_get_comment(dap_http_user_agent_ptr_t);
 #endif
diff --git a/test/http_server/CMakeLists.txt b/test/http_server/CMakeLists.txt
index a1a01f6..7c84471 100644
--- a/test/http_server/CMakeLists.txt
+++ b/test/http_server/CMakeLists.txt
@@ -6,7 +6,7 @@ file(GLOB SRC *.h *.c)
 
 add_executable(${PROJECT_NAME} ${SRC})
 
-target_link_libraries(${PROJECT_NAME} dap_test dap_core)
+target_link_libraries(${PROJECT_NAME} dap_test dap_core dap_http_server)
 
 add_test(
     NAME ${PROJECT_NAME}
diff --git a/test/http_server/dap_http_user_agent_test.c b/test/http_server/dap_http_user_agent_test.c
new file mode 100644
index 0000000..569a7ac
--- /dev/null
+++ b/test/http_server/dap_http_user_agent_test.c
@@ -0,0 +1,133 @@
+#include "dap_http_user_agent_test.h"
+
+
+static void dap_http_user_agent_test_new_delete()
+{
+    dap_http_user_agent_ptr_t agent =
+            dap_http_user_agent_new("DapVpn", 2, 3, NULL);
+
+    dap_assert(dap_http_user_agent_get_major_version(agent) == 2, "Major version");
+    dap_assert(dap_http_user_agent_get_minor_version(agent) == 3, "Minor version");
+
+    dap_http_user_agent_delete(agent);
+    dap_pass_msg("Allocate and delete object");
+}
+
+static void dap_http_user_agent_test_new_from_string()
+{
+    const char* user_agent_string = "DapVpn/23.32 somecomment";
+
+    dap_http_user_agent_ptr_t agent =
+            dap_http_user_agent_new_from_str(user_agent_string);
+
+    dap_assert(dap_http_user_agent_get_major_version(agent) == 23,
+               "Check major version");
+    dap_assert(dap_http_user_agent_get_minor_version(agent) == 32,
+               "Check minor version");
+
+    dap_assert(dap_str_equals(dap_http_user_agent_get_name(agent), "DapVpn"),
+               "Check agent name");
+    dap_assert(dap_str_equals(dap_http_user_agent_get_comment(agent), "somecomment"),
+               "Check comment");
+
+    dap_http_user_agent_delete(agent);
+    dap_pass_msg("dap_http_user_agent_test_new_from_string");
+}
+
+static void dap_http_user_agent_test_new_from_string_without_comment()
+{
+    const char* user_agent_string = "DapVpn/23.32";
+
+    dap_http_user_agent_ptr_t agent =
+            dap_http_user_agent_new_from_str(user_agent_string);
+
+    dap_assert(dap_http_user_agent_get_major_version(agent) == 23,
+               "Check major version");
+    dap_assert(dap_http_user_agent_get_minor_version(agent) == 32,
+               "Check minor version");
+
+    dap_assert(dap_str_equals(dap_http_user_agent_get_name(agent), "DapVpn"),
+               "Check agent name");
+    dap_assert(dap_http_user_agent_get_comment(agent) == NULL,
+               "Check comment");
+
+    dap_http_user_agent_delete(agent);
+    dap_pass_msg("dap_http_user_agent_test_new_from_string_without_comment");
+}
+
+static void dap_http_user_agent_test_to_string()
+{
+    dap_http_user_agent_ptr_t agent =
+            dap_http_user_agent_new("DapVpn", 2, 3, "Comment");
+    const char* expected_string = "DapVpn/2.3 Comment";
+    const char* result = dap_http_user_agent_to_string(agent);
+
+    dap_assert(dap_str_equals(expected_string, result), result);
+
+    dap_http_user_agent_delete(agent);
+
+    dap_pass_msg("Allocate and delete object");
+}
+
+static void dap_http_user_agent_test_to_string_without_comment()
+{
+    dap_http_user_agent_ptr_t agent =
+            dap_http_user_agent_new("DapVpn", 2, 3, NULL);
+    const char* expected_string = "DapVpn/2.3";
+    const char* result = dap_http_user_agent_to_string(agent);
+
+    dap_assert(dap_str_equals(expected_string, result), result);
+
+    dap_http_user_agent_delete(agent);
+
+    dap_pass_msg("Allocate and delete object");
+}
+
+static void dap_http_user_agent_test_compare_versions()
+{
+    dap_http_user_agent_ptr_t agent1 =
+            dap_http_user_agent_new("DapVpn", 2, 3, NULL);
+
+    dap_http_user_agent_ptr_t agent2 =
+            dap_http_user_agent_new("DapVpn", 2, 4, NULL);
+
+    dap_http_user_agent_ptr_t agent3 =
+            dap_http_user_agent_new("DapVpn", 3, 1, NULL);
+    dap_http_user_agent_ptr_t agent4 =
+            dap_http_user_agent_new("OterName", 3, 11, NULL);
+
+
+    int result = dap_http_user_agent_versions_compare(agent1, agent4);
+    dap_assert(result == -3, "Checks different names");
+
+    result = dap_http_user_agent_versions_compare(agent1, agent2);
+    dap_assert(result == -1, "Checks agent1, agent2(above))");
+
+    result = dap_http_user_agent_versions_compare(agent1, agent1);
+    dap_assert(result == 0, "Checks agent1, agent1");
+
+    result = dap_http_user_agent_versions_compare(agent3, agent2);
+    dap_assert(result == 1, "Checks agent3(above major), agent2");
+
+    result = dap_http_user_agent_versions_compare(agent2, agent1);
+    dap_assert(result == 1, "Checks agent3(above major), agent2");
+
+
+    dap_http_user_agent_delete(agent1);
+    dap_http_user_agent_delete(agent2);
+    dap_http_user_agent_delete(agent3);
+    dap_http_user_agent_delete(agent4);
+
+    dap_pass_msg("Allocate and delete object");
+}
+
+void dap_http_user_agent_test_run(void)
+{
+    dap_print_module_name("dap_http_user_agent");
+    dap_http_user_agent_test_new_delete();
+    dap_http_user_agent_test_new_from_string();
+    dap_http_user_agent_test_new_from_string_without_comment();
+    dap_http_user_agent_test_to_string();
+    dap_http_user_agent_test_to_string_without_comment();
+    dap_http_user_agent_test_compare_versions();
+}
diff --git a/test/http_server/dap_http_user_agent_test.h b/test/http_server/dap_http_user_agent_test.h
new file mode 100644
index 0000000..1ef51b7
--- /dev/null
+++ b/test/http_server/dap_http_user_agent_test.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "dap_test.h"
+#include "dap_http_user_agent.h"
+
+void dap_http_user_agent_test_run(void);
diff --git a/test/http_server/main.c b/test/http_server/main.c
index d015aec..32fe6fa 100644
--- a/test/http_server/main.c
+++ b/test/http_server/main.c
@@ -1,7 +1,9 @@
 #include "dap_common.h"
+#include "dap_http_user_agent_test.h"
 
 int main(void) {
     // switch off debug info from library
     set_log_level(L_CRITICAL);
+    dap_http_user_agent_test_run();
     return 0;
 }
-- 
GitLab