blob: b21a837af02cc3c32cc1d92652f495fdda574ef7 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* 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.
*/
// Author: morlovich@google.com (Maksim Orlovich)
// Unit-test the arena
#include "pagespeed/kernel/base/arena.h"
#include <cstddef>
#include <set>
#include "pagespeed/kernel/base/gtest.h"
namespace net_instaweb {
class ArenaTest : public testing::Test {
public:
ArenaTest() {
ClearStats();
}
protected:
friend class KidA;
friend class KidB;
class Base {
public:
explicit Base(ArenaTest* owner) : owner_(owner) {}
virtual ~Base() {
// Watch out for double-delete
EXPECT_TRUE(owner_ != NULL);
owner_ = NULL;
}
// When testing creation, we invoke this method.
virtual void Made() = 0;
void* operator new(size_t size, Arena<Base>* arena) {
return arena->Allocate(size);
}
protected:
ArenaTest* owner_;
};
// We expect KidA to have size of 2 pointers: the vtable and owner,
// (just like base). On 32-bit this will be 8-bytes, so with 8-byte
// alignment area it will divide the block size
class KidA : public Base {
public:
explicit KidA(ArenaTest* o) : Base(o) {}
~KidA() {
++owner_->destroyed_a_;
}
virtual void Made() {
++owner_->made_a_;
}
};
// KidB is 3 pointers long, so on 64-bit along with the next pointer
// we will be using 32 bytes per allocation and it will divide
// the block size.
//
// The difference in size between A and B lets us test mixed combinations of
// different sizes.
class KidB : public Base {
public:
explicit KidB(ArenaTest* o) : Base(o) {}
~KidB() {
++owner_->destroyed_b_;
}
virtual void Made() {
++owner_->made_b_;
}
private:
void* different_size;
};
// Tests a given mixture of allocations of KidA and KidB --
// making sure we get sane pointers and delete things.
void TestCombo(int num_a, int num_b) {
for (int a = 0; a < num_a; ++a) {
CheckPtr(new (&arena_) KidA(this));
}
for (int b = 0; b < num_b; ++b) {
CheckPtr(new (&arena_) KidB(this));
}
arena_.DestroyObjects();
EXPECT_EQ(num_a, made_a_);
EXPECT_EQ(num_b, made_b_);
EXPECT_EQ(num_a, destroyed_a_);
EXPECT_EQ(num_b, destroyed_b_);
}
// Checks to make sure the pointer is sane, and calls Made on it.
void CheckPtr(Base* p) {
EXPECT_TRUE(seen_ptrs_.find(p) == seen_ptrs_.end());
seen_ptrs_.insert(p);
p->Made();
}
void ClearStats() {
made_a_ = 0;
made_b_ = 0;
destroyed_a_ = 0;
destroyed_b_ = 0;
seen_ptrs_.clear();
}
int made_a_;
int made_b_;
int destroyed_a_;
int destroyed_b_;
Arena<Base> arena_;
std::set<void*> seen_ptrs_;
};
// Empty arena should be OK without a Destroy
TEST_F(ArenaTest, TestEmpty) {
}
// calling Destroy on empty is fine.
TEST_F(ArenaTest, TestEmptyDestroy) {
arena_.DestroyObjects();
}
TEST_F(ArenaTest, TestJustA) {
TestCombo(10000, 0);
}
TEST_F(ArenaTest, TestJustA2) {
// On 32-bit this should perfectly fill all the blocks it uses
TestCombo(2048, 0);
}
TEST_F(ArenaTest, TestJustB) {
TestCombo(0, 10000);
}
TEST_F(ArenaTest, TestJustB2) {
// On 64-bit this should perfectly fill all the blocks it uses
TestCombo(0, 2048);
}
TEST_F(ArenaTest, TestMix) {
TestCombo(10000, 20000);
}
// Make sure we work again after a clear
TEST_F(ArenaTest, TestReuse) {
TestCombo(10000, 20000);
ClearStats();
TestCombo(20000, 10000);
}
// Tests for alignment helper.
TEST_F(ArenaTest, TestAlign) {
// A few that work regardless of arch, to sanity-check
// the more through loop below
EXPECT_EQ(8, arena_.ExpandToAlign(8));
EXPECT_EQ(16, arena_.ExpandToAlign(15));
EXPECT_EQ(16, arena_.ExpandToAlign(14));
EXPECT_EQ(16, arena_.ExpandToAlign(13));
for (size_t t = 0; t < 1000u; ++t) {
size_t expanded = arena_.ExpandToAlign(t);
if ((t % arena_.kAlign) == 0) {
// Well-aligned case.
EXPECT_EQ(t, expanded);
} else {
// Otherwise should be next multiple
EXPECT_EQ(((t / arena_.kAlign) + 1) * arena_.kAlign, expanded);
}
}
}
} // namespace net_instaweb