/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.shindig.gadgets;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.verify;
import static org.easymock.classextension.EasyMock.replay;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import org.apache.shindig.common.cache.Cache;
import org.apache.shindig.common.cache.CacheProvider;
import org.apache.shindig.common.cache.LruCacheProvider;
import org.apache.shindig.common.uri.Uri;
import org.apache.shindig.common.util.TimeSource;
import org.apache.shindig.gadgets.http.HttpRequest;
import org.apache.shindig.gadgets.http.HttpResponse;
import org.apache.shindig.gadgets.http.HttpResponseBuilder;
import org.apache.shindig.gadgets.http.RequestPipeline;
import org.apache.shindig.gadgets.spec.GadgetSpec;
import org.apache.shindig.gadgets.spec.MessageBundle;
import org.easymock.EasyMock;
import org.junit.Test;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicLong;
/**
* Tests for DefaultMessageBundleFactory
*/
public class DefaultMessageBundleFactoryTest {
private static final Uri BUNDLE_URI = Uri.parse("http://example.org/messagex.xml");
private static final Uri SPEC_URI = Uri.parse("http://example.org/gadget.xml");
private static final String MSG_0_NAME = "messageZero";
private static final String MSG_1_NAME = "message1";
private static final String MSG_2_NAME = "message 2";
private static final String MSG_0_VALUE = "Message 0 VALUE";
private static final String MSG_0_ALT_VALUE = "Message 0 Alternative VALUE";
private static final String MSG_1_VALUE = "msg one val";
private static final String MSG_2_VALUE = "message two val.";
private static final Locale PARENT_LOCALE = new Locale("en", "ALL");
private static final Locale LOCALE = new Locale("en", "US");
private static final String BASIC_BUNDLE
= "<messagebundle>" +
" <msg name='" + MSG_0_NAME + "'>" + MSG_0_VALUE + "</msg>" +
" <msg name='" + MSG_1_NAME + "'>" + MSG_1_VALUE + "</msg>" +
"</messagebundle>";
private static final String BASIC_SPEC
= "<Module>" +
"<ModulePrefs title='foo'>" +
" <Locale lang='all' country='ALL'>" +
" <msg name='" + MSG_0_NAME + "'>" + MSG_0_VALUE + "</msg>" +
" </Locale>" +
" <Locale lang='" + LOCALE.getLanguage() + "'>" +
" <msg name='" + MSG_0_NAME + "'>" + MSG_0_ALT_VALUE + "</msg>" +
" <msg name='" + MSG_1_NAME + "'>" + MSG_1_VALUE + "</msg>" +
" <msg name='" + MSG_2_NAME + "'>" + MSG_2_VALUE + "</msg>" +
" </Locale>" +
" <Locale lang='" + LOCALE.getLanguage() + "' country='" + LOCALE.getCountry() + "' " +
" messages='" + BUNDLE_URI + "'/>" +
"</ModulePrefs>" +
"<Content type='html'/>" +
"</Module>";
private static final int MAX_AGE = 10000;
private final RequestPipeline pipeline = EasyMock.createNiceMock(RequestPipeline.class);
private final CacheProvider cacheProvider = new LruCacheProvider(10);
private final Cache<String, MessageBundle> cache
= cacheProvider.createCache(DefaultMessageBundleFactory.CACHE_NAME);
private final DefaultMessageBundleFactory bundleFactory
= new DefaultMessageBundleFactory(pipeline, cacheProvider, MAX_AGE);
private final GadgetSpec gadgetSpec;
public DefaultMessageBundleFactoryTest() {
try {
gadgetSpec = new GadgetSpec(SPEC_URI, BASIC_SPEC);
} catch (GadgetException e) {
throw new RuntimeException(e);
}
}
@Test
public void getBundle() throws Exception {
HttpResponse response = new HttpResponse(BASIC_BUNDLE);
expect(pipeline.execute(isA(HttpRequest.class))).andReturn(response);
replay(pipeline);
MessageBundle bundle = bundleFactory.getBundle(gadgetSpec, LOCALE, true);
assertEquals(MSG_0_VALUE, bundle.getMessages().get(MSG_0_NAME));
assertEquals(MSG_1_VALUE, bundle.getMessages().get(MSG_1_NAME));
assertEquals(MSG_2_VALUE, bundle.getMessages().get(MSG_2_NAME));
}
@Test
public void getBundleFromCache() throws Exception {
HttpResponse response = new HttpResponse(BASIC_BUNDLE);
expect(pipeline.execute(isA(HttpRequest.class))).andReturn(response).once();
replay(pipeline);
MessageBundle bundle0 = bundleFactory.getBundle(gadgetSpec, LOCALE, false);
MessageBundle bundle1 = bundleFactory.getBundle(gadgetSpec, LOCALE, false);
assertSame("Different objects returned out of the cache", bundle0, bundle1);
}
@Test
public void getParentBundle() throws Exception {
MessageBundle bundle = bundleFactory.getBundle(gadgetSpec, PARENT_LOCALE, true);
assertEquals(MSG_0_ALT_VALUE, bundle.getMessages().get(MSG_0_NAME));
assertEquals(MSG_1_VALUE, bundle.getMessages().get(MSG_1_NAME));
assertEquals(MSG_2_VALUE, bundle.getMessages().get(MSG_2_NAME));
}
@Test
public void getAllAllBundle() throws Exception {
MessageBundle bundle = bundleFactory.getBundle(gadgetSpec, new Locale("all", "ALL"), true);
assertEquals(MSG_0_VALUE, bundle.getMessages().get(MSG_0_NAME));
}
@Test
public void ignoreCacheDoesNotStore() throws Exception {
bundleFactory.getBundle(gadgetSpec, new Locale("all", "ALL"), true);
assertEquals(0, cache.getSize());
}
@Test
public void badResponseServedFromCache() throws Exception {
HttpResponse expiredResponse = new HttpResponseBuilder()
.setResponse(BASIC_BUNDLE.getBytes("UTF-8"))
.addHeader("Pragma", "no-cache")
.create();
HttpResponse badResponse = HttpResponse.error();
expect(pipeline.execute(isA(HttpRequest.class)))
.andReturn(expiredResponse).once();
expect(pipeline.execute(isA(HttpRequest.class)))
.andReturn(badResponse).once();
replay(pipeline);
final AtomicLong time = new AtomicLong();
bundleFactory.cache.setTimeSource(new TimeSource() {
@Override
public long currentTimeMillis() {
return time.get();
}
});
time.set(System.currentTimeMillis());
MessageBundle bundle0 = bundleFactory.getBundle(gadgetSpec, LOCALE, false);
time.set(time.get() + MAX_AGE + 1);
MessageBundle bundle1 = bundleFactory.getBundle(gadgetSpec, LOCALE, false);
verify(pipeline);
assertSame("Did not respond from cache when refresh failed.", bundle0, bundle1);
}
@Test
public void badResponseIsEmptyWhenNotInCache() throws Exception {
HttpResponse badResponse = HttpResponse.error();
expect(pipeline.execute(isA(HttpRequest.class)))
.andReturn(badResponse).once();
replay(pipeline);
MessageBundle bundle = bundleFactory.getBundle(gadgetSpec, LOCALE, false);
verify(pipeline);
assertEquals(0, bundle.getMessages().size());
}
@Test
public void ttlPropagatesToFetcher() throws Exception {
CapturingFetcher capturingFetcher = new CapturingFetcher();
MessageBundleFactory factory
= new DefaultMessageBundleFactory(capturingFetcher, cacheProvider, MAX_AGE);
factory.getBundle(gadgetSpec, LOCALE, false);
assertEquals(MAX_AGE / 1000, capturingFetcher.request.getCacheTtl());
}
private static class CapturingFetcher implements RequestPipeline {
HttpRequest request;
protected CapturingFetcher() {
}
public HttpResponse execute(HttpRequest request) {
this.request = request;
return new HttpResponse(BASIC_BUNDLE);
}
public void normalizeProtocol(HttpRequest request) throws GadgetException { }
}
}