| // Copyright (C) 2022 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. |
| |
| package com.googlesource.gerrit.modules.cache.chroniclemap; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static com.googlesource.gerrit.modules.cache.chroniclemap.CacheKeysIndex.tempIndexFile; |
| import static java.util.stream.Collectors.toList; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.verifyNoInteractions; |
| |
| import com.google.gerrit.metrics.DisabledMetricMaker; |
| import com.google.gerrit.server.cache.serialize.StringCacheSerializer; |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.function.Consumer; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.TemporaryFolder; |
| |
| public class CacheKeysIndexTest { |
| private static final String CACHE_NAME = "test-cache"; |
| @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); |
| |
| private CacheKeysIndex<String> index; |
| private File indexFile; |
| |
| @Before |
| public void setup() throws IOException { |
| CacheSerializers.registerCacheKeySerializer(CACHE_NAME, StringCacheSerializer.INSTANCE); |
| indexFile = temporaryFolder.newFolder().toPath().resolve("cache.index").toFile(); |
| index = new CacheKeysIndex<>(new DisabledMetricMaker(), CACHE_NAME, indexFile, false); |
| } |
| |
| @Test |
| public void add_shouldUpdateElementPositionWhenAlreadyInSet() { |
| index.add("foo", 2L); |
| index.add("bar", 1L); |
| assertThat(keys(index)).containsExactly("foo", "bar"); |
| |
| index.add("foo", 1L); |
| assertThat(keys(index)).containsExactly("bar", "foo"); |
| } |
| |
| @Test |
| public void add_shouldUpdateElementInsertionTimeWhenNewerGetsAdded() { |
| index.add("foo", 1L); |
| index.add("foo", 2L); |
| |
| CacheKeysIndex<String>.TimedKey key = index.keys().iterator().next(); |
| assertThat(key.getKey()).isEqualTo("foo"); |
| assertThat(key.getCreated()).isEqualTo(2L); |
| } |
| |
| @Test |
| public void refresh_shouldRefreshInsertionTimeOnRefresh() { |
| index.add("foo", 1L); |
| index.refresh("foo"); |
| |
| CacheKeysIndex<String>.TimedKey key = index.keys().iterator().next(); |
| assertThat(key.getKey()).isEqualTo("foo"); |
| assertThat(key.getCreated()).isGreaterThan(1L); |
| } |
| |
| @Test |
| @SuppressWarnings("unchecked") |
| public void remove_shouldRemoveAndConsumeKeysOlderThan() { |
| long than = 5l; |
| index.add("newerThan", 10L); |
| index.add("olderThan", 2L); |
| Consumer<String> consumer = mock(Consumer.class); |
| |
| index.removeAndConsumeKeysOlderThan(than, consumer); |
| |
| verify(consumer).accept("olderThan"); |
| assertThat(keys(index)).containsExactly("newerThan"); |
| } |
| |
| @Test |
| @SuppressWarnings("unchecked") |
| public void remove_shouldReturnFalseIfThereIsNoLruToRemove() { |
| Consumer<String> consumer = mock(Consumer.class); |
| |
| boolean actual = index.removeAndConsumeLruKey(consumer); |
| |
| assertThat(actual).isEqualTo(false); |
| verifyNoInteractions(consumer); |
| } |
| |
| @Test |
| @SuppressWarnings("unchecked") |
| public void remove_shouldRemoveAndConsumeLruKey() { |
| index.add("older", 1L); |
| index.add("newer", 1L); |
| Consumer<String> consumer = mock(Consumer.class); |
| |
| boolean actual = index.removeAndConsumeLruKey(consumer); |
| |
| assertThat(actual).isEqualTo(true); |
| verify(consumer).accept("older"); |
| assertThat(keys(index)).containsExactly("newer"); |
| } |
| |
| @Test |
| public void persist_shouldPersistAndRestoreKeys() { |
| index.add("older", 1L); |
| index.add("newer", 1L); |
| assertThat(indexFile.exists()).isFalse(); |
| |
| index.persist(); |
| assertThat(indexFile.isFile()).isTrue(); |
| assertThat(indexFile.canRead()).isTrue(); |
| |
| index.clear(); |
| assertThat(keys(index)).isEmpty(); |
| |
| index.restore(true); |
| assertThat(keys(index)).containsExactly("newer", "older"); |
| } |
| |
| @Test |
| public void restore_shouldDeleteExistingTemporaryIndexStorageFileDuringRestore() |
| throws IOException { |
| File indexTempFile = tempIndexFile(indexFile); |
| indexTempFile.createNewFile(); |
| assertThat(indexTempFile.isFile()).isTrue(); |
| |
| index.restore(true); |
| |
| assertThat(indexTempFile.exists()).isFalse(); |
| } |
| |
| private static List<String> keys(CacheKeysIndex<String> index) { |
| return index.keys().stream().map(CacheKeysIndex.TimedKey::getKey).collect(toList()); |
| } |
| } |