Package org.auraframework.impl.javascript.testsuite

Source Code of org.auraframework.impl.javascript.testsuite.JavascriptMockHandler

/*
* Copyright (C) 2013 salesforce.com, inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.auraframework.impl.javascript.testsuite;

import java.lang.reflect.Array;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.auraframework.Aura;
import org.auraframework.def.BaseComponentDef;
import org.auraframework.def.DefDescriptor;
import org.auraframework.def.Definition;
import org.auraframework.system.Location;
import org.auraframework.test.mock.Answer;
import org.auraframework.test.mock.Invocation;
import org.auraframework.test.mock.Stub;
import org.auraframework.throwable.AuraRuntimeException;
import org.auraframework.throwable.quickfix.DefinitionNotFoundException;
import org.auraframework.throwable.quickfix.InvalidDefinitionException;
import org.auraframework.throwable.quickfix.QuickFixException;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.common.collect.Lists;

/**
* Parse JSTEST mock definitions into mocks to be applied when running tests.
*
* @param <D> the type of Definition being modified in the registry
*/
public abstract class JavascriptMockHandler<D extends Definition> {
    private final Map<String, Object> sourceMap;
    private final DefDescriptor<? extends BaseComponentDef> compDesc;

    protected JavascriptMockHandler(DefDescriptor<? extends BaseComponentDef> targetDescriptor,
            Map<String, Object> map) {
        this.sourceMap = map;
        this.compDesc = targetDescriptor;
    }

    protected DefDescriptor<? extends BaseComponentDef> getTargetDescriptor() {
        return compDesc;
    }

    public D getDefinition() {
        try {
            return createDefinition(sourceMap);
        } catch (QuickFixException qfe) {
            return createDefinition(qfe);
        }
    }

    /**
     * Mocks will have handlers to parse their respective mock objects.
     */
    protected abstract D createDefinition(Map<String, Object> map) throws QuickFixException;

    /**
     * Mocks will have handlers to parse their respective mock objects.
     */
    protected abstract D createDefinition(Throwable t);

    /**
     *
     * @param descStr
     * @param defClass
     * @return
     * @throws DefinitionNotFoundException
     * @throws QuickFixException
     */
    protected D getBaseDefinition(String descStr, Class<D> defClass)
            throws DefinitionNotFoundException, QuickFixException {
        if (descStr != null) {
            return Aura.getDefinitionService().getDefinition(descStr, defClass);
        }
        D ret = getDefaultBaseDefinition();
        if (ret == null) {
            throw new AuraRuntimeException("Descriptor not specified, and default definition not found for " +
                getTargetDescriptor() /*, Location */);
        }
        return ret;
    }

    protected abstract D getDefaultBaseDefinition() throws QuickFixException;

    protected <T extends Definition> DefDescriptor<T> getDescriptor(String descStr, Class<T> defClass) {
        if (descStr != null) {
            return Aura.getDefinitionService().getDefDescriptor(descStr, defClass);
        }
        return null;
    }

    /**
     * Read a single or list of Stubs
     *
     * @param object
     *            the parsed json representation
     * @return a list of Stubs
     * @throws QuickFixException
     */
    protected List<Stub<?>> getStubs(Object object) throws QuickFixException {
        List<Stub<?>> stubs = Lists.newLinkedList();
        if (!(object instanceof List)) {
            object = Lists.newArrayList(object);
        }
        for (Object item : (List<?>) object) {
            Stub<?> answer = getStub(item);
            if (answer != null) {
                stubs.add(answer);
            }
        }
        return stubs;
    }

    /**
     * Read a Stub that has an optional Invocation definition, "method"; and a list
     * of Answers, "answers".
     *
     * @param <T>
     *
     * @param object
     *            the parsed json representation
     * @return a Stub object
     * @throws QuickFixException
     */
    protected Stub<?> getStub(Object object) throws QuickFixException {
        if (object instanceof Map) {
            Invocation invocation = getInvocation(((Map<?, ?>) object).get("method"));
            @SuppressWarnings("rawtypes")
            Class retType = invocation.getReturnType();
            @SuppressWarnings("unchecked")
            List<Answer<Object>> answers = getAnswers(((Map<?, ?>) object).get("answers"), retType);
            return new Stub<>(invocation, answers);
        }
        return null;
    }

    protected abstract Invocation getDefaultInvocation() throws QuickFixException;

    /**
     * Read an Invocation, which must have a "name" and, optionally,
     * a "params" list of the input parameters to the method.
     *
     * @param object
     *            the parsed json representation
     * @return an Invocation object
     * @throws QuickFixException
     */
    protected Invocation getInvocation(Object object) throws QuickFixException {
        if (object == null) {
            return getDefaultInvocation();
        } else if (object instanceof Map) {
            Map<?, ?> methodMap = (Map<?, ?>) object;
            String name = (String) methodMap.get("name");
            if (name == null) {
                throw new InvalidDefinitionException("A mock's stubbed method must specify 'name'", getLocation());
            }
            List<?> params = (List<?>) methodMap.get("params");
            String typeStr = (String) methodMap.get("type");
            Class<?> type = Object.class;
            if (typeStr != null) {
                try {
                    type = classForSimpleName(typeStr);
                } catch (ClassNotFoundException e) {
                }
            }
            return new Invocation(name, params, type);
        }
        return null;
    }

    protected Location getLocation() {
        return null;
    }

    /**
     * Read a single or list of answers
     *
     * @param object
     *            the parsed json representation
     * @param retClass
     *            the expected type from the Answers
     * @return a list of Answers
     * @throws QuickFixException
     */
    protected <T> List<Answer<T>> getAnswers(Object object, Class<T> retClass) throws QuickFixException {
        List<Answer<T>> answers = Lists.newLinkedList();
        if (!(object instanceof List)) {
            object = Lists.newArrayList(object);
        }
        for (Object item : (List<?>) object) {
            Answer<T> answer = getAnswer(item, retClass);
            if (answer != null) {
                answers.add(answer);
            }
        }
        return answers;
    }

    /**
     * Read an Answer, which must have either a 'value' or 'error',
     * corresponding to Returns or ThrowsExceptionClass instances.
     *
     * @param object
     *            the parsed json representation
     * @param retClass
     *            the expected type from the Answer
     * @return an Answer object
     * @throws QuickFixException
     */
    protected <T> Answer<T> getAnswer(Object object, Class<T> retClass) throws QuickFixException {
        if (object instanceof Map) {
            Map<?, ?> map = (Map<?, ?>) object;
            T value = getValue(map.get("value"), retClass);
            String error = (String) map.get("error");
            if (value != null) {
                if (error == null) {
                    return new Returns<>(value);
                }
            } else {
                if (error != null) {
                    return new ThrowsExceptionClass<>(error);
                }
            }
        }
        throw new InvalidDefinitionException("Mock answer must specify either 'value' or 'error'", null /* getLocation() */);
    }

    /**
     * Read a value. Usually, this will be returned by an Answer and/or may
     * contain nested Answers.
     *
     * @param object
     *            the parsed json representation
     * @param retClass
     *            the expected type of the value
     * @return the value
     * @throws QuickFixException
     */
    @SuppressWarnings("unchecked")
    protected <T> T getValue(Object object, Class<T> retClass) throws QuickFixException {
        return (T) object;
    }

    /**
     * Gets the Class from a simple name representation. Classes from the java.lang package may omit the package name.
     * For example, "java.lang.String[][]" and "String[][]" will return the same Class object.
     *
     * @param simpleName
     * @return the Class, if found
     * @throws ClassNotFoundException
     */
    public Class<?> classForSimpleName(String simpleName) throws ClassNotFoundException {
        Matcher matcher = arrayPattern.matcher(simpleName);
        if (matcher.matches()) {
            int dims = (simpleName.length() - matcher.end(1)) / 2;
            String className = matcher.group(1);
            Class<?> clazz = primitiveMap.get(className);
            if (clazz == null) {
                try {
                    // allow convenience of shorter Object alternatives
                    clazz = Class.forName("java.lang." + className);
                } catch (ClassNotFoundException e) {
                    clazz = Class.forName(className);
                }
            }
            return (dims == 0) ? clazz : Array.newInstance(clazz, new int[dims]).getClass();
        }
        throw new ClassNotFoundException("Unknown type: " + simpleName);
    }

    private static final Pattern arrayPattern = Pattern.compile("^([\\w\\.]+?)(\\[\\])*\\z");
    private static final Map<String, Class<?>> primitiveMap;
    static {
        Builder<String, Class<?>> builder = ImmutableMap.builder();
        for (Class<?> c : new Class[] { boolean.class, byte.class, char.class, int.class, long.class, double.class,
                float.class, short.class, void.class }) {
            builder.put(c.getCanonicalName(), c);
        }
        primitiveMap = builder.build();
    }

    /**
     * An Answer that just returns a value.
     */
    public class Returns<T> implements Answer<T> {
        private T value;

        public Returns(T value) {
            this.value = value;
        }

        @Override
        public T answer() throws Throwable {
            return value;
        }
    }

    /**
     * An Answer that throws a Throwable.
     */
    public class ThrowsExceptionClass<T> implements Answer<T> {
        private Class<? extends Throwable> toThrow;

        public ThrowsExceptionClass(String className) {
            try {
                this.toThrow = Class.forName(className).asSubclass(
                        Throwable.class);
            } catch (ClassNotFoundException e) {
                throw new AuraRuntimeException(e);
            }
        }

        @Override
        public T answer() throws Throwable {
            // try to instantiate the class with no args, or with a string
            Throwable t;
            try {
                t = toThrow.newInstance();
            } catch (Exception e) {
                t = toThrow.getConstructor(String.class).newInstance("MOCKED");
            }
            throw t;
        }
    }
}
TOP

Related Classes of org.auraframework.impl.javascript.testsuite.JavascriptMockHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.