From 4d6c315c1e943642bd639c6647e4d76baa418aee Mon Sep 17 00:00:00 2001
From: Marko Budiselic <mbudiselicbuda@gmail.com>
Date: Sun, 18 Dec 2016 19:21:29 +0100
Subject: [PATCH 1/2] Block Allocator Test - initial implementation

Summary: Block Allocator Test - initial implementation

Test Plan: ctest -R memgraph_unit_block_allocator

Reviewers: sale

Subscribers: sale, buda

Differential Revision: https://memgraph.phacility.com/D20
---
 include/utils/auto_scope.hpp             |   7 ++
 include/utils/memory/block_allocator.hpp |  39 ++++++---
 tests/unit/CMakeLists.txt                |   2 +
 tests/unit/basic_bloom_filter.cpp        |  47 +++++-----
 tests/unit/block_allocator.cpp           |  24 +++++
 tests/unit/parameter_index.cpp           |   5 +-
 tests/unit/program_argument.cpp          | 106 ++++++++++++-----------
 7 files changed, 142 insertions(+), 88 deletions(-)
 create mode 100644 tests/unit/block_allocator.cpp

diff --git a/include/utils/auto_scope.hpp b/include/utils/auto_scope.hpp
index 10de5751f..119078a91 100644
--- a/include/utils/auto_scope.hpp
+++ b/include/utils/auto_scope.hpp
@@ -55,3 +55,10 @@ private:
             TOKEN_PASTE(auto_, counter)(TOKEN_PASTE(auto_func_, counter));
 
 #define Auto(Destructor) Auto_INTERNAL(Destructor, __COUNTER__)
+
+// -- example:
+// Auto(f());
+// -- is expended to:
+// auto auto_func_1 = [&]() { f(); };
+// OnScopeExit<decltype(auto_func_1)> auto_1(auto_func_1);
+// -- f() is called at the end of a scope
diff --git a/include/utils/memory/block_allocator.hpp b/include/utils/memory/block_allocator.hpp
index f0c0e6475..f7eb3791f 100644
--- a/include/utils/memory/block_allocator.hpp
+++ b/include/utils/memory/block_allocator.hpp
@@ -5,6 +5,9 @@
 
 #include "utils/auto_scope.hpp"
 
+/* @brief Allocates blocks of block_size and stores
+ * the pointers on allocated blocks inside a vector.
+ */
 template <size_t block_size>
 class BlockAllocator
 {
@@ -23,29 +26,45 @@ public:
     BlockAllocator(size_t capacity = 0)
     {
         for (size_t i = 0; i < capacity; ++i)
-            blocks.emplace_back();
+            unused_.emplace_back();
     }
 
     ~BlockAllocator()
     {
-        for (auto b : blocks) {
-            free(b.data);
-        }
-        blocks.clear();
+        for (auto block : unused_)
+            free(block.data);
+        unused_.clear();
+        for (auto block : release_)
+            free(block.data);
+        release_.clear();
+    }
+
+    size_t unused_size() const
+    {
+        return unused_.size();
+    }
+
+    size_t release_size() const
+    {
+        return release_.size();
     }
 
     // Returns nullptr on no memory.
     void *acquire()
     {
-        if (blocks.size() == 0) blocks.emplace_back();
+        if (unused_.size() == 0) unused_.emplace_back();
 
-        auto ptr = blocks.back().data;
-        Auto(blocks.pop_back());
+        auto ptr = unused_.back().data;
+        Auto(unused_.pop_back());
         return ptr;
     }
 
-    void release(void *ptr) { blocks.emplace_back(ptr); }
+    void release(void *ptr) { release_.emplace_back(ptr); }
 
 private:
-    std::vector<Block> blocks;
+    // TODO: try implement with just one vector
+    // but consecutive acquire release calls should work
+    // TODO: measure first!
+    std::vector<Block> unused_;
+    std::vector<Block> release_;
 };
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index df6ecb100..85b360288 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -26,6 +26,8 @@ foreach(test_cpp ${test_type_cpps})
     set_target_properties(${target_name} PROPERTIES OUTPUT_NAME ${exec_name})
 
     # link libraries
+    # gtest
+    target_link_libraries(${target_name} gtest gtest_main)
     # filesystem
     target_link_libraries(${target_name} stdc++fs)
     # threads (cross-platform)
diff --git a/tests/unit/basic_bloom_filter.cpp b/tests/unit/basic_bloom_filter.cpp
index ac4df7fc2..15a41294c 100644
--- a/tests/unit/basic_bloom_filter.cpp
+++ b/tests/unit/basic_bloom_filter.cpp
@@ -9,37 +9,34 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wwritable-strings"
 
-using StringHashFunction = std::function<uint64_t(const std::string&)>;
- 
-TEST_CASE("BloomFilter Test") {
-  StringHashFunction hash1 = fnv64<std::string>;
-  StringHashFunction hash2 = fnv1a64<std::string>;
+using StringHashFunction = std::function<uint64_t(const std::string &)>;
 
-  auto c = [](auto x) -> int {
-    return x % 4;
-  } ;
-  std::vector<StringHashFunction> funcs = {
-    hash1, hash2
-  };
+TEST_CASE("BloomFilter Test")
+{
+    StringHashFunction hash1 = fnv64<std::string>;
+    StringHashFunction hash2 = fnv1a64<std::string>;
 
-  BloomFilter<std::string, 64> bloom(funcs);
+    auto c                                = [](auto x) -> int { return x % 4; };
+    std::vector<StringHashFunction> funcs = {hash1, hash2};
 
-  std::string test = "test";
-  std::string kifla = "kifla";
+    BloomFilter<std::string, 64> bloom(funcs);
 
-  std::cout << hash1(test) << std::endl;
-  std::cout << hash2(test) << std::endl;
-  
-  std::cout << hash1(kifla) << std::endl;
-  std::cout << hash2(kifla) << std::endl;
+    std::string test  = "test";
+    std::string kifla = "kifla";
 
-  std::cout << bloom.contains(test) << std::endl;
-  bloom.insert(test);
-  std::cout << bloom.contains(test) << std::endl;
+    std::cout << hash1(test) << std::endl;
+    std::cout << hash2(test) << std::endl;
 
-  std::cout << bloom.contains(kifla) << std::endl;
-  bloom.insert(kifla);
-  std::cout << bloom.contains(kifla) << std::endl;
+    std::cout << hash1(kifla) << std::endl;
+    std::cout << hash2(kifla) << std::endl;
+
+    std::cout << bloom.contains(test) << std::endl;
+    bloom.insert(test);
+    std::cout << bloom.contains(test) << std::endl;
+
+    std::cout << bloom.contains(kifla) << std::endl;
+    bloom.insert(kifla);
+    std::cout << bloom.contains(kifla) << std::endl;
 }
 
 #pragma clang diagnostic pop
diff --git a/tests/unit/block_allocator.cpp b/tests/unit/block_allocator.cpp
new file mode 100644
index 000000000..35bf9cfdc
--- /dev/null
+++ b/tests/unit/block_allocator.cpp
@@ -0,0 +1,24 @@
+#include "gtest/gtest.h"
+
+#include "utils/memory/block_allocator.hpp"
+
+TEST(BlockAllocatorTest, UnusedVsReleaseSize)
+{
+    BlockAllocator<64> block_allocator(10);
+    void *block = block_allocator.acquire();
+    block_allocator.release(block);
+    EXPECT_EQ(block_allocator.unused_size(), 9);
+    EXPECT_EQ(block_allocator.release_size(), 1);
+}
+
+TEST(BlockAllocatorTest, CountMallocAndFreeCalls)
+{
+    // TODO: implementation
+    EXPECT_EQ(true, true);
+}
+
+int main(int argc, char **argv)
+{
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/tests/unit/parameter_index.cpp b/tests/unit/parameter_index.cpp
index 542591dd6..b2d137c79 100644
--- a/tests/unit/parameter_index.cpp
+++ b/tests/unit/parameter_index.cpp
@@ -8,12 +8,13 @@ using ParameterIndexKey::Type::Projection;
 
 auto main() -> int
 {
-    std::map<ParameterIndexKey, uint64_t> parameter_index; 
+    std::map<ParameterIndexKey, uint64_t> parameter_index;
 
     parameter_index[ParameterIndexKey(InternalId, "n1")] = 0;
     parameter_index[ParameterIndexKey(InternalId, "n2")] = 1;
 
-    permanent_assert(parameter_index.size() == 2, "Parameter index size should be 2");
+    permanent_assert(parameter_index.size() == 2,
+                     "Parameter index size should be 2");
 
     return 0;
 }
diff --git a/tests/unit/program_argument.cpp b/tests/unit/program_argument.cpp
index a12ae190f..c5c54996e 100644
--- a/tests/unit/program_argument.cpp
+++ b/tests/unit/program_argument.cpp
@@ -6,80 +6,84 @@
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wwritable-strings"
 
-TEST_CASE("ProgramArgument FlagOnly Test") {
-  CLEAR_ARGS();
+TEST_CASE("ProgramArgument FlagOnly Test")
+{
+    CLEAR_ARGS();
 
-  int argc = 2;
-  char* argv[] = {"ProgramArgument FlagOnly Test", "-test"};
+    int argc     = 2;
+    char *argv[] = {"ProgramArgument FlagOnly Test", "-test"};
 
-  REGISTER_ARGS(argc, argv);
-  REGISTER_REQUIRED_ARGS({"-test"});
+    REGISTER_ARGS(argc, argv);
+    REGISTER_REQUIRED_ARGS({"-test"});
 
-  REQUIRE(CONTAINS_FLAG("-test") == true);
+    REQUIRE(CONTAINS_FLAG("-test") == true);
 }
 
-TEST_CASE("ProgramArgument Single Entry Test") {
-  CLEAR_ARGS();
+TEST_CASE("ProgramArgument Single Entry Test")
+{
+    CLEAR_ARGS();
 
-  int argc = 3;
-  char* argv[] = {"ProgramArgument Single Entry Test", "-bananas", "99"};
+    int argc     = 3;
+    char *argv[] = {"ProgramArgument Single Entry Test", "-bananas", "99"};
 
-  REGISTER_REQUIRED_ARGS({"-bananas"});
-  REGISTER_ARGS(argc, argv);
+    REGISTER_REQUIRED_ARGS({"-bananas"});
+    REGISTER_ARGS(argc, argv);
 
-  REQUIRE(GET_ARG("-bananas", "100").get_int() == 99);
+    REQUIRE(GET_ARG("-bananas", "100").get_int() == 99);
 }
 
-TEST_CASE("ProgramArgument Multiple Entries Test") {
-  CLEAR_ARGS();
+TEST_CASE("ProgramArgument Multiple Entries Test")
+{
+    CLEAR_ARGS();
 
-  int argc = 4;
-  char* argv[] = {"ProgramArgument Multiple Entries Test", "-files",
-                  "first_file.txt", "second_file.txt"};
+    int argc     = 4;
+    char *argv[] = {"ProgramArgument Multiple Entries Test", "-files",
+                    "first_file.txt", "second_file.txt"};
 
-  REGISTER_ARGS(argc, argv);
+    REGISTER_ARGS(argc, argv);
 
-  auto files = GET_ARGS("-files", {});
+    auto files = GET_ARGS("-files", {});
 
-  REQUIRE(files[0].get_string() == "first_file.txt");
+    REQUIRE(files[0].get_string() == "first_file.txt");
 }
 
-TEST_CASE("ProgramArgument Combination Test") {
-  CLEAR_ARGS();
+TEST_CASE("ProgramArgument Combination Test")
+{
+    CLEAR_ARGS();
 
-  int argc = 14;
-  char* argv[] = {"ProgramArgument Combination Test",
-                  "-run_tests",
-                  "-tests",
-                  "Test1",
-                  "Test2",
-                  "Test3",
-                  "-run_times",
-                  "10",
-                  "-export",
-                  "test1.txt",
-                  "test2.txt",
-                  "test3.txt",
-                  "-import",
-                  "data.txt"};
+    int argc     = 14;
+    char *argv[] = {"ProgramArgument Combination Test",
+                    "-run_tests",
+                    "-tests",
+                    "Test1",
+                    "Test2",
+                    "Test3",
+                    "-run_times",
+                    "10",
+                    "-export",
+                    "test1.txt",
+                    "test2.txt",
+                    "test3.txt",
+                    "-import",
+                    "data.txt"};
 
-  REGISTER_ARGS(argc, argv);
+    REGISTER_ARGS(argc, argv);
 
-  REQUIRE(CONTAINS_FLAG("-run_tests") == true);
+    REQUIRE(CONTAINS_FLAG("-run_tests") == true);
 
-  auto tests = GET_ARGS("-tests", {});
-  REQUIRE(tests[0].get_string() == "Test1");
-  REQUIRE(tests[1].get_string() == "Test2");
-  REQUIRE(tests[2].get_string() == "Test3");
+    auto tests = GET_ARGS("-tests", {});
+    REQUIRE(tests[0].get_string() == "Test1");
+    REQUIRE(tests[1].get_string() == "Test2");
+    REQUIRE(tests[2].get_string() == "Test3");
 
-  REQUIRE(GET_ARG("-run_times", "0").get_int() == 10);
+    REQUIRE(GET_ARG("-run_times", "0").get_int() == 10);
 
-  auto exports = GET_ARGS("-export", {});
-  REQUIRE(exports[0].get_string() == "test1.txt");
-  REQUIRE(exports[1].get_string() == "test2.txt");
-  REQUIRE(exports[2].get_string() == "test3.txt");
+    auto exports = GET_ARGS("-export", {});
+    REQUIRE(exports[0].get_string() == "test1.txt");
+    REQUIRE(exports[1].get_string() == "test2.txt");
+    REQUIRE(exports[2].get_string() == "test3.txt");
 
-  REQUIRE(GET_ARG("-import", "test.txt").get_string() == "data.txt");
+    REQUIRE(GET_ARG("-import", "test.txt").get_string() == "data.txt");
 }
 
 #pragma clang diagnostic pop

From dc3433aa8ad283df577fbabe69f736aa6678fce3 Mon Sep 17 00:00:00 2001
From: Marko Budiselic <mbudiselicbuda@gmail.com>
Date: Sun, 18 Dec 2016 20:26:08 +0100
Subject: [PATCH 2/2] Stack Allocator Unit Test

Summary: Stack Allocator Unit Test

Test Plan: manual (unit tests are not passing because malloc and free counters have to be added)

Reviewers: sale

Subscribers: sale, buda

Differential Revision: https://memgraph.phacility.com/D21
---
 include/utils/memory/stack_allocator.hpp |  1 +
 tests/unit/block_allocator.cpp           |  2 +-
 tests/unit/stack_allocator.cpp           | 34 ++++++++++++++++++++++++
 3 files changed, 36 insertions(+), 1 deletion(-)
 create mode 100644 tests/unit/stack_allocator.cpp

diff --git a/include/utils/memory/stack_allocator.hpp b/include/utils/memory/stack_allocator.hpp
index 287bdad6a..15623e49d 100644
--- a/include/utils/memory/stack_allocator.hpp
+++ b/include/utils/memory/stack_allocator.hpp
@@ -3,6 +3,7 @@
 #include <cmath>
 
 #include "utils/exceptions/out_of_memory.hpp"
+#include "utils/likely.hpp"
 #include "utils/memory/block_allocator.hpp"
 
 // http://en.cppreference.com/w/cpp/language/new
diff --git a/tests/unit/block_allocator.cpp b/tests/unit/block_allocator.cpp
index 35bf9cfdc..e2de1e405 100644
--- a/tests/unit/block_allocator.cpp
+++ b/tests/unit/block_allocator.cpp
@@ -14,7 +14,7 @@ TEST(BlockAllocatorTest, UnusedVsReleaseSize)
 TEST(BlockAllocatorTest, CountMallocAndFreeCalls)
 {
     // TODO: implementation
-    EXPECT_EQ(true, true);
+    EXPECT_EQ(true, false);
 }
 
 int main(int argc, char **argv)
diff --git a/tests/unit/stack_allocator.cpp b/tests/unit/stack_allocator.cpp
new file mode 100644
index 000000000..006ffbe36
--- /dev/null
+++ b/tests/unit/stack_allocator.cpp
@@ -0,0 +1,34 @@
+#include "gtest/gtest.h"
+
+#include "utils/memory/stack_allocator.hpp"
+
+struct Object
+{
+    int a;
+    int b;
+
+    Object(int a, int b) : a(a), b(b) {}
+};
+
+TEST(StackAllocatorTest, AllocationAndObjectValidity)
+{
+    StackAllocator allocator;
+    for (int i = 0; i < 64 * 1024; ++i)
+    {
+        auto object = allocator.make<Object>(1, 2);
+        ASSERT_EQ(object->a, 1);
+        ASSERT_EQ(object->b, 2);
+    }
+}
+
+TEST(StackAllocatorTest, CountMallocAndFreeCalls)
+{
+    // TODO: implementation
+    EXPECT_EQ(true, false);
+}
+
+int main(int argc, char **argv)
+{
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}