Add SlabAllocator and tests

Bug: dawn:340
Change-Id: I6fa1948261e8e6f91324464dade3e9954bd833e5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15861
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/tests/unittests/PlacementAllocatedTests.cpp b/src/tests/unittests/PlacementAllocatedTests.cpp
new file mode 100644
index 0000000..d483d6e
--- /dev/null
+++ b/src/tests/unittests/PlacementAllocatedTests.cpp
@@ -0,0 +1,115 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "common/PlacementAllocated.h"
+
+using namespace testing;
+
+namespace {
+
+    enum class DestructedClass {
+        Foo,
+        Bar,
+    };
+
+    class MockDestructor {
+      public:
+        MOCK_METHOD2(Call, void(void*, DestructedClass));
+    };
+
+    std::unique_ptr<StrictMock<MockDestructor>> mockDestructor;
+
+    class PlacementAllocatedTests : public Test {
+        void SetUp() override {
+            mockDestructor = std::make_unique<StrictMock<MockDestructor>>();
+        }
+
+        void TearDown() override {
+            mockDestructor = nullptr;
+        }
+    };
+
+    struct Foo : PlacementAllocated {
+        virtual ~Foo() {
+            mockDestructor->Call(this, DestructedClass::Foo);
+        }
+    };
+
+    struct Bar : Foo {
+        ~Bar() override {
+            mockDestructor->Call(this, DestructedClass::Bar);
+        }
+    };
+}  // namespace
+
+// Test that deletion calls the destructor and does not free memory.
+TEST_F(PlacementAllocatedTests, DeletionDoesNotFreeMemory) {
+    void* ptr = malloc(sizeof(Foo));
+
+    Foo* foo = new (ptr) Foo();
+
+    EXPECT_CALL(*mockDestructor, Call(foo, DestructedClass::Foo));
+    delete foo;
+
+    // Touch the memory, this shouldn't crash.
+    static_assert(sizeof(Foo) >= sizeof(uint32_t), "");
+    *reinterpret_cast<uint32_t*>(foo) = 42;
+
+    free(ptr);
+}
+
+// Test that destructing an instance of a derived class calls the derived, then base destructor, and
+// does not free memory.
+TEST_F(PlacementAllocatedTests, DeletingDerivedClassCallsBaseDestructor) {
+    void* ptr = malloc(sizeof(Bar));
+
+    Bar* bar = new (ptr) Bar();
+
+    {
+        InSequence s;
+        EXPECT_CALL(*mockDestructor, Call(bar, DestructedClass::Bar));
+        EXPECT_CALL(*mockDestructor, Call(bar, DestructedClass::Foo));
+        delete bar;
+    }
+
+    // Touch the memory, this shouldn't crash.
+    static_assert(sizeof(Bar) >= sizeof(uint32_t), "");
+    *reinterpret_cast<uint32_t*>(bar) = 42;
+
+    free(ptr);
+}
+
+// Test that destructing an instance of a base class calls the derived, then base destructor, and
+// does not free memory.
+TEST_F(PlacementAllocatedTests, DeletingBaseClassCallsDerivedDestructor) {
+    void* ptr = malloc(sizeof(Bar));
+
+    Foo* foo = new (ptr) Bar();
+
+    {
+        InSequence s;
+        EXPECT_CALL(*mockDestructor, Call(foo, DestructedClass::Bar));
+        EXPECT_CALL(*mockDestructor, Call(foo, DestructedClass::Foo));
+        delete foo;
+    }
+
+    // Touch the memory, this shouldn't crash.
+    static_assert(sizeof(Bar) >= sizeof(uint32_t), "");
+    *reinterpret_cast<uint32_t*>(foo) = 42;
+
+    free(ptr);
+}