blob: 2b3d84697c28dd2275424bb0cccde9e4d61957c4 [file] [log] [blame]
/*
* 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.karaf.features;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import org.apache.felix.resolver.ResolverImpl;
import org.apache.karaf.features.internal.resolver.Slf4jResolverLog;
import org.apache.karaf.features.internal.service.FeaturesServiceImpl;
import org.apache.karaf.features.internal.service.StateStorage;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.startlevel.FrameworkStartLevel;
import org.osgi.service.resolver.Resolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class FeaturesServiceTest extends TestBase {
private static final String FEATURE_WITH_INVALID_BUNDLE = "<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <feature name='f1'><bundle>%s</bundle><bundle>zfs:unknown</bundle></feature>"
+ " <feature name='f2'><bundle>%s</bundle></feature>"
+ "</features>";
File dataFile;
Logger logger = LoggerFactory.getLogger(FeaturesServiceTest.class);;
Resolver resolver = new ResolverImpl(new Slf4jResolverLog(logger));
@Before
public void setUp() throws IOException {
dataFile = File.createTempFile("features", null, null);
}
private URI createTempRepo(String repoContent, Object ... variables) throws IOException {
File tmp = File.createTempFile("karaf", ".feature");
PrintWriter pw = new PrintWriter(new FileWriter(tmp));
pw.printf(repoContent, variables);
pw.close();
return tmp.toURI();
}
/*
TODO: migrate those tests
@Test
public void testInstallFeature() throws Exception {
URI uri = createTempRepo(
"<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <feature name='f1'><bundle start='true'>bundle-f1</bundle></feature>"
+ "</features>");
BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
Bundle installedBundle = createDummyBundle(12345L, "bundle-f1", headers());
FeaturesServiceImpl svc = testAddRepository("bundle-f1", uri, bundleManager, installedBundle);
reset(bundleManager);
expect(bundleManager.installBundleIfNeeded(eq("bundle-f1"), eq(0), eq((String)null))).andReturn(new BundleInstallerResult(installedBundle, true));
expect(bundleManager.getDataFile(EasyMock.anyObject(String.class))).andReturn(dataFile);
ignoreRefreshes(bundleManager);
replay(bundleManager);
svc.installFeature("f1", org.apache.karaf.features.internal.model.Feature.DEFAULT_VERSION, EnumSet.of(FeaturesService.Option.NoAutoRefreshBundles));
verify(bundleManager);
Feature[] installed = svc.listInstalledFeatures();
assertEquals(1, installed.length);
assertEquals("f1", installed[0].getName());
}
private FeaturesServiceImpl testAddRepository(String name, URI uri, BundleManager bundleManager,
Bundle installedBundle) throws IOException, BundleException, Exception {
expect(bundleManager.getDataFile(EasyMock.anyObject(String.class))).andReturn(dataFile);
expect(bundleManager.installBundleIfNeeded(eq(name), eq(0), eq((String)null))).andReturn(new BundleInstallerResult(installedBundle, true)).anyTimes();
replay(bundleManager);
FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
svc.addRepository(uri);
Repository[] repositories = svc.listRepositories();
verify(bundleManager);
assertNotNull(repositories);
assertEquals(1, repositories.length);
assertNotNull(repositories[0]);
Feature[] features = repositories[0].getFeatures();
assertNotNull(features);
assertEquals(1, features.length);
assertNotNull(features[0]);
assertEquals("f1", features[0].getName());
assertNotNull(features[0].getDependencies());
assertEquals(0, features[0].getDependencies().size());
assertNotNull(features[0].getBundles());
assertEquals(1, features[0].getBundles().size());
assertEquals(name, features[0].getBundles().get(0).getLocation());
assertTrue(features[0].getBundles().get(0).isStart());
return svc;
}
@SuppressWarnings("unchecked")
@Test
public void testUninstallFeatureWithTwoVersions() throws Exception {
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <feature name='f1' version='0.1'><bundle>bundle-0.1</bundle></feature>"
+ " <feature name='f1' version='0.2'><bundle>bundle-0.1</bundle></feature>"
+ "</features>");
Bundle bundlef101 = createDummyBundle(12345L, "bundle-0.1", headers());
BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
expect(bundleManager.getDataFile(EasyMock.anyObject(String.class))).andReturn(dataFile).anyTimes();
expect(bundleManager.installBundleIfNeeded("bundle-0.1", 0, null)).andReturn(new BundleInstallerResult(bundlef101, true));
expect(bundleManager.installBundleIfNeeded("bundle-0.1", 0, null)).andReturn(new BundleInstallerResult(bundlef101, false));
expect(bundleManager.getBundleContext()).andReturn(bundleContext);
ignoreRefreshes(bundleManager);
bundleManager.uninstall(Collections.EMPTY_LIST, true);
EasyMock.expectLastCall().times(2);
replay(bundleManager);
FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
svc.addRepository(uri);
try {
svc.uninstallFeature("f1");
fail("Uninstall should have failed as feature is not installed");
} catch (Exception e) {
// ok
}
svc.installFeature("f1", "0.1", EnumSet.of(FeaturesService.Option.NoAutoRefreshBundles));
svc.installFeature("f1", "0.2", EnumSet.of(FeaturesService.Option.NoAutoRefreshBundles));
try {
svc.uninstallFeature("f1");
fail("Uninstall should have failed as feature is installed in multiple versions");
} catch (Exception e) {
// ok
}
svc.uninstallFeature("f1", "0.1");
svc.uninstallFeature("f1");
verify(bundleManager);
}
@Test
public void testAddAndRemoveRepository() throws Exception {
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <feature name='f1' version='0.1'><bundle>bundle-f1-0.1</bundle></feature>"
+ "</features>");
BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
replay(bundleManager);
FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
EasyMock.verify(bundleManager);
svc.addRepository(uri);
svc.removeRepository(uri);
verify(bundleManager);
}
// Tests install of a Repository that includes a feature
// with a feature dependency
// The dependant feature is in the same repository
// Tests uninstall of features
@SuppressWarnings("unchecked")
@Test
public void testInstallFeatureWithDependantFeatures() throws Exception {
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <feature name='f1' version='0.1'><feature version='0.1'>f2</feature><bundle>bundle-f1-0.1</bundle></feature>"
+ " <feature name='f2' version='0.1'><bundle>bundle-f2-0.1</bundle></feature>"
+ "</features>");
BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
Bundle bundlef101 = createDummyBundle(12345L, "bundle-f1-0.1", headers());
Bundle bundlef201 = createDummyBundle(54321L, "bundle-f2-0.1", headers());
expect(bundleManager.getDataFile(EasyMock.<String> anyObject())).andReturn(dataFile).anyTimes();
expect(bundleManager.installBundleIfNeeded("bundle-f1-0.1", 0, null))
.andReturn(new BundleInstallerResult(bundlef101, true));
expect(bundleManager.installBundleIfNeeded("bundle-f2-0.1", 0, null))
.andReturn(new BundleInstallerResult(bundlef201, true));
expect(bundleManager.getBundleContext()).andReturn(bundleContext).anyTimes();
expect(bundleContext.getBundle(12345)).andReturn(bundlef101).anyTimes();
ignoreRefreshes(bundleManager);
bundleManager.uninstall(Collections.EMPTY_LIST, true);
EasyMock.expectLastCall().anyTimes();
replay(bundleManager);
FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
svc.addRepository(uri);
svc.installFeature("f1", "0.1");
svc.uninstallFeature("f1", "0.1");
verify(bundleManager);
}
@SuppressWarnings("unchecked")
private BundleManager prepareBundleManagerForInstallUninstall(String bundleUri, String bundlename) throws Exception {
BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
Bundle installedBundle = createDummyBundle(12345L, bundlename, headers());
expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
expect(bundleManager.installBundleIfNeeded(bundleUri, 0, null)).andReturn(new BundleInstallerResult(installedBundle, true));
expect(bundleManager.getBundleContext()).andReturn(bundleContext);
ignoreRefreshes(bundleManager);
bundleManager.uninstall(Collections.EMPTY_LIST, true);
EasyMock.expectLastCall().times(2);
return bundleManager;
}
@Test
public void testInstallFeatureWithDependantFeaturesAndVersionWithoutPreinstall() throws Exception {
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <feature name='f1' version='0.1'><feature version='0.1'>f2</feature></feature>"
+ " <feature name='f2' version='0.1'><bundle>bundle-0.1</bundle></feature>"
+ " <feature name='f2' version='0.2'><bundle>bundle-0.2</bundle></feature>"
+ "</features>");
BundleManager bundleManager = prepareBundleManagerForInstallUninstall("bundle-0.1", "bundle-0.1");
replay(bundleManager);
FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
svc.addRepository(uri);
svc.installFeature("f1", "0.1");
svc.uninstallFeature("f1", "0.1");
svc.uninstallFeature("f2", "0.1");
verify(bundleManager);
}
@Test
public void testInstallFeatureWithDependantFeaturesAndNoVersionWithoutPreinstall() throws Exception {
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <feature name='f1' version='0.1'><feature>f2</feature></feature>"
+ " <feature name='f2' version='0.1'><bundle>bundle-0.1</bundle></feature>"
+ " <feature name='f2' version='0.2'><bundle>bundle-0.2</bundle></feature>"
+ "</features>");
BundleManager bundleManager = prepareBundleManagerForInstallUninstall("bundle-0.2", "bundle-0.2");
replay(bundleManager);
FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
svc.addRepository(uri);
svc.installFeature("f1", "0.1");
svc.uninstallFeature("f1", "0.1");
svc.uninstallFeature("f2", "0.2");
verify(bundleManager);
}
@SuppressWarnings("unchecked")
@Test
public void testInstallFeatureWithDependantFeaturesAndRangeWithoutPreinstall() throws Exception {
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <feature name='f1' version='0.1'><feature version='[0.1,0.3)'>f2</feature></feature>"
+ " <feature name='f2' version='0.1'><bundle>bundle-0.1</bundle></feature>"
+ " <feature name='f2' version='0.2'><bundle>bundle-0.2</bundle></feature>"
+ "</features>");
BundleManager bundleManager = EasyMock.createMock(BundleManager.class);
BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
Bundle bundleVer02 = createDummyBundle(54321L, "bundleVer02", headers());
expect(bundleManager.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
expect(bundleManager.installBundleIfNeeded("bundle-0.2", 0, null)).andReturn(new BundleInstallerResult(bundleVer02, true));
expect(bundleManager.getBundleContext()).andReturn(bundleContext);
ignoreRefreshes(bundleManager);
bundleManager.uninstall(Collections.EMPTY_LIST, true);
EasyMock.expectLastCall().times(2);
replay(bundleManager);
FeaturesServiceImpl svc = new FeaturesServiceImpl(bundleManager);
svc.addRepository(uri);
svc.installFeature("f1", "0.1");
svc.uninstallFeature("f1", "0.1");
svc.uninstallFeature("f2", "0.2");
verify(bundleManager);
}
@SuppressWarnings("unchecked")
@Test
public void testInstallFeatureWithDependantFeaturesAndRangeWithPreinstall() throws Exception {
String bundleVer01Uri = "bundle-0.1";
String bundleVer02Uri = "bundle-0.2";
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ "<feature name='f1' version='0.1'><feature version='[0.1,0.3)'>f2</feature></feature>"
+ " <feature name='f2' version='0.1'><bundle>%s</bundle></feature>"
+ " <feature name='f2' version='0.2'><bundle>%s</bundle></feature>"
+ "</features>", bundleVer01Uri, bundleVer02Uri);
BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
replay(bundleContext);
FeaturesServiceImpl svc = new FeaturesServiceImpl(null, bundleContext, new Storage(), null, null, null, null);
svc.addRepository(uri);
svc.installFeature("f2", "0.1");
svc.installFeature("f1", "0.1");
svc.uninstallFeature("f1", "0.1");
svc.uninstallFeature("f2", "0.1");
verify(bundleContext);
}
*/
@Test
public void testGetFeaturesShouldHandleDifferentVersionPatterns() throws Exception {
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <feature name='f1' version='0.1'><feature version='[0.1,0.3)'>f2</feature></feature>"
+ " <feature name='f2' version='0.1'><bundle>bundle1</bundle></feature>"
+ " <feature name='f2' version='0.2'><bundle>bundle2</bundle></feature>"
+ "</features>");
FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, new Storage(), null, null, null, resolver, null, null, null, null, null, null, null, 0, 0, 0, null);
svc.addRepository(uri);
assertEquals(feature("f2", "0.2"), svc.getFeatures("f2", "[0.1,0.3)")[0]);
assertEquals(feature("f2", "0.2"), svc.getFeatures("f2", "0.0.0")[0]);
assertEquals(feature("f2", "0.2"), svc.getFeatures("f2", "0.2")[0]);
assertEquals(0, svc.getFeatures("f2", "0.3").length);
}
@Test
public void testInstallBatchFeatureWithFailure() throws Exception {
String bundle1Uri = "file:bundle1";
String bundle2Uri = "file:bundle2";
URI uri = createTempRepo(FEATURE_WITH_INVALID_BUNDLE, bundle1Uri, bundle2Uri);
BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
Bundle bundle = EasyMock.createMock(Bundle.class);
FrameworkStartLevel fsl = EasyMock.createMock(FrameworkStartLevel.class);
expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
expect(bundleContext.getBundle()).andReturn(bundle);
expect(bundle.adapt(FrameworkStartLevel.class)).andReturn(fsl);
expect(fsl.getInitialBundleStartLevel()).andReturn(50);
expect(fsl.getStartLevel()).andReturn(100);
replay(bundleContext, bundle, fsl);
FeaturesServiceImpl svc = new FeaturesServiceImpl(null, bundleContext, new Storage(), null, null, null, resolver, null, null, null, null, null, null, null, 0, 0, 0, null);
svc.addRepository(uri);
try {
List<String> features = new ArrayList<String>();
for (Feature feature : svc.listFeatures()) {
features.add(feature.getId());
}
Collections.reverse(features);
svc.installFeatures(new CopyOnWriteArraySet<String>(features),
EnumSet.noneOf(FeaturesService.Option.class));
fail("Call should have thrown an exception");
} catch (Exception t) {
// Expected
}
verify(bundleContext);
}
/**
* This test checks schema validation of submited uri.
*/
@Test
public void testSchemaValidation() throws Exception {
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <featur><bundle>somebundle</bundle></featur></features>");
FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, new Storage(), null, null, null, resolver, null, null, null, null, null, null, null, 0, 0, 0, null);
try {
svc.addRepository(uri);
fail("exception expected");
} catch (Exception e) {
assertTrue(e.getMessage().contains("Unable to validate"));
}
}
/**
* This test checks feature service behavior with old, non namespaced descriptor.
*/
@Test
public void testLoadOldFeatureFile() throws Exception {
URI uri = createTempRepo("<features name='test' xmlns='http://karaf.apache.org/xmlns/features/v1.0.0'>"
+ " <feature name='f1'><bundle>file:bundle1</bundle><bundle>file:bundle2</bundle></feature>"
+ "</features>");
FeaturesServiceImpl svc = new FeaturesServiceImpl(null, null, new Storage(), null, null, null, resolver, null, null, null, null, null, null, null, 0, 0, 0, null);
svc.addRepository(uri);
Feature[] features = svc.getFeatures("f1");
Assert.assertEquals(1, features.length);
Feature feature = features[0];
Assert.assertNotNull("No feature named fi found", feature);
List<BundleInfo> bundles = feature.getBundles();
Assert.assertEquals(2, bundles.size());
}
static class Storage extends StateStorage {
@Override
protected InputStream getInputStream() throws IOException {
return null;
}
@Override
protected OutputStream getOutputStream() throws IOException {
return null;
}
}
}