several improvements

- bump of sbt verstion
- switch to Junit jupiter
- added LRU cache
This commit is contained in:
2020-11-21 10:09:08 +01:00
parent 6493a02569
commit 5ddb9e6cbf
13 changed files with 543 additions and 57 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
target
.idea

View File

@@ -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")

View File

@@ -1 +1 @@
sbt.version=1.3.1
sbt.version=1.4.1

5
project/build.scala Normal file
View File

@@ -0,0 +1,5 @@
object Versions {
val slf4j = "1.7.30"
val log4j = "2.13.2"
val lombok = "1.18.16"
}

View File

@@ -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")

View File

@@ -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;
}
}
}

View 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);
}
}
}

View File

@@ -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);

View File

@@ -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);
}
}
}

View 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());
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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());
}
}