mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-27 00:32:07 +01:00
[core] Introduce Builder.failOnUnknownStates() and unit tests
The previous approach of emitting a severe log message when a state (descriptor) was unknown was misleading. There are valid cases where some states are not known, if, for example, a module was explicitly disabled. Using Builder.failOnUnknownStates() in unit tests is far cleaner, as the existence of unknown states is tested in a controlled environment: one where are states are supposed to be known.
This commit is contained in:
parent
b6c8754012
commit
d33a5a23c3
3 changed files with 52 additions and 17 deletions
|
@ -54,7 +54,7 @@ public final class ModularXmppClientToServerConnectionConfiguration extends Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
initialStateDescriptorVertex = StateDescriptorGraph.constructStateDescriptorGraph(backwardEdgeStateDescriptors);
|
initialStateDescriptorVertex = StateDescriptorGraph.constructStateDescriptorGraph(backwardEdgeStateDescriptors, builder.failOnUnknownStates);
|
||||||
}
|
}
|
||||||
catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
||||||
| NoSuchMethodException | SecurityException e) {
|
| NoSuchMethodException | SecurityException e) {
|
||||||
|
@ -91,6 +91,8 @@ public final class ModularXmppClientToServerConnectionConfiguration extends Conn
|
||||||
|
|
||||||
private final Map<Class<? extends ModularXmppClientToServerConnectionModuleDescriptor>, ModularXmppClientToServerConnectionModuleDescriptor> modulesDescriptors = new HashMap<>();
|
private final Map<Class<? extends ModularXmppClientToServerConnectionModuleDescriptor>, ModularXmppClientToServerConnectionModuleDescriptor> modulesDescriptors = new HashMap<>();
|
||||||
|
|
||||||
|
private boolean failOnUnknownStates;
|
||||||
|
|
||||||
private Builder() {
|
private Builder() {
|
||||||
SmackConfiguration.addAllKnownModulesTo(this);
|
SmackConfiguration.addAllKnownModulesTo(this);
|
||||||
}
|
}
|
||||||
|
@ -163,6 +165,17 @@ public final class ModularXmppClientToServerConnectionConfiguration extends Conn
|
||||||
return getThis();
|
return getThis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fail if there are unknown states in Smack's state descriptor graph. This method is used mostly for testing
|
||||||
|
* the internals of Smack. Users can safely ignore it.
|
||||||
|
*
|
||||||
|
* @return a reference to this builder.
|
||||||
|
*/
|
||||||
|
public Builder failOnUnknownStates() {
|
||||||
|
failOnUnknownStates = true;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Builder getThis() {
|
protected Builder getThis() {
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2018 Florian Schmaus
|
* Copyright 2018-2021 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -28,7 +28,6 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.DisconnectedStateDescriptor;
|
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection.DisconnectedStateDescriptor;
|
||||||
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
|
||||||
|
@ -47,8 +46,6 @@ import org.jivesoftware.smack.util.MultiMap;
|
||||||
*/
|
*/
|
||||||
public class StateDescriptorGraph {
|
public class StateDescriptorGraph {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(StateDescriptorGraph.class.getName());
|
|
||||||
|
|
||||||
private static GraphVertex<StateDescriptor> addNewStateDescriptorGraphVertex(
|
private static GraphVertex<StateDescriptor> addNewStateDescriptorGraphVertex(
|
||||||
Class<? extends StateDescriptor> stateDescriptorClass,
|
Class<? extends StateDescriptor> stateDescriptorClass,
|
||||||
Map<Class<? extends StateDescriptor>, GraphVertex<StateDescriptor>> graphVertexes)
|
Map<Class<? extends StateDescriptor>, GraphVertex<StateDescriptor>> graphVertexes)
|
||||||
|
@ -102,7 +99,8 @@ public class StateDescriptorGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void handleStateDescriptorGraphVertex(GraphVertex<StateDescriptor> node,
|
private static void handleStateDescriptorGraphVertex(GraphVertex<StateDescriptor> node,
|
||||||
HandleStateDescriptorGraphVertexContext context)
|
HandleStateDescriptorGraphVertexContext context,
|
||||||
|
boolean failOnUnknownStates)
|
||||||
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
|
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
|
||||||
Class<? extends StateDescriptor> stateDescriptorClass = node.element.getClass();
|
Class<? extends StateDescriptor> stateDescriptorClass = node.element.getClass();
|
||||||
boolean alreadyHandled = context.recurseInto(stateDescriptorClass);
|
boolean alreadyHandled = context.recurseInto(stateDescriptorClass);
|
||||||
|
@ -126,7 +124,7 @@ public class StateDescriptorGraph {
|
||||||
case 1:
|
case 1:
|
||||||
GraphVertex<StateDescriptor> soleSuccessorNode = successorStateDescriptors.values().iterator().next();
|
GraphVertex<StateDescriptor> soleSuccessorNode = successorStateDescriptors.values().iterator().next();
|
||||||
node.addOutgoingEdge(soleSuccessorNode);
|
node.addOutgoingEdge(soleSuccessorNode);
|
||||||
handleStateDescriptorGraphVertex(soleSuccessorNode, context);
|
handleStateDescriptorGraphVertex(soleSuccessorNode, context, failOnUnknownStates);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,9 +142,8 @@ public class StateDescriptorGraph {
|
||||||
StateDescriptor successorStateDescriptor = successorStateDescriptorGraphNode.element;
|
StateDescriptor successorStateDescriptor = successorStateDescriptorGraphNode.element;
|
||||||
Class<? extends StateDescriptor> successorStateDescriptorClass = successorStateDescriptor.getClass();
|
Class<? extends StateDescriptor> successorStateDescriptorClass = successorStateDescriptor.getClass();
|
||||||
for (Class<? extends StateDescriptor> subordinateClass : successorStateDescriptor.getSubordinates()) {
|
for (Class<? extends StateDescriptor> subordinateClass : successorStateDescriptor.getSubordinates()) {
|
||||||
if (!successorClasses.contains(subordinateClass)) {
|
if (failOnUnknownStates && !successorClasses.contains(subordinateClass)) {
|
||||||
LOGGER.severe(successorStateDescriptor + " points to a subordinate '" + subordinateClass + "' which is not part of the successor set");
|
throw new IllegalStateException(successorStateDescriptor + " points to a subordinate '" + subordinateClass + "' which is not part of the successor set");
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphVertex<Class<? extends StateDescriptor>> superiorClassNode = lookupAndCreateIfRequired(
|
GraphVertex<Class<? extends StateDescriptor>> superiorClassNode = lookupAndCreateIfRequired(
|
||||||
|
@ -157,10 +154,9 @@ public class StateDescriptorGraph {
|
||||||
superiorClassNode.addOutgoingEdge(subordinateClassNode);
|
superiorClassNode.addOutgoingEdge(subordinateClassNode);
|
||||||
}
|
}
|
||||||
for (Class<? extends StateDescriptor> superiorClass : successorStateDescriptor.getSuperiors()) {
|
for (Class<? extends StateDescriptor> superiorClass : successorStateDescriptor.getSuperiors()) {
|
||||||
if (!successorClasses.contains(superiorClass)) {
|
if (failOnUnknownStates && !successorClasses.contains(superiorClass)) {
|
||||||
LOGGER.severe(successorStateDescriptor + " points to a superior '" + superiorClass
|
throw new IllegalStateException(successorStateDescriptor + " points to a superior '" + superiorClass
|
||||||
+ "' which is not part of the successor set");
|
+ "' which is not part of the successor set");
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphVertex<Class<? extends StateDescriptor>> subordinateClassNode = lookupAndCreateIfRequired(
|
GraphVertex<Class<? extends StateDescriptor>> subordinateClassNode = lookupAndCreateIfRequired(
|
||||||
|
@ -193,11 +189,13 @@ public class StateDescriptorGraph {
|
||||||
node.addOutgoingEdge(successorVertex);
|
node.addOutgoingEdge(successorVertex);
|
||||||
|
|
||||||
// Recurse further.
|
// Recurse further.
|
||||||
handleStateDescriptorGraphVertex(successorVertex, context);
|
handleStateDescriptorGraphVertex(successorVertex, context, failOnUnknownStates);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GraphVertex<StateDescriptor> constructStateDescriptorGraph(Set<Class<? extends StateDescriptor>> backwardEdgeStateDescriptors)
|
public static GraphVertex<StateDescriptor> constructStateDescriptorGraph(
|
||||||
|
Set<Class<? extends StateDescriptor>> backwardEdgeStateDescriptors,
|
||||||
|
boolean failOnUnknownStates)
|
||||||
throws InstantiationException, IllegalAccessException, IllegalArgumentException,
|
throws InstantiationException, IllegalAccessException, IllegalArgumentException,
|
||||||
InvocationTargetException, NoSuchMethodException, SecurityException {
|
InvocationTargetException, NoSuchMethodException, SecurityException {
|
||||||
Map<Class<? extends StateDescriptor>, GraphVertex<StateDescriptor>> graphVertexes = new HashMap<>();
|
Map<Class<? extends StateDescriptor>, GraphVertex<StateDescriptor>> graphVertexes = new HashMap<>();
|
||||||
|
@ -219,7 +217,7 @@ public class StateDescriptorGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleStateDescriptorGraphVertexContext context = new HandleStateDescriptorGraphVertexContext(graphVertexes, inferredForwardEdges);
|
HandleStateDescriptorGraphVertexContext context = new HandleStateDescriptorGraphVertexContext(graphVertexes, inferredForwardEdges);
|
||||||
handleStateDescriptorGraphVertex(initialNode, context);
|
handleStateDescriptorGraphVertex(initialNode, context, failOnUnknownStates);
|
||||||
|
|
||||||
return initialNode;
|
return initialNode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2020 Florian Schmaus
|
* Copyright 2020-2021 Florian Schmaus
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
package org.jivesoftware.smack.full;
|
package org.jivesoftware.smack.full;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
@ -25,6 +26,8 @@ import java.io.StringWriter;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration;
|
||||||
|
import org.jivesoftware.smack.compression.CompressionModuleDescriptor;
|
||||||
import org.jivesoftware.smack.util.EqualsUtil;
|
import org.jivesoftware.smack.util.EqualsUtil;
|
||||||
import org.jivesoftware.smack.util.HashCode;
|
import org.jivesoftware.smack.util.HashCode;
|
||||||
|
|
||||||
|
@ -34,6 +37,7 @@ import org.jgrapht.graph.DirectedPseudograph;
|
||||||
import org.jgrapht.io.DOTImporter;
|
import org.jgrapht.io.DOTImporter;
|
||||||
import org.jgrapht.io.ImportException;
|
import org.jgrapht.io.ImportException;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.jxmpp.stringprep.XmppStringprepException;
|
||||||
|
|
||||||
public class ModularXmppClientToServerConnectionStateGraphTest {
|
public class ModularXmppClientToServerConnectionStateGraphTest {
|
||||||
|
|
||||||
|
@ -80,4 +84,24 @@ public class ModularXmppClientToServerConnectionStateGraphTest {
|
||||||
assertEquals(expectedStateGraph, currentStateGraph);
|
assertEquals(expectedStateGraph, currentStateGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoUnknownStates() throws XmppStringprepException {
|
||||||
|
ModularXmppClientToServerConnectionConfiguration.builder()
|
||||||
|
.setUsernameAndPassword("user", "password")
|
||||||
|
.setXmppDomain("example.org")
|
||||||
|
.failOnUnknownStates() // This is the actual option that enableds this test.
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void throwsOnUnknownStates() throws XmppStringprepException {
|
||||||
|
assertThrows(IllegalStateException.class, () ->
|
||||||
|
ModularXmppClientToServerConnectionConfiguration.builder()
|
||||||
|
.setUsernameAndPassword("user", "password")
|
||||||
|
.setXmppDomain("example.org")
|
||||||
|
.removeModule(CompressionModuleDescriptor.class)
|
||||||
|
.failOnUnknownStates() // This is the actual option that enableds this test.
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue