1
0
Fork 0
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:
Florian Schmaus 2021-07-06 14:06:39 +02:00
parent b6c8754012
commit d33a5a23c3
3 changed files with 52 additions and 17 deletions

View file

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

View file

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

View file

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