IVGCVSW-6470 Create MemoryStrategyBenchmark

 * Refactor the strategy library to be more generic
 * Shorten the names of the current strategies
 * Change validatorStrat to throw exceptions

Change-Id: I0d9c9ef609b2d8675e5788610d1accac6767c660
Signed-off-by: Finn Williams <finwil01@e127804.cambridge.arm.com>
Signed-off-by: Jim Flynn <jim.flynn@arm.com>
diff --git a/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/CMakeLists.txt b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/CMakeLists.txt
new file mode 100644
index 0000000..43ec9db
--- /dev/null
+++ b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/CMakeLists.txt
@@ -0,0 +1,19 @@
+#
+# Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+# SPDX-License-Identifier: MIT
+#
+
+list(APPEND armnnMemoryOptimizationStrategies_sources
+            MemoryOptimizerStrategyLibrary.hpp
+            MemoryOptimizerStrategyFactory.hpp
+            strategies/ConstantMemoryStrategy.hpp
+            strategies/ConstantMemoryStrategy.cpp
+            strategies/StrategyValidator.hpp
+            strategies/StrategyValidator.cpp
+)
+
+if(BUILD_UNIT_TESTS)
+    add_subdirectory(test)
+endif()
+
+add_library(armnnMemoryOptimizationStrategies OBJECT ${armnnMemoryOptimizationStrategies_sources})
\ No newline at end of file
diff --git a/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/MemoryOptimizerStrategyFactory.hpp b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/MemoryOptimizerStrategyFactory.hpp
new file mode 100644
index 0000000..aff0995
--- /dev/null
+++ b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/MemoryOptimizerStrategyFactory.hpp
@@ -0,0 +1,28 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <armnn/backends/IMemoryOptimizerStrategy.hpp>
+
+#include <algorithm>
+
+namespace armnn
+{
+
+class MemoryOptimizerStrategyFactory
+{
+public:
+    MemoryOptimizerStrategyFactory() {}
+
+    template <typename T>
+    std::unique_ptr<IMemoryOptimizerStrategy> CreateMemoryOptimizerStrategy()
+    {
+        return std::make_unique<T>();
+    }
+
+};
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/MemoryOptimizerStrategyLibrary.hpp b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/MemoryOptimizerStrategyLibrary.hpp
new file mode 100644
index 0000000..5e20a9f
--- /dev/null
+++ b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/MemoryOptimizerStrategyLibrary.hpp
@@ -0,0 +1,52 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include <armnn/backends/IMemoryOptimizerStrategy.hpp>
+#include "MemoryOptimizerStrategyFactory.hpp"
+#include <algorithm>
+
+#include "strategies/ConstantMemoryStrategy.hpp"
+#include "strategies/StrategyValidator.hpp"
+
+namespace
+{
+// Default Memory Optimizer Strategies
+static const std::vector<std::string> memoryOptimizationStrategies(
+{
+    "ConstantMemoryStrategy",
+    "StrategyValidator"
+});
+
+#define CREATE_MEMORY_OPTIMIZER_STRATEGY(strategyName, memoryOptimizerStrategy)                                  \
+{                                                                                                                \
+    MemoryOptimizerStrategyFactory memoryOptimizerStrategyFactory;                                               \
+    memoryOptimizerStrategy = memoryOptimizerStrategyFactory.CreateMemoryOptimizerStrategy<strategyName>();      \
+}                                                                                                                \
+
+} // anonymous namespace
+namespace armnn
+{
+    std::unique_ptr<IMemoryOptimizerStrategy> GetMemoryOptimizerStrategy(const std::string& strategyName)
+    {
+        auto doesStrategyExist = std::find(memoryOptimizationStrategies.begin(),
+                                           memoryOptimizationStrategies.end(),
+                                           strategyName) != memoryOptimizationStrategies.end();
+        if (doesStrategyExist)
+        {
+            std::unique_ptr<IMemoryOptimizerStrategy> memoryOptimizerStrategy = nullptr;
+            CREATE_MEMORY_OPTIMIZER_STRATEGY(armnn::ConstantMemoryStrategy,
+                                             memoryOptimizerStrategy);
+            return  memoryOptimizerStrategy;
+        }
+        return nullptr;
+    }
+
+
+    const std::vector<std::string>& GetMemoryOptimizerStrategyNames()
+    {
+        return memoryOptimizationStrategies;
+    }
+} // namespace armnn
\ No newline at end of file
diff --git a/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/ConstantMemoryStrategy.cpp b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/ConstantMemoryStrategy.cpp
new file mode 100644
index 0000000..55f7f89
--- /dev/null
+++ b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/ConstantMemoryStrategy.cpp
@@ -0,0 +1,43 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "ConstantMemoryStrategy.hpp"
+
+namespace armnn
+{
+
+std::string ConstantMemoryStrategy::GetName() const
+{
+    return m_Name;
+}
+
+MemBlockStrategyType ConstantMemoryStrategy::GetMemBlockStrategyType() const
+{
+    return m_MemBlockStrategyType;
+}
+
+// A IMemoryOptimizerStrategy must ensure that
+// 1: All MemBlocks have been assigned to a MemBin
+// 2: No MemBlock is assigned to multiple MemBins
+// 3: No two Memblocks in a MemBin overlap in both the X and Y axis
+std::vector<MemBin> ConstantMemoryStrategy::Optimize(std::vector<MemBlock>& memBlocks)
+{
+    std::vector<MemBin> memBins;
+    memBins.reserve(memBlocks.size());
+
+    for (auto& memBlock : memBlocks)
+    {
+        MemBin memBin;
+        memBin.m_MemSize = memBlock.m_MemSize;
+        memBin.m_MemBlocks.reserve(1);
+        memBlock.m_Offset = 0;
+        memBin.m_MemBlocks.push_back(memBlock);
+        memBins.push_back(memBin);
+    }
+
+    return memBins;
+}
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/ConstantMemoryStrategy.hpp b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/ConstantMemoryStrategy.hpp
new file mode 100644
index 0000000..249c133
--- /dev/null
+++ b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/ConstantMemoryStrategy.hpp
@@ -0,0 +1,31 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include <armnn/Types.hpp>
+#include <armnn/backends/IMemoryOptimizerStrategy.hpp>
+
+namespace armnn
+{
+// ConstLayerMemoryOptimizer: Create a unique MemBin for each MemBlock and assign it an offset of 0
+class ConstantMemoryStrategy : public IMemoryOptimizerStrategy
+{
+public:
+    ConstantMemoryStrategy()
+    : m_Name(std::string("ConstantMemoryStrategy"))
+    , m_MemBlockStrategyType(MemBlockStrategyType::SingleAxisPacking) {}
+
+    std::string GetName() const override;
+
+    MemBlockStrategyType GetMemBlockStrategyType() const override;
+
+    std::vector<MemBin> Optimize(std::vector<MemBlock>& memBlocks) override;
+
+private:
+    std::string m_Name;
+    MemBlockStrategyType m_MemBlockStrategyType;
+};
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/StrategyValidator.cpp b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/StrategyValidator.cpp
new file mode 100644
index 0000000..48cdfb0
--- /dev/null
+++ b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/StrategyValidator.cpp
@@ -0,0 +1,132 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <unordered_map>
+#include <iostream>
+#include "StrategyValidator.hpp"
+
+namespace armnn
+{
+
+std::vector<MemBin> StrategyValidator::Optimize(std::vector<MemBlock>& memBlocks)
+{
+    // Condition #1: All Memblocks have been assigned to a MemBin
+
+    // Condition #2: No Memblock is assigned to multiple MemBins
+
+    // Condition #3: No two Memblocks in a MemBin overlap in both the X and Y axis
+    //               Memblocks in a MemBin can overlap on the X axis for SingleAxisPacking
+    //               Memblocks in a MemBin can overlap on the Y axis or the X for MultiAxisPacking but not both
+
+    std::unordered_map<unsigned int, bool> validationMap;
+
+    for (auto memBlock : memBlocks)
+    {
+        validationMap[memBlock.m_Index] = false;
+    }
+
+    auto memBinVect = m_Strategy->Optimize(memBlocks);
+
+    // Compare each of the input memblocks against every assignedBlock in each bin
+    // if we get through all bins without finding a block return
+    // if at any stage the block is found twice return
+
+    for (auto memBin : memBinVect)
+    {
+        for (auto block : memBin.m_MemBlocks)
+        {
+            try
+            {
+                if (!validationMap.at(block.m_Index))
+                {
+                    validationMap.at(block.m_Index) = true;
+                }
+                else
+                {
+                    throw MemoryValidationException("Condition #2: Memblock is assigned to multiple MemBins");
+                }
+            }
+            catch (const std::out_of_range&)
+            {
+                throw MemoryValidationException("Unknown index ");
+            }
+        }
+    }
+
+    for (auto memBlock : memBlocks)
+    {
+        if (!validationMap.at(memBlock.m_Index))
+        {
+            throw MemoryValidationException("Condition #1: Block not found in any bin");
+        }
+    }
+
+    // Check for overlaps once we know blocks are all assigned and no duplicates
+    for (auto bin : memBinVect)
+    {
+        for (unsigned int i = 0; i < bin.m_MemBlocks.size(); ++i)
+        {
+            auto assignedBlock = bin.m_MemBlocks[i];
+            auto xStart = assignedBlock.m_Offset;
+            auto xEnd = assignedBlock.m_Offset + assignedBlock.m_MemSize;
+
+            auto yStart = assignedBlock.m_StartOfLife;
+            auto yEnd = assignedBlock.m_EndOfLife;
+            auto assignedIndex = assignedBlock.m_Index;
+
+            // Only compare with blocks after the current one as previous have already been checked
+            for (unsigned int j = i + 1; j < bin.m_MemBlocks.size(); ++j)
+            {
+                auto otherAssignedBlock = bin.m_MemBlocks[j];
+                auto xStartAssigned = otherAssignedBlock.m_Offset;
+                auto xEndAssigned = otherAssignedBlock.m_Offset + otherAssignedBlock.m_MemSize;
+
+                auto yStartAssigned = otherAssignedBlock.m_StartOfLife;
+                auto yEndAssigned = otherAssignedBlock.m_EndOfLife;
+                auto otherIndex = otherAssignedBlock.m_Index;
+
+                // If overlapping on both X and Y then invalid
+                // Inside left of rectangle & Inside right of rectangle
+                if ((((xStart >= xStartAssigned) && (xEnd <= xEndAssigned)) &&
+                     // Inside bottom of rectangle & Inside top of rectangle
+                     ((yStart >= yStartAssigned) && (yEnd <= yEndAssigned))) &&
+                     // Cant overlap with itself
+                     (assignedIndex != otherIndex))
+                {
+                    // Condition #3: two Memblocks overlap on both the X and Y axis
+                    throw MemoryValidationException("Condition #3: two Memblocks overlap on both the X and Y axis");
+                }
+
+                switch (m_Strategy->GetMemBlockStrategyType())
+                {
+                    case (MemBlockStrategyType::SingleAxisPacking):
+                    {
+                        // Inside bottom of rectangle  & Inside top of rectangle
+                        if (((yStart >= yStartAssigned) && (yEnd <= yEndAssigned)) &&
+                            // Cant overlap with itself
+                            (assignedIndex != otherIndex))
+                        {
+                            throw MemoryValidationException("Condition #3: "
+                                        "invalid as two Memblocks overlap on the Y axis for SingleAxisPacking");
+
+                        }
+                        break;
+                    }
+                    case (MemBlockStrategyType::MultiAxisPacking):
+                    {
+                        break;
+                    }
+                    default:
+                        throw MemoryValidationException("Unknown MemBlockStrategyType");
+                }
+            }
+        }
+    }
+
+    // None of the conditions broken so return true
+    return memBinVect;
+}
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/StrategyValidator.hpp b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/StrategyValidator.hpp
new file mode 100644
index 0000000..e1f9111
--- /dev/null
+++ b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/strategies/StrategyValidator.hpp
@@ -0,0 +1,41 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include <armnn/Types.hpp>
+#include <armnn/Exceptions.hpp>
+#include <armnn/backends/IMemoryOptimizerStrategy.hpp>
+
+namespace armnn
+{
+
+class StrategyValidator : public IMemoryOptimizerStrategy
+{
+public:
+
+    void SetStrategy(std::shared_ptr<IMemoryOptimizerStrategy> strategy)
+    {
+        m_Strategy = strategy;
+        m_MemBlockStrategyType = strategy->GetMemBlockStrategyType();
+    }
+
+    std::string GetName() const override
+    {
+        return "StrategyValidator";
+    }
+
+    MemBlockStrategyType GetMemBlockStrategyType() const override
+    {
+        return m_MemBlockStrategyType;
+    }
+
+    std::vector<MemBin> Optimize(std::vector<MemBlock>& memBlocks) override;
+
+private:
+    std::shared_ptr<IMemoryOptimizerStrategy> m_Strategy;
+    MemBlockStrategyType m_MemBlockStrategyType;
+};
+
+} // namespace armnn
\ No newline at end of file
diff --git a/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/test/CMakeLists.txt b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/test/CMakeLists.txt
new file mode 100644
index 0000000..b96782a
--- /dev/null
+++ b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/test/CMakeLists.txt
@@ -0,0 +1,12 @@
+#
+# Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+# SPDX-License-Identifier: MIT
+#
+
+list(APPEND armnnMemoryOptimizationStrategiesUnitTests_sources
+            ConstMemoryStrategyTests.cpp
+            ValidatorStrategyTests.cpp
+)
+
+add_library(armnnMemoryOptimizationStrategiesUnitTests OBJECT ${armnnMemoryOptimizationStrategiesUnitTests_sources})
+target_include_directories(armnnMemoryOptimizationStrategiesUnitTests PRIVATE ${PROJECT_SOURCE_DIR}/src/backends)
diff --git a/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/test/ConstMemoryStrategyTests.cpp b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/test/ConstMemoryStrategyTests.cpp
new file mode 100644
index 0000000..64312f3
--- /dev/null
+++ b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/test/ConstMemoryStrategyTests.cpp
@@ -0,0 +1,77 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <backendsCommon/memoryOptimizerStrategyLibrary/strategies/ConstantMemoryStrategy.hpp>
+#include <backendsCommon/memoryOptimizerStrategyLibrary/strategies/StrategyValidator.hpp>
+
+#include <doctest/doctest.h>
+#include <vector>
+
+using namespace armnn;
+
+TEST_SUITE("ConstMemoryStrategyTestSuite")
+{
+
+TEST_CASE("ConstMemoryStrategyTest")
+{
+    // create a few memory blocks
+    MemBlock memBlock0(0, 2, 20, 0, 0);
+    MemBlock memBlock1(2, 3, 10, 20, 1);
+    MemBlock memBlock2(3, 5, 15, 30, 2);
+    MemBlock memBlock3(5, 6, 20, 50, 3);
+    MemBlock memBlock4(7, 8, 5, 70, 4);
+
+    std::vector<MemBlock> memBlocks;
+    memBlocks.reserve(5);
+    memBlocks.push_back(memBlock0);
+    memBlocks.push_back(memBlock1);
+    memBlocks.push_back(memBlock2);
+    memBlocks.push_back(memBlock3);
+    memBlocks.push_back(memBlock4);
+
+    // Optimize the memory blocks with ConstantMemoryStrategy
+    ConstantMemoryStrategy constLayerMemoryOptimizerStrategy;
+    CHECK_EQ(constLayerMemoryOptimizerStrategy.GetName(), std::string("ConstantMemoryStrategy"));
+    CHECK_EQ(constLayerMemoryOptimizerStrategy.GetMemBlockStrategyType(), MemBlockStrategyType::SingleAxisPacking);
+    auto memBins = constLayerMemoryOptimizerStrategy.Optimize(memBlocks);
+    CHECK(memBins.size() == 5);
+
+    CHECK(memBins[1].m_MemBlocks.size() == 1);
+    CHECK(memBins[1].m_MemBlocks[0].m_Offset == 0);
+    CHECK(memBins[1].m_MemBlocks[0].m_MemSize == 10);
+    CHECK(memBins[1].m_MemBlocks[0].m_Index == 1);
+
+    CHECK(memBins[4].m_MemBlocks.size() == 1);
+    CHECK(memBins[4].m_MemBlocks[0].m_Offset == 0);
+    CHECK(memBins[4].m_MemBlocks[0].m_MemSize == 5);
+    CHECK(memBins[4].m_MemBlocks[0].m_Index == 4);
+}
+
+TEST_CASE("ConstLayerMemoryOptimizerStrategyValidatorTest")
+{
+    // create a few memory blocks
+    MemBlock memBlock0(0, 2, 20, 0, 0);
+    MemBlock memBlock1(2, 3, 10, 20, 1);
+    MemBlock memBlock2(3, 5, 15, 30, 2);
+    MemBlock memBlock3(5, 6, 20, 50, 3);
+    MemBlock memBlock4(7, 8, 5, 70, 4);
+
+    std::vector<MemBlock> memBlocks;
+    memBlocks.reserve(5);
+    memBlocks.push_back(memBlock0);
+    memBlocks.push_back(memBlock1);
+    memBlocks.push_back(memBlock2);
+    memBlocks.push_back(memBlock3);
+    memBlocks.push_back(memBlock4);
+
+    // Optimize the memory blocks with ConstLayerMemoryOptimizerStrategy
+    auto ptr = std::make_shared<ConstantMemoryStrategy>();
+    StrategyValidator validator;
+    validator.SetStrategy(ptr);
+    // Ensure ConstLayerMemoryOptimizerStrategy is valid
+    CHECK_NOTHROW(validator.Optimize(memBlocks));
+}
+
+}
diff --git a/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/test/ValidatorStrategyTests.cpp b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/test/ValidatorStrategyTests.cpp
new file mode 100644
index 0000000..bc04105
--- /dev/null
+++ b/src/backends/backendsCommon/memoryOptimizerStrategyLibrary/test/ValidatorStrategyTests.cpp
@@ -0,0 +1,283 @@
+//
+// Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <backendsCommon/memoryOptimizerStrategyLibrary/strategies/StrategyValidator.hpp>
+
+#include <doctest/doctest.h>
+#include <vector>
+
+using namespace armnn;
+
+TEST_SUITE("MemoryOptimizerStrategyValidatorTestSuite")
+{
+
+// TestMemoryOptimizerStrategy: Create a MemBin and put all blocks in it so the can overlap.
+class TestMemoryOptimizerStrategy : public IMemoryOptimizerStrategy
+{
+public:
+    TestMemoryOptimizerStrategy(MemBlockStrategyType type)
+            : m_Name(std::string("testMemoryOptimizerStrategy"))
+            , m_MemBlockStrategyType(type) {}
+
+    std::string GetName() const override
+    {
+        return m_Name;
+    }
+
+    MemBlockStrategyType GetMemBlockStrategyType() const override
+    {
+        return m_MemBlockStrategyType;
+    }
+
+    std::vector<MemBin> Optimize(std::vector<MemBlock>& memBlocks) override
+    {
+        std::vector<MemBin> memBins;
+        memBins.reserve(memBlocks.size());
+
+        MemBin memBin;
+        memBin.m_MemBlocks.reserve(memBlocks.size());
+        memBin.m_MemSize = 0;
+        for (auto& memBlock : memBlocks)
+        {
+
+            memBin.m_MemSize = memBin.m_MemSize + memBlock.m_MemSize;
+            memBin.m_MemBlocks.push_back(memBlock);
+        }
+        memBins.push_back(memBin);
+
+        return memBins;
+    }
+
+private:
+    std::string m_Name;
+    MemBlockStrategyType m_MemBlockStrategyType;
+};
+
+TEST_CASE("MemoryOptimizerStrategyValidatorTestOverlapX")
+{
+    // create a few memory blocks
+    MemBlock memBlock0(0, 5, 20, 0, 0);
+    MemBlock memBlock1(5, 10, 10, 0, 1);
+    MemBlock memBlock2(10, 15, 15, 0, 2);
+    MemBlock memBlock3(15, 20, 20, 0, 3);
+    MemBlock memBlock4(20, 25, 5, 0, 4);
+
+    std::vector<MemBlock> memBlocks;
+    memBlocks.reserve(5);
+    memBlocks.push_back(memBlock0);
+    memBlocks.push_back(memBlock1);
+    memBlocks.push_back(memBlock2);
+    memBlocks.push_back(memBlock3);
+    memBlocks.push_back(memBlock4);
+
+    // Optimize the memory blocks with TestMemoryOptimizerStrategySingle
+    TestMemoryOptimizerStrategy testMemoryOptimizerStrategySingle(MemBlockStrategyType::SingleAxisPacking);
+    auto ptr = std::make_shared<TestMemoryOptimizerStrategy>(testMemoryOptimizerStrategySingle);
+    StrategyValidator validator;
+    validator.SetStrategy(ptr);
+    // SingleAxisPacking can overlap on X axis.
+    CHECK_NOTHROW(validator.Optimize(memBlocks));
+
+    // Optimize the memory blocks with TestMemoryOptimizerStrategyMulti
+    TestMemoryOptimizerStrategy testMemoryOptimizerStrategyMulti(MemBlockStrategyType::MultiAxisPacking);
+    auto ptrMulti = std::make_shared<TestMemoryOptimizerStrategy>(testMemoryOptimizerStrategyMulti);
+    StrategyValidator validatorMulti;
+    validatorMulti.SetStrategy(ptrMulti);
+    // MultiAxisPacking can overlap on X axis.
+    CHECK_NOTHROW(validatorMulti.Optimize(memBlocks));
+}
+
+TEST_CASE("MemoryOptimizerStrategyValidatorTestOverlapXAndY")
+{
+    // create a few memory blocks
+    MemBlock memBlock0(0, 5, 20, 0, 0);
+    MemBlock memBlock1(0, 10, 10, 0, 1);
+    MemBlock memBlock2(0, 15, 15, 0, 2);
+    MemBlock memBlock3(0, 20, 20, 0, 3);
+    MemBlock memBlock4(0, 25, 5, 0, 4);
+
+    std::vector<MemBlock> memBlocks;
+    memBlocks.reserve(5);
+    memBlocks.push_back(memBlock0);
+    memBlocks.push_back(memBlock1);
+    memBlocks.push_back(memBlock2);
+    memBlocks.push_back(memBlock3);
+    memBlocks.push_back(memBlock4);
+
+    // Optimize the memory blocks with TestMemoryOptimizerStrategySingle
+    TestMemoryOptimizerStrategy testMemoryOptimizerStrategySingle(MemBlockStrategyType::SingleAxisPacking);
+    auto ptr = std::make_shared<TestMemoryOptimizerStrategy>(testMemoryOptimizerStrategySingle);
+    StrategyValidator validator;
+    validator.SetStrategy(ptr);
+    // SingleAxisPacking cannot overlap on both X and Y axis.
+    CHECK_THROWS(validator.Optimize(memBlocks));
+
+    // Optimize the memory blocks with TestMemoryOptimizerStrategyMulti
+    TestMemoryOptimizerStrategy testMemoryOptimizerStrategyMulti(MemBlockStrategyType::MultiAxisPacking);
+    auto ptrMulti = std::make_shared<TestMemoryOptimizerStrategy>(testMemoryOptimizerStrategyMulti);
+    StrategyValidator validatorMulti;
+    validatorMulti.SetStrategy(ptrMulti);
+    // MultiAxisPacking cannot overlap on both X and Y axis.
+    CHECK_THROWS(validatorMulti.Optimize(memBlocks));
+}
+
+TEST_CASE("MemoryOptimizerStrategyValidatorTestOverlapY")
+{
+    // create a few memory blocks
+    MemBlock memBlock0(0, 2, 20, 0, 0);
+    MemBlock memBlock1(0, 3, 10, 20, 1);
+    MemBlock memBlock2(0, 5, 15, 30, 2);
+    MemBlock memBlock3(0, 6, 20, 50, 3);
+    MemBlock memBlock4(0, 8, 5, 70, 4);
+
+    std::vector<MemBlock> memBlocks;
+    memBlocks.reserve(5);
+    memBlocks.push_back(memBlock0);
+    memBlocks.push_back(memBlock1);
+    memBlocks.push_back(memBlock2);
+    memBlocks.push_back(memBlock3);
+    memBlocks.push_back(memBlock4);
+
+    // Optimize the memory blocks with TestMemoryOptimizerStrategySingle
+    TestMemoryOptimizerStrategy testMemoryOptimizerStrategySingle(MemBlockStrategyType::SingleAxisPacking);
+    auto ptr = std::make_shared<TestMemoryOptimizerStrategy>(testMemoryOptimizerStrategySingle);
+    StrategyValidator validator;
+    validator.SetStrategy(ptr);
+    // SingleAxisPacking cannot overlap on Y axis
+    CHECK_THROWS(validator.Optimize(memBlocks));
+
+    // Optimize the memory blocks with TestMemoryOptimizerStrategyMulti
+    TestMemoryOptimizerStrategy testMemoryOptimizerStrategyMulti(MemBlockStrategyType::MultiAxisPacking);
+    auto ptrMulti = std::make_shared<TestMemoryOptimizerStrategy>(testMemoryOptimizerStrategyMulti);
+    StrategyValidator validatorMulti;
+    validatorMulti.SetStrategy(ptrMulti);
+    // MultiAxisPacking can overlap on Y axis
+    CHECK_NOTHROW(validatorMulti.Optimize(memBlocks));
+}
+
+// TestMemoryOptimizerStrategyDuplicate: Create a MemBin and put all blocks in it duplicating each so validator
+// can check
+class TestMemoryOptimizerStrategyDuplicate : public TestMemoryOptimizerStrategy
+{
+public:
+    TestMemoryOptimizerStrategyDuplicate(MemBlockStrategyType type)
+            : TestMemoryOptimizerStrategy(type)
+    {}
+
+    std::vector<MemBin> Optimize(std::vector<MemBlock>& memBlocks) override
+    {
+        std::vector<MemBin> memBins;
+        memBins.reserve(memBlocks.size());
+
+        MemBin memBin;
+        memBin.m_MemBlocks.reserve(memBlocks.size());
+        for (auto& memBlock : memBlocks)
+        {
+            memBin.m_MemSize = memBin.m_MemSize + memBlock.m_MemSize;
+            memBin.m_MemBlocks.push_back(memBlock);
+            // Put block in twice so it gets found twice
+            memBin.m_MemBlocks.push_back(memBlock);
+        }
+        memBins.push_back(memBin);
+
+        return memBins;
+    }
+};
+
+TEST_CASE("MemoryOptimizerStrategyValidatorTestDuplicateBlocks")
+{
+    // create a few memory blocks
+    MemBlock memBlock0(0, 2, 20, 0, 0);
+    MemBlock memBlock1(2, 3, 10, 20, 1);
+    MemBlock memBlock2(3, 5, 15, 30, 2);
+    MemBlock memBlock3(5, 6, 20, 50, 3);
+    MemBlock memBlock4(7, 8, 5, 70, 4);
+
+    std::vector<MemBlock> memBlocks;
+    memBlocks.reserve(5);
+    memBlocks.push_back(memBlock0);
+    memBlocks.push_back(memBlock1);
+    memBlocks.push_back(memBlock2);
+    memBlocks.push_back(memBlock3);
+    memBlocks.push_back(memBlock4);
+
+    // Optimize the memory blocks with TestMemoryOptimizerStrategySingle
+    // Duplicate strategy is invalid as same block is found twice
+    TestMemoryOptimizerStrategyDuplicate testMemoryOptimizerStrategySingle(MemBlockStrategyType::SingleAxisPacking);
+    auto ptr = std::make_shared<TestMemoryOptimizerStrategyDuplicate>(testMemoryOptimizerStrategySingle);
+    StrategyValidator validator;
+    validator.SetStrategy(ptr);
+    CHECK_THROWS(validator.Optimize(memBlocks));
+
+    // Optimize the memory blocks with TestMemoryOptimizerStrategyMulti
+    TestMemoryOptimizerStrategyDuplicate testMemoryOptimizerStrategyMulti(MemBlockStrategyType::MultiAxisPacking);
+    auto ptrMulti = std::make_shared<TestMemoryOptimizerStrategyDuplicate>(testMemoryOptimizerStrategyMulti);
+    StrategyValidator validatorMulti;
+    validatorMulti.SetStrategy(ptrMulti);
+    CHECK_THROWS(validatorMulti.Optimize(memBlocks));
+}
+
+// TestMemoryOptimizerStrategySkip: Create a MemBin and put all blocks in it skipping every other block so validator
+// can check
+class TestMemoryOptimizerStrategySkip : public TestMemoryOptimizerStrategy
+{
+public:
+    TestMemoryOptimizerStrategySkip(MemBlockStrategyType type)
+            : TestMemoryOptimizerStrategy(type)
+    {}
+
+    std::vector<MemBin> Optimize(std::vector<MemBlock>& memBlocks) override
+    {
+        std::vector<MemBin> memBins;
+        memBins.reserve(memBlocks.size());
+
+        MemBin memBin;
+        memBin.m_MemBlocks.reserve(memBlocks.size());
+        for (unsigned int i = 0; i < memBlocks.size()-1; i+=2)
+        {
+            auto memBlock = memBlocks[i];
+            memBin.m_MemSize = memBin.m_MemSize + memBlock.m_MemSize;
+            memBin.m_MemBlocks.push_back(memBlock);
+        }
+        memBins.push_back(memBin);
+
+        return memBins;
+    }
+};
+
+TEST_CASE("MemoryOptimizerStrategyValidatorTestSkipBlocks")
+{
+    // create a few memory blocks
+    MemBlock memBlock0(0, 2, 20, 0, 0);
+    MemBlock memBlock1(2, 3, 10, 20, 1);
+    MemBlock memBlock2(3, 5, 15, 30, 2);
+    MemBlock memBlock3(5, 6, 20, 50, 3);
+    MemBlock memBlock4(7, 8, 5, 70, 4);
+
+    std::vector<MemBlock> memBlocks;
+    memBlocks.reserve(5);
+    memBlocks.push_back(memBlock0);
+    memBlocks.push_back(memBlock1);
+    memBlocks.push_back(memBlock2);
+    memBlocks.push_back(memBlock3);
+    memBlocks.push_back(memBlock4);
+
+    // Optimize the memory blocks with TestMemoryOptimizerStrategySingle
+    // Skip strategy is invalid as every second block is not found
+    TestMemoryOptimizerStrategySkip testMemoryOptimizerStrategySingle(MemBlockStrategyType::SingleAxisPacking);
+    auto ptr = std::make_shared<TestMemoryOptimizerStrategySkip>(testMemoryOptimizerStrategySingle);
+    StrategyValidator validator;
+    validator.SetStrategy(ptr);
+    CHECK_THROWS(validator.Optimize(memBlocks));
+
+    // Optimize the memory blocks with TestMemoryOptimizerStrategyMulti
+    TestMemoryOptimizerStrategySkip testMemoryOptimizerStrategyMulti(MemBlockStrategyType::MultiAxisPacking);
+    auto ptrMulti = std::make_shared<TestMemoryOptimizerStrategySkip>(testMemoryOptimizerStrategyMulti);
+    StrategyValidator validatorMulti;
+    validatorMulti.SetStrategy(ptrMulti);
+    CHECK_THROWS(validatorMulti.Optimize(memBlocks));
+}
+
+}