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

import java.io.File;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import org.auraframework.Aura;
import org.auraframework.def.DefDescriptor;
import org.auraframework.def.Definition;
import org.auraframework.impl.source.StringSourceLoader;
import org.auraframework.impl.util.AuraImplFiles;
import org.auraframework.service.DefinitionService;
import org.auraframework.system.AuraContext;
import org.auraframework.system.Source;
import org.auraframework.system.SourceListener;
import org.auraframework.system.SourceListener.SourceMonitorEvent;
import org.auraframework.test.AuraTestingUtil;

import com.google.common.collect.Sets;

public class AuraTestingUtilImpl implements AuraTestingUtil {
    private final Set<DefDescriptor<?>> cleanUpDds = Sets.newHashSet();
    private static AtomicLong nonce = new AtomicLong(System.currentTimeMillis());

    public AuraTestingUtilImpl() {
    }

    @Override
    public void setUp() {
        // Do nothing
    }

    @Override
    public void tearDown() {
        StringSourceLoader loader = StringSourceLoader.getInstance();
        for (DefDescriptor<?> dd : cleanUpDds) {
            loader.removeSource(dd);
        }
        cleanUpDds.clear();
    }

    @Override
    public File getAuraJavascriptSourceDirectory() {
        return AuraImplFiles.AuraJavascriptSourceDirectory.asFile();
    }

    @Override
    public String getNonce() {
        return Long.toString(nonce.incrementAndGet());
    }

    @Override
    public String getNonce(String prefix) {
        return (prefix == null ? "" : prefix) + getNonce();
    }

    @Override
    public <T extends Definition> Source<T> getSource(DefDescriptor<T> descriptor) {
        // Look up in the registry if a context is available. Otherwise, we're
        // probably running a context-less unit test
        // and better be using StringSourceLoader
        AuraContext context = Aura.getContextService().getCurrentContext();
        if (context != null) {
            return context.getDefRegistry().getSource(descriptor);
        } else {
            return StringSourceLoader.getInstance().getSource(descriptor);
        }
    }

    @Override
    public <T extends Definition> DefDescriptor<T> addSourceAutoCleanup(Class<T> defClass, String contents) {
        return addSourceAutoCleanup(defClass, contents, null);
    }

    @Override
    public <T extends Definition> DefDescriptor<T> addSourceAutoCleanup(Class<T> defClass, String contents,
            String namePrefix) {
        StringSourceLoader loader = StringSourceLoader.getInstance();
        DefDescriptor<T> descriptor = loader.addSource(defClass, contents, namePrefix).getDescriptor();
        cleanUpDds.add(descriptor);
        return descriptor;
    }

    @Override
    public <T extends Definition> DefDescriptor<T> addSourceAutoCleanup(DefDescriptor<T> descriptor, String contents) {
        StringSourceLoader loader = StringSourceLoader.getInstance();
        loader.putSource(descriptor, contents, false);
        cleanUpDds.add(descriptor);
        return descriptor;
    }

    @Override
    public <T extends Definition> void clearCachedDefs(Collection<T> defs) throws Exception {
        if (defs == null || defs.isEmpty()) {
            return;
        }

        // Get the Descriptors for the provided Definitions
        final DefinitionService definitionService = Aura.getDefinitionService();
        final Set<DefDescriptor<?>> cached = Sets.newHashSet();
        for (T def : defs) {
            if (def != null) {
                cached.add(def.getDescriptor());
            }
        }

        // Wait for the change notifications to get processed. We expect listeners to get processed in the order in
        // which they subscribe.
        final CountDownLatch latch = new CountDownLatch(cached.size());
        SourceListener listener = new SourceListener() {
            private Set<DefDescriptor<?>> descriptors = Sets.newHashSet(cached);
            @Override
            public void onSourceChanged(DefDescriptor<?> source, SourceMonitorEvent event) {
                if (descriptors.remove(source)) {
                    latch.countDown();
                }
                if (descriptors.isEmpty()) {
                    definitionService.unsubscribeToChangeNotification(this);
                }
            }
        };
        definitionService.subscribeToChangeNotification(listener);
        for (DefDescriptor<?> desc : cached) {
            definitionService.onSourceChanged(desc, SourceMonitorEvent.changed);
        }
        latch.await(30, TimeUnit.SECONDS);
    }
}

@@ -1361,16 +1361,73 @@
 ingUtil;
+%0Aimport org.auraframework.throwable.AuraRuntimeException;
 %0A%0Aimport
@@ -1522,16 +1522,80 @@
 gUtil %7B%0A
+    public static final long CACHE_CLEARING_TIMEOUT_SECS = 60;%0A%0A
     priv
@@ -4836,24 +4836,25 @@
 et(cached);%0A
+%0A
             
@@ -5437,24 +5437,29 @@
   %7D%0A        
+if (!
 latch.await(
@@ -5462,30 +5462,281 @@
 ait(
-30, TimeUnit.SECONDS);
+CACHE_CLEARING_TIMEOUT_SECS, TimeUnit.SECONDS)) %7B%0A            throw new AuraRuntimeException(String.format(%0A                    %22Timed out after %25s seconds waiting for cached Aura definitions to clear: %25s%22,%0A                    CACHE_CLEARING_TIMEOUT_SECS, defs));%0A        %7D
 %0A   

increase timeout and fail on timeout when clearing caches Description: @bug W-1701238@ @rev fabbot@