blob: e9bcd4d5e459f733bd13d0e9da2372959e07b664 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 "memory_manager.h"
#include "mock_resource_provider.h"
#include "resource_in_memory_cache.h"
#include "resource_provider.h"
#include "server_connection.h"
#include "test_utilities.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <memory>
#include <vector>
using namespace ::testing;
namespace gapir {
namespace test {
namespace {
const uint32_t MEMORY_SIZE = 4096;
const uint32_t CACHE_SIZE = 2048;
const Resource A("A", 64);
const Resource B("B", 256);
const Resource C("C", 512);
const Resource D("D", 1024);
const Resource E("E", 2048);
const Resource Z("Z", 1);
class ResourceInMemoryCacheTest : public Test {
protected:
virtual void SetUp() {
std::vector<uint32_t> memorySizes = {MEMORY_SIZE};
mMemoryManager.reset(new MemoryManager(memorySizes));
mMemoryManager->setVolatileMemory(MEMORY_SIZE - CACHE_SIZE);
// ResourceInMemoryCache -> PatternedResourceProvider -> MockResourceProvider
mFallbackProvider = new StrictMock<MockResourceProvider>();
auto patternedResourceProvider = new PatternedResourceProvider(
std::unique_ptr<StrictMock<MockResourceProvider>>(mFallbackProvider));
mResourceInMemoryCache = ResourceInMemoryCache::create(
std::unique_ptr<PatternedResourceProvider>(patternedResourceProvider),
mMemoryManager->getBaseAddress());
mResourceInMemoryCache->resize(CACHE_SIZE);
mServer = createServerConnection("", 0);
}
inline void expectCacheHit(std::vector<Resource> resources) {
auto pattern = PatternedResourceProvider::patternFor(resources);
size_t size = pattern.size();
std::vector<uint8_t> got(size);
// Test as a single request.
EXPECT_TRUE(mResourceInMemoryCache->get(
resources.data(), resources.size(), *mServer, got.data(), size));
EXPECT_EQ(got, pattern);
// Test individually
size_t offset = 0;
for (auto resource : resources) {
EXPECT_TRUE(mResourceInMemoryCache->get(&resource, 1, *mServer, &got[offset], resource.size));
offset += resource.size;
}
EXPECT_EQ(got, pattern);
}
inline void expectCacheMiss(std::vector<Resource> resources) {
size_t size = 0;
for (auto resource : resources) {
size += resource.size;
}
std::vector<uint8_t> got(size);
EXPECT_CALL(*mFallbackProvider, get(_, _, _, got.data(), size))
.With(Args<0, 1>(ElementsAreArray(resources)))
.WillOnce(Return(true))
.RetiresOnSaturation();
EXPECT_TRUE(mResourceInMemoryCache->get(
resources.data(), resources.size(), *mServer, got.data(), size));
auto pattern = PatternedResourceProvider::patternFor(resources);
EXPECT_EQ(got, pattern);
}
StrictMock<MockResourceProvider>* mFallbackProvider;
std::unique_ptr<MemoryManager> mMemoryManager;
std::unique_ptr<ResourceInMemoryCache> mResourceInMemoryCache;
std::unique_ptr<ServerConnection> mServer;
};
} // anonymous namespace
// Test that get() calls with uncached data propagages to the fallback provider.
TEST_F(ResourceInMemoryCacheTest, PopulateNoFill) {
InSequence x;
expectCacheMiss({A});
expectCacheMiss({B});
expectCacheMiss({C, D});
}
// Test that get() calls with cached data propagages to the fallback provider.
TEST_F(ResourceInMemoryCacheTest, CacheHit) {
InSequence x;
expectCacheMiss({A});
expectCacheMiss({B});
expectCacheHit({A, B});
}
TEST_F(ResourceInMemoryCacheTest, Prefetch) {
InSequence x;
uint8_t* cache = reinterpret_cast<uint8_t*>(mMemoryManager->getBaseAddress());
// Request resources of sizes: 64, 256, 512, 1024, 2048
// As the cache is only 2048 bytes, only the first 4 resources will be fetched.
size_t abcd_size = 64+256+512+1024;
EXPECT_CALL(*mFallbackProvider, get(_, _, _, cache, abcd_size))
.With(Args<0, 1>(ElementsAre(A, B, C, D)))
.WillOnce(Return(true));
Resource resources[] = {A, B, C, D, E};
mResourceInMemoryCache->prefetch(resources, 5, *mServer, nullptr, 0);
// These should be cached.
expectCacheHit({C, B});
// This shouldn't.
expectCacheMiss({Z});
}
TEST_F(ResourceInMemoryCacheTest, Resize) {
InSequence x;
mResourceInMemoryCache->resize(D.size / 2);
// D is too big to fit in the cache.
expectCacheMiss({D});
expectCacheMiss({D});
mResourceInMemoryCache->resize(D.size);
// Resizing clears the cache.
expectCacheMiss({D});
// But now should be big enough to hold D.
expectCacheHit({D});
}
TEST_F(ResourceInMemoryCacheTest, CachingLogic) {
InSequence x;
uint8_t* cache = reinterpret_cast<uint8_t*>(mMemoryManager->getBaseAddress());
Resource A1("A1", 1), B1("B1", 1), C1("C1", 1), D1("D1", 1);
Resource E1("E1", 1), F1("F1", 1), G1("G1", 1), H1("H1", 1);
Resource A2("A2", 2), B2("B2", 2), C2("C2", 2), D2("D2", 2);
mResourceInMemoryCache->resize(8);
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheMiss({A1, B1, C1, D1, E1, F1, G1, H1});
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ A1 ┃ B1 ┃ C1 ┃ D1 ┃ E1 ┃ F2 ┃ G1 ┃ H1 ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({A1, B1, C1, D1, E1, F1, G1, H1});
expectCacheMiss({A2, B2});
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ A2 ┃ B2 ┃ E1 ┃ F2 ┃ G1 ┃ H1 ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({A2, B2, E1, F1, G1, H1});
expectCacheMiss({A1, B1, C1});
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ A2 ┃ B2 ┃ A1 ┃ B1 ┃ C1 ┃ H1 ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({A1, B1, C1, A2, B2, H1});
expectCacheMiss({C2});
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ C2 ┃ ┃ B2 ┃ A1 ┃ B1 ┃ C1 ┃ C2 ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({A1, B1, C1, B2, C2});
expectCacheMiss({D1});
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ C2 ┃ D1 ┃ B2 ┃ A1 ┃ B1 ┃ C1 ┃ C2 ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({B2, A1, B1, C1, C2, D1});
Resource resources1[] = {A1, B1, C1, D1, E1};
EXPECT_CALL(*mFallbackProvider, get(_, _, _, cache, 5))
.With(Args<0, 1>(ElementsAre(A1, B1, C1, D1, E1)))
.WillOnce(Return(true));
mResourceInMemoryCache->prefetch(resources1, 5, *mServer, nullptr, 0);
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ A1 ┃ B1 ┃ C1 ┃ D1 ┃ E1 ┃ ┃ ┃ ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({A1, B1, C1, D1, E1});
expectCacheMiss({A2, B2});
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ B2 ┃ B1 ┃ C1 ┃ D1 ┃ E1 ┃ A2 ┃ B2 ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({B2, B1, C1, D1, E1, A2});
Resource resources2[] = {A1, B1, A2, B2, C2, D2};
EXPECT_CALL(*mFallbackProvider, get(_, _, _, cache, 8))
.With(Args<0, 1>(ElementsAre(A1, B1, A2, B2, C2)))
.WillOnce(Return(true));
mResourceInMemoryCache->prefetch(resources2, 6, *mServer, nullptr, 0);
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ A1 ┃ B1 ┃ A2 ┃ B2 ┃ C2 ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({A1, B1, A2, B2, C2});
expectCacheMiss({D2});
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ D2 ┃ A2 ┃ B2 ┃ C2 ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({A2, B2, C2, D2});
}
TEST_F(ResourceInMemoryCacheTest, PrefecthOverrun) {
InSequence x;
uint8_t* cache = reinterpret_cast<uint8_t*>(mMemoryManager->getBaseAddress());
Resource A1("A1", 1), B1("B1", 1), C1("C1", 1), D1("D1", 1);
Resource E1("E1", 1), F1("F1", 1), G1("G1", 1), H1("H1", 1);
Resource A2("A2", 2), B2("B2", 2), C2("C2", 2), D2("D2", 2);
mResourceInMemoryCache->resize(8);
Resource resources1[] = {A1, B1, C1, D1, E1};
EXPECT_CALL(*mFallbackProvider, get(_, _, _, cache, 5))
.With(Args<0, 1>(ElementsAre(A1, B1, C1, D1, E1)))
.WillOnce(Return(true));
mResourceInMemoryCache->prefetch(resources1, 5, *mServer, nullptr, 0);
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ A1 ┃ B1 ┃ C1 ┃ D1 ┃ E1 ┃ ┃ ┃ ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({A1, B1, C1, D1, E1});
expectCacheMiss({A2, B2});
// ┏━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┳━━━━━━┓
// ┃ B2 ┃ B1 ┃ C1 ┃ D1 ┃ E1 ┃ A2 ┃ B2 ┃
// ┗━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┻━━━━━━┛
expectCacheHit({B2, B1, C1, D1, E1, A2});
expectCacheMiss({A1});
}
} // namespace test
} // namespace gapir