Package org.auraframework.impl.system

Source Code of org.auraframework.impl.system.MasterDefRegistryImplTest$LockTestInfo

/*
* 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.system;

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

import org.auraframework.Aura;
import org.auraframework.adapter.ConfigAdapter;
import org.auraframework.adapter.RegistryAdapter;
import org.auraframework.cache.Cache;
import org.auraframework.def.ApplicationDef;
import org.auraframework.def.ClientLibraryDef;
import org.auraframework.def.ComponentDef;
import org.auraframework.def.ControllerDef;
import org.auraframework.def.DefDescriptor;
import org.auraframework.def.DefDescriptor.DefType;
import org.auraframework.def.Definition;
import org.auraframework.def.DefinitionAccess;
import org.auraframework.def.DescriptorFilter;
import org.auraframework.def.HelperDef;
import org.auraframework.def.LayoutsDef;
import org.auraframework.def.NamespaceDef;
import org.auraframework.def.RendererDef;
import org.auraframework.def.StyleDef;
import org.auraframework.def.TypeDef;
import org.auraframework.impl.AuraImpl;
import org.auraframework.impl.AuraImplTestCase;
import org.auraframework.impl.source.StringSourceLoader;
import org.auraframework.instance.BaseComponent;
import org.auraframework.service.BuilderService;
import org.auraframework.service.CachingService;
import org.auraframework.service.ContextService;
import org.auraframework.system.AuraContext;
import org.auraframework.system.AuraContext.Authentication;
import org.auraframework.system.AuraContext.Format;
import org.auraframework.system.AuraContext.Mode;
import org.auraframework.system.DefRegistry;
import org.auraframework.system.DependencyEntry;
import org.auraframework.system.Location;
import org.auraframework.system.MasterDefRegistry;
import org.auraframework.system.Source;
import org.auraframework.system.SourceListener;
import org.auraframework.system.SubDefDescriptor;
import org.auraframework.test.AuraTestingUtil;
import org.auraframework.test.ServiceLocatorMocker;
import org.auraframework.test.annotation.ThreadHostileTest;
import org.auraframework.test.annotation.UnAdaptableTest;
import org.auraframework.test.util.AuraPrivateAccessor;
import org.auraframework.throwable.NoAccessException;
import org.auraframework.throwable.quickfix.DefinitionNotFoundException;
import org.auraframework.throwable.quickfix.InvalidDefinitionException;
import org.auraframework.throwable.quickfix.QuickFixException;
import org.auraframework.util.ServiceLoader;
import org.auraframework.util.json.Json;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.internal.util.MockUtil;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

/**
* @see org.auraframework.impl.registry.RootDefFactoryTest
*/
@ThreadHostileTest("Don't you go clearing my caches.")
public class MasterDefRegistryImplTest extends AuraImplTestCase {
    @Mock
    Definition globalDef;
    @Mock
    DefinitionAccess defAccess;
    @Mock
    DefDescriptor<ComponentDef> referencingDesc;
    @Mock
    Cache<String, String> mockAccessCheckCache;

    public MasterDefRegistryImplTest(String name) {
        super(name);
    }

    private MasterDefRegistryImpl getDefRegistry(boolean asMocks) {
        Collection<RegistryAdapter> providers = AuraImpl.getRegistryAdapters();
        List<DefRegistry<?>> mdrregs = Lists.newArrayList();

        AuraContext context = Aura.getContextService().getCurrentContext();
        for (RegistryAdapter provider : providers) {
            DefRegistry<?>[] registries = provider.getRegistries(context.getMode(), context.getAccess(), null);
            if (registries != null) {
                for (DefRegistry<?> reg : registries) {
                    Set<String> ns = reg.getNamespaces();

                    if (ns != null) {
                        mdrregs.add(asMocks ? Mockito.spy(reg) : reg);
                    }
                }
            }
        }
        MasterDefRegistryImpl registry = new MasterDefRegistryImpl(mdrregs.toArray(new DefRegistry<?>[mdrregs.size()]));
        return asMocks ? Mockito.spy(registry) : registry;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private void spyOnDefs(final MasterDefRegistryImpl registry) throws QuickFixException {
        final MockUtil mockUtil = new MockUtil();
        for (DefRegistry<?> subReg : registry.getAllRegistries()) {
            Mockito.doAnswer(new Answer<Definition>() {
                @Override
                public Definition answer(InvocationOnMock invocation) throws Throwable {
                    Definition ret = (Definition) invocation.callRealMethod();
                    if (ret == null) {
                        return ret;
                    }
                    if (mockUtil.isMock(ret)) {
                        return ret;
                    } else {
                        ret = Mockito.spy(ret);
                        registry.addLocalDef(ret);
                        return ret;
                    }
                }
            }).when(subReg).getDef(Mockito.<DefDescriptor> any());
        }
    }

    /**
     * Verify some of the assertions (copied here) made by compileDef (excluding #2 & #5).
     * <ol>
     * <li>Each definition has 'validateDefinition()' called on it exactly once.</li>
     * <li>No definition is marked as valid until all definitions in the dependency set have been validated</li>
     * <li>Each definition has 'validateReferences()' called on it exactly once, after the definitions have been put in
     * local cache</li>
     * <li>All definitions are marked valid by the DefRegistry after the validation is complete</li>
     * <li>No definition should be available to other threads until it is marked valid</li>
     * <ol>
     */
    private void assertCompiledDef(Definition def) throws QuickFixException {
        Mockito.verify(def, Mockito.times(1)).validateDefinition();
        Mockito.verify(def, Mockito.times(1)).validateReferences();
        Mockito.verify(def, Mockito.times(1)).markValid();
        assertEquals("definition not valid: " + def, true, def.isValid());
    }

    private void assertIdenticalDependencies(DefDescriptor<?> desc1, DefDescriptor<?> desc2) throws Exception {
        MasterDefRegistryImpl registry = getDefRegistry(false);
        Set<DefDescriptor<?>> deps1 = registry.getDependencies(registry.getUid(null, desc1));
        Set<DefDescriptor<?>> deps2 = registry.getDependencies(registry.getUid(null, desc2));
        assertNotNull(deps1);
        assertNotNull(deps2);
        assertEquals("Descriptors should have the same number of dependencies", deps1.size(), deps2.size());

        // Loop through and check individual dependencies. Order doesn't matter.
        for (DefDescriptor<?> dep : deps1) {
            assertTrue("Descriptors do not have identical dependencies",
                    checkDependenciesContains(deps2, dep.getQualifiedName()));
        }
    }

    private boolean checkDependenciesContains(Set<DefDescriptor<?>> deps, String depSearch) {
        for (DefDescriptor<?> dep : deps) {
            if (dep.getQualifiedName().equals(depSearch)) {
                return true;
            }
        }
        return false;
    }

    public void testFindRegex() throws Exception {
        String namespace = "testFindRegex" + getAuraTestingUtil().getNonce();
        DefDescriptor<ApplicationDef> houseboat = addSourceAutoCleanup(ApplicationDef.class,
                String.format(baseApplicationTag, "", ""), String.format("%s:houseboat", namespace));
        addSourceAutoCleanup(ApplicationDef.class, String.format(baseApplicationTag, "", ""),
                String.format("%s:houseparty", namespace));
        addSourceAutoCleanup(ApplicationDef.class, String.format(baseApplicationTag, "", ""),
                String.format("%s:pantsparty", namespace));

        MasterDefRegistryImpl masterDefReg = getDefRegistry(false);

        assertTrue("find() not finding all sources",
                masterDefReg.find(new DescriptorFilter(String.format("markup://%s:*", namespace))).size() == 3);
        assertEquals("find() fails with wildcard as prefix", 1,
                masterDefReg.find(new DescriptorFilter("*://" + houseboat.getDescriptorName())).size());
        assertEquals("find() fails with wildcard as namespace", 1,
                masterDefReg.find(new DescriptorFilter("markup://*:" + houseboat.getName())).size());
        assertEquals("find() fails with wildcard as name", 1,
                masterDefReg.find(new DescriptorFilter(houseboat.getQualifiedName())).size());
        assertEquals("find() fails with wildcard at end of name", 2,
                masterDefReg.find(new DescriptorFilter(String.format("markup://%s:house*", namespace))).size());
        assertEquals("find() fails with wildcard at beginning of name", 2,
                masterDefReg.find(new DescriptorFilter(String.format("markup://%s:*party*", namespace))).size());

        assertEquals("find() should not find nonexistent name", 0,
                masterDefReg.find(new DescriptorFilter(String.format("markup://%s:househunters", namespace))).size());
        assertEquals("find() should not find nonexistent name ending with wildcard", 0,
                masterDefReg.find(new DescriptorFilter(String.format("markup://%s:househunters*", namespace))).size());
        assertEquals("find() should not find nonexistent name with preceeding wildcard", 0,
                masterDefReg.find(new DescriptorFilter(String.format("markup://%s:*notherecaptain", namespace))).size());
    }

    private static class AddableDef<T extends Definition> {
        private final Class<T> defClass;
        private final String format;
        private final String content;

        public AddableDef(Class<T> defClass, String format, String content) {
            this.defClass = defClass;
            this.format = format;
            this.content = content;
        }

        public Class<T> getDefClass() {
            return this.defClass;
        }

        public String getFQN(String namespace, String name) {
            return String.format(this.format, namespace, name);
        }

        public String getContent() {
            return content;
        }
    }

    private static AddableDef<?> addable[] = new AddableDef[] {
            // Ignoring top level bundle defs.
            // APPLICATION(ApplicationDef.class, Format.XML, DefDescriptor.MARKUP_PREFIX, ":"),
            // COMPONENT(ComponentDef.class, Format.XML, DefDescriptor.MARKUP_PREFIX, ":"),
            // EVENT(EventDef.class, Format.XML, DefDescriptor.MARKUP_PREFIX, ":"),
            // INTERFACE(InterfaceDef.class, Format.XML, DefDescriptor.MARKUP_PREFIX, ":"),
            // LAYOUTS(LayoutsDef.class, Format.XML, DefDescriptor.MARKUP_PREFIX, ":"),
            // NAMESPACE(NamespaceDef.class, Format.XML, DefDescriptor.MARKUP_PREFIX, ""),
            new AddableDef<>(ControllerDef.class, "js://%s.%s",
                    "({method: function(cmp) {}})"),
            new AddableDef<>(HelperDef.class, "js://%s.%s",
                    "({method: function(cmp) {}})"),
            // new AddableDef<ProviderDef>(ProviderDef.class, "js://%s.%s",
            //        "({provide: function(cmp) {}})"),
            new AddableDef<>(RendererDef.class, "js://%s.%s",
                    "({render: function(cmp) {}})"),
            new AddableDef<>(StyleDef.class, "css://%s.%s",
                    ".THIS {display:block;}"),
            // Ignoring TESTSUITE(TestSuiteDef.class, Format.JS, DefDescriptor.JAVASCRIPT_PREFIX, "."),
            // Ignoring THEME(ThemeDef.class, Format.XML, DefDescriptor.MARKUP_PREFIX, ":");
    };

    private MasterDefRegistry resetDefRegistry() {
        ContextService contextService = Aura.getContextService();
        if (contextService.isEstablished()) {
            contextService.endContext();
        }
        contextService.startContext(Mode.UTEST, Format.JSON, Authentication.AUTHENTICATED);
        return contextService.getCurrentContext().getDefRegistry();
    }

    private <T extends Definition> void checkAddRemove(DefDescriptor<?> tld, String suid,
                                                       AddableDef<T> toAdd) throws QuickFixException {
        DefDescriptor<T> dd;
        String uid, ouid;
        Set<DefDescriptor<?>> deps;
        AuraTestingUtil util = getAuraTestingUtil();
        MasterDefRegistry mdr;

        dd = DefDescriptorImpl.getInstance(toAdd.getFQN(tld.getNamespace(), tld.getName()),
                toAdd.getDefClass());
        util.addSourceAutoCleanup(dd, toAdd.getContent());
        mdr = resetDefRegistry();
        uid = mdr.getUid(null, tld);
        assertFalse("UID should change on add for " + dd.getDefType() + "@" + dd, suid.equals(uid));
        deps = mdr.getDependencies(uid);
        assertTrue("dependencies should contain the newly created " + dd.getDefType() + "@" + dd,
                deps.contains(dd));
        ouid = uid;
        util.removeSource(dd);
        mdr = resetDefRegistry();
        uid = mdr.getUid(null, tld);
        assertNotSame("UID should change on removal for " + dd.getDefType() + "@" + dd, ouid, uid);
        deps = mdr.getDependencies(uid);
        assertFalse("dependencies should not contain the deleted " + dd, deps.contains(dd));
    }

    private <T extends Definition> void checkOneTLD(String fqn, Class<T> clazz, String content)
            throws QuickFixException {
        AuraTestingUtil util = getAuraTestingUtil();
        String uid;

        DefDescriptor<T> tld = DefDescriptorImpl.getInstance(fqn, clazz);
        util.addSourceAutoCleanup(tld, content);
        MasterDefRegistry mdr = resetDefRegistry();
        // prime the cache.
        uid = mdr.getUid(null, tld);
        assertNotNull(tld + " did not give us a UID", uid);
        for (AddableDef<?> adding : addable) {
            checkAddRemove(tld, uid, adding);
        }
        util.removeSource(tld);
    }

    public void testComponentChChChChanges() throws Exception {
        checkOneTLD("markup://chchch:changes" + getAuraTestingUtil().getNonce(),
                ComponentDef.class, "<aura:component></aura:component>");
    }

    public void testApplicationChChChChanges() throws Exception {
        checkOneTLD("markup://chchch:changes" + getAuraTestingUtil().getNonce(),
                ApplicationDef.class, "<aura:application></aura:application>");
    }

    public void testStringCache() throws Exception {
        String namespace = "testStringCache" + getAuraTestingUtil().getNonce();
        DefDescriptor<ApplicationDef> houseboat = addSourceAutoCleanup(ApplicationDef.class,
                String.format(baseApplicationTag, "", ""), String.format("%s:houseboat", namespace));
        MasterDefRegistryImpl masterDefReg = getDefRegistry(false);
        String uid = masterDefReg.getUid(null, houseboat);
        assertNull("Found string in new MDR", masterDefReg.getCachedString(uid, houseboat, "test1"));
        masterDefReg.putCachedString(uid, houseboat, "test1", "value");
        assertEquals("value", masterDefReg.getCachedString(uid, houseboat, "test1"));
    }

    public void testNonPrivilegedStringCache() throws Exception {
        String namespace = "testNonPrivilegedStringCache" + getAuraTestingUtil().getNonce();

        ConfigAdapter configAdapter = Aura.getConfigAdapter();
        assertFalse(namespace + "  should not have been isPriveleged", configAdapter.isPrivilegedNamespace(namespace));
        DefDescriptor<ApplicationDef> houseboat = getAuraTestingUtil().addSourceAutoCleanup(ApplicationDef.class,
                String.format(baseApplicationTag, "", ""), String.format("%s:houseboat", namespace), false);
        MasterDefRegistryImpl masterDefReg = getDefRegistry(false);
        String uid = masterDefReg.getUid(null, houseboat);
        assertNull("Found string in new MDR", masterDefReg.getCachedString(uid, houseboat, "test1"));
        masterDefReg.putCachedString(uid, houseboat, "test1", "value");
        assertNull("Found string in new MDR", masterDefReg.getCachedString(uid, houseboat, "test1"));
    }

    public void testGetUidClientOutOfSync() throws Exception {
        String namespace = "testStringCache" + getAuraTestingUtil().getNonce();
        String namePrefix = String.format("%s:houseboat", namespace);
        DefDescriptor<ApplicationDef> houseboat = addSourceAutoCleanup(ApplicationDef.class,
                String.format(baseApplicationTag, "", ""), namePrefix);
        MasterDefRegistryImpl masterDefReg = getDefRegistry(false);
        String uid = masterDefReg.getUid(null, houseboat);
        assertNotNull(uid);
        // Check unchanged app gets same UID value
        assertEquals(uid, masterDefReg.getUid(uid, houseboat));

        //
        // When given an incorrect UID, masterDefReg simply returns the correct one.
        String newUid = masterDefReg.getUid(uid + " or not", houseboat);
        assertEquals(uid, newUid);
    }

    /**
     * Verify getting the UID of a dependency doesn't affect the original UID.
     */
    public void testUidDependencies() throws Exception {
        DefDescriptor<ComponentDef> child = addSourceAutoCleanup(ComponentDef.class,
                "<aura:component></aura:component>", "testUidDependenciesChild");
        DefDescriptor<ApplicationDef> parent = addSourceAutoCleanup(ApplicationDef.class,
                "<aura:application><" + child.getDescriptorName() + "/></aura:application>",
                "testUidDependenciesParent");

        MasterDefRegistryImpl masterDefReg1 = getDefRegistry(false);
        String parentUid1 = masterDefReg1.getUid(null, parent);

        MasterDefRegistryImpl masterDefReg2 = getDefRegistry(false);
        masterDefReg2.getUid(null, child);
        String parentUid2 = masterDefReg2.getUid(null, parent);

        assertTrue("UIDs do not match after getting a dependencies UID", parentUid1.equals(parentUid2));
    }

    /**
     * Verify UID values and dependencies against a gold file.
     *
     * This does a recursive set of dependencies checks to build a gold file with the resulting descriptors and UIDs to
     * ensure that we get both a valid set and can tell what changed (and thus verify that it should have changed).
     *
     * The format of the file is:
     * <ul>
     * <li>Top level descriptor ':' global UID.
     * <li>
     * <li>dependency ':' own hash
     * <li>
     * <li>...</li>
     * </ul>
     */
    public void testUidValue() throws Exception {
        StringBuilder buffer = new StringBuilder();
        String cmpName = "ui:outputNumber";
        DefDescriptor<ComponentDef> desc = Aura.getDefinitionService()
                .getDefDescriptor(cmpName, ComponentDef.class);
        MasterDefRegistryImpl masterDefReg = getDefRegistry(false);
        String uid = masterDefReg.getUid(null, desc);
        assertNotNull("Could not retrieve UID for component " + cmpName, uid);
        Set<DefDescriptor<?>> dependencies = masterDefReg.getDependencies(uid);
        assertNotNull("Could not retrieve dependencies for component " + cmpName, dependencies);

        buffer.append(desc.toString());
        buffer.append(" : ");
        buffer.append(uid);
        buffer.append("\n");

        for (DefDescriptor<?> dep : dependencies) {
            buffer.append(dep);
            buffer.append(" : ");
            buffer.append(masterDefReg.getDef(dep).getOwnHash());
            buffer.append("\n");
        }
        goldFileText(buffer.toString());
    }

    public void testGetUidDescriptorNull() throws Exception {
        MasterDefRegistryImpl registry = getDefRegistry(false);
        assertNull(registry.getUid(null, null));
    }

    public void testGetUidDescriptorDoesntExist() throws Exception {
        MasterDefRegistryImpl registry = getDefRegistry(false);
        assertNull(registry.getUid(null, DefDescriptorImpl.getInstance("unknown:soldier", ComponentDef.class)));
    }

    public void testGetUidLocalDef() throws Exception {
        MasterDefRegistryImpl registry = getDefRegistry(false);
        ComponentDef def = Mockito.spy(registry.getDef(DefDescriptorImpl.getInstance("aura:component",
                ComponentDef.class)));
        registry.invalidate(null); // clear any cached results from the preceding getDef call
        registry = getDefRegistry(false);
        registry.addLocalDef(def);
        assertNotNull(registry.getUid(null, def.getDescriptor()));
    }

    public void testGetUidSameAcrossInstances() throws Exception {
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        MasterDefRegistryImpl registry1 = getDefRegistry(false);
        String uid1 = registry1.getUid(null, cmpDesc);
        MasterDefRegistryImpl registry2 = getDefRegistry(false);
        registry2.invalidate(null);
        String uid2 = registry2.getUid(null, cmpDesc);
        assertEquals("Expected same UID for def from separate registry instances", uid1, uid2);
    }

    public void testGetUidUnique() throws Exception {
        DefDescriptor<ComponentDef> cmpDesc1 = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        DefDescriptor<ComponentDef> cmpDesc2 = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        MasterDefRegistryImpl registry = getDefRegistry(false);
        String uid1 = registry.getUid(null, cmpDesc1);
        String uid2 = registry.getUid(null, cmpDesc2);
        assertTrue("Components with same markup and dependencies should have different UIDs", !uid1.equals(uid2));
    }

    public void testGetUidCachedForChangedDefinition() throws Exception {
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        MasterDefRegistryImpl registry = getDefRegistry(false);
        String uid = registry.getUid(null, cmpDesc);

        // UID cached for current registry
        registry.getSource(cmpDesc).addOrUpdate(
                "<aura:component><aura:attribute name='str' type='String'/></aura:component>");
        String uidNew = registry.getUid(null, cmpDesc);
        assertEquals("UID not cached", uid, uidNew);

        // UID not cached for new registry
        MasterDefRegistryImpl registryNext = getDefRegistry(false);
        String uidNext = registryNext.getUid(null, cmpDesc);
        assertFalse("UID not cached in new registry", uid.equals(uidNext));
    }

    public void testGetUidCachedForRemovedDefinition() throws Exception {
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>", null);
        MasterDefRegistryImpl registry = getDefRegistry(false);
        String uid = registry.getUid(null, cmpDesc);

        // UID cached for current registry
        getAuraTestingUtil().removeSource(cmpDesc);
        String uidNew = registry.getUid(null, cmpDesc);
        assertEquals("UID not cached", uid, uidNew);

        // UID not cached for new registry
        MasterDefRegistryImpl registryNext = getDefRegistry(false);
        String uidNext = registryNext.getUid(null, cmpDesc);
        assertNull("UID cached in new registry", uidNext);
    }

    public void testGetUidForQuickFixException() throws Exception {
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class,
                "<aura:component><unknown:component/></aura:component>", null);
        MasterDefRegistryImpl registry = getDefRegistry(true);
        try {
            registry.getUid(null, cmpDesc);
            fail("Expected DefinitionNotFoundException");
        } catch (DefinitionNotFoundException e) {
            checkExceptionStart(e, null, "No COMPONENT named markup://unknown:component found");
        }
        Mockito.verify(registry, Mockito.times(1)).compileDE(Mockito.eq(cmpDesc));

        // another request for getUid will not re-compile
        Mockito.reset(registry);
        try {
            registry.getUid(null, cmpDesc);
            fail("Expected DefinitionNotFoundException");
        } catch (DefinitionNotFoundException e) {
            checkExceptionStart(e, null, "No COMPONENT named markup://unknown:component found");
        }
        Mockito.verify(registry, Mockito.times(0)).compileDE(Mockito.eq(cmpDesc));
    }

    public void testGetUidForNonQuickFixException() throws Exception {
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class,
                "<aura:component invalidAttribute=''/>", null);
        MasterDefRegistryImpl registry = getDefRegistry(true);
        try {
            registry.getUid(null, cmpDesc);
            fail("Expected InvalidDefinitionException");
        } catch (Throwable t) {
            checkExceptionContains(t, InvalidDefinitionException.class,
                    "Invalid attribute \"invalidAttribute\"");
        }

        // another request for getUid will not re-compile again
        Mockito.reset(registry);
        try {
            registry.getUid(null, cmpDesc);
            fail("Expected InvalidDefinitionException");
        } catch (Throwable e) {
            checkExceptionContains(e, InvalidDefinitionException.class,
                    "Invalid attribute \"invalidAttribute\"");
        }
        Mockito.verify(registry, Mockito.times(0)).compileDE(Mockito.eq(cmpDesc));
    }

    public void testCompileDef() throws Exception {
        // create test component with 2 explicit dependencies
        DefDescriptor<ComponentDef> cmpDesc1 = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        DefDescriptor<ComponentDef> cmpDesc2 = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(
                ComponentDef.class,
                String.format(baseComponentTag, "",
                        String.format("<%s/><%s/>", cmpDesc1.getDescriptorName(), cmpDesc2.getDescriptorName())));

        // spy on MDR
        final MasterDefRegistryImpl registry = getDefRegistry(true);
        registry.invalidate(null);
        spyOnDefs(registry);

        // get def UID to trigger compileDef, etc.
        String uid = registry.getUid(null, cmpDesc);
        assertNotNull(uid);
        ComponentDef def = registry.getDef(cmpDesc);
        assertNotNull(def);
        Mockito.verify(registry, Mockito.times(1)).compileDE(Mockito.eq(cmpDesc));
        assertCompiledDef(def);

        // check all dependencies
        MockUtil mockUtil = new MockUtil();
        Set<DefDescriptor<?>> dependencies = registry.getDependencies(uid);
        for (DefDescriptor<?> dep : dependencies) {
            Definition depDef = registry.getDef(dep);
            if (mockUtil.isMock(depDef)) {
                // why not controllers?
                if (dep.getDefType().equals(DefType.CONTROLLER)) {
                    continue;
                }
                assertCompiledDef(depDef);
            }
        }
    }

    public void testCompileDefLocalDef() throws Exception {
        // build a mock def
        String descName = String.format("%s:ghost", System.nanoTime());
        ComponentDef def = Mockito.mock(ComponentDef.class);

        Mockito.doReturn(DefDescriptorImpl.getInstance(descName, ComponentDef.class)).when(def).getDescriptor();

        // spy on MDR's registries to spy on defs
        final MasterDefRegistryImpl registry = getDefRegistry(true);
        registry.invalidate(null);
        spyOnDefs(registry);
        registry.addLocalDef(def);

        // get def UID to trigger compileDef, etc.
        String uid = registry.getUid(null, def.getDescriptor());
        assertNotNull(uid);
        Mockito.verify(registry, Mockito.times(1)).compileDE(Mockito.eq(def.getDescriptor()));
        Mockito.doReturn(true).when(def).isValid();
        assertCompiledDef(def);

        // check all dependencies
        MockUtil mockUtil = new MockUtil();
        Set<DefDescriptor<?>> dependencies = registry.getDependencies(uid);
        for (DefDescriptor<?> dep : dependencies) {
            Definition depDef = registry.getDef(dep);
            if (mockUtil.isMock(depDef)) {
                assertCompiledDef(depDef);
            }
        }
    }

    public void testCompileDefOnlyOnce() throws Exception {
        // getDef on registry should compile the def
        String cmpContent = "<aura:component/>";
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class, cmpContent);
        MasterDefRegistryImpl registry = getDefRegistry(true);
        registry.getDef(cmpDesc);
        Mockito.verify(registry, Mockito.times(1)).compileDE(Mockito.eq(cmpDesc));

        // another getDef on same registry should not re-compile the def
        Mockito.reset(registry);
        assertNotNull(registry.getDef(cmpDesc));
        Mockito.verify(registry, Mockito.times(0)).compileDE(Mockito.eq(cmpDesc));

        // another getDef on other registry instance should now compile zero additional times
        registry = getDefRegistry(true);
        assertNotNull(registry.getDef(cmpDesc));
        Mockito.verify(registry, Mockito.times(0)).compileDE(Mockito.eq(cmpDesc));
    }

    public void testGetDefDescriptorNull() throws Exception {
        MasterDefRegistryImpl registry = getDefRegistry(false);
        assertNull(registry.getDef(null));
    }

    public void testGetDefDescriptorDoesntExist() throws Exception {
        MasterDefRegistryImpl registry = getDefRegistry(false);
        assertNull(registry.getDef(DefDescriptorImpl.getInstance("unknown:soldier", ComponentDef.class)));
    }

    public void testGetDefCachedForChangedDefinition() throws Exception {
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        MasterDefRegistryImpl registry = getDefRegistry(false);
        ComponentDef def = registry.getDef(cmpDesc);
        assertNull(def.getAttributeDef("str"));

        // Definition cached for current registry
        registry.getSource(cmpDesc).addOrUpdate(
                "<aura:component><aura:attribute name='str' type='String'/></aura:component>");
        ComponentDef defNew = registry.getDef(cmpDesc);
        assertNull(defNew.getAttributeDef("str"));

        // Definition not cached for new registry
        MasterDefRegistryImpl registryNext = getDefRegistry(false);
        ComponentDef defNext = registryNext.getDef(cmpDesc);
        assertNotNull(defNext.getAttributeDef("str"));
    }

    public void testGetDefCachedForRemovedDefinition() throws Exception {
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>", null);
        MasterDefRegistryImpl registry = getDefRegistry(false);
        ComponentDef def = registry.getDef(cmpDesc);
        assertNotNull(def);

        // Definition cached for current registry
        getAuraTestingUtil().removeSource(cmpDesc);
        ComponentDef defNew = registry.getDef(cmpDesc);
        assertNotNull(defNew);

        // Definition not cached for new registry
        MasterDefRegistryImpl registryNext = getDefRegistry(false);
        ComponentDef defNext = registryNext.getDef(cmpDesc);
        assertNull(defNext);
    }

    /**
     * Circular dependencies case 1: A has inner component B, and B has an explicit dependency on A (via aura:dependency
     * tag)
     */
    public void testCircularDependenciesInnerCmp() throws Exception {
        DefDescriptor<ComponentDef> cmpDescA = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        DefDescriptor<ComponentDef> cmpDescB = addSourceAutoCleanup(
                ComponentDef.class,
                String.format("<aura:component><aura:dependency resource=\"%s\"/></aura:component>",
                        cmpDescA.getQualifiedName()));
        updateStringSource(cmpDescA,
                String.format("<aura:component><%s/></aura:component>", cmpDescB.getDescriptorName()));
        // Circular dependency cases should result in identical dependencies
        assertIdenticalDependencies(cmpDescA, cmpDescB);
    }

    /**
     * Circular dependencies case 2: D extends C, and C has explicit dependency on D (via aura:dependency tag)
     */
    public void testCircularDependenciesExtendsCmp() throws Exception {
        DefDescriptor<ComponentDef> cmpDescC = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        DefDescriptor<ComponentDef> cmpDescD = addSourceAutoCleanup(ComponentDef.class,
                String.format("<aura:component extends=\"%s\"/>", cmpDescC.getDescriptorName()));
        updateStringSource(cmpDescC, String.format(
                "<aura:component extensible=\"true\"><aura:dependency resource=\"%s\"/></aura:component>",
                cmpDescD.getQualifiedName()));
        // Circular dependency cases should result in identical dependencies
        assertIdenticalDependencies(cmpDescC, cmpDescD);
    }

    /**
     * Circular dependencies case 3: E has dependency on F, and F has dependency on E (both through aura:dependency tag)
     */
    public void testCircularDependenciesDepTag() throws Exception {
        DefDescriptor<ComponentDef> cmpDescE = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        DefDescriptor<ComponentDef> cmpDescF = addSourceAutoCleanup(
                ComponentDef.class,
                String.format("<aura:component><aura:dependency resource=\"%s\"/></aura:component>",
                        cmpDescE.getQualifiedName()));
        updateStringSource(
                cmpDescE,
                String.format("<aura:component><aura:dependency resource=\"%s\"/></aura:component>",
                        cmpDescF.getQualifiedName()));
        // Circular dependency cases should result in identical dependencies
        assertIdenticalDependencies(cmpDescE, cmpDescF);
    }

    /**
     * Verify correct dependencies are attached to a component.
     */
    public void testGetDependencies() throws Exception {
        DefDescriptor<ComponentDef> depCmpDesc1 = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        DefDescriptor<ComponentDef> depCmpDesc2 = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        DefDescriptor<ComponentDef> cmpDesc1 = addSourceAutoCleanup(ComponentDef.class, "<aura:component/>");
        // Manually add dependency to inner component
        DefDescriptor<ComponentDef> cmpDesc2 = addSourceAutoCleanup(ComponentDef.class,
                "<aura:component><aura:dependency resource=\"" + depCmpDesc1.getQualifiedName()
                        + "\"/></aura:component>");
        DefDescriptor<ComponentDef> cmpDesc = addSourceAutoCleanup(
                ComponentDef.class,
                String.format(
                        baseComponentTag,
                        "",
                        String.format("<aura:dependency resource=\"" + depCmpDesc2.getQualifiedName()
                                + "\"/><%s/><%s/>", cmpDesc1.getDescriptorName(), cmpDesc2.getDescriptorName())));

        MasterDefRegistryImpl registry = getDefRegistry(false);
        String uid = registry.getUid(null, cmpDesc);
        Set<DefDescriptor<?>> deps = registry.getDependencies(uid);
        assertTrue("Component should have dependency on aura:component by default",
                checkDependenciesContains(deps, "markup://aura:component"));
        assertTrue("Component should have dependency on aura:rootComponent by default",
                checkDependenciesContains(deps, "markup://aura:rootComponent"));
        assertTrue("Component should not have a dependency on aura:application",
                !checkDependenciesContains(deps, "markup://aura:application"));
        assertTrue("No dependency on self found in Component",
                checkDependenciesContains(deps, cmpDesc.getQualifiedName()));
        assertTrue("Dependency on inner component not found",
                checkDependenciesContains(deps, cmpDesc1.getQualifiedName()));
        assertTrue("Dependency on inner component not found",
                checkDependenciesContains(deps, cmpDesc2.getQualifiedName()));
        assertTrue("Explicitly declared dependency on inner component not found",
                checkDependenciesContains(deps, depCmpDesc1.getQualifiedName()));
        assertTrue("Explicitly declared dependency on top level component not found",
                checkDependenciesContains(deps, depCmpDesc2.getQualifiedName()));
    }

    /**
     * Verify that the file source listener picks up a newly created file and sends out a notification to clear the
     * proper caches.
     */
    // TODO(W-1589052): UnAdaptable since breaks when trying to write/delete files from jars
    @UnAdaptableTest
    @ThreadHostileTest("changes test namespace")
    public void testSourceChangeClearsCachesInDevMode() throws Exception {
        // Make sure we're in Dev mode.
        ContextService contextService = Aura.getContextService();
        if (contextService.isEstablished()) {
            contextService.endContext();
        }
        contextService.startContext(Mode.DEV, Format.JSON, Authentication.AUTHENTICATED);

        MasterDefRegistryImpl mdr = getDefRegistry(false);
        DefDescriptor<ComponentDef> cmpDesc = Aura.getDefinitionService().getDefDescriptor("test:deleteMeAfterTest",
                ComponentDef.class);

        // Make sure it's actually in the caches
        mdr.getDef(cmpDesc);

        // Get the UID before adding the file since getUid() messes with caches
        String uid = mdr.getUid(null, cmpDesc);

        // Tell test to delete added component and directory files at end of test
        Source<?> source = mdr.getSource(cmpDesc);
        File f = new File(source.getUrl().replace("file:", ""));
        deleteFileOnTeardown(f);
        deleteFileOnTeardown(f.getParentFile());

        // Save file to filesystem and wait for source change to clear caches
        BuilderService builderService = Aura.getBuilderService();
        ComponentDef def = builderService.getComponentDefBuilder().setDescriptor(cmpDesc).build();
        Aura.getDefinitionService().save(def);

        // Make sure we actually have something to clear from the cache before verifying it's not in there.
        // if (!isInDefsCache(cmpDesc, mdr)) {
        //    fail("Test setup failure: def not added to MasterDefRegistry cache");
        // }
        assertNotCached(cmpDesc, mdr, uid);
    }

    /**
     * Wait for MasterDefRegistry and CachingDefRegistry caches to be cleared after a source change.
     */
    private void assertNotCached(DefDescriptor<ComponentDef> cmpDesc, MasterDefRegistryImpl mdr, String uid)
            throws Exception {
        long startTime = System.nanoTime();
        long timeoutInMilliseconds = 10000;
        long intervalInMilliseconds = 100;

        while (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) < timeoutInMilliseconds) {
            if (isMdrCacheCleared(cmpDesc, mdr, uid)) {
                return;
            }
            Thread.sleep(intervalInMilliseconds);
        }
        fail("Caches did not clear within " + (timeoutInMilliseconds / 1000) + " seconds after a source change");
    }

    /**
     * Return true if DefDescriptor has been removed from MasterDefRegistry static cache. This does not take into
     * account the non-static local cache.
     */
    private boolean isMdrCacheCleared(DefDescriptor<ComponentDef> cmpDesc, MasterDefRegistryImpl mdr, String uid)
            throws Exception {
        Cache<String, DependencyEntry> dependencies = AuraPrivateAccessor.get(mdr, "depsCache");
        String key = AuraPrivateAccessor.invoke(mdr, "makeGlobalKey", uid, cmpDesc);
        Object cacheReturn = dependencies.getIfPresent(key);

        return cacheReturn == null && !isInDefsCache(cmpDesc, mdr);
    }

    /**
     * Verify caches are cleared after a source change to a component file. In this case only the component def itself
     * should be cleared from the cache.
     */
    @ThreadHostileTest("requires cache to remain stable")
    public void testInvalidateCacheCmpFile() throws Exception {
        MasterDefRegistryImpl mdr = getDefRegistry(false);

        Map<DefType, DefDescriptor<?>> defs = addDefsToCaches(mdr);
        DefDescriptor<?> cmpDef = defs.get(DefType.COMPONENT);
        Aura.getCachingService().notifyDependentSourceChange(Collections.<WeakReference<SourceListener>> emptySet(),
                cmpDef, SourceListener.SourceMonitorEvent.CHANGED, null);

        assertFalse("ComponentDef not cleared from cache", isInDefsCache(defs.get(DefType.COMPONENT), mdr));
        assertTrue("ControllerDef in same bundle as cmp should not be cleared from cache",
                isInDefsCache(defs.get(DefType.CONTROLLER), mdr));
        assertTrue("NamespaceDef should not be cleared from cache", isInDefsCache(defs.get(DefType.NAMESPACE), mdr));
    }

    /**
     * Verify caches are cleared after a source change to a namespace def file. In this case all items in the cache with
     * the same namespace as the def should be cleared.
     */
    @ThreadHostileTest("requires cache to remain stable")
    public void testInvalidateCacheNamespaceFile() throws Exception {
        MasterDefRegistryImpl mdr = getDefRegistry(false);

        Map<DefType, DefDescriptor<?>> defs = addDefsToCaches(mdr);
        DefDescriptor<?> namespaceDef = defs.get(DefType.NAMESPACE);
        Aura.getCachingService().notifyDependentSourceChange(Collections.<WeakReference<SourceListener>> emptySet(),
                namespaceDef, SourceListener.SourceMonitorEvent.CHANGED, null);

        assertFalse("NamespaceDef not cleared from cache", isInDefsCache(defs.get(DefType.NAMESPACE), mdr));
        assertFalse("ComponentDef in same namespace as changed namespaceDef not cleared from cache",
                isInDefsCache(defs.get(DefType.COMPONENT), mdr));
        assertFalse("ControllerDef in same namespace as changed namespaceDef not cleared from cache",
                isInDefsCache(defs.get(DefType.CONTROLLER), mdr));
        assertTrue("ControllerDef in different namespace as changed namespaceDef should not be cleared from cache",
                isInDefsCache(defs.get(DefType.RENDERER), mdr));
    }

    /**
     * Verify caches are cleared after a source change to a Layouts def file. In this case all items in the layouts
     * bundle should be cleared.
     */
    @ThreadHostileTest("requires cache to remain stable")
    public void testInvalidateCacheLayoutsFile() throws Exception {
        MasterDefRegistryImpl mdr = getDefRegistry(false);

        Map<DefType, DefDescriptor<?>> defs = addDefsToCaches(mdr);
        DefDescriptor<?> layoutsDef = defs.get(DefType.LAYOUTS);
        Aura.getCachingService().notifyDependentSourceChange(Collections.<WeakReference<SourceListener>> emptySet(),
                layoutsDef, SourceListener.SourceMonitorEvent.CHANGED, null);

        assertFalse("LayoutsDef not cleared from cache", isInDefsCache(defs.get(DefType.LAYOUTS), mdr));
        assertFalse("ApplicationDef in same bundle as LayoutsDef not cleared from cache",
                isInDefsCache(defs.get(DefType.APPLICATION), mdr));
        assertTrue("NamespaceDef should not be cleared from cache on LayoutsDef source change",
                isInDefsCache(defs.get(DefType.NAMESPACE), mdr));
        assertTrue("Cmp in same namespace but different bundle as Layouts def should not be cleared from cache",
                isInDefsCache(defs.get(DefType.COMPONENT), mdr));
    }

    /**
     * Create a set of DefDescriptors and add them to the MDR caches by calling getDef() on them.
     *
     * @return List of DefDescriptors that have been added to the mdr caches.
     */
    private Map<DefType, DefDescriptor<?>> addDefsToCaches(MasterDefRegistryImpl mdr) throws Exception {
        DefDescriptor<NamespaceDef> namespaceDef = DefDescriptorImpl.getInstance("test", NamespaceDef.class);
        DefDescriptor<ComponentDef> cmpDef = DefDescriptorImpl.getInstance("test:test_button",
                ComponentDef.class);
        DefDescriptor<ControllerDef> cmpControllerDef = DefDescriptorImpl.getInstance(
                "js://test.test_button", ControllerDef.class);
        DefDescriptor<RendererDef> otherNamespaceDef = DefDescriptorImpl.getInstance(
                "js://gvpTest.labelProvider", RendererDef.class);
        DefDescriptor<ApplicationDef> appInLayoutsBundleDef = DefDescriptorImpl.getInstance("test:layouts",
                ApplicationDef.class);
        DefDescriptor<LayoutsDef> layoutsDef = DefDescriptorImpl.getInstance("test:layouts",
                LayoutsDef.class);

        Map<DefType, DefDescriptor<?>> map = new HashMap<>();
        map.put(DefType.NAMESPACE, namespaceDef);
        map.put(DefType.COMPONENT, cmpDef);
        map.put(DefType.CONTROLLER, cmpControllerDef);
        map.put(DefType.RENDERER, otherNamespaceDef);
        map.put(DefType.APPLICATION, appInLayoutsBundleDef);
        map.put(DefType.LAYOUTS, layoutsDef);

        for (DefType defType : map.keySet()) {
            DefDescriptor<?> dd = map.get(defType);
            dd.getDef();
        }

        return map;
    }

    private boolean isInDescriptorFilterCache(DescriptorFilter filter, Set<DefDescriptor<?>> results,
                                              MasterDefRegistryImpl mdr) throws Exception {
        // taking the long road in determining what is in the cache because the current key implementation for
        // the descriptor cache is difficult to recreate.
        Cache<String, Set<DefDescriptor<?>>> cache = AuraPrivateAccessor.get(mdr, "descriptorFilterCache");
        for (String key : cache.getKeySet()) {
            if (key.startsWith(filter.toString() + "|")) {
                return results.equals(cache.getIfPresent(key));
            }
        }
        return false;
    }

    private boolean isInDepsCache(DefDescriptor<?> dd, MasterDefRegistryImpl mdr) throws Exception {
        Cache<String, ?> cache = AuraPrivateAccessor.get(mdr, "depsCache");
        String ddKey = dd.getDescriptorName().toLowerCase();
        for (String key : cache.getKeySet()) {
            if (key.endsWith(ddKey)) {
                return cache.getIfPresent(key) != null;
            }
        }
        return false;
    }

    private boolean isInDefsCache(DefDescriptor<?> dd, MasterDefRegistryImpl mdr) throws Exception {
        Cache<DefDescriptor<?>, Optional<? extends Definition>> cache = AuraPrivateAccessor.get(mdr, "defsCache");
        return null != cache.getIfPresent(dd);
    }

    private boolean isInExistsCache(DefDescriptor<?> dd, MasterDefRegistryImpl mdr) throws Exception {
        Cache<DefDescriptor<?>, Boolean> cache = AuraPrivateAccessor.get(mdr, "existsCache");
        return Boolean.TRUE == cache.getIfPresent(dd);
    }

    /**
     * Verify basic functionality of MasterDefRegistryImpl.getClientLibraries. The same methods are test in
     * ClientLibraryServiceImplTest, where we use ClientLibraryService.getUrls()
     *
     * @throws Exception
     */
    public void testGetClientLibraries() throws Exception {
        MasterDefRegistry mdr = getAuraMDR();
        List<ClientLibraryDef> libDefs = mdr.getClientLibraries(null);
        assertNull(libDefs);

        DefDescriptor<ApplicationDef> appDesc = Aura.getDefinitionService().getDefDescriptor(
                "clientLibraryTest:testDependencies", ApplicationDef.class);
        AuraContext cntx = Aura.getContextService().getCurrentContext();
        cntx.setApplicationDescriptor(appDesc);
        Aura.getDefinitionService().updateLoaded(appDesc);

        libDefs = mdr.getClientLibraries(cntx.getUid(appDesc));

        // 13 from clientLibraryTest:testDependencies and its dependencies + 4 from aura:component
        // Update this number when you add new aura:clientLibrary tags to these components
        assertEquals(16, libDefs.size());
    }

    public void testAssertAccess_IfGlobalAccessThenPassesCheck() throws Exception {
        when(globalDef.getAccess()).thenReturn(defAccess);
        when(defAccess.isGlobal()).thenReturn(true);
        MasterDefRegistry mdr = getAuraMDR();
        mdr.assertAccess(null, globalDef);

        verify(globalDef).getAccess();
        verify(defAccess).isGlobal();
    }

    public void testAssertAccess_IfReferencedByUnsecuredPrefixThenPassesCheck() throws Exception {
        when(globalDef.getAccess()).thenReturn(defAccess);
        when(defAccess.isGlobal()).thenReturn(false);
        when(defAccess.requiresAuthentication()).thenReturn(true);
        when(referencingDesc.getPrefix()).thenReturn("aura");
        MasterDefRegistry mdr = getAuraMDR();
        mdr.assertAccess(referencingDesc, globalDef);

        verify(referencingDesc).getPrefix();
    }

    /**
     * Verify that if access cache has a reason to block access, then MDR throws NoAccessException.
     *
     * @throws Exception
     */
    public void testAssertAccess_UsesCachedValueIfPresent_BlockAccess() throws Exception {
        DefDescriptor<ComponentDef> desc = addSourceAutoCleanup(ComponentDef.class,
                String.format(baseComponentTag, "", ""));
        when(mockAccessCheckCache.getIfPresent(anyString())).thenReturn("Error");
        MasterDefRegistryImpl mdr = (MasterDefRegistryImpl) getAuraMDR();
        try {
            mdr.assertAccess(null, desc.getDef(), mockAccessCheckCache);
            fail("Expected NoAccessException because accessCache has reason to block def");
        } catch (Exception e) {
            this.assertExceptionMessageStartsWith(e, NoAccessException.class, "Error");
        }
        verify(mockAccessCheckCache).getIfPresent(anyString());
    }

    /**
     * Verify that if access cache doesn't have any message to block access, then access checks passes through.
     *
     * @throws Exception
     */
    public void testAssertAccess_UsesCachedValueIfPresent_AllowAccess() throws Exception {
        DefDescriptor<ComponentDef> desc = addSourceAutoCleanup(ComponentDef.class,
                String.format(baseComponentTag, "", ""));
        when(mockAccessCheckCache.getIfPresent(anyString())).thenReturn("");
        MasterDefRegistryImpl mdr = (MasterDefRegistryImpl) getAuraMDR();
        mdr.assertAccess(null, desc.getDef(), mockAccessCheckCache);

        verify(mockAccessCheckCache).getIfPresent(anyString());
    }

    public void testAssertAccess_StoreAccessInfoInCacheIfNotPresent() throws Exception {
        DefDescriptor<ComponentDef> desc = getAuraTestingUtil().addSourceAutoCleanup(ComponentDef.class,
                String.format(baseComponentTag, "", ""), StringSourceLoader.DEFAULT_CUSTOM_NAMESPACE + ":testComp",
                false);
        when(mockAccessCheckCache.getIfPresent(anyString())).thenReturn(null);

        MasterDefRegistryImpl mdr = (MasterDefRegistryImpl) getAuraMDR();
        mdr.assertAccess(desc, desc.getDef(), mockAccessCheckCache);

        verify(mockAccessCheckCache).put(anyString(), anyString());

        mdr.assertAccess(desc, desc.getDef(), mockAccessCheckCache);
        verify(mockAccessCheckCache, times(2)).getIfPresent(anyString());
    }

    public void testExistsCache() throws Exception {
        ConfigAdapter configAdapter = Aura.getConfigAdapter();
        MasterDefRegistry mdr = getAuraMDR();
        MasterDefRegistryImpl mdri = (MasterDefRegistryImpl) mdr;
        Map<DefType, DefDescriptor<?>> defs = addDefsToCaches(mdri);
        Map<DefType, DefDescriptor<?>> nonPrivDefs = addNonPriveledgedDefsToMDR(mdri);
        for (DefDescriptor<?> dd : defs.values()) {
            assertTrue(dd + " should exist.", dd.exists());
        }
        for (DefDescriptor<?> dd : nonPrivDefs.values()) {
            assertTrue(dd + " should exist.", dd.exists());
        }

        DefDescriptor<?> nsDef = defs.get(DefType.NAMESPACE);
        DefDescriptor<?> layoutDef = defs.get(DefType.LAYOUTS);
        DefDescriptor<?> rendererDef = defs.get(DefType.RENDERER);
        DefDescriptor<?> appDef = defs.get(DefType.APPLICATION);
        DefDescriptor<?> controllerDef = defs.get(DefType.CONTROLLER);
        DefDescriptor<?> cmpDef = defs.get(DefType.COMPONENT);

        DefDescriptor<?> npNSDef = nonPrivDefs.get(DefType.NAMESPACE);
        DefDescriptor<?> npLayoutDef = nonPrivDefs.get(DefType.LAYOUTS);
        DefDescriptor<?> npRendererDef = nonPrivDefs.get(DefType.RENDERER);
        DefDescriptor<?> nsAppDef = nonPrivDefs.get(DefType.APPLICATION);
        DefDescriptor<?> nsControllerDef = nonPrivDefs.get(DefType.CONTROLLER);
        DefDescriptor<?> nsCmpDef = nonPrivDefs.get(DefType.COMPONENT);

        // only picking 3 defs to test the ns as they are mostly dupes
        assertTrue(nsDef.getNamespace() + "  should have been isPriveleged",
                configAdapter.isPrivilegedNamespace(nsDef.getNamespace()));
        assertTrue(layoutDef.getNamespace() + "  should have been isPriveleged",
                configAdapter.isPrivilegedNamespace(layoutDef.getNamespace()));
        assertTrue(rendererDef.getNamespace() + "  should have been isPriveleged",
                configAdapter.isPrivilegedNamespace(rendererDef.getNamespace()));

        assertFalse(npLayoutDef.getNamespace() + "  should not have been isPriveleged",
                configAdapter.isPrivilegedNamespace(npLayoutDef.getNamespace()));
        assertFalse(npNSDef.getNamespace() + "  should not have been isPriveleged",
                configAdapter.isPrivilegedNamespace(npNSDef.getNamespace()));
        assertFalse(npRendererDef.getNamespace() + "  should not have been isPriveleged",
                configAdapter.isPrivilegedNamespace(npRendererDef.getNamespace()));

        MasterDefRegistry mdr2 = restartContextGetNewMDR();
        MasterDefRegistryImpl mdri2 = (MasterDefRegistryImpl) mdr2;

        // objects wont be in eists cache yet, just defsCache, need to call exists to prime exists cache

        for (DefDescriptor<?> dd : defs.values()) {
            assertTrue(dd + " should exist.", dd.exists());
        }
        assertTrue("nsDef is in cache", isInExistsCache(nsDef, mdri2));
        assertTrue("LayoutsDef is in cache", isInExistsCache(defs.get(DefType.LAYOUTS), mdri2));
        assertTrue("RendererDef is in cache", isInExistsCache(rendererDef, mdri2));
        assertTrue("app is in cache", isInExistsCache(appDef, mdri2));
        assertTrue("controller is in cache", isInExistsCache(controllerDef, mdri2));
        assertTrue("cmp is in cache", isInExistsCache(cmpDef, mdri2));

        assertFalse("npNSDef is not in cache", isInExistsCache(npNSDef, mdri2));
        assertFalse("npLayoutsDef is not in cache", isInExistsCache(npLayoutDef, mdri2));
        assertFalse("npRendererDef is notin cache", isInExistsCache(npRendererDef, mdri2));
        assertFalse("nsApp is not in cache", isInExistsCache(nsAppDef, mdri2));
        assertFalse("nsController is not in cache", isInExistsCache(nsControllerDef, mdri2));
        assertFalse("nsCmp is not in cache", isInExistsCache(nsCmpDef, mdri2));

        MasterDefRegistry mdr3 = restartContextGetNewMDR();
        MasterDefRegistryImpl mdri3 = (MasterDefRegistryImpl) mdr3;

        assertTrue("nsDef is in cache", isInExistsCache(nsDef, mdri3));
        assertTrue("LayoutsDef is in cache", isInExistsCache(defs.get(DefType.LAYOUTS), mdri3));
        assertTrue("RendererDef is in cache", isInExistsCache(rendererDef, mdri3));
        assertTrue("app is in cache", isInExistsCache(appDef, mdri3));
        assertTrue("controller is in cache", isInExistsCache(controllerDef, mdri3));
        assertTrue("cmp is in cache", isInExistsCache(cmpDef, mdri3));

        assertFalse("npNSDef is not in cache", isInExistsCache(npNSDef, mdri3));
        assertFalse("npLayoutsDef is not in cache", isInExistsCache(npLayoutDef, mdri3));
        assertFalse("npRendererDef is notin cache", isInExistsCache(npRendererDef, mdri3));
        assertFalse("nsApp is not in cache", isInExistsCache(nsAppDef, mdri3));
        assertFalse("nsController is not in cache", isInExistsCache(nsControllerDef, mdri3));
        assertFalse("nsCmp is not in cache", isInExistsCache(nsCmpDef, mdri3));
    }

    public void testDefsCache() throws Exception {
        ConfigAdapter configAdapter = Aura.getConfigAdapter();
        MasterDefRegistry mdr = getAuraMDR();
        MasterDefRegistryImpl mdri = (MasterDefRegistryImpl) mdr;
        Map<DefType, DefDescriptor<?>> defs = addDefsToCaches(mdri);
        Map<DefType, DefDescriptor<?>> nonPrivDefs = addNonPriveledgedDefsToMDR(mdri);

        DefDescriptor<?> nsDef = defs.get(DefType.NAMESPACE);
        DefDescriptor<?> layoutDef = defs.get(DefType.LAYOUTS);
        DefDescriptor<?> rendererDef = defs.get(DefType.RENDERER);
        DefDescriptor<?> appDef = defs.get(DefType.APPLICATION);
        DefDescriptor<?> controllerDef = defs.get(DefType.CONTROLLER);
        DefDescriptor<?> cmpDef = defs.get(DefType.COMPONENT);

        DefDescriptor<?> npNSDef = nonPrivDefs.get(DefType.NAMESPACE);
        DefDescriptor<?> npLayoutDef = nonPrivDefs.get(DefType.LAYOUTS);
        DefDescriptor<?> npRendererDef = nonPrivDefs.get(DefType.RENDERER);
        DefDescriptor<?> nsAppDef = nonPrivDefs.get(DefType.APPLICATION);
        DefDescriptor<?> nsControllerDef = nonPrivDefs.get(DefType.CONTROLLER);
        DefDescriptor<?> nsCmpDef = nonPrivDefs.get(DefType.COMPONENT);

        // only picking 3 defs to test the ns as they are mostly dupes
        assertTrue(nsDef.getNamespace() + "  should have been isPriveleged",
                configAdapter.isPrivilegedNamespace(nsDef.getNamespace()));
        assertTrue(layoutDef.getNamespace() + "  should have been isPriveleged",
                configAdapter.isPrivilegedNamespace(layoutDef.getNamespace()));
        assertTrue(rendererDef.getNamespace() + "  should have been isPriveleged",
                configAdapter.isPrivilegedNamespace(rendererDef.getNamespace()));

        assertFalse(npLayoutDef.getNamespace() + "  should not have been isPriveleged",
                configAdapter.isPrivilegedNamespace(npLayoutDef.getNamespace()));
        assertFalse(npNSDef.getNamespace() + "  should not have been isPriveleged",
                configAdapter.isPrivilegedNamespace(npNSDef.getNamespace()));
        assertFalse(npRendererDef.getNamespace() + "  should not have been isPriveleged",
                configAdapter.isPrivilegedNamespace(npRendererDef.getNamespace()));

        assertTrue("nsDef is in cache", isInDefsCache(nsDef, mdri));
        assertTrue("LayoutsDef is in cache", isInDefsCache(layoutDef, mdri));
        assertTrue("RendererDef is in cache", isInDefsCache(rendererDef, mdri));
        assertTrue("app is in cache", isInDefsCache(appDef, mdri));
        assertTrue("controller is in cache", isInDefsCache(controllerDef, mdri));
        assertTrue("cmp is in cache", isInDefsCache(cmpDef, mdri));

        assertFalse("npNSDef is not in cache", isInDefsCache(npNSDef, mdri));
        assertFalse("npLayoutsDef is not in cache", isInDefsCache(npLayoutDef, mdri));
        assertFalse("npRendererDef is not in cache", isInDefsCache(npRendererDef, mdri));
        assertFalse("nsApp is not in cache", isInDefsCache(nsAppDef, mdri));
        assertFalse("nsController is not in cache", isInDefsCache(nsControllerDef, mdri));
        assertFalse("nsCmp is not in cache", isInDefsCache(nsCmpDef, mdri));

        MasterDefRegistry mdr2 = restartContextGetNewMDR();
        MasterDefRegistryImpl mdri2 = (MasterDefRegistryImpl) mdr2;

        assertTrue("nsDef is in cache", isInDefsCache(nsDef, mdri2));
        assertTrue("LayoutsDef is in cache", isInDefsCache(defs.get(DefType.LAYOUTS), mdri2));
        assertTrue("RendererDef is in cache", isInDefsCache(rendererDef, mdri2));
        assertTrue("app is in cache", isInDefsCache(appDef, mdri2));
        assertTrue("controller is in cache", isInDefsCache(controllerDef, mdri2));
        assertTrue("cmp is in cache", isInDefsCache(cmpDef, mdri2));

        assertFalse("npNSDef is not in cache", isInDefsCache(npNSDef, mdri2));
        assertFalse("npLayoutsDef is not in cache", isInDefsCache(npLayoutDef, mdri2));
        assertFalse("npRendererDef is notin cache", isInDefsCache(npRendererDef, mdri2));
        assertFalse("nsApp is not in cache", isInDefsCache(nsAppDef, mdri2));
        assertFalse("nsController is not in cache", isInDefsCache(nsControllerDef, mdri2));
        assertFalse("nsCmp is not in cache", isInDefsCache(nsCmpDef, mdri2));
    }

    public void testDescriptorFilterCache() throws Exception {
        ConfigAdapter configAdapter = Aura.getConfigAdapter();
        MasterDefRegistry mdr = getAuraMDR();
        MasterDefRegistryImpl mdri = (MasterDefRegistryImpl) mdr;
        Map<DefType, DefDescriptor<?>> defs = addDefsToCaches(mdri);
        Map<DefType, DefDescriptor<?>> nonPrivDefs = addNonPriveledgedDefsToMDR(mdri);

        DefDescriptor<?> nsDef = defs.get(DefType.NAMESPACE);
        DefDescriptor<?> layoutDef = defs.get(DefType.LAYOUTS);
        DefDescriptor<?> rendererDef = defs.get(DefType.RENDERER);

        DefDescriptor<?> npNSDef = nonPrivDefs.get(DefType.NAMESPACE);
        DefDescriptor<?> npLayoutDef = nonPrivDefs.get(DefType.LAYOUTS);
        DefDescriptor<?> npRendererDef = nonPrivDefs.get(DefType.RENDERER);

        // only picking 3 defs to test the ns as they are mostly dupes
        assertTrue(nsDef.getNamespace() + "  should have been isPriveleged",
                configAdapter.isPrivilegedNamespace(nsDef.getNamespace()));
        assertTrue(layoutDef.getNamespace() + "  should have been isPriveleged",
                configAdapter.isPrivilegedNamespace(layoutDef.getNamespace()));
        assertTrue(rendererDef.getNamespace() + "  should have been isPriveleged",
                configAdapter.isPrivilegedNamespace(rendererDef.getNamespace()));

        assertFalse(npLayoutDef.getNamespace() + "  should not have been isPriveleged",
                configAdapter.isPrivilegedNamespace(npLayoutDef.getNamespace()));
        assertFalse(npNSDef.getNamespace() + "  should not have been isPriveleged",
                configAdapter.isPrivilegedNamespace(npNSDef.getNamespace()));
        assertFalse(npRendererDef.getNamespace() + "  should not have been isPriveleged",
                configAdapter.isPrivilegedNamespace(npRendererDef.getNamespace()));

        DescriptorFilter filter = new DescriptorFilter("*://test:*");
        Set<DefDescriptor<?>> results = mdr.find(filter);
        assertTrue("results should be cached", isInDescriptorFilterCache(filter, results, mdri));
        DescriptorFilter filter2 = new DescriptorFilter("*://gvpTest:*");
        Set<DefDescriptor<?>> results2 = mdr.find(filter2);
        assertTrue("results2 should be cached", isInDescriptorFilterCache(filter2, results2, mdri));

        DescriptorFilter filter3 = new DescriptorFilter("*://cstring:*");
        Set<DefDescriptor<?>> results3 = mdr.find(filter3);
        assertFalse("results3 should not be cached", isInDescriptorFilterCache(filter3, results3, mdri));
        DescriptorFilter filter4 = new DescriptorFilter("*://cstring1:*");
        Set<DefDescriptor<?>> results4 = mdr.find(filter4);
        assertFalse("results4 should be cached", isInDescriptorFilterCache(filter4, results4, mdri));

        DescriptorFilter filter5 = new DescriptorFilter("*://*:*");
        Set<DefDescriptor<?>> results5 = mdr.find(filter5);
        assertFalse("results5 should not be cached", isInDescriptorFilterCache(filter5, results5, mdri));
        // DescriptorFilter filter6 = new DescriptorFilter("*://*test:*");
        // Set<DefDescriptor<?>> results6 = mdr.find(filter6);
        // assertFalse("results6 should be cached", isInDescriptorFilterCache(filter6, results6, mdri));

        MasterDefRegistry mdr2 = restartContextGetNewMDR();
        MasterDefRegistryImpl mdri2 = (MasterDefRegistryImpl) mdr2;
        assertTrue("results should still be cached", isInDescriptorFilterCache(filter, results, mdri2));
        assertTrue("results2 should still be cached", isInDescriptorFilterCache(filter2, results2, mdri2));
        assertFalse("results3 should not be cached", isInDescriptorFilterCache(filter3, results3, mdri2));
        assertFalse("results4 should not be cached", isInDescriptorFilterCache(filter4, results4, mdri2));
        assertFalse("results5 should not be cached", isInDescriptorFilterCache(filter5, results5, mdri2));
        // assertFalse("results6 should not be cached", isInDescriptorFilterCache(filter6, results6, mdri2));
    }

    public void testDepsCache() throws Exception {
        String unprivilegedNamespace = getAuraTestingUtil().getNonce("alien");

        // in privileged namespace
        DefDescriptor<ComponentDef> privilegedCmp = getAuraTestingUtil().addSourceAutoCleanup(
                ComponentDef.class, String.format(baseComponentTag, "access='global'", ""), null, true);
        // in unprivileged namespace depending on privileged cmp
        DefDescriptor<ComponentDef> unprivilegedCmp = getAuraTestingUtil().addSourceAutoCleanup(
                DefDescriptorImpl.getInstance(String.format("markup://%s:cmp", unprivilegedNamespace),
                        ComponentDef.class),
                String.format(baseComponentTag, "access='global'",
                        String.format("<%s/>", privilegedCmp.getDescriptorName())),
                false);

        // in privileged namespace depending on unprivileged cmp
        DefDescriptor<ComponentDef> privilegedRoot = getAuraTestingUtil().addSourceAutoCleanup(
                ComponentDef.class,
                String.format(baseComponentTag, "access='global'",
                        String.format("<%s/>", unprivilegedCmp.getDescriptorName())),
                null, true);

        ConfigAdapter configAdapter = Aura.getConfigAdapter();
        assertTrue(configAdapter.isPrivilegedNamespace(privilegedCmp.getNamespace()));
        assertFalse(configAdapter.isPrivilegedNamespace(unprivilegedCmp.getNamespace()));
        assertTrue(configAdapter.isPrivilegedNamespace(privilegedRoot.getNamespace()));

        MasterDefRegistry mdr = Aura.getContextService().getCurrentContext().getDefRegistry();
        MasterDefRegistryImpl mdri = (MasterDefRegistryImpl) mdr;
        mdr.getDef(privilegedCmp);
        assertTrue(isInDepsCache(privilegedCmp, mdri));
        assertFalse(isInDepsCache(unprivilegedCmp, mdri));
        assertFalse(isInDepsCache(privilegedRoot, mdri));

        mdr.invalidate(DefDescriptorImpl.getInstance("aura:component", ComponentDef.class)); // invalidate the world
        mdr.getDef(unprivilegedCmp);
        assertFalse(isInDepsCache(privilegedCmp, mdri));
        assertFalse(isInDepsCache(unprivilegedCmp, mdri));
        assertFalse(isInDepsCache(privilegedRoot, mdri));

        mdr.invalidate(DefDescriptorImpl.getInstance("aura:component", ComponentDef.class)); // invalidate the world
        try {
            mdr.getDef(privilegedRoot);
            fail("Shouldn't be able to have a privileged cmp depend on an unprivileged cmp");
        } catch (Throwable t) {
            this.assertExceptionMessageStartsWith(t,
                    DefinitionNotFoundException.class, String.format(
                            "No COMPONENT named %s found",
                            unprivilegedCmp.getQualifiedName()));
        }
    }

    public void testJavaProtocolIsCached() throws Exception {
        DefDescriptor<ControllerDef> controllerDef = DefDescriptorImpl.getInstance(
                "java://org.auraframework.java.controller.TestController", ControllerDef.class);
        controllerDef.getDef();
        String prefix = controllerDef.getPrefix();
        assertEquals(prefix, "java");

        ConfigAdapter configAdapter = Aura.getConfigAdapter();
        assertFalse(configAdapter.isPrivilegedNamespace(controllerDef.getNamespace()));

        MasterDefRegistry mdr = Aura.getContextService().getCurrentContext().getDefRegistry();
        // mdr.getDef(controllerDef);
        MasterDefRegistryImpl mdri = (MasterDefRegistryImpl) mdr;
        assertTrue(isInDepsCache(controllerDef, mdri));
    }

    private MasterDefRegistry restartContextGetNewMDR() {
        // simulate new request
        MasterDefRegistry mdr = getAuraMDR();
        AuraContext ctx = Aura.getContextService().getCurrentContext();
        Mode mode = ctx.getMode();
        Format format = ctx.getFormat();
        Authentication access = ctx.getAccess();
        Aura.getContextService().endContext();

        Aura.getContextService().startContext(mode, format, access);
        MasterDefRegistry mdr2 = getAuraMDR();
        assertFalse("MasterDefRegistry should be different after restart of context", mdr == mdr2);
        return mdr2;
    }

    /**
     * Create a set of DefDescriptors and add them to the MDR caches by calling getDef() on them.
     *
     * @return List of DefDescriptors that have been added to the mdr caches.
     */
    private Map<DefType, DefDescriptor<?>> addNonPriveledgedDefsToMDR(MasterDefRegistryImpl mdr) throws Exception {
        DefDescriptor<NamespaceDef> namespaceDef = DefDescriptorImpl.getInstance("cstring", NamespaceDef.class);
        DefDescriptor<ComponentDef> cmpDef = getAuraTestingUtil().addSourceAutoCleanup(ComponentDef.class,
                "<aura:component>"
                        + "<aura:attribute name='label' type='String'/>"
                        + "<aura:attribute name='class' type='String'/>"
                        + "<aura:registerevent name='press' type='test:test_press'/>"
                        + "<div onclick='{!c.press}' class='{!v.class}'>{!v.label}</div>"
                        + "</aura:component>", "cstring:test_button", false);
        DefDescriptor<ControllerDef> cmpControllerDef = getAuraTestingUtil().addSourceAutoCleanup(ControllerDef.class,
                "{    press : function(cmp, event){        cmp.getEvent('press').fire();    }}",
                "cstring.test_button", false);
        DefDescriptor<RendererDef> otherNamespaceDef = getAuraTestingUtil()
                .addSourceAutoCleanup(
                        RendererDef.class,
                        "({render: function(cmp) {"
                                + "cmp.getValue('v.simplevalue1').setValue($A.get('$Label' + '.Related_Lists' + '.task_mode_today', cmp));"
                                + "cmp.getValue('v.simplevalue2').setValue($A.get('$Label.DOESNT.EXIST', cmp));"
                                + "cmp.getValue('v.simplevalue3').setValue($A.get('$Label.Related_Lists.DOESNTEXIST', cmp));"
                                + "// Both section and name are required. This request will return undefined and no action is requested."
                                + "cmp.getValue('v.simplevalue4').setValue($A.get('$Label.DOESNTEXIST', cmp));"
                                + "// These requests are here to test that there are no multiple action requests for the same $Label"
                                + "// See LabelValueProviderUITest.java"
                                + "var tmt = $A.get('$Label.Related_Lists.task_mode_today', cmp);"
                                + "tmt = $A.get('$Label.Related_Lists.task_mode_today', cmp);"
                                + "tmt = $A.get('$Label.Related_Lists.task_mode_today', cmp);"
                                + "tmt = $A.get('$Label.Related_Lists.task_mode_today', cmp);"
                                + "tmt = $A.get('$Label.Related_Lists.task_mode_today', cmp);"
                                + "return this.superRender();"
                                + "}})",
                        "cstring1.labelProvider", false);
        DefDescriptor<ApplicationDef> appInLayoutsBundleDef = getAuraTestingUtil().addSourceAutoCleanup(
                ApplicationDef.class,
                "<aura:application>    before    <div aura:id='content'/>    after</aura:application>",
                "cstring:layouts", false);
        DefDescriptor<LayoutsDef> layoutsDef = getAuraTestingUtil().addSourceAutoCleanup(LayoutsDef.class,
                "<aura:layouts default='def'>"
                        + "<aura:layout name='def'>"
                        + "<aura:layoutItem container='target' action='{!c.act}'/>"
                        + "</aura:layout>"
                        + "</aura:layouts>", "cstring:nplayout", false);

        Map<DefType, DefDescriptor<?>> map = new HashMap<>();
        map.put(DefType.NAMESPACE, namespaceDef);
        map.put(DefType.COMPONENT, cmpDef);
        map.put(DefType.CONTROLLER, cmpControllerDef);
        map.put(DefType.RENDERER, otherNamespaceDef);
        map.put(DefType.APPLICATION, appInLayoutsBundleDef);
        map.put(DefType.LAYOUTS, layoutsDef);

        for (DefType defType : map.keySet()) {
            DefDescriptor<?> dd = map.get(defType);
            dd.getDef();
        }

        return map;
    }

    private MasterDefRegistry getAuraMDR() {
        return Aura.getContextService().getCurrentContext().getDefRegistry();
    }

    /**
     * A fake type def, this could probably be a mock, the tricky part being isValid().
     */
    @SuppressWarnings("serial")
    private static class FakeTypeDef implements TypeDef {
        private boolean valid;
        DefDescriptor<TypeDef> desc;

        public FakeTypeDef(DefDescriptor<TypeDef> desc) {
            this.desc = desc;
        }

        @Override
        public void validateDefinition() throws QuickFixException {
        }

        @Override
        public void appendDependencies(Set<DefDescriptor<?>> dependencies) {
        }

        @Override
        public void validateReferences() throws QuickFixException {
        }

        @Override
        public void markValid() {
            valid = true;
        }

        @Override
        public boolean isValid() {
            return valid;
        }

        @Override
        public String getName() {
            return "name";
        }

        @Override
        public Location getLocation() {
            return null;
        }

        @Override
        public Visibility getVisibility() {
            return Visibility.PRIVATE;
        }

        @Override
        public DefinitionAccess getAccess() {
            return null;
        }

        @Override
        public <D extends Definition> D getSubDefinition(SubDefDescriptor<D, ?> descriptor) {
            return null;
        }

        @Override
        public void retrieveLabels() throws QuickFixException {
        }

        @Override
        public String getAPIVersion() {
            return null;
        }

        @Override
        public String getDescription() {
            return "description";
        }

        @Override
        public String getOwnHash() {
            return "aaaa";
        }

        @Override
        public void appendSupers(Set<DefDescriptor<?>> supers) throws QuickFixException {
        }

        @Override
        public void serialize(Json json) throws IOException {

        }

        @Override
        public DefDescriptor<TypeDef> getDescriptor() {
            return desc;
        }

        @Override
        public Object valueOf(Object stringRep) {
            return null;
        }

        @Override
        public Object wrap(Object o) {
            return null;
        }

        @Override
        public Object getExternalType(String prefix) throws QuickFixException {
            return null;
        }

        @Override
        public Object initialize(Object config, BaseComponent<?, ?> valueProvider) throws QuickFixException {
            return null;
        }

        @Override
        public void appendDependencies(Object instance, Set<DefDescriptor<?>> deps) {
        }
    }

    /**
     * A fake registry to check locking as we call.
     */
    @SuppressWarnings("serial")
    private static class FakeRegistry implements DefRegistry<TypeDef> {
        public DefDescriptor<TypeDef> desc;
        public TypeDef def;
        private final Lock rLock;
        private final Lock wLock;

        public FakeRegistry(Lock rLock, Lock wLock) {
            this.desc = Aura.getDefinitionService().getDefDescriptor("java://fake.type", TypeDef.class);
            this.def = new FakeTypeDef(desc);
            this.rLock = rLock;
            this.wLock = wLock;
        }

        @Override
        public TypeDef getDef(DefDescriptor<TypeDef> descriptor) throws QuickFixException {
            Mockito.verify(rLock, Mockito.times(1)).lock();
            Mockito.verify(rLock, Mockito.never()).unlock();
            if (descriptor.equals(desc)) {
                return def;
            }
            return null;
        }

        @Override
        public boolean hasFind() {
            return true;
        }

        @Override
        public Set<DefDescriptor<TypeDef>> find(DefDescriptor<TypeDef> matcher) {
            Mockito.verify(rLock, Mockito.times(1)).lock();
            Mockito.verify(rLock, Mockito.never()).unlock();
            Set<DefDescriptor<TypeDef>> found = Sets.newHashSet();
            found.add(desc);
            return found;
        }

        @Override
        public Set<DefDescriptor<?>> find(DescriptorFilter matcher) {
            Mockito.verify(rLock, Mockito.times(1)).lock();
            Mockito.verify(rLock, Mockito.never()).unlock();
            Set<DefDescriptor<?>> found = Sets.newHashSet();
            found.add(desc);
            return found;
        }

        @Override
        public void save(TypeDef def) {
            Mockito.verify(wLock, Mockito.times(1)).lock();
            Mockito.verify(wLock, Mockito.never()).unlock();
        }

        @Override
        public boolean exists(DefDescriptor<TypeDef> descriptor) {
            Mockito.verify(rLock, Mockito.times(1)).lock();
            Mockito.verify(rLock, Mockito.never()).unlock();
            return desc.equals(descriptor);
        }

        @Override
        public Set<DefType> getDefTypes() {
            Set<DefType> types = Sets.newHashSet();
            types.add(DefType.TYPE);
            return types;
        }

        @Override
        public Set<String> getPrefixes() {
            Set<String> prefixes = Sets.newHashSet();
            prefixes.add("java");
            return prefixes;
        }

        @Override
        public Set<String> getNamespaces() {
            Set<String> prefixes = Sets.newHashSet();
            prefixes.add("fake");
            return prefixes;
        }

        @Override
        public Source<TypeDef> getSource(DefDescriptor<TypeDef> descriptor) {
            return null;
        }

        @Override
        public void clear() {
            Mockito.verify(wLock, Mockito.times(1)).lock();
            Mockito.verify(wLock, Mockito.never()).unlock();
        }

        @Override
        public boolean isCacheable() {
            return true;
        }

        @Override
        public boolean isStatic() {
            return false;
        }
    }

    /**
     * A private class to hold all the info for a lock test.
     *
     * This sets up the mocks so that we can test locking, if it is instantiated, you _must_ call clear() in a finally
     * block. The locking is not real here, so have a care.
     */
    private static class LockTestInfo {
        public final MasterDefRegistryImpl mdr;
        public final FakeRegistry reg;
        public final Lock rLock;
        public final Lock wLock;

        public LockTestInfo() {
            ServiceLoader sl = ServiceLocatorMocker.spyOnServiceLocator();
            this.rLock = Mockito.mock(Lock.class, "rLock");
            this.wLock = Mockito.mock(Lock.class, "wLock");
            CachingService acs = Mockito.spy(sl.get(CachingService.class));
            Mockito.stub(sl.get(CachingService.class)).toReturn(acs);
            Mockito.stub(acs.getReadLock()).toReturn(rLock);
            Mockito.stub(acs.getWriteLock()).toReturn(wLock);
            this.reg = new FakeRegistry(rLock, wLock);
            this.mdr = new MasterDefRegistryImpl(reg);
        }

        public void clear() {
            ServiceLocatorMocker.unmockServiceLocator();
        }
    }

    /**
     * Test getDef to ensure locking is minimized.
     *
     * This asserts that within an MDR we only lock once for any number of getDef calls for a single def.
     */
    public void testGetDefLocking() throws Exception {
        LockTestInfo lti = null;

        lti = new LockTestInfo();
        try {
            assertEquals(lti.reg.def, lti.mdr.getDef(lti.reg.desc));
            Mockito.verify(lti.rLock, Mockito.times(1)).lock();
            Mockito.verify(lti.rLock, Mockito.times(1)).unlock();
            assertEquals(lti.reg.def, lti.mdr.getDef(lti.reg.desc));
            Mockito.verify(lti.rLock, Mockito.times(1)).lock();
            Mockito.verify(lti.rLock, Mockito.times(1)).unlock();
            Mockito.verify(lti.wLock, Mockito.never()).lock();
            Mockito.verify(lti.wLock, Mockito.never()).unlock();
        } finally {
            lti.clear();
        }
    }

    /**
     * Test save to ensure locking is minimized.
     *
     * This asserts that within an MDR we lock with a write lock for a save.
     */
    public void testSaveLocking() throws Exception {
        LockTestInfo lti = null;

        lti = new LockTestInfo();
        try {
            lti.mdr.save(lti.reg.def);
            Mockito.verify(lti.wLock, Mockito.times(1)).lock();
            Mockito.verify(lti.wLock, Mockito.times(1)).unlock();
            Mockito.verify(lti.rLock, Mockito.never()).lock();
            Mockito.verify(lti.rLock, Mockito.never()).unlock();
        } finally {
            lti.clear();
        }
    }

    /**
     * Test find(desc) to ensure locking is minimized.
     *
     * This asserts that within an MDR we only lock once for a call to find.
     */
    public void testFindDescLocking() throws Exception {
        LockTestInfo lti = null;

        lti = new LockTestInfo();
        try {
            lti.mdr.find(lti.reg.desc);
            Mockito.verify(lti.rLock, Mockito.times(1)).lock();
            Mockito.verify(lti.rLock, Mockito.times(1)).unlock();
            Mockito.verify(lti.wLock, Mockito.never()).lock();
            Mockito.verify(lti.wLock, Mockito.never()).unlock();
        } finally {
            lti.clear();
        }
    }

    /**
     * Test find(matcher) to ensure locking is minimized.
     *
     * This asserts that within an MDR we only lock once for a call to find.
     */
    public void testFindMatcherLocking() throws Exception {
        LockTestInfo lti = null;

        lti = new LockTestInfo();
        try {
            lti.mdr.find(new DescriptorFilter("bah:humbug"));
            Mockito.verify(lti.rLock, Mockito.times(1)).lock();
            Mockito.verify(lti.rLock, Mockito.times(1)).unlock();
            lti.mdr.find(new DescriptorFilter("bah:humbug"));
            // we always lock, so we cannot check for a single lock here.
            Mockito.verify(lti.rLock, Mockito.times(2)).lock();
            Mockito.verify(lti.rLock, Mockito.times(2)).unlock();
            Mockito.verify(lti.wLock, Mockito.never()).lock();
            Mockito.verify(lti.wLock, Mockito.never()).unlock();
        } finally {
            lti.clear();
        }
    }

    /**
     * Test exists to ensure locking is minimized.
     *
     * This asserts that within an MDR we only lock once for any number of calls to exists.
     */
    public void testExistsLocking() throws Exception {
        LockTestInfo lti = null;

        lti = new LockTestInfo();
        try {
            lti.mdr.exists(lti.reg.desc);
            Mockito.verify(lti.rLock, Mockito.times(1)).lock();
            Mockito.verify(lti.rLock, Mockito.times(1)).unlock();
            lti.mdr.exists(lti.reg.desc);
            Mockito.verify(lti.rLock, Mockito.times(1)).lock();
            Mockito.verify(lti.rLock, Mockito.times(1)).unlock();
            Mockito.verify(lti.wLock, Mockito.never()).lock();
            Mockito.verify(lti.wLock, Mockito.never()).unlock();
        } finally {
            lti.clear();
        }
    }

    /**
     * Test getUid for locking.
     *
     * getUid always takes the lock, maybe we should avoid this?
     */
    public void testGetUidLocking() throws Exception {
        LockTestInfo lti = null;

        lti = new LockTestInfo();
        try {
            lti.mdr.getUid(null, lti.reg.desc);
            Mockito.verify(lti.rLock, Mockito.times(1)).lock();
            Mockito.verify(lti.rLock, Mockito.times(1)).unlock();
            lti.mdr.getUid(null, lti.reg.desc);
            // We lock every time here. Probably should not.
            Mockito.verify(lti.rLock, Mockito.times(2)).lock();
            Mockito.verify(lti.rLock, Mockito.times(2)).unlock();
            Mockito.verify(lti.wLock, Mockito.never()).lock();
            Mockito.verify(lti.wLock, Mockito.never()).unlock();
        } finally {
            lti.clear();
        }
    }
}
TOP

Related Classes of org.auraframework.impl.system.MasterDefRegistryImplTest$LockTestInfo

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.