/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.frontend.js.loader.modules.extender.internal;
import aQute.bnd.osgi.Constants;
import aQute.lib.converter.Converter;
import com.liferay.portal.kernel.test.ReflectionTestUtil;
import com.liferay.portal.kernel.util.Portal;
import com.liferay.portal.kernel.util.PortalUtil;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.util.PortalImpl;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletContext;
import org.json.JSONException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.component.ComponentContext;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletConfig;
import org.springframework.mock.web.MockServletContext;
/**
* @author Raymond Augé
*/
@RunWith(PowerMockRunner.class)
public class JSLoaderModulesServletTest extends PowerMockito {
@Before
public void setUp() {
_portal = PortalUtil.getPortal();
_portalUtil.setPortal(
new PortalImpl() {
@Override
public String getPathContext() {
return StringPool.BLANK;
}
});
}
@After
public void tearDown() {
_portalUtil.setPortal(_portal);
}
@Test
public void testBasicOutput() throws Exception {
JSLoaderModulesServlet jsLoaderModulesServlet =
buildJSLoaderModulesServlet();
MockHttpServletRequest mockHttpServletRequest =
new MockHttpServletRequest();
MockHttpServletResponse mockHttpServletResponse =
new MockHttpServletResponse();
jsLoaderModulesServlet.service(
mockHttpServletRequest, mockHttpServletResponse);
Assert.assertNotNull(mockHttpServletResponse.getContentAsString());
Assert.assertEquals(
Details.CONTENT_TYPE, mockHttpServletResponse.getContentType());
}
@Test
public void testMultipleModulesOutput() throws Exception {
JSLoaderModulesServlet jsLoaderModulesServlet =
buildJSLoaderModulesServlet();
JSLoaderModulesTracker jsLoaderModulesTracker =
jsLoaderModulesServlet.getJSLoaderModulesTracker();
ServiceReference<ServletContext> serviceReference =
buildServiceReference(
"test", new Version("1.0.0"), true, 0,
getResource("dependencies/config1.js"));
jsLoaderModulesTracker.addingService(serviceReference);
serviceReference = buildServiceReference(
"foo", new Version("13.2.23"), true, 0,
getResource("dependencies/config2.js"));
jsLoaderModulesTracker.addingService(serviceReference);
MockHttpServletRequest mockHttpServletRequest =
new MockHttpServletRequest();
MockHttpServletResponse mockHttpServletResponse =
new MockHttpServletResponse();
jsLoaderModulesServlet.service(
mockHttpServletRequest, mockHttpServletResponse);
String content = mockHttpServletResponse.getContentAsString();
content = content.replace('"', '\'');
assertContains("'test': '/test-1.0.0'", content);
assertContains("'test@1.0.0': '/test-1.0.0'", content);
assertContains(
"'test/some.es':{'dependencies':['exports','test@1.0.0/other.es']}",
content);
assertContains(
"'test@1.0.0/some.es':{'dependencies':['exports'," +
"'test@1.0.0/other.es']}",
content);
assertContains("'foo': '/foo-13.2.23'", content);
assertContains("'foo@13.2.23': '/foo-13.2.23'", content);
assertContains(
"'foo/foo.es':{'dependencies':['exports','foo@13.2.23/fum.es'," +
"'jquery@2.15.3/jquery.js']}",
content);
assertContains(
"'foo@13.2.23/foo.es':{'dependencies':['exports'," +
"'foo@13.2.23/fum.es','jquery@2.15.3/jquery.js']}",
content);
}
@Test
public void testMultipleVersionsModuleOutput() throws Exception {
JSLoaderModulesServlet jsLoaderModulesServlet =
buildJSLoaderModulesServlet();
JSLoaderModulesTracker jsLoaderModulesTracker =
jsLoaderModulesServlet.getJSLoaderModulesTracker();
ServiceReference<ServletContext> serviceReference =
buildServiceReference(
"test", new Version("1.0.0"), true, 0,
getResource("dependencies/config1.js"));
jsLoaderModulesTracker.addingService(serviceReference);
serviceReference = buildServiceReference(
"test", new Version("1.2.0"), true, 0,
getResource("dependencies/config1.js"));
jsLoaderModulesTracker.addingService(serviceReference);
MockHttpServletRequest mockHttpServletRequest =
new MockHttpServletRequest();
MockHttpServletResponse mockHttpServletResponse =
new MockHttpServletResponse();
jsLoaderModulesServlet.service(
mockHttpServletRequest, mockHttpServletResponse);
String content = mockHttpServletResponse.getContentAsString();
content = content.replace('"', '\'');
assertContains("'test': '/test-1.2.0'", content);
assertContains("'test@1.2.0': '/test-1.2.0'", content);
assertContains("'test@1.0.0': '/test-1.0.0'", content);
assertContains(
"'test/some.es':{'dependencies':['exports','test@1.2.0/other.es']}",
content);
assertNotContains(
"'test/some.es':{'dependencies':['exports','test@1.0.0/other.es']}",
content);
assertContains(
"'test@1.2.0/some.es':{'dependencies':['exports'," +
"'test@1.2.0/other.es']}",
content);
assertContains(
"'test@1.0.0/some.es':{'dependencies':['exports'," +
"'test@1.0.0/other.es']}",
content);
}
@Test
public void testSingleModuleOutput() throws Exception {
JSLoaderModulesServlet jsLoaderModulesServlet =
buildJSLoaderModulesServlet();
JSLoaderModulesTracker jsLoaderModulesTracker =
jsLoaderModulesServlet.getJSLoaderModulesTracker();
ServiceReference<ServletContext> serviceReference =
buildServiceReference(
"test", new Version("1.0.0"), true, 0,
getResource("dependencies/config1.js"));
jsLoaderModulesTracker.addingService(serviceReference);
MockHttpServletRequest mockHttpServletRequest =
new MockHttpServletRequest();
MockHttpServletResponse mockHttpServletResponse =
new MockHttpServletResponse();
jsLoaderModulesServlet.service(
mockHttpServletRequest, mockHttpServletResponse);
String content = mockHttpServletResponse.getContentAsString();
content = content.replace('"', '\'');
assertContains("'test': '/test-1.0.0'", content);
assertContains("'test@1.0.0': '/test-1.0.0'", content);
assertContains(
"'test/some.es':{'dependencies':['exports','test@1.0.0/other.es']}",
content);
assertContains("'test/other.es':{'dependencies':['exports']}", content);
assertContains(
"'test@1.0.0/some.es':{'dependencies':['exports'," +
"'test@1.0.0/other.es']}",
content);
assertContains(
"'test@1.0.0/other.es':{'dependencies':['exports']}", content);
}
@Test(expected = JSONException.class)
public void testSingleModuleOutputEmptyConfiguration() throws Exception {
JSLoaderModulesServlet jsLoaderModulesServlet =
buildJSLoaderModulesServlet();
JSLoaderModulesTracker jsLoaderModulesTracker =
jsLoaderModulesServlet.getJSLoaderModulesTracker();
ServiceReference<ServletContext> serviceReference =
buildServiceReference(
"test", new Version("1.0.0"), true, 0,
getResource("dependencies/empty.js"));
jsLoaderModulesTracker.addingService(serviceReference);
}
@Test
public void testSingleModuleOutputIdempotent() throws Exception {
JSLoaderModulesServlet jsLoaderModulesServlet =
buildJSLoaderModulesServlet();
JSLoaderModulesTracker jsLoaderModulesTracker =
jsLoaderModulesServlet.getJSLoaderModulesTracker();
ServiceReference<ServletContext> serviceReference =
buildServiceReference(
"test", new Version("1.0.0"), true, 0,
getResource("dependencies/config1.js"));
jsLoaderModulesTracker.addingService(serviceReference);
jsLoaderModulesTracker.addingService(serviceReference);
MockHttpServletRequest mockHttpServletRequest =
new MockHttpServletRequest();
MockHttpServletResponse mockHttpServletResponse =
new MockHttpServletResponse();
jsLoaderModulesServlet.service(
mockHttpServletRequest, mockHttpServletResponse);
String content = mockHttpServletResponse.getContentAsString();
content = content.replace('"', '\'');
assertOccurrences("'test': '/test-1.0.0'", content, 1);
assertOccurrences("'test@1.0.0': '/test-1.0.0'", content, 1);
assertOccurrences(
"'test/some.es':{'dependencies':['exports','test@1.0.0/other.es']}",
content, 1);
assertOccurrences(
"'test/other.es':{'dependencies':['exports']}", content, 1);
assertOccurrences(
"'test@1.0.0/some.es':{'dependencies':['exports'," +
"'test@1.0.0/other.es']}",
content, 1);
assertOccurrences(
"'test@1.0.0/other.es':{'dependencies':['exports']}", content, 1);
}
@Test(expected = JSONException.class)
public void testSingleModuleOutputMalformedConfiguration()
throws Exception {
JSLoaderModulesServlet jsLoaderModulesServlet =
buildJSLoaderModulesServlet();
JSLoaderModulesTracker jsLoaderModulesTracker =
new JSLoaderModulesTracker();
jsLoaderModulesTracker.setDetails(
Converter.cnv(Details.class, new HashMap<>()));
jsLoaderModulesServlet.setJSLoaderModulesTracker(
jsLoaderModulesTracker);
ServiceReference<ServletContext> serviceReference =
buildServiceReference(
"test", new Version("1.0.0"), true, 0,
getResource("dependencies/malformed.js"));
jsLoaderModulesTracker.addingService(serviceReference);
}
@Test
public void testSingleModuleOutputNoConfiguration() throws Exception {
JSLoaderModulesServlet jsLoaderModulesServlet =
buildJSLoaderModulesServlet();
JSLoaderModulesTracker jsLoaderModulesTracker =
new JSLoaderModulesTracker();
jsLoaderModulesTracker.setDetails(
Converter.cnv(Details.class, new HashMap<>()));
jsLoaderModulesServlet.setJSLoaderModulesTracker(
jsLoaderModulesTracker);
ServiceReference<ServletContext> serviceReference =
buildServiceReference("test", new Version("1.0.0"), true, 0, null);
jsLoaderModulesTracker.addingService(serviceReference);
MockHttpServletRequest mockHttpServletRequest =
new MockHttpServletRequest();
MockHttpServletResponse mockHttpServletResponse =
new MockHttpServletResponse();
jsLoaderModulesServlet.service(
mockHttpServletRequest, mockHttpServletResponse);
String content = mockHttpServletResponse.getContentAsString();
content = content.replace('"', '\'');
assertContains("'test': '/test-1.0.0'", content);
assertContains("'test@1.0.0': '/test-1.0.0'", content);
assertNotContains("':{'dependencies':['", content);
}
@Test
public void testUnversionedModuleOutput() throws Exception {
JSLoaderModulesServlet jsLoaderModulesServlet =
buildJSLoaderModulesServlet(
Collections.<String, Object>singletonMap(
"applyVersioning", Boolean.FALSE));
JSLoaderModulesTracker jsLoaderModulesTracker =
jsLoaderModulesServlet.getJSLoaderModulesTracker();
ServiceReference<ServletContext> serviceReference =
buildServiceReference(
"test", new Version("1.0.0"), true, 0,
getResource("dependencies/config1.js"));
jsLoaderModulesServlet.setJSLoaderModulesTracker(
jsLoaderModulesTracker);
jsLoaderModulesTracker.addingService(serviceReference);
MockHttpServletRequest mockHttpServletRequest =
new MockHttpServletRequest();
MockHttpServletResponse mockHttpServletResponse =
new MockHttpServletResponse();
jsLoaderModulesServlet.service(
mockHttpServletRequest, mockHttpServletResponse);
String content = mockHttpServletResponse.getContentAsString();
content = content.replace('"', '\'');
assertContains("'test': '/test-1.0.0'", content);
assertContains("'test@1.0.0': '/test-1.0.0'", content);
assertContains(
"'test/some.es':{'dependencies':['exports','test/other.es']}",
content);
assertNotContains(
"'test/some.es':{'dependencies':['exports','test@1.0.0/other.es']}",
content);
assertContains("'test/other.es':{'dependencies':['exports']}", content);
assertNotContains(
"'test@1.0.0/some.es':{'dependencies':['exports'," +
"'test@1.0.0/other.es']}",
content);
assertNotContains(
"'test@1.0.0/other.es':{'dependencies':['exports']}", content);
}
protected void assertContains(String expected, String content) {
Assert.assertTrue(
"Does not contain <" + expected + ">", content.contains(expected));
}
protected void assertNotContains(String expected, String content) {
Assert.assertFalse(
"Contains <" + expected + ">", content.contains(expected));
}
protected void assertOccurrences(
String expected, String content, int occurences) {
int count = 0;
for (int i = content.indexOf(expected); i != -1;
i = content.indexOf(expected, i + 1)) {
count++;
}
Assert.assertEquals(occurences, count);
}
protected JSLoaderModulesServlet buildJSLoaderModulesServlet()
throws Exception {
return buildJSLoaderModulesServlet(
Collections.<String, Object>emptyMap());
}
protected JSLoaderModulesServlet buildJSLoaderModulesServlet(
Map<String, Object> properties)
throws Exception {
JSLoaderModulesServlet jsLoaderModulesServlet =
new JSLoaderModulesServlet();
ReflectionTestUtil.setFieldValue(
jsLoaderModulesServlet, "_portal", PortalUtil.getPortal());
jsLoaderModulesServlet.activate(
mock(ComponentContext.class), mock(Details.class));
MockServletContext mockServletContext = new MockServletContext();
mockServletContext.setContextPath("/loader");
jsLoaderModulesServlet.init(new MockServletConfig(mockServletContext));
jsLoaderModulesServlet.setDetails(
Converter.cnv(Details.class, properties));
JSLoaderModulesTracker jsLoaderModulesTracker =
new JSLoaderModulesTracker();
jsLoaderModulesTracker.setDetails(
Converter.cnv(Details.class, properties));
jsLoaderModulesServlet.setJSLoaderModulesTracker(
jsLoaderModulesTracker);
return jsLoaderModulesServlet;
}
protected ServiceReference<ServletContext> buildServiceReference(
String bsn, Version version, boolean capability, int ranking, URL url) {
Bundle bundle = mock(Bundle.class);
mockBundle(bundle, bsn, version, url, capability);
TestServiceReference mockServiceReference = new TestServiceReference(
bundle, new String[] {ServletContext.class.getName()},
_counter.incrementAndGet(), ranking);
mockServiceReference.setProperties(
Collections.<String, Object>singletonMap(
"osgi.web.contextpath", "/" + bsn + "-" + version.toString()));
return mockServiceReference;
}
protected URL getResource(String name) {
Class<?> clazz = getClass();
return clazz.getResource(name);
}
protected void mockBundle(
Bundle bundle, String bsn, Version version, URL url,
boolean capability) {
doReturn(
url
).when(
bundle
).getEntry(
Details.CONFIG_JSON
);
doReturn(
new Hashtable<String, String>()
).when(
bundle
).getHeaders();
doReturn(
bsn
).when(
bundle
).getSymbolicName();
doReturn(
version
).when(
bundle
).getVersion();
doReturn(
mockBundleWiring(bsn, capability)
).when(
bundle
).adapt(
BundleWiring.class
);
}
protected BundleCapability mockBundleCapability(String bsn) {
BundleCapability bundleCapability = mock(BundleCapability.class);
doReturn(
Collections.<String, Object>singletonMap(
Details.OSGI_WEBRESOURCE, bsn)
).when(
bundleCapability
).getAttributes();
return bundleCapability;
}
protected BundleWire mockBundleWire() {
BundleWire bundleWire = mock(BundleWire.class);
doReturn(
mockJQueryBundleCapability()
).when(
bundleWire
).getCapability();
return bundleWire;
}
protected BundleWiring mockBundleWiring(String bsn, boolean capability) {
BundleWiring bundleWiring = mock(BundleWiring.class);
List<BundleCapability> bundleCapabilities = Collections.emptyList();
if (capability) {
bundleCapabilities = Arrays.asList(mockBundleCapability(bsn));
}
doReturn(
bundleCapabilities
).when(
bundleWiring
).getCapabilities(
Details.OSGI_WEBRESOURCE
);
List<BundleWire> bundleWires = Collections.emptyList();
if (capability) {
bundleWires = Arrays.asList(mockBundleWire());
}
doReturn(
bundleWires
).when(
bundleWiring
).getRequiredWires(
Details.OSGI_WEBRESOURCE
);
return bundleWiring;
}
protected BundleCapability mockJQueryBundleCapability() {
BundleCapability bundleCapability = mock(BundleCapability.class);
Map<String, Object> properties = new HashMap<>();
properties.put(Details.OSGI_WEBRESOURCE, "jquery");
properties.put(Constants.VERSION_ATTRIBUTE, new Version("2.15.3"));
doReturn(
properties
).when(
bundleCapability
).getAttributes();
return bundleCapability;
}
private final AtomicInteger _counter = new AtomicInteger(0);
private Portal _portal;
private final PortalUtil _portalUtil = new PortalUtil();
private static class TestServiceReference
implements ServiceReference<ServletContext> {
public TestServiceReference(
Bundle bundle, String[] objectClasses, int id, int ranking) {
_bundle = bundle;
_objectClasses = objectClasses;
_id = id;
_ranking = ranking;
_properties.put("objectClass", objectClasses);
_properties.put("service.id", _id);
_properties.put("service.ranking", _ranking);
}
@Override
public int compareTo(Object object) {
TestServiceReference testServiceReference =
(TestServiceReference)object;
if (_ranking != testServiceReference._ranking) {
if (_ranking < testServiceReference._ranking) {
return -1;
}
return 1;
}
if (_id == testServiceReference._id) {
return 0;
}
if (_id < testServiceReference._id) {
return 1;
}
return -1;
}
@Override
public Bundle getBundle() {
return _bundle;
}
@Override
public Object getProperty(String key) {
return _properties.get(key);
}
@Override
public String[] getPropertyKeys() {
Set<String> keys = _properties.keySet();
return keys.toArray(new String[keys.size()]);
}
@Override
public Bundle[] getUsingBundles() {
return null;
}
@Override
public boolean isAssignableTo(Bundle bundle, String className) {
return false;
}
public void setProperties(Map<String, Object> properties) {
_properties.putAll(properties);
_properties.put("objectClass", _objectClasses);
_properties.put("service.id", _id);
_properties.put("service.ranking", _ranking);
}
private final Bundle _bundle;
private int _id;
private final String[] _objectClasses;
private final Map<String, Object> _properties = new HashMap<>();
private final int _ranking;
}
}