Skip to content

Commit 6e0c4ac

Browse files
test: Add tests for without cache and new parse()
1 parent 3ba556c commit 6e0c4ac

3 files changed

Lines changed: 179 additions & 4 deletions

File tree

src/main/java/net/marcellperger/first/util/Util.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.function.Function;
1010
import java.util.function.Predicate;
1111

12+
@SuppressWarnings("unused")
1213
public class Util {
1314
protected Util() {}
1415

@@ -125,6 +126,10 @@ public static void expectOrFail(boolean cond) {
125126
public static @NotNull NoSuchMethodError excToError(@NotNull NoSuchMethodException exc) {
126127
return withCause(new NoSuchMethodError(exc.getMessage()), exc.getCause());
127128
}
129+
@Contract("_ -> new")
130+
public static @NotNull NoSuchFieldError excToError(@NotNull NoSuchFieldException exc) {
131+
return withCause(new NoSuchFieldError(exc.getMessage()), exc.getCause());
132+
}
128133

129134
@Contract("_ -> new")
130135
public static @NotNull UncheckedException intoUnchecked(Exception exc) {
@@ -151,4 +156,13 @@ public static void realAssert(boolean b, Throwable cause) {
151156
public static void realAssert(boolean b, String msg, Throwable cause) {
152157
if(!b) throw new AssertionError(msg, cause);
153158
}
159+
160+
@Contract(value = "_, _ -> new", pure = true)
161+
public static @NotNull <K, V> Map.Entry<K, V> makeEntryMut(K k, V v) {
162+
return new AbstractMap.SimpleEntry<>(k, v);
163+
}
164+
@Contract(value = "_, _ -> new", pure = true)
165+
public static @NotNull <K, V> Map.Entry<K, V> makeEntry(K k, V v) {
166+
return new AbstractMap.SimpleImmutableEntry<>(k, v);
167+
}
154168
}

src/main/java/net/marcellperger/first/util/UtilCollectors.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
import org.jetbrains.annotations.Contract;
44
import org.jetbrains.annotations.NotNull;
55

6+
import java.util.Map;
7+
import java.util.Map.Entry;
68
import java.util.stream.Collector;
79
import java.util.stream.Collectors;
810

11+
@SuppressWarnings("unused")
912
public class UtilCollectors {
1013
protected UtilCollectors() {} // prevent instantiation, allow subclassing
1114

@@ -17,4 +20,11 @@ protected UtilCollectors() {} // prevent instantiation, allow subclassing
1720
new CollectionSizeException("UtilCollectors.singleItem expected a single item")).getFirst()
1821
);
1922
}
23+
24+
public static <K, V> @NotNull Collector<Entry<K, V>, ?, Map<K, V>> entriesToUnmodifiableMap() {
25+
return Collectors.toUnmodifiableMap(Entry::getKey, Entry::getValue);
26+
}
27+
public static <K, V> @NotNull Collector<Entry<K, V>, ?, Map<K, V>> entriesToMap() {
28+
return Collectors.toMap(Entry::getKey, Entry::getValue);
29+
}
2030
}

src/test/java/net/marcellperger/first/parser/ParserTest.java

Lines changed: 155 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,55 @@
11
package net.marcellperger.first.parser;
22

33
import net.marcellperger.first.*;
4+
import net.marcellperger.first.util.Util;
5+
import net.marcellperger.first.util.UtilCollectors;
6+
import org.jetbrains.annotations.NotNull;
47
import org.junit.jupiter.api.Test;
58

9+
import java.lang.reflect.Field;
10+
import java.util.Arrays;
11+
import java.util.HashMap;
12+
import java.util.Map;
13+
import java.util.Map.Entry;
14+
615
import static org.junit.jupiter.api.Assertions.*;
716

817
class ParserTest {
18+
boolean nocache = false;
19+
920
void assertInfixParsesTo(String src, int level, MathSymbol expected) {
21+
// Expects it to be full parse
22+
if (!nocache) assertInfixParsesToInner(src, level, expected);
23+
else try (WithSuppressingCache ignored = WithSuppressingCache.start()) {
24+
assertInfixParsesToInner(src, level, expected);
25+
}
26+
}
27+
28+
void assertInfixParsesTo(Pair<MathSymbol, String> exprPair, int level) {
29+
assertInfixParsesTo(exprPair.right(), level, exprPair.left());
30+
}
31+
32+
void assertParsesToInner(String src, MathSymbol expected) {
33+
// Expects it to be full parse
34+
Parser p = new Parser(src);
35+
MathSymbol actual = assertDoesNotThrow(p::parse);
36+
// No need to check EOF as parse() checks that it consumed the whole string
37+
assertEquals(expected, actual);
38+
}
39+
40+
void assertParsesTo(String src, MathSymbol expected) {
41+
// Expects it to be full parse
42+
if (!nocache) assertParsesToInner(src, expected);
43+
else try (WithSuppressingCache ignored = WithSuppressingCache.start()) {
44+
assertParsesToInner(src, expected);
45+
}
46+
}
47+
48+
void assertParsesTo(Pair<MathSymbol, String> exprPair) {
49+
assertParsesTo(exprPair.right(), exprPair.left());
50+
}
51+
52+
void assertInfixParsesToInner(String src, int level, MathSymbol expected) {
1053
// Expects it to be full parse
1154
Parser p = new Parser(src);
1255
MathSymbol actual = assertDoesNotThrow(() -> p.parseInfixPrecedenceLevel(level));
@@ -15,10 +58,6 @@ void assertInfixParsesTo(String src, int level, MathSymbol expected) {
1558
assertEquals(expected, actual);
1659
}
1760

18-
void assertInfixParsesTo(Pair<MathSymbol, String> exprPair, int level) {
19-
assertInfixParsesTo(exprPair.right(), level, exprPair.left());
20-
}
21-
2261
@Test
2362
void parseInfixPrecedenceLevel() {
2463
assertInfixParsesTo("1.0/2.0", 1,
@@ -44,4 +83,116 @@ void parseInfixPrecedenceLevel() {
4483
assertInfixParsesTo(".9/2./3.3", 2,
4584
new DivOperation(new DivOperation(new BasicDoubleSymbol(.9), new BasicDoubleSymbol(2.)), new BasicDoubleSymbol(3.3)));
4685
}
86+
87+
@Test
88+
void parseInfixPrecedenceLevel_nocache() {
89+
boolean origNocache = nocache;
90+
nocache = true;
91+
try {
92+
parseInfixPrecedenceLevel();
93+
} finally {
94+
nocache = origNocache;
95+
}
96+
}
97+
98+
@Test
99+
void parsePrecedenceLevel() {
100+
assertParsesTo("1.0/2.0",
101+
new DivOperation(new BasicDoubleSymbol(1.0), new BasicDoubleSymbol(2.0)));
102+
assertParsesTo(".3*6.",
103+
new MulOperation(new BasicDoubleSymbol(.3), new BasicDoubleSymbol(6.)));
104+
assertParsesTo("2.1*5.3+1.1",
105+
new AddOperation(new MulOperation(new BasicDoubleSymbol(2.1), new BasicDoubleSymbol(5.3)), new BasicDoubleSymbol(1.1)));
106+
assertParsesTo("0.9-2.1/.3",
107+
new SubOperation(new BasicDoubleSymbol(0.9), new DivOperation(new BasicDoubleSymbol(2.1), new BasicDoubleSymbol(.3))));
108+
assertParsesTo("(2.2+1.1)+3.7",
109+
new AddOperation(new AddOperation(new BasicDoubleSymbol(2.2), new BasicDoubleSymbol(1.1)), new BasicDoubleSymbol(3.7)));
110+
assertParsesTo(CommonData.getBigData1_groupingParens());
111+
assertParsesTo(CommonData.getBigData2_groupingParens());
112+
assertParsesTo("2.2+1.1+3.7",
113+
new AddOperation(new AddOperation(new BasicDoubleSymbol(2.2), new BasicDoubleSymbol(1.1)), new BasicDoubleSymbol(3.7)));
114+
assertParsesTo("2.2+1.1+3.7+0.2",
115+
new AddOperation(new AddOperation(new AddOperation(new BasicDoubleSymbol(2.2), new BasicDoubleSymbol(1.1)), new BasicDoubleSymbol(3.7)), new BasicDoubleSymbol(0.2)));
116+
assertParsesTo(CommonData.getBigData1_minimumParens());
117+
assertParsesTo(CommonData.getBigData2_minimumParens());
118+
assertParsesTo(".9/2./3.3",
119+
new DivOperation(new DivOperation(new BasicDoubleSymbol(.9), new BasicDoubleSymbol(2.)), new BasicDoubleSymbol(3.3)));
120+
assertParsesTo(".9/2./3.3",
121+
new DivOperation(new DivOperation(new BasicDoubleSymbol(.9), new BasicDoubleSymbol(2.)), new BasicDoubleSymbol(3.3)));
122+
}
123+
124+
@Test
125+
void parsePrecedenceLevel_nocache() {
126+
boolean origNocache = nocache;
127+
nocache = true;
128+
try {
129+
parsePrecedenceLevel();
130+
} finally {
131+
nocache = origNocache;
132+
}
133+
}
134+
135+
static class WithSuppressingCache implements AutoCloseable {
136+
public static WithSuppressingCache getInstance() {
137+
return INSTANCE;
138+
}
139+
protected static WithSuppressingCache INSTANCE = new WithSuppressingCache();
140+
protected WithSuppressingCache() {}
141+
142+
WithSuppressingCache startInstance() {
143+
clearCache();
144+
return this;
145+
}
146+
static WithSuppressingCache start() {
147+
return getInstance().startInstance();
148+
}
149+
WithSuppressingCache stop() {
150+
restoreCache();
151+
return this;
152+
}
153+
154+
@Override
155+
public void close() {
156+
restoreCache();
157+
}
158+
159+
160+
Map<SymbolInfo, BinOpBiConstructor<?>> origCache = new HashMap<>();
161+
void clearCache() {
162+
Field cacheField = getBiConstructorCache();
163+
164+
origCache = Arrays.stream(SymbolInfo.values()).<Entry<SymbolInfo, BinOpBiConstructor<?>>>map(sym -> {
165+
try {
166+
BinOpBiConstructor<?> cachedValue = (BinOpBiConstructor<?>)cacheField.get(sym);
167+
cacheField.set(sym, null);
168+
return Util.makeEntry(sym, cachedValue);
169+
} catch (IllegalAccessException e) {
170+
throw Util.excToError(e);
171+
}
172+
}).collect(UtilCollectors.entriesToUnmodifiableMap());
173+
}
174+
175+
void restoreCache() {
176+
Field cacheField = getBiConstructorCache();
177+
origCache.forEach((key, value) -> {
178+
try {
179+
cacheField.set(key, value);
180+
} catch (IllegalAccessException ex) {
181+
throw Util.excToError(ex);
182+
}
183+
});
184+
}
185+
186+
@NotNull
187+
private static Field getBiConstructorCache() {
188+
Field cacheField;
189+
try {
190+
cacheField = SymbolInfo.class.getDeclaredField("biConstructorCache");
191+
} catch (NoSuchFieldException e) {
192+
throw Util.excToError(e);
193+
}
194+
cacheField.setAccessible(true);
195+
return cacheField;
196+
}
197+
}
47198
}

0 commit comments

Comments
 (0)