Let APIUtils.needsSynchronization include NET, use a HashMap, add tests.

This commit is contained in:
asofold 2015-02-16 12:59:40 +01:00
parent 81d10a314c
commit 7ab21d9e72
2 changed files with 88 additions and 15 deletions

View File

@ -3,6 +3,7 @@ package fr.neatmonster.nocheatplus.hooks;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -18,28 +19,47 @@ public class APIUtils {
/** Only the children. */ /** Only the children. */
private static final Map<CheckType, CheckType[]> childrenMap = new HashMap<CheckType, CheckType[]>(); private static final Map<CheckType, CheckType[]> childrenMap = new HashMap<CheckType, CheckType[]>();
/** Check including children, for convenient iteration. */ /** Check including children, for convenient iteration. */
private static final Map<CheckType, CheckType[]> withChildrenMap = new HashMap<CheckType, CheckType[]>(); private static final Map<CheckType, CheckType[]> withChildrenMap = new HashMap<CheckType, CheckType[]>();
/** Checks/groups that might be run off the primary thread. */
private static final Set<CheckType> needSync = new HashSet<CheckType>();
static { static {
// Parent/children relations.
final Map<CheckType, Set<CheckType>> map = new HashMap<CheckType, Set<CheckType>>(); final Map<CheckType, Set<CheckType>> map = new HashMap<CheckType, Set<CheckType>>();
for (final CheckType type : CheckType.values()) for (final CheckType type : CheckType.values())
map.put(type, new LinkedHashSet<CheckType>()); map.put(type, new LinkedHashSet<CheckType>());
for (final CheckType type : CheckType.values()){ for (final CheckType type : CheckType.values()){
if (type != CheckType.ALL) map.get(CheckType.ALL).add(type); if (type != CheckType.ALL) map.get(CheckType.ALL).add(type);
for (final CheckType other : CheckType.values()){ for (final CheckType other : CheckType.values()){
if (isParent(other, type)) map.get(other).add(type); if (isParent(other, type)) map.get(other).add(type);
} }
} }
for (final CheckType parent : map.keySet()){ for (final CheckType parent : map.keySet()){
final Set<CheckType> set = map.get(parent); final Set<CheckType> set = map.get(parent);
final CheckType[] a = new CheckType[set.size()]; final CheckType[] a = new CheckType[set.size()];
childrenMap.put(parent, set.toArray(a)); childrenMap.put(parent, set.toArray(a));
final CheckType[] aw = new CheckType[set.size() + 1]; final CheckType[] aw = new CheckType[set.size() + 1];
set.toArray(aw); set.toArray(aw);
aw[set.size()] = parent; aw[set.size()] = parent;
withChildrenMap.put(parent, aw); withChildrenMap.put(parent, aw);
}
// needSync: Note that tests use the same definitions.
for (final CheckType checkType : new CheckType[]{CheckType.CHAT, CheckType.NET}) {
needSync.add(checkType);
}
boolean added = true;
while (added) { // Just in case.
added = false;
for (final CheckType checkType: CheckType.values()) {
// Fill in needSync.
if (checkType.getParent() != null && !needSync.contains(checkType) && needSync.contains(checkType.getParent())) {
needSync.add(checkType);
added = true;
}
}
} }
} }
@ -54,7 +74,7 @@ public class APIUtils {
public static final Collection<CheckType> getChildren(final CheckType type) { public static final Collection<CheckType> getChildren(final CheckType type) {
return Arrays.asList(childrenMap.get(type)); return Arrays.asList(childrenMap.get(type));
} }
/** /**
* Return an unmodifiable collection of the given check type with children. Always returns a collection, does * Return an unmodifiable collection of the given check type with children. Always returns a collection, does
* contain the check type itself. * contain the check type itself.
@ -78,14 +98,20 @@ public class APIUtils {
* @return true, if is parent * @return true, if is parent
*/ */
public static final boolean isParent(final CheckType supposedParent, final CheckType supposedChild) { public static final boolean isParent(final CheckType supposedParent, final CheckType supposedChild) {
if (supposedParent == supposedChild) return false; if (supposedParent == supposedChild) {
else if (supposedParent == CheckType.ALL) return true; return false;
}
else if (supposedParent == CheckType.ALL) {
return true;
}
CheckType parent = supposedChild.getParent(); CheckType parent = supposedChild.getParent();
while (parent != null) while (parent != null)
if (parent == supposedParent) if (parent == supposedParent) {
return true; return true;
else }
else {
parent = parent.getParent(); parent = parent.getParent();
}
return false; return false;
} }
@ -99,7 +125,7 @@ public class APIUtils {
* @return true, if successful * @return true, if successful
*/ */
public static final boolean needsSynchronization(final CheckType type) { public static final boolean needsSynchronization(final CheckType type) {
return type == CheckType.CHAT || isParent(CheckType.CHAT, type); return needSync.contains(type);
} }
} }

View File

@ -0,0 +1,47 @@
package fr.neatmonster.nocheatplus;
import static org.junit.Assert.fail;
import org.junit.Test;
import fr.neatmonster.nocheatplus.checks.CheckType;
import fr.neatmonster.nocheatplus.hooks.APIUtils;
public class TestCheckType {
@Test
public void testIsParent() {
for (CheckType parent : CheckType.values()) {
if (parent != CheckType.ALL && !APIUtils.isParent(CheckType.ALL, parent)) {
fail("Expect ALL to be parent of " + parent + " .");
}
// Rough simplified check by naming.
String parentName = parent.getName();
for (final CheckType child : CheckType.values()) {
if (child == parent) {
if (APIUtils.isParent(parent, child)) {
fail("Check can't be parent of itself: " + parent);
}
// Ignore otherwise.
continue;
}
String childName = child.getName();
if (childName.startsWith(parentName) && childName.charAt(parentName.length()) == '.' && !APIUtils.isParent(parent, child)) {
fail("Expect " + parentName + " to be parent of " + childName + ".");
}
}
}
}
@Test
public void testNeedsSynchronization() {
for (CheckType parent : new CheckType[]{CheckType.CHAT, CheckType.NET}) {
for (CheckType type : CheckType.values()) {
if ((parent == type || APIUtils.isParent(parent, type)) && !APIUtils.needsSynchronization(type)) {
fail("Expect " + type + " to need synchronization, as it is child of " + parent);
}
}
}
}
}