com.jivesoftware.os.tasmo.test.Expectations.java Source code

Java tutorial

Introduction

Here is the source code for com.jivesoftware.os.tasmo.test.Expectations.java

Source

/*
 * $Revision$
 * $Date$
 *
 * Copyright (C) 1999-$year$ Jive Software. All rights reserved.
 *
 * This software is the proprietary information of Jive Software. Use is subject to license terms.
 */
package com.jivesoftware.os.tasmo.test;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jivesoftware.os.jive.utils.logger.MetricLogger;
import com.jivesoftware.os.jive.utils.logger.MetricLoggerFactory;
import com.jivesoftware.os.tasmo.event.api.ReservedFields;
import com.jivesoftware.os.tasmo.id.Id;
import com.jivesoftware.os.tasmo.id.ObjectId;
import com.jivesoftware.os.tasmo.id.TenantIdAndCentricId;
import com.jivesoftware.os.tasmo.model.ViewBinding;
import com.jivesoftware.os.tasmo.model.Views;
import com.jivesoftware.os.tasmo.model.path.ModelPath;
import com.jivesoftware.os.tasmo.model.path.ModelPathStep;
import com.jivesoftware.os.tasmo.model.path.ModelPathStepType;
import com.jivesoftware.os.tasmo.view.reader.service.shared.ViewValue;
import com.jivesoftware.os.tasmo.view.reader.service.shared.ViewValueStore;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.testng.Assert;

/**
 *
 */
public class Expectations {

    private static final MetricLogger LOG = MetricLoggerFactory.getLogger();
    private static final ObjectMapper MAPPER = new ObjectMapper();
    private final ViewValueStore viewValueStore;
    private final Map<ViewKey, ModelPath> viewModelPaths = Maps.newHashMap();
    private final List<Expectation> expectations = Lists.newArrayList();

    public Expectations(ViewValueStore viewValueStore, Views views) {
        this.viewValueStore = viewValueStore;

        List<ViewBinding> bindings = views.getViewBindings();
        for (ViewBinding viewBinding : bindings) {
            for (ModelPath modelPath : viewBinding.getModelPaths()) {
                viewModelPaths.put(new ViewKey(viewBinding.getViewClassName(), modelPath.getId()), modelPath);
            }
        }
    }

    public void buildExpectations(long testCase, Expectations expectations, ViewBinding binding, EventFire input,
            Set<Id> deletedIds) throws Exception {
        for (Branch viewBranch : calculateBranches(input, binding.getModelPaths().get(0), deletedIds)) {

            for (Map.Entry<String, String> entry : input.getLeafNodeFields().entrySet()) {
                ObjectId[] branchIds = viewBranch.getBranchIds();

                LOG.trace("Adding view path expecation: " + binding.getModelPaths().get(0).getId()
                        + Arrays.toString(branchIds) + ":" + entry.getKey() + "->"
                        + (viewBranch.isDeleted() ? null : entry.getValue()));

                expectations.addExpectation(testCase, branchIds[0], binding.getViewClassName(),
                        binding.getModelPaths().get(0).getId(), branchIds, entry.getKey(),
                        viewBranch.isDeleted() ? null : entry.getValue());
            }
        }

    }

    public static class Branch {

        private final ObjectId[] branchIds;
        private final boolean deleted;

        public Branch(ObjectId[] branchIds, boolean deleted) {
            this.branchIds = branchIds;
            this.deleted = deleted;
        }

        public ObjectId[] getBranchIds() {
            return branchIds;
        }

        public boolean isDeleted() {
            return deleted;
        }
    }

    private void walkTree(IdTreeNode parent, List<Id[]> allBranches) {
        if (parent.children().isEmpty()) {
            List<Id> ids = new ArrayList<>();
            while (true) {
                ids.add(parent.value());
                parent = parent.parent();
                if (parent == null) {
                    break;
                }
            }

            ids = Lists.reverse(ids);
            allBranches.add(ids.toArray(new Id[ids.size()]));
        } else {
            for (IdTreeNode child : parent.children()) {
                walkTree(child, allBranches);
            }
        }
    }

    public List<Branch> calculateBranches(EventFire input, ModelPath path, Set<Id> deletedIds) {
        List<Id[]> branchIds = new ArrayList<>();
        walkTree(input.getIdTree(), branchIds);
        List<Branch> branches = new ArrayList<>();

        for (Id[] branch : branchIds) {
            boolean delete = false;
            ObjectId[] objectIds = new ObjectId[branch.length];
            for (int i = 0; i < branch.length; i++) {
                ModelPathStep step = path.getPathMembers().get(i);
                String className = step.getStepType().isBackReferenceType()
                        ? step.getDestinationClassNames().iterator().next()
                        : step.getOriginClassNames().iterator().next();
                objectIds[i] = new ObjectId(className, branch[i]);

                delete |= deletedIds.contains(branch[i]);
            }

            branches.add(new Branch(objectIds, delete));
        }

        return branches;

    }

    void addExpectation(long testCase, ObjectId rootId, String viewClassName, String viewFieldName,
            ObjectId[] pathIds, String fieldName, Object value) {
        ObjectId viewId = new ObjectId(viewClassName, rootId.getId());
        ModelPath modelPath = viewModelPaths.get(new ViewKey(viewClassName, viewFieldName));
        expectations.add(new Expectation(testCase, viewId, viewClassName, viewFieldName, modelPath, pathIds,
                fieldName, value));
    }

    void assertExpectations(TenantIdAndCentricId tenantIdAndCentricId, ObjectNode view) throws IOException {
        List<AssertNode> asserts = new ArrayList<>();
        for (Expectation expectation : expectations) {
            ViewValue got = viewValueStore.get(tenantIdAndCentricId, expectation.viewId, expectation.modelPathId,
                    expectation.modelPathInstanceIds);
            JsonNode node = null;
            if (got != null) {
                node = MAPPER.readValue(new ByteArrayInputStream(got.getValue()), JsonNode.class);
            }
            assertExpectation(expectation, node, asserts);
            node = findExpectationNode(expectation.path.getPathMembers(), expectation.modelPathInstanceIds, view,
                    expectation.value != null);
            if (node != null) {
                assertExpectation(expectation, node, asserts);
            }
        }
        StringBuilder errors = new StringBuilder();
        for (AssertNode a : asserts) {
            if (!a.isTrue()) {
                errors.append(a.expectation.toString()).append(" WANTED: ").append(a.want).append(" but WAS:")
                        .append(a.was).append("\n");
            }
        }
        if (errors.length() > 0) {
            Assert.fail(errors.toString());
        }
    }

    private void assertExpectation(Expectation expectation, JsonNode node, List<AssertNode> asserts) {
        try {
            if (expectation.value == null) {
                if (node != null) {
                    JsonNode value = node.get(expectation.fieldName);
                    if (value != null) {
                        Assert.assertTrue(value instanceof NullNode,
                                "value:" + value + " expected to be a NullNode but was " + value.getClass() + "  "
                                        + expectation.toString());
                    }
                }
            } else {
                JsonNode was;
                if (node != null && expectation.fieldName != null) {
                    was = node.get(expectation.fieldName);
                } else {
                    was = node;
                }
                JsonNode want = MAPPER.convertValue(expectation.value, JsonNode.class);
                //Assert.assertEquals(was, want, expectation.toString() + " WANTED: " + want + " but WAS:" + was);

                asserts.add(new AssertNode(expectation, want, was));
            }
        } catch (IllegalArgumentException x) {
            System.out.println("Failed while asserting " + expectation);
            throw x;
        }
    }

    private JsonNode findExpectationNode(List<ModelPathStep> pathMembers, ObjectId[] ids, JsonNode node,
            boolean shouldBePresent) {
        if (shouldBePresent) {
            Assert.assertNotNull(node);
        } else if (node == null) {
            return null;
        }
        if (ids.length == 1) {
            String objectId = node.get(ReservedFields.VIEW_OBJECT_ID).asText();
            Assert.assertEquals(objectId, ids[0].toStringForm(), "Unexpected objectId");
            return node;
        }
        ModelPathStep firstStep = pathMembers.get(0);
        String fieldName = firstStep.getRefFieldName();
        switch (firstStep.getStepType()) {
        case backRefs:
            fieldName = ReservedFields.ALL_BACK_REF_FIELD_PREFIX + fieldName;
            break;
        case latest_backRef:
            fieldName = ReservedFields.LATEST_BACK_REF_FIELD_PREFIX + fieldName;
            break;
        case count:
            // Count nodes don't bring values, nothing to validate
            return null;
        }
        JsonNode subNode = node.get(fieldName);
        if (shouldBePresent) {
            Assert.assertNotNull(subNode, "Expected field " + fieldName + " in JSON " + node);
        } else if (subNode == null) {
            return null;
        }
        if (subNode instanceof ArrayNode) {
            Assert.assertTrue(
                    firstStep.getStepType() == ModelPathStepType.backRefs
                            || firstStep.getStepType() == ModelPathStepType.refs,
                    "Encountered array for " + fieldName + " in " + node + ", single value expected");
            JsonNode nodeInArray = null;
            for (JsonNode jsonNode : subNode) {
                JsonNode idNode = jsonNode.get(ReservedFields.VIEW_OBJECT_ID);
                if (idNode != null && ids[1].toStringForm().equals(idNode.asText())) {
                    nodeInArray = jsonNode;
                    break;
                }
            }
            // Technically we could skip this, assign jsonNode to subNode directly above and let recursion handle it.
            // But the assertion wouldn't be informative in this case.
            if (shouldBePresent) {
                Assert.assertNotNull(nodeInArray,
                        "Didn't find expected id " + ids[1] + " in " + fieldName + " of " + node);
            } else if (nodeInArray == null) {
                return null;
            }
            subNode = nodeInArray;
        }
        return findExpectationNode(pathMembers.subList(1, pathMembers.size()),
                Arrays.copyOfRange(ids, 1, ids.length), subNode, shouldBePresent);
    }

    static class AssertNode {

        Expectation expectation;
        JsonNode want;
        JsonNode was;

        public AssertNode(Expectation expectation, JsonNode want, JsonNode was) {
            this.expectation = expectation;
            this.want = want;
            this.was = was;
        }

        public boolean isTrue() {
            return want.equals(was);
        }

    }

    void clear() {
        expectations.clear();
    }

    static class ViewKey {

        String viewClassName;
        String viewFieldName;

        public ViewKey(String viewClassName, String viewFieldName) {
            this.viewClassName = viewClassName;
            this.viewFieldName = viewFieldName;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 71 * hash + (this.viewClassName != null ? this.viewClassName.hashCode() : 0);
            hash = 71 * hash + (this.viewFieldName != null ? this.viewFieldName.hashCode() : 0);
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final ViewKey other = (ViewKey) obj;
            if (this.viewClassName != other.viewClassName
                    && (this.viewClassName == null || !this.viewClassName.equals(other.viewClassName))) {
                return false;
            }
            if (this.viewFieldName != other.viewFieldName
                    && (this.viewFieldName == null || !this.viewFieldName.equals(other.viewFieldName))) {
                return false;
            }
            return true;
        }
    }

    static class Expectation {

        long testCase;
        ObjectId viewId;
        String viewClassName;
        String modelPathId;
        ModelPath path;
        ObjectId[] modelPathInstanceIds;
        String fieldName;
        Object value;

        public Expectation(long testCase, ObjectId viewId, String viewClassName, String viewFieldName,
                ModelPath path, ObjectId[] modelPathInstanceIds, String fieldName, Object value) {
            this.testCase = testCase;
            this.viewId = viewId;
            this.viewClassName = viewClassName;
            this.modelPathId = viewFieldName;
            this.path = path;
            this.modelPathInstanceIds = modelPathInstanceIds;
            this.fieldName = fieldName;
            this.value = value;
        }

        @Override
        public String toString() {
            return "Expectation{" + "testCase=" + testCase + ", viewId=" + viewId + ", viewClassName="
                    + viewClassName + ", modelPathId=" + modelPathId + ", path=" + path + ", modelPathInstanceIds="
                    + Arrays.deepToString(modelPathInstanceIds) + ", fieldName=" + fieldName + ", value=" + value
                    + '}';
        }

        @Override
        public int hashCode() {
            int hash = 3;
            hash = 37 * hash + (this.viewId != null ? this.viewId.hashCode() : 0);
            hash = 37 * hash + (this.viewClassName != null ? this.viewClassName.hashCode() : 0);
            hash = 37 * hash + (this.modelPathId != null ? this.modelPathId.hashCode() : 0);
            hash = 37 * hash + (this.path != null ? this.path.hashCode() : 0);
            hash = 37 * hash + (this.modelPathInstanceIds != null ? this.modelPathInstanceIds.hashCode() : 0);
            hash = 37 * hash + (this.fieldName != null ? this.fieldName.hashCode() : 0);
            hash = 37 * hash + (this.value != null ? this.value.hashCode() : 0);
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final Expectation other = (Expectation) obj;
            if (this.viewId != other.viewId && (this.viewId == null || !this.viewId.equals(other.viewId))) {
                return false;
            }
            if ((this.viewClassName == null) ? (other.viewClassName != null)
                    : !this.viewClassName.equals(other.viewClassName)) {
                return false;
            }
            if ((this.modelPathId == null) ? (other.modelPathId != null)
                    : !this.modelPathId.equals(other.modelPathId)) {
                return false;
            }
            if (this.path != other.path && (this.path == null || !this.path.equals(other.path))) {
                return false;
            }
            if (this.modelPathInstanceIds != other.modelPathInstanceIds && (this.modelPathInstanceIds == null
                    || !this.modelPathInstanceIds.equals(other.modelPathInstanceIds))) {
                return false;
            }
            if ((this.fieldName == null) ? (other.fieldName != null) : !this.fieldName.equals(other.fieldName)) {
                return false;
            }
            if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
                return false;
            }
            return true;
        }
    }
}