several improvements
- bump of sbt verstion - switch to Junit jupiter - added LRU cache
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
target
|
||||
.idea
|
12
build.sbt
12
build.sbt
@@ -6,21 +6,21 @@ maintainer := "oggioni.walter@gmail.com"
|
||||
|
||||
version := "1.0"
|
||||
resolvers += Resolver.mavenLocal
|
||||
resolvers in ThisBuild += Resolver.jcenterRepo
|
||||
|
||||
git.useGitDescribe := true
|
||||
crossPaths := false
|
||||
|
||||
autoScalaLibrary := false
|
||||
|
||||
libraryDependencies += "org.projectlombok" % "lombok" % "1.18.8" % Provided
|
||||
libraryDependencies += "org.projectlombok" % "lombok" % Versions.lombok % Provided
|
||||
|
||||
libraryDependencies += "org.slf4j" % "slf4j-api" % "1.7.28"
|
||||
libraryDependencies += "org.slf4j" % "slf4j-api" % Versions.slf4j
|
||||
|
||||
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test
|
||||
libraryDependencies += "org.apache.logging.log4j" % "log4j-core" % "2.12.1" % Test
|
||||
libraryDependencies += "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.12.1" % Test
|
||||
libraryDependencies += "net.aichler" % "jupiter-interface" % JupiterKeys.jupiterVersion.value % Test
|
||||
libraryDependencies += "org.junit.jupiter" % "junit-jupiter-params" % JupiterKeys.junitJupiterVersion.value % Test
|
||||
|
||||
javacOptions in (Compile, compile) ++= Seq("-source", "1.8", "-target", "1.8")
|
||||
javacOptions in (Compile, compile) ++= Seq("--release", "8")
|
||||
|
||||
Compile / packageBin / packageOptions +=
|
||||
Package.ManifestAttributes("Automatic-Module-Name" -> "net.woggioni.jwo")
|
||||
|
@@ -1 +1 @@
|
||||
sbt.version=1.3.1
|
||||
sbt.version=1.4.1
|
||||
|
5
project/build.scala
Normal file
5
project/build.scala
Normal file
@@ -0,0 +1,5 @@
|
||||
object Versions {
|
||||
val slf4j = "1.7.30"
|
||||
val log4j = "2.13.2"
|
||||
val lombok = "1.18.16"
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
resolvers += Resolver.jcenterRepo
|
||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.9.3")
|
||||
addSbtPlugin("de.heikoseeberger" % "sbt-header" % "4.1.0")
|
||||
addSbtPlugin("net.aichler" % "sbt-jupiter-interface" % "0.8.4-SNAPSHOT")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.6")
|
||||
addSbtPlugin("com.thoughtworks.sbt" % "delombokjavadoc" % "1.0.0+66-8fbcf18c")
|
||||
|
||||
|
@@ -384,4 +384,13 @@ public class JWO {
|
||||
new Tuple2<>(fileName.substring(0, index), fileName.substring(index)));
|
||||
}
|
||||
}
|
||||
|
||||
public static <T, U> T dynamicCast(U obj, Class<T> cls) {
|
||||
if(obj == null) return null;
|
||||
else if(cls.isInstance(obj.getClass())) {
|
||||
return (T) obj;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
297
src/main/java/net/woggioni/jwo/cache/LruCache.java
vendored
Normal file
297
src/main/java/net/woggioni/jwo/cache/LruCache.java
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
package net.woggioni.jwo.cache;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.woggioni.jwo.Chronometer;
|
||||
import net.woggioni.jwo.CollectionUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class LruCache<K, V> implements Map<K, V> {
|
||||
private final Map<K, V> linkedHashMap;
|
||||
private final Object mtx;
|
||||
private final int maxSize;
|
||||
private final Function<? super K, ? extends V> loader;
|
||||
private final Map<K, V> fallback;
|
||||
|
||||
@Getter
|
||||
private Stats stats = new Stats();
|
||||
|
||||
private final Chronometer chronometer = new Chronometer();
|
||||
|
||||
public LruCache(int maxSize) {
|
||||
this(maxSize, false);
|
||||
}
|
||||
|
||||
public LruCache(int maxSize, boolean threadSafe) {
|
||||
this(maxSize, threadSafe, null, null);
|
||||
}
|
||||
|
||||
public LruCache(int maxSize, boolean threadSafe, Function<? super K, ? extends V> loader) {
|
||||
this(maxSize, threadSafe, loader, null);
|
||||
}
|
||||
|
||||
public LruCache(int maxSize, boolean threadSafe, Function<? super K, ? extends V> loader, Map<K,V> fallback) {
|
||||
this.linkedHashMap = new LinkedHashMap<>();
|
||||
this.mtx = threadSafe ? linkedHashMap : null;
|
||||
this.maxSize = maxSize;
|
||||
if (loader != null) {
|
||||
if(fallback == null) {
|
||||
this.loader = (K key) -> {
|
||||
this.stats.miss++;
|
||||
return loader.apply(key);
|
||||
};
|
||||
} else {
|
||||
this.loader = (K key) -> {
|
||||
this.stats.miss++;
|
||||
V value = fallback.get(key);
|
||||
if (value == null) {
|
||||
return loader.apply(key);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
}
|
||||
} else {
|
||||
this.loader = null;
|
||||
}
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
private <T> T withMutex(Supplier<T> supplier) {
|
||||
if(mtx != null) {
|
||||
synchronized (mtx) {
|
||||
return supplier.get();
|
||||
}
|
||||
} else {
|
||||
return supplier.get();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
if(fallback == null) {
|
||||
return withMutex(linkedHashMap::size);
|
||||
} else {
|
||||
return withMutex(fallback::size);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return withMutex(linkedHashMap::isEmpty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
K k;
|
||||
try {
|
||||
k = (K) key;
|
||||
} catch (ClassCastException cce) {
|
||||
return false;
|
||||
}
|
||||
return withMutex(() -> {
|
||||
boolean result = linkedHashMap.containsKey(key);
|
||||
if(!result && fallback != null) {
|
||||
result = fallback.containsKey(key);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return withMutex(() -> {
|
||||
boolean result = linkedHashMap.containsValue(value);
|
||||
if(!result && fallback != null) {
|
||||
result = fallback.containsValue(value);
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private V getOrFallback(K key) {
|
||||
V value = linkedHashMap.get(key);
|
||||
if(value == null) {
|
||||
stats.miss++;
|
||||
if (fallback != null) {
|
||||
value = fallback.get(key);
|
||||
if (value != null) {
|
||||
linkedHashMap.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
K k;
|
||||
try {
|
||||
k = (K) key;
|
||||
} catch (ClassCastException cce) {
|
||||
return null;
|
||||
}
|
||||
return withMutex(() -> {
|
||||
stats.calls++;
|
||||
if (loader != null) {
|
||||
try {
|
||||
chronometer.reset();
|
||||
V result = getOrFallback(k);
|
||||
if (result == null) {
|
||||
V newValue;
|
||||
if ((newValue = loader.apply(k)) != null) {
|
||||
put(k, newValue);
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
stats.loadingTime += chronometer.elapsed();
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
stats.exceptions++;
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
return getOrFallback(k);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K k, V v) {
|
||||
return withMutex(() -> {
|
||||
if (linkedHashMap.size() == maxSize) {
|
||||
Iterator<Entry<K, V>> it = linkedHashMap.entrySet().iterator();
|
||||
if (it.hasNext()) {
|
||||
Map.Entry<K, V> entry = it.next();
|
||||
remove(entry.getKey());
|
||||
stats.evictions++;
|
||||
}
|
||||
}
|
||||
if(fallback != null) {
|
||||
fallback.put(k, v);
|
||||
}
|
||||
linkedHashMap.put(k, v);
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
return withMutex( () -> {
|
||||
if(fallback != null) {
|
||||
V result = fallback.remove(key);
|
||||
if(result != null) {
|
||||
linkedHashMap.remove(key);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return linkedHashMap.remove(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> map) {
|
||||
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
withMutex(() -> {
|
||||
linkedHashMap.clear();
|
||||
if(fallback != null) {
|
||||
fallback.clear();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return withMutex(() -> {
|
||||
if(fallback == null) {
|
||||
return linkedHashMap.keySet();
|
||||
} else {
|
||||
return Stream.concat(
|
||||
linkedHashMap.keySet().stream(), fallback.keySet().stream()
|
||||
).collect(CollectionUtils.toUnmodifiableSet());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return withMutex(() -> {
|
||||
if(fallback == null) {
|
||||
return linkedHashMap.values();
|
||||
} else {
|
||||
return Stream.concat(
|
||||
linkedHashMap.values().stream(), fallback.values().stream()
|
||||
).collect(CollectionUtils.toUnmodifiableList());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return withMutex(() -> {
|
||||
if(fallback == null) {
|
||||
return linkedHashMap.entrySet();
|
||||
} else {
|
||||
return Stream.concat(
|
||||
linkedHashMap.entrySet().stream(), fallback.entrySet().stream()
|
||||
).collect(CollectionUtils.toUnmodifiableSet());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Stats resetStats() {
|
||||
return withMutex(() -> {
|
||||
stats = new Stats();
|
||||
return stats;
|
||||
});
|
||||
}
|
||||
|
||||
@Getter
|
||||
public class Stats {
|
||||
|
||||
private int miss;
|
||||
|
||||
private int calls;
|
||||
|
||||
private int exceptions;
|
||||
|
||||
private int evictions;
|
||||
|
||||
private long loadingTime;
|
||||
|
||||
public int getSize() {
|
||||
return LruCache.this.size();
|
||||
}
|
||||
|
||||
public float getHitRate() {
|
||||
return (calls - miss) / (float) calls;
|
||||
}
|
||||
|
||||
public float getAverageLoadingTime(Chronometer.UnitOfMeasure unitOfMeasure) {
|
||||
return (float) stats.loadingTime / calls / unitOfMeasure.nanoseconds_size;
|
||||
}
|
||||
|
||||
public float getTotalLoadingTime(Chronometer.UnitOfMeasure unitOfMeasure) {
|
||||
return (float) stats.loadingTime / unitOfMeasure.nanoseconds_size;
|
||||
}
|
||||
|
||||
public float getAverageLoadingTime() {
|
||||
return getAverageLoadingTime(Chronometer.UnitOfMeasure.SECONDS);
|
||||
}
|
||||
|
||||
public float getTotalLoadingTime() {
|
||||
return getAverageLoadingTime(Chronometer.UnitOfMeasure.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,8 +1,9 @@
|
||||
package net.woggioni.jwo;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
@@ -20,22 +21,24 @@ public class JWOTest {
|
||||
if (n > 3) return Optional.of(n);
|
||||
else return Optional.empty();
|
||||
}).collect(Collectors.toList());
|
||||
Assert.assertEquals(Collections.singletonList(4), l);
|
||||
Assertions.assertEquals(Collections.singletonList(4), l);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optional2StreamTest() {
|
||||
Integer integer = 3;
|
||||
Optional<Integer> s = Optional.of(integer);
|
||||
JWO.optional2Stream(s).forEach(n -> Assert.assertEquals(integer, n));
|
||||
JWO.optional2Stream(s).forEach(n -> Assertions.assertEquals(integer, n));
|
||||
s = Optional.empty();
|
||||
JWO.optional2Stream(s).forEach(n -> Assert.fail());
|
||||
JWO.optional2Stream(s).forEach(n -> Assertions.fail(
|
||||
"Stream should have been empty and this piece of code never executed")
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testRenderTemplate() {
|
||||
Map valuesMap = new HashMap<String, String>();
|
||||
Map<String, Object> valuesMap = new HashMap<>();
|
||||
valuesMap.put("author", "John Doe");
|
||||
valuesMap.put("date", "2020-03-25 16:22");
|
||||
valuesMap.put("adjective", "simple");
|
||||
@@ -43,18 +46,18 @@ public class JWOTest {
|
||||
try (Reader reader = new InputStreamReader(
|
||||
JWOTest.class.getResourceAsStream("/render_template_test.txt"))) {
|
||||
String rendered = JWO.renderTemplate(reader, valuesMap);
|
||||
Assert.assertEquals(expected, rendered);
|
||||
Assertions.assertEquals(expected, rendered);
|
||||
}
|
||||
try (Reader reader = new InputStreamReader(
|
||||
JWOTest.class.getResourceAsStream("/render_template_test.txt"))) {
|
||||
String rendered = JWO.renderTemplate(JWO.readAll(reader), valuesMap);
|
||||
Assert.assertEquals(expected, rendered);
|
||||
Assertions.assertEquals(expected, rendered);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String renderTemplateNaive(String template, Map<String, Object> valuesMap){
|
||||
StringBuffer formatter = new StringBuffer(template);
|
||||
StringBuilder formatter = new StringBuilder(template);
|
||||
Object absent = new Object();
|
||||
|
||||
Matcher matcher = Pattern.compile("\\$\\{(\\w+)}").matcher(template);
|
||||
|
@@ -1,8 +1,8 @@
|
||||
package net.woggioni.jwo;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -21,7 +21,7 @@ public class Leb128Test {
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
Leb128.Leb128Decoder decoder = new Leb128.Leb128Decoder(new ByteArrayInputStream(bytes));
|
||||
numbers.forEach(n -> Assert.assertEquals((long) n, decoder.decode()));
|
||||
numbers.forEach(n -> Assertions.assertEquals((long) n, decoder.decode()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -36,14 +36,14 @@ public class Leb128Test {
|
||||
byte[] bytes = baos.toByteArray();
|
||||
|
||||
Leb128.Leb128Decoder decoder = new Leb128.Leb128Decoder(new ByteArrayInputStream(bytes));
|
||||
numbers.forEach(n -> Assert.assertEquals(n, decoder.decodeDouble(), 0.0));
|
||||
numbers.forEach(n -> Assertions.assertEquals(n, decoder.decodeDouble(), 0.0));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void reverseTest() {
|
||||
long n = 101325;
|
||||
Assert.assertEquals(n, Leb128.reverse(Leb128.reverse(n)));
|
||||
Assertions.assertEquals(n, Leb128.reverse(Leb128.reverse(n)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -55,7 +55,7 @@ public class Leb128Test {
|
||||
try(ByteArrayOutputStream os = new ByteArrayOutputStream()) {
|
||||
Leb128.encode(os, reverse);
|
||||
byte[] bytes = os.toByteArray();
|
||||
Assert.assertEquals(3, bytes.length);
|
||||
Assertions.assertEquals(3, bytes.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
178
src/test/java/net/woggioni/jwo/cache/LruCacheTest.java
vendored
Normal file
178
src/test/java/net/woggioni/jwo/cache/LruCacheTest.java
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
package net.woggioni.jwo.cache;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.jwo.JWO;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
@EqualsAndHashCode
|
||||
class RandomObject implements Serializable {
|
||||
@EqualsAndHashCode.Include
|
||||
UUID uuid = UUID.randomUUID();
|
||||
|
||||
String name = uuid.toString();
|
||||
|
||||
String md5 = md5(name);
|
||||
|
||||
String md5half1 = md5.substring(0, 16);
|
||||
|
||||
String md5half2 = md5.substring(16);
|
||||
|
||||
@SneakyThrows
|
||||
private String md5(String source) {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
md.update(source.getBytes());
|
||||
byte[] digest = md.digest();
|
||||
return JWO.bytesToHex(digest);
|
||||
}
|
||||
}
|
||||
|
||||
public class LruCacheTest {
|
||||
|
||||
private static final int CACHE_MAX_SIZE = 50;
|
||||
private static final int NUMBER_OF_ENTRIES = 100;
|
||||
|
||||
int loaderInvocations = 0;
|
||||
List<RandomObject> objects;
|
||||
|
||||
LruCache<String, RandomObject> lruCache;
|
||||
|
||||
@AfterEach
|
||||
public void teardown() {
|
||||
lruCache.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheWithoutFallbackAndLoader() {
|
||||
lruCache = new LruCache<>(CACHE_MAX_SIZE, true);
|
||||
objects = new ArrayList<>();
|
||||
for (int i = 0; i < NUMBER_OF_ENTRIES; i++) {
|
||||
RandomObject randomObject = new RandomObject();
|
||||
lruCache.put(randomObject.name, randomObject);
|
||||
objects.add(randomObject);
|
||||
}
|
||||
|
||||
//Since NUMBER_OF_ENTRIES > CACHE_MAX_SIZE the cache size should have reached its maximum
|
||||
Assertions.assertEquals(CACHE_MAX_SIZE, lruCache.size());
|
||||
|
||||
// The cache should not contain any of the first NUMBER_OF_ENTRIES - CACHE_MAX_SIZE elements (they should have been evicted)
|
||||
objects.stream().limit(NUMBER_OF_ENTRIES - CACHE_MAX_SIZE)
|
||||
.forEach(value -> Assertions.assertFalse(lruCache.containsKey(value.name)));
|
||||
|
||||
// The cache should contain all the CACHE_MAX_SIZE elements that were last inserted
|
||||
objects.stream().skip(CACHE_MAX_SIZE)
|
||||
.peek(value -> Assertions.assertTrue(lruCache.containsKey(value.name)))
|
||||
.forEach(value -> Assertions.assertEquals(value, lruCache.get(value.name)));
|
||||
|
||||
//Removing the first inserted element should be a no-op since it should have been already evicted
|
||||
RandomObject randomObject = objects.get(0);
|
||||
Assertions.assertNull(lruCache.remove(randomObject.name));
|
||||
Assertions.assertFalse(lruCache.containsKey(randomObject.name));
|
||||
Assertions.assertEquals(CACHE_MAX_SIZE, lruCache.size());
|
||||
|
||||
//Removing the last inserted element should return it and decrease the cache size by 1
|
||||
randomObject = objects.get(objects.size() - 1);
|
||||
Assertions.assertEquals(randomObject, lruCache.remove(randomObject.name));
|
||||
Assertions.assertFalse(lruCache.containsKey(randomObject.name));
|
||||
Assertions.assertEquals(CACHE_MAX_SIZE - 1, lruCache.size());
|
||||
|
||||
//Clearing the cache should reduce its size to 0
|
||||
lruCache.clear();
|
||||
Assertions.assertEquals(0, lruCache.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheWithFallback() {
|
||||
Map<String, RandomObject> fallback = new HashMap<>();
|
||||
lruCache = new LruCache<>(CACHE_MAX_SIZE, true, null, fallback);
|
||||
objects = new ArrayList<>();
|
||||
for (int i = 0; i < NUMBER_OF_ENTRIES; i++) {
|
||||
RandomObject randomObject = new RandomObject();
|
||||
fallback.put(randomObject.name, randomObject);
|
||||
objects.add(randomObject);
|
||||
}
|
||||
|
||||
//The cache should contain all the elements that were inserted in the fallback map
|
||||
objects.forEach(value -> Assertions.assertTrue(lruCache.containsKey(value.name)));
|
||||
|
||||
//The cache size should be equal to the number of inserted elements
|
||||
Assertions.assertEquals(NUMBER_OF_ENTRIES, lruCache.size());
|
||||
|
||||
//Removing the first inserted element should return it and decrease the cache size by 1
|
||||
RandomObject randomObject = objects.get(0);
|
||||
Assertions.assertEquals(randomObject, lruCache.remove(randomObject.name));
|
||||
Assertions.assertFalse(lruCache.containsKey(randomObject.name));
|
||||
Assertions.assertEquals(NUMBER_OF_ENTRIES - 1, lruCache.size());
|
||||
|
||||
//Clearing the cache should reduce its size to 0
|
||||
lruCache.clear();
|
||||
Assertions.assertEquals(0, lruCache.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cacheWithLoader() {
|
||||
Function<String, RandomObject> loader = key -> {
|
||||
loaderInvocations++;
|
||||
return objects.stream().filter(o -> Objects.equals(key, o.name)).findFirst().get();
|
||||
};
|
||||
lruCache = new LruCache<>(CACHE_MAX_SIZE, true, loader);
|
||||
LruCache<String, RandomObject>.Stats stats = lruCache.getStats();
|
||||
objects = new ArrayList<>();
|
||||
for (int i = 0; i < NUMBER_OF_ENTRIES; i++) {
|
||||
RandomObject randomObject = new RandomObject();
|
||||
objects.add(randomObject);
|
||||
lruCache.put(randomObject.name, randomObject);
|
||||
}
|
||||
|
||||
//Since NUMBER_OF_ENTRIES > CACHE_MAX_SIZE the cache size should have reached its maximum
|
||||
Assertions.assertEquals(CACHE_MAX_SIZE, lruCache.size());
|
||||
|
||||
int evictionsAfterLoad = stats.getEvictions();
|
||||
int loaderInvocations = this.loaderInvocations;
|
||||
// The cache should contain all the CACHE_MAX_SIZE elements that were last inserted without loading any with the loader
|
||||
objects.stream().skip(CACHE_MAX_SIZE)
|
||||
.peek(value -> Assertions.assertTrue(lruCache.containsKey(value.name)))
|
||||
.forEach(value -> Assertions.assertEquals(value, lruCache.get(value.name)));
|
||||
// Since the entries were already present in the cache, no new invocation of the loader should have been performed,
|
||||
// nor any new eviction should have occurred
|
||||
Assertions.assertEquals(evictionsAfterLoad, stats.getEvictions());
|
||||
Assertions.assertEquals(loaderInvocations, this.loaderInvocations);
|
||||
|
||||
// The cache should not contain any of the first NUMBER_OF_ENTRIES - CACHE_MAX_SIZE elements
|
||||
int evictedElements = NUMBER_OF_ENTRIES - CACHE_MAX_SIZE;
|
||||
objects.stream().limit(evictedElements)
|
||||
.forEach(value -> Assertions.assertFalse(lruCache.containsKey(value.name)));
|
||||
|
||||
// Calling "get" for entries that were not present in the cache, should trigger a new invocation of the loader
|
||||
// for each of them, and, since the cache was full, an equal number of evictions should occurr
|
||||
evictionsAfterLoad = stats.getEvictions();
|
||||
loaderInvocations = this.loaderInvocations;
|
||||
objects.stream().limit(evictedElements)
|
||||
.forEach(value -> lruCache.get(value.name));
|
||||
Assertions.assertEquals(evictionsAfterLoad + evictedElements, stats.getEvictions());
|
||||
Assertions.assertEquals(loaderInvocations + evictedElements, this.loaderInvocations);
|
||||
|
||||
//Removing the last inserted element should be a no-op since it should have been already evicted
|
||||
RandomObject randomObject = objects.get(objects.size() - 1);
|
||||
Assertions.assertNull(lruCache.remove(randomObject.name));
|
||||
Assertions.assertFalse(lruCache.containsKey(randomObject.name));
|
||||
Assertions.assertEquals(CACHE_MAX_SIZE, lruCache.size());
|
||||
|
||||
//Removing the first inserted element should return it and decrease the cache size by 1
|
||||
randomObject = objects.get(0);
|
||||
Assertions.assertEquals(randomObject, lruCache.remove(randomObject.name));
|
||||
Assertions.assertFalse(lruCache.containsKey(randomObject.name));
|
||||
Assertions.assertEquals(CACHE_MAX_SIZE - 1, lruCache.size());
|
||||
|
||||
//Clearing the cache should reduce its size to 0
|
||||
lruCache.clear();
|
||||
Assertions.assertEquals(0, lruCache.size());
|
||||
}
|
||||
}
|
@@ -1,38 +1,31 @@
|
||||
package net.woggioni.jwo.compression;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.jwo.JWO;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@RunWith(Parameterized.class)
|
||||
public class CompressionOutputStreamTest {
|
||||
|
||||
@Parameterized.Parameters(name = "Format {0}, level {1}")
|
||||
public static Iterable<Object[]> data() {
|
||||
private static Stream<Arguments> testParameters() {
|
||||
return Arrays.stream(CompressionFormat.values())
|
||||
.filter(format -> JWO.which(format.executable).isPresent())
|
||||
.flatMap(v -> IntStream.of(1, 3, 5, 7, 9).mapToObj(l -> new Object[]{v, l}))
|
||||
.collect(Collectors.toList());
|
||||
.flatMap(v -> IntStream.of(1, 3, 5, 7, 9).mapToObj(l -> Arguments.of(v, l)));
|
||||
}
|
||||
|
||||
private final CompressionFormat compressionFormat;
|
||||
private final int level;
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void test() {
|
||||
@ParameterizedTest
|
||||
@MethodSource("testParameters")
|
||||
public void test(CompressionFormat compressionFormat, int level) {
|
||||
MessageDigest inputDigest = MessageDigest.getInstance("MD5");
|
||||
byte[] compressed;
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
@@ -55,6 +48,6 @@ public class CompressionOutputStreamTest {
|
||||
byte[] buffer = new byte[1024];
|
||||
while(is.read(buffer, 0, buffer.length) >= 0) {}
|
||||
}
|
||||
Assert.assertArrayEquals(inputDigest.digest(), outputDigest.digest());
|
||||
Assertions.assertArrayEquals(inputDigest.digest(), outputDigest.digest());
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,8 @@
|
||||
package net.woggioni.jwo.io;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.jwo.io.CircularBuffer;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -28,10 +27,8 @@ public class CircularBufferTest {
|
||||
} else {
|
||||
char c = (char) b;
|
||||
outputDigest.update((byte) b);
|
||||
System.out.print(c);
|
||||
}
|
||||
}
|
||||
System.out.println();
|
||||
Assert.assertArrayEquals(streamDigest.digest(), outputDigest.digest());
|
||||
Assertions.assertArrayEquals(streamDigest.digest(), outputDigest.digest());
|
||||
}
|
||||
}
|
||||
|
@@ -3,8 +3,8 @@ package net.woggioni.jwo.tree;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.woggioni.jwo.tuple.Tuple2;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
@@ -60,24 +60,24 @@ public class TreeWalkerTest {
|
||||
TreeNodeVisitor<Node, Void> nodeVisitor = new TreeNodeVisitor<Node, Void>() {
|
||||
@Override
|
||||
public VisitOutcome visitPre(List<StackContext<Node, Void>> stackContextList) {
|
||||
Assert.assertTrue(it_pre.hasNext());
|
||||
Assert.assertEquals(it_pre.next(),
|
||||
Assertions.assertTrue(it_pre.hasNext());
|
||||
Assertions.assertEquals(it_pre.next(),
|
||||
stackContextList.get(stackContextList.size() - 1).getNode().getId());
|
||||
return VisitOutcome.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPost(List<StackContext<Node, Void>> stackContextList) {
|
||||
Assert.assertTrue(it_post.hasNext());
|
||||
Assert.assertEquals(it_post.next(),
|
||||
Assertions.assertTrue(it_post.hasNext());
|
||||
Assertions.assertEquals(it_post.next(),
|
||||
stackContextList.get(stackContextList.size() - 1).getNode().getId());
|
||||
}
|
||||
};
|
||||
TreeWalker<Node, Void> walker =
|
||||
new TreeWalker<>(nodeVisitor);
|
||||
walker.walk(testNodeMap.get(1));
|
||||
Assert.assertFalse(it_pre.hasNext());
|
||||
Assert.assertFalse(it_post.hasNext());
|
||||
Assertions.assertFalse(it_pre.hasNext());
|
||||
Assertions.assertFalse(it_post.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -88,9 +88,9 @@ public class TreeWalkerTest {
|
||||
TreeNodeVisitor<Node, Void> linkVisitor = new TreeNodeVisitor<Node, Void>() {
|
||||
@Override
|
||||
public VisitOutcome visitPre(List<StackContext<Node, Void>> nodePath) {
|
||||
Assert.assertTrue(it.hasNext());
|
||||
Assertions.assertTrue(it.hasNext());
|
||||
Integer id = nodePath.get(nodePath.size() - 1).getNode().getId();
|
||||
Assert.assertEquals(it.next(), id);
|
||||
Assertions.assertEquals(it.next(), id);
|
||||
if(Objects.equals(4, nodePath.get(nodePath.size() - 1).getNode().getId())) {
|
||||
return VisitOutcome.SKIP;
|
||||
} else {
|
||||
@@ -101,6 +101,6 @@ public class TreeWalkerTest {
|
||||
TreeWalker<Node, Void> walker =
|
||||
new TreeWalker<>(linkVisitor);
|
||||
walker.walk(testNodeMap.get(1));
|
||||
Assert.assertFalse(it.hasNext());
|
||||
Assertions.assertFalse(it.hasNext());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user