package org.dcache.services.info.base; import com.google.common.collect.Ordering; import org.junit.Assert; import java.util.HashMap; import java.util.Map; /** * The TestStateExhibitor is a light-weight implementation of the * StateExhibitor interface. It can be preprogrammed to mimic a realistic * dCache state. Classes that implement the StateVisitor interface may visit * this state, allowing unit testing. * <p> * When using a StateTransition object, either when visiting or when updating * the metrics it is possible that the StateTransition contains an invalid * transition. If such an inconsistency is detected then * {@link Assert#fail} is called with an appropriate error message. */ public class TestStateExhibitor implements StateExhibitor, Cloneable { /** * Information about a specific Node (either a branch or a metric value) */ private static class Node { private final Map<String, Node> _children = new HashMap<>(); private final Map<String, String> _metadata = new HashMap<>(); private final StateValue _metricValue; public Node() { _metricValue = null; } public Node( StateValue metric) { _metricValue = metric; } public boolean isMetric() { return _metricValue != null; } /** * Obtain a child Node with corresponding name. If Node doesn't exist * it is created. If metric is null, any created node will be a * branch, otherwise it will be a metric node. * * @param childName * @param metric * @return */ private Node getOrCreateChild( String childName, StateValue metric) { Node child; if( _children.containsKey( childName)) { child = _children.get(childName); } else { if( metric != null) { child = new Node(metric); } else { child = new Node(); } _children.put( childName, child); } return child; } /** * Add a metric at the specific StatePath * * @param path * @param metric */ public void addMetric( StatePath path, StateValue metric) { String childName = path.getFirstElement(); Node child = getOrCreateChild( childName, path.isSimplePath() ? metric : null); if( !path.isSimplePath()) { child.addMetric(path.childPath(), metric); } } /** * Ensure we have a branch at specific StatePath. * * @param path */ public void addBranch( StatePath path) { String childName = path.getFirstElement(); Node child = getOrCreateChild( childName, null); if( !path.isSimplePath()) { child.addBranch(path.childPath()); } } public void addListItem( StatePath path, String type, String idName) { String childName = path.getFirstElement(); Node child = getOrCreateChild( childName, null); if( !path.isSimplePath()) { child.addListItem( path.childPath(), type, idName); } else { child._metadata.put( State.METADATA_BRANCH_CLASS_KEY, type); child._metadata.put( State.METADATA_BRANCH_IDNAME_KEY, idName); } } /** * Visit the current state. This simulates visiting a real dCache * state. * * @param visitor * @param ourPath */ public void visit( StateVisitor visitor, StatePath ourPath) { if( isMetric()) { _metricValue.acceptVisitor( ourPath, visitor); return; } if( !visitor.isVisitable( ourPath)) { return; } Map<String,String> visitMetadata = _metadata.isEmpty() ? null : _metadata; visitor.visitCompositePreDescend( ourPath, visitMetadata); for (String childName : Ordering.natural().sortedCopy(_children.keySet())) { Node child = _children.get(childName); StatePath childPath = (ourPath == null) ? new StatePath( childName) : ourPath.newChild( childName); child.visit( visitor, childPath); } visitor.visitCompositePostDescend( ourPath, visitMetadata); } } private final Node _rootNode = new Node(); public void addMetric( StatePath path, StateValue metric) { _rootNode.addMetric( path, metric); } public void addBranch( StatePath path) { _rootNode.addBranch( path); } public void addListItem( StatePath path, String type, String idName) { _rootNode.addListItem( path, type, idName); } @Override public void visitState( StateVisitor visitor) { _rootNode.visit( visitor, null); } }