/* Copyright (c) 2014 Boundless and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/edl-v10.html * * Contributors: * Gabriel Roldan (Boundless) - initial implementation */ package org.locationtech.geogig.osm.internal.coordcache; import java.io.File; import java.io.FileFilter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.locationtech.geogig.api.Platform; import org.locationtech.geogig.api.TestPlatform; import org.locationtech.geogig.osm.internal.OSMCoordinateSequence; import com.google.common.base.Stopwatch; import com.google.common.collect.ImmutableList; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateSequence; public abstract class PointCacheTest extends Assert { private PointCache cache; @Rule public ExpectedException thrown = ExpectedException.none(); @Rule public TemporaryFolder tmpFolder = new TemporaryFolder(); @Before public void before() { tmpFolder.newFolder(".geogig"); TestPlatform platform = new TestPlatform(tmpFolder.getRoot()); platform.setUserHome(tmpFolder.newFolder("fakeHome")); cache = createCache(platform); } @After public void after() { if (cache != null) { cache.dispose(); cache = null; } } protected abstract PointCache createCache(Platform platform); @Test public void testGetNull() { thrown.expect(NullPointerException.class); thrown.expectMessage("ids"); cache.get(null); } @Test public void testGetEmpty() { CoordinateSequence coords = cache.get(ImmutableList.<Long> of()); assertNotNull(coords); assertEquals(0, coords.size()); } @Test public void testPutNullId() { thrown.expect(NullPointerException.class); thrown.expectMessage("id"); cache.put(null, coord(0, 0)); } @Test public void testPutNullCoord() { thrown.expect(NullPointerException.class); thrown.expectMessage("coord"); cache.put(1L, null); } @Test public void testDisposeIsIdempotent() { cache.dispose(); cache.dispose(); } @Test public void testGetNonExistentCoordinate() { cache.put(3L, coord(0, 0)); cache.put(2L, coord(0, 0)); cache.put(1L, coord(0, 0)); List<Long> ids = ImmutableList.<Long> of(1L, 5L, 2L, 3L); thrown.expect(IllegalArgumentException.class); cache.get(ids); } @Test public void testGet() { cache.put(3L, coord(3, 3)); cache.put(2L, coord(2, 2)); cache.put(1L, coord(1, 1)); List<Long> ids = ImmutableList.<Long> of(1L, 2L, 3L); CoordinateSequence sequence = cache.get(ids); assertNotNull(sequence); assertEquals(3, sequence.size()); assertEquals(1D, sequence.getOrdinate(0, 0), 1E-9); assertEquals(1D, sequence.getOrdinate(0, 1), 1E-9); assertEquals(2D, sequence.getOrdinate(1, 0), 1E-9); assertEquals(2D, sequence.getOrdinate(1, 1), 1E-9); assertEquals(3D, sequence.getOrdinate(2, 0), 1E-9); assertEquals(3D, sequence.getOrdinate(2, 1), 1E-9); } @Test public void testLargeSequences1M() { testLargeSequences(1000 * 1000); } @Test public void testLargeSequences10M() { testLargeSequences(10 * 1000 * 1000); } @Ignore @Test public void testLargeSequences25M() { testLargeSequences(25 * 1000 * 1000); } @Ignore @Test public void testLargeSequences50M() { testLargeSequences(50 * 1000 * 1000); } @Ignore @Test public void testLargeSequences100M() { testLargeSequences(100 * 1000 * 1000); } @Test public void testLargeSequencesNonSequentialQueries1M() { testLargeSequencesNonSequentialQueries(1000 * 1000); } @Test public void testLargeSequencesNonSequentialQueries10M() { testLargeSequencesNonSequentialQueries(10 * 1000 * 1000); } @Ignore @Test public void testLargeSequencesNonSequentialQueries25M() { testLargeSequencesNonSequentialQueries(25 * 1000 * 1000); } @Ignore @Test public void testLargeSequencesNonSequentialQueries50M() { testLargeSequencesNonSequentialQueries(50 * 1000 * 1000); } @Ignore @Test public void testLargeSequencesNonSequentialQueries100M() { testLargeSequencesNonSequentialQueries(100 * 1000 * 1000); } private void testLargeSequences(final int numNodes) { List<Long> nodeIds = new ArrayList<Long>(numNodes / 6); Stopwatch sw = Stopwatch.createStarted(); for (int n = 0; n < numNodes; n++) { if (n % 20 == 0) { nodeIds.add(Long.valueOf(n)); } cache.put((long) n, coord(n, n)); } System.err.printf("%,d nodes added in %s\n", numNodes, sw.stop()); Collections.shuffle(nodeIds); sw.reset().start(); CoordinateSequence sequence = cache.get(nodeIds); System.err.printf("requested %,d coordinates in %s\n", nodeIds.size(), sw.stop()); assertNotNull(sequence); assertEquals(nodeIds.size(), sequence.size()); long approxDbSize = caclDbSize(); System.err.printf("Approx db size: %,f MB\n\n", ((double) approxDbSize / 1024D / 1024D)); } private void testLargeSequencesNonSequentialQueries(final int numNodes) { after(); before(); List<Long> nodeIds = new ArrayList<Long>(numNodes / 6); Stopwatch sw = Stopwatch.createStarted(); for (int n = 0; n < numNodes; n++) { if (n % 20 == 0) { nodeIds.add(Long.valueOf(n)); } cache.put((long) n, coord(n, n)); if (n > 1 && n % 100 == 0) { Collections.shuffle(nodeIds); List<Long> ids = nodeIds.subList(0, Math.min(nodeIds.size(), 100)); cache.get(ids); nodeIds.clear(); } } System.err.printf("%,d nodes added in %s\n", numNodes, sw.stop()); } private long caclDbSize() { final File repoDir = new File(tmpFolder.getRoot(), ".geogig"); final AtomicLong sizeContainer = new AtomicLong(); repoDir.listFiles(new FileFilter() { @Override public boolean accept(final File file) { if (file.isFile()) { long length = file.length(); sizeContainer.addAndGet(length); } else { file.listFiles(this); } return true; } }); return sizeContainer.get(); } private OSMCoordinateSequence coord(double x, double y) { return new OSMCoordinateSequence(new Coordinate[] { new Coordinate(x, y) }); } }