/** * diqube: Distributed Query Base. * * Copyright (C) 2015 Bastian Gloeckle * * This file is part of diqube. * * diqube is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.diqube.server.queryremote.flatten; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; import java.util.Arrays; import java.util.Deque; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.diqube.context.Profiles; import org.diqube.data.flatten.FlattenDataFactory; import org.diqube.data.flatten.FlattenedTable; import org.diqube.file.DiqubeFileFactory; import org.diqube.file.DiqubeFileReader; import org.diqube.file.DiqubeFileWriter; import org.diqube.server.queryremote.flatten.FlattenedControlFileFlattenedTableDiskCache.CachedDataInfo; import org.diqube.threads.ExecutorManager; import org.diqube.util.Holder; import org.diqube.util.Pair; import org.mockito.Mockito; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests {@link FlattenedControlFileFlattenedTableDiskCache}. * * @author Bastian Gloeckle */ public class FlattenedControlFileFlattenedTableDiskCacheTest { private static final String TABLE = "table"; private static final String FLATTEN_BY = "a[*]"; private AnnotationConfigApplicationContext dataContext; private Path cacheDir; private FlattenedControlFileFlattenedTableDiskCache diskCache; private Holder<DiqubeFileWriter> writerHolder; private Holder<DiqubeFileReader> readerHolder; @BeforeMethod public void before() throws IOException { dataContext = new AnnotationConfigApplicationContext(); dataContext.getEnvironment().setActiveProfiles(Profiles.UNIT_TEST); dataContext.scan("org.diqube"); dataContext.refresh(); cacheDir = Files.createTempDirectory(FlattenedControlFileFlattenedTableDiskCacheTest.class.getSimpleName()); writerHolder = new Holder<>(); readerHolder = new Holder<>(); DiqubeFileFactory fileFactory = Mockito.mock(DiqubeFileFactory.class); Mockito.when(fileFactory.createDiqubeFileWriter(Mockito.any())).thenAnswer(invocation -> { DiqubeFileWriter res = Mockito.mock(DiqubeFileWriter.class); writerHolder.setValue(res); return res; }); Mockito.when(fileFactory.createDiqubeFileReader(Mockito.any())).thenAnswer(invocation -> { DiqubeFileReader res = Mockito.mock(DiqubeFileReader.class); readerHolder.setValue(res); return res; }); diskCache = new FlattenedControlFileFlattenedTableDiskCache(fileFactory, dataContext.getBean(FlattenDataFactory.class), dataContext.getBean(ExecutorManager.class), cacheDir.toFile()); } @AfterMethod public void after() throws Throwable { diskCache.finalize(); dataContext.close(); Files.walkFileTree(cacheDir, new FileVisitor<Path>() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { file.toFile().delete(); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { dir.toFile().delete(); return FileVisitResult.CONTINUE; } }); cacheDir.toFile().delete(); } @Test public void offerAddsTable() { // GIVEN FlattenedTable flattenedTable = mockedFlattenedTable(0L, 10L); // WHEN diskCache.offer(flattenedTable, TABLE, FLATTEN_BY, true); // THEN Assert.assertEquals(diskCache.loadCurrentData().keySet(), new HashSet<>(Arrays.asList(new Pair<>(TABLE, FLATTEN_BY))), "Expected correct table/flattenBy pair is cached"); } @Test public void tableRemoveRemovesCache() { // GIVEN FlattenedTable flattenedTable = mockedFlattenedTable(0L, 10L); // WHEN diskCache.offer(flattenedTable, TABLE, FLATTEN_BY, true); diskCache.tableUnloaded(TABLE); // THEN Assert.assertEquals(diskCache.loadCurrentData().keySet(), new HashSet<>(), "Expected correct table/flattenBy pair is cached"); } @Test public void oldVersionsAreRemoved() { // GIVEN FlattenedTable flattenedTable = mockedFlattenedTable(0L, 10L); FlattenedTable flattenedTableNewVersion = mockedFlattenedTable(0L); // WHEN diskCache.offer(flattenedTable, TABLE, FLATTEN_BY, true); diskCache.offer(flattenedTableNewVersion, TABLE, FLATTEN_BY, true); // THEN Pair<String, String> keyPair = new Pair<>(TABLE, FLATTEN_BY); Map<Pair<String, String>, Deque<CachedDataInfo>> data = diskCache.loadCurrentData(); Assert.assertEquals(data.keySet(), new HashSet<>(Arrays.asList(keyPair)), "Expected correct table/flattenBy pair is cached"); Set<Set<Long>> expectedRowIdSet = new HashSet<>(); expectedRowIdSet.add(new HashSet<>(Arrays.asList(0L))); Assert.assertEquals(data.get(keyPair).stream().map(info -> info.getOrigFirstRowIds()).collect(Collectors.toSet()), expectedRowIdSet, "Expected correct table/flattenBy pair is cached"); } private FlattenedTable mockedFlattenedTable(Long... firstRowIds) { FlattenedTable res = Mockito.mock(FlattenedTable.class); Mockito.when(res.getOriginalFirstRowIdsOfShards()).thenReturn(new HashSet<>(Arrays.asList(firstRowIds))); return res; } }