--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+
+ This program and the accompanying materials are made available under the
+ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ and is available at http://www.eclipse.org/legal/epl-v10.html
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.opendaylight.odlparent</groupId>
+ <artifactId>bundle-parent</artifactId>
+ <version>1.8.0-SNAPSHOT</version>
+ <relativePath>../bundle-parent</relativePath>
+ </parent>
+
+ <artifactId>bundles-test</artifactId>
+ <packaging>bundle</packaging>
+ <name>ODL :: odlparent :: ${project.artifactId}</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.bundle</groupId>
+ <artifactId>org.apache.karaf.bundle.core</artifactId>
+ <version>${karaf.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-library</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-core</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.truth</groupId>
+ <artifactId>truth</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <propertyExpansion>checkstyle.violationSeverity=error</propertyExpansion>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <configuration>
+ <failOnError>true</failOnError>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <!--
+ Maven Site Configuration
+
+ The following configuration is necessary for maven-site-plugin to
+ correctly identify the correct deployment path for OpenDaylight Maven
+ sites.
+ -->
+ <url>${odl.site.url}/${project.groupId}/${stream}/${project.artifactId}/</url>
+
+ <distributionManagement>
+ <site>
+ <id>opendaylight-site</id>
+ <url>${nexus.site.url}/${project.artifactId}/</url>
+ </site>
+ </distributionManagement>
+
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.odlparent.bundlestest;
+
+import static org.apache.karaf.bundle.core.BundleState.Active;
+import static org.apache.karaf.bundle.core.BundleState.Installed;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.karaf.bundle.core.BundleInfo;
+import org.apache.karaf.bundle.core.BundleService;
+import org.apache.karaf.bundle.core.BundleState;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * System readiness diagnostic summary information.
+ *
+ * @author Michael Vorburger.ch
+ */
+public class BundleDiagInfos {
+
+ private final List<String> okBundleStateInfoTexts;
+ private final List<String> nokBundleStateInfoTexts;
+ private final List<String> whitelistedBundleStateInfoTexts;
+ private final Map<BundleState, Integer> bundleStatesCounters;
+
+ private static final Map<String, BundleState> WHITELISTED_BUNDLES = ImmutableMap.of(
+ "slf4j.log4j12", Installed );
+
+ public static BundleDiagInfos forContext(BundleContext bundleContext, BundleService bundleService) {
+ List<String> okBundleStateInfoTexts = new ArrayList<>();
+ List<String> nokBundleStateInfoTexts = new ArrayList<>();
+ List<String> whitelistedBundleStateInfoTexts = new ArrayList<>();
+ Map<BundleState, Integer> bundleStatesCounters = new EnumMap<>(BundleState.class);
+ for (BundleState bundleState : BundleState.values()) {
+ bundleStatesCounters.put(bundleState, 0);
+ }
+
+ for (Bundle bundle : bundleContext.getBundles()) {
+ String bundleSymbolicName = bundle.getSymbolicName();
+ BundleInfo karafBundleInfo = bundleService.getInfo(bundle);
+ BundleState karafBundleState = karafBundleInfo.getState();
+
+ String bundleStateDiagText = "OSGi state = " + bundleStatetoText(bundle.getState())
+ + ", Karaf bundleState = " + karafBundleState;
+ String diagText = bundleService.getDiag(bundle);
+ if (!Strings.isNullOrEmpty(diagText)) {
+ bundleStateDiagText += ", due to: " + diagText;
+ }
+
+ if (WHITELISTED_BUNDLES.get(bundleSymbolicName) != null) {
+ if (WHITELISTED_BUNDLES.get(bundleSymbolicName).equals(karafBundleState)) {
+ String msg = "WHITELISTED " + bundleSymbolicName + ": " + bundleStateDiagText;
+ whitelistedBundleStateInfoTexts.add(msg);
+ continue;
+ }
+ }
+
+ bundleStatesCounters.compute(karafBundleState, (key, counter) -> counter + 1);
+
+ // BundleState comparison as in Karaf's "diag" command,
+ // see https://github.com/apache/karaf/blob/master/bundle/core/src/main/java/org/apache/karaf/bundle/command/Diag.java
+ // but we intentionally, got a little further than Karaf's "diag" command,
+ // and instead of only checking some states, we check what's really Active,
+ // but accept that some remain just Resolved:
+ if (karafBundleState != Active && !(karafBundleState == BundleState.Resolved)) {
+ String msg = "NOK " + bundleSymbolicName + ": " + bundleStateDiagText;
+ nokBundleStateInfoTexts.add(msg);
+ } else {
+ String msg = "OK " + bundleSymbolicName + ": " + bundleStateDiagText;
+ okBundleStateInfoTexts.add(msg);
+ }
+ }
+
+ return new BundleDiagInfos(okBundleStateInfoTexts, nokBundleStateInfoTexts,
+ whitelistedBundleStateInfoTexts, bundleStatesCounters);
+ }
+
+ private static String bundleStatetoText(int state) {
+ switch (state) {
+ case Bundle.INSTALLED:
+ return "Installed";
+ case Bundle.RESOLVED:
+ return "Resolved";
+ case Bundle.STARTING:
+ return "Starting";
+ case Bundle.ACTIVE:
+ return "Active";
+ case Bundle.STOPPING:
+ return "Stopping";
+ case Bundle.UNINSTALLED:
+ return "Uninstalled";
+ default:
+ return state + "???";
+ }
+ }
+
+ private BundleDiagInfos(List<String> okBundleStateInfoTexts, List<String> nokBundleStateInfoTexts,
+ List<String> whitelistedBundleStateInfoTexts, Map<BundleState, Integer> bundleStatesCounters) {
+ this.okBundleStateInfoTexts = ImmutableList.copyOf(okBundleStateInfoTexts);
+ this.nokBundleStateInfoTexts = ImmutableList.copyOf(nokBundleStateInfoTexts);
+ this.whitelistedBundleStateInfoTexts = ImmutableList.copyOf(whitelistedBundleStateInfoTexts);
+ this.bundleStatesCounters = ImmutableMap.copyOf(bundleStatesCounters);
+ }
+
+ public SystemState getSystemState() {
+ if (bundleStatesCounters.get(BundleState.Failure) > 0) {
+ return SystemState.Failure;
+ } else if (bundleStatesCounters.get(BundleState.Stopping) > 0) {
+ return SystemState.Stopping;
+ } else if (bundleStatesCounters.get(BundleState.Installed) == 0
+ // No, just Resolved is OK, so do not: && bundleStatesCounters.get(BundleState.Resolved) == 0
+ && bundleStatesCounters.get(BundleState.Unknown) == 0
+ && bundleStatesCounters.get(BundleState.GracePeriod) == 0
+ && bundleStatesCounters.get(BundleState.Waiting) == 0
+ && bundleStatesCounters.get(BundleState.Starting) == 0
+ // BundleState.Active *should* be ~= total # of bundles (minus Resolved, and whitelisted installed)
+ && bundleStatesCounters.get(BundleState.Stopping) == 0
+ && bundleStatesCounters.get(BundleState.Failure) == 0) {
+ return SystemState.Active;
+ } else {
+ return SystemState.Booting;
+ }
+ }
+
+ public String getFullDiagnosticText() {
+ StringBuilder sb = new StringBuilder(getSummaryText());
+ int failureNumber = 1;
+ for (String nokBundleStateInfoText : getNokBundleStateInfoTexts()) {
+ sb.append('\n');
+ sb.append(failureNumber++);
+ sb.append(". ");
+ sb.append(nokBundleStateInfoText);
+ }
+ return sb.toString();
+ }
+
+ public String getSummaryText() {
+ return "diag: " + getSystemState() + " " + bundleStatesCounters.toString();
+ }
+
+ public List<String> getNokBundleStateInfoTexts() {
+ return ImmutableList.copyOf(nokBundleStateInfoTexts);
+ }
+
+ public List<String> getOkBundleStateInfoTexts() {
+ return ImmutableList.copyOf(okBundleStateInfoTexts);
+ }
+
+ public List<String> getWhitelistedBundleStateInfoTexts() {
+ return ImmutableList.copyOf(whitelistedBundleStateInfoTexts);
+ }
+
+ @Override
+ public String toString() {
+ return getFullDiagnosticText();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.odlparent.bundlestest;
+
+import static org.opendaylight.odlparent.bundlestest.SystemState.Active;
+import static org.opendaylight.odlparent.bundlestest.SystemState.Failure;
+import static org.opendaylight.odlparent.bundlestest.SystemState.Stopping;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+
+/**
+ * Hamcrest Matcher for {@link BundleDiagInfos}.
+ *
+ * @author Michael Vorburger.ch
+ */
+class BundleServiceSummaryMatcher extends BaseMatcher<BundleDiagInfos> {
+
+ @Override
+ public boolean matches(Object item) {
+ SystemState systemState = ((BundleDiagInfos) item).getSystemState();
+ return systemState.equals(Active) || systemState.equals(Stopping) || systemState.equals(Failure);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("system either ready with all bundles Active, "
+ + "or Stopping or Failure (but not still booting in GracePeriod, Waiting, Starting, Unknown;"
+ + "but just Resolved and some exceptional Installed is OK)");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.odlparent.bundlestest;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Utilities for OSGi's {@link ServiceReference}.
+ *
+ * @author Michael Vorburger.ch
+ */
+public class ServiceReferenceUtil {
+
+ public Map<String, Object> getProperties(ServiceReference<?> serviceRef) {
+ String[] propertyKeys = serviceRef.getPropertyKeys();
+ Map<String, Object> properties = new HashMap<>(propertyKeys.length);
+ for (String propertyKey : propertyKeys) {
+ Object propertyValue = serviceRef.getProperty(propertyKey);
+ if (propertyValue.getClass().isArray()) {
+ propertyValue = Arrays.asList((Object[]) propertyValue);
+ }
+ properties.put(propertyKey, propertyValue);
+ }
+ return properties;
+ }
+
+ public List<String> getUsingBundleSymbolicNames(ServiceReference<?> serviceRef) {
+ if (serviceRef.getUsingBundles() == null) {
+ return Collections.emptyList();
+ } else {
+ return Arrays.asList(serviceRef.getUsingBundles()).stream()
+ .map(bundle -> bundle.getSymbolicName()).collect(Collectors.toList());
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.odlparent.bundlestest;
+
+import org.apache.karaf.bundle.core.BundleState;
+
+/**
+ * Karaf OSGi System State.
+ *
+ * @see BundleState
+ *
+ * @author Michael Vorburger.ch
+ */
+public enum SystemState {
+
+ Booting,
+
+ Active,
+
+ Stopping,
+
+ Failure
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.odlparent.bundlestest;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.junit.Assert.fail;
+
+import org.apache.karaf.bundle.core.BundleService;
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionTimeoutException;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility to verify bundle diagnostic state from OSGi integration tests.
+ *
+ * @author Michael Vorburger.ch
+ */
+public class TestBundleDiag {
+
+ private static final Logger LOG = LoggerFactory.getLogger(TestBundleDiag.class);
+
+ private final BundleContext bundleContext;
+ private final BundleService bundleService;
+
+ public TestBundleDiag(BundleContext bundleContext, BundleService bundleService) {
+ this.bundleContext = bundleContext;
+ this.bundleService = bundleService;
+ }
+
+ /**
+ * Does the equivalent of the "diag" CLI command, and fails the test if anything incl. bundle wiring is NOK.
+ *
+ * <p>The implementation is based on Karaf's BundleService, and not the BundleStateService,
+ * because each Karaf supported DI system (such as Blueprint and Declarative Services, see String constants
+ * in BundleStateService), will have a separate BundleStateService. The BundleService however will
+ * contain the combined status of all BundleStateServices.
+ *
+ * @author Michael Vorburger, based on guidance from Christian Schneider
+ */
+ public void checkBundleDiagInfos() {
+ try {
+ Awaitility.await("checkBundleDiagInfos")
+ .pollDelay(0, MILLISECONDS)
+ .pollInterval(250, MILLISECONDS)
+ .atMost(5, MINUTES)
+ .conditionEvaluationListener(
+ condition -> LOG.info("{} (elapsed time {}ms, remaining time {}ms)",
+ ((BundleDiagInfos) condition.getValue()).getSummaryText(),
+ condition.getElapsedTimeInMS(), condition.getRemainingTimeInMS()))
+ .until(() -> getBundleDiagInfos(), new BundleServiceSummaryMatcher());
+
+ // If we're here then either BundleServiceSummaryMatcher quit because of Active, Failure or Stopping..
+ BundleDiagInfos bundleInfos = getBundleDiagInfos();
+ SystemState systemState = bundleInfos.getSystemState();
+ if (systemState.equals(SystemState.Failure) || systemState.equals(SystemState.Stopping)) {
+ LOG.error("diag failure; BundleService reports bundle(s) which failed or are already stopping"
+ + " (details in following INFO and ERROR log messages...)");
+ logBundleDiagInfos(bundleInfos);
+ fail(bundleInfos.getFullDiagnosticText());
+
+ } else {
+ // Inform the developer of the green SystemState.Active
+ LOG.info(bundleInfos.getFullDiagnosticText());
+ }
+
+ } catch (ConditionTimeoutException e) {
+ // If this happens then it got stuck waiting in SystemState.Booting,
+ // typically due to bundles still in BundleState GracePeriod or Waiting
+ LOG.error("diag failure; BundleService reports bundle(s) which are still not active"
+ + " (details in following INFO and ERROR log messages...)");
+ BundleDiagInfos bundleInfos = getBundleDiagInfos();
+ logBundleDiagInfos(bundleInfos);
+ throw e; // fail the test!
+ }
+ }
+
+ private void logBundleDiagInfos(BundleDiagInfos bundleInfos) {
+ try {
+ logOSGiServices();
+ } catch (IllegalStateException e) {
+ LOG.warn("logOSGiServices() failed (never mind); too late during shutdown already?", e);
+ }
+ for (String okBundleStateInfo : bundleInfos.getOkBundleStateInfoTexts()) {
+ LOG.info(okBundleStateInfo);
+ }
+ for (String whitelistedBundleStateInfo : bundleInfos.getWhitelistedBundleStateInfoTexts()) {
+ LOG.warn(whitelistedBundleStateInfo);
+ }
+ for (String nokBundleStateInfo : bundleInfos.getNokBundleStateInfoTexts()) {
+ LOG.error(nokBundleStateInfo);
+ }
+ }
+
+ private BundleDiagInfos getBundleDiagInfos() {
+ return BundleDiagInfos.forContext(bundleContext, bundleService);
+ }
+
+ private void logOSGiServices() {
+ ServiceReferenceUtil util = new ServiceReferenceUtil();
+ LOG.info("Now going to log all known services, to help diagnose root cause of "
+ + "diag failure BundleService reported bundle(s) which are not active");
+ try {
+ for (ServiceReference<?> serviceRef : bundleContext.getAllServiceReferences(null, null)) {
+ LOG.info("{} defines OSGi Service {} used by {}", serviceRef.getBundle().getSymbolicName(),
+ util.getProperties(serviceRef), util.getUsingBundleSymbolicNames(serviceRef));
+ }
+ } catch (InvalidSyntaxException e) {
+ LOG.error("logOSGiServices() failed due to InvalidSyntaxException", e);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.opendaylight.odlparent.bundlestest;
--- /dev/null
+/*
+ * Copyright (c) 2016 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.odlparent.bundlestest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.Arrays;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Unit test for ServiceReferenceUtil.
+ *
+ * @author Michael Vorburger.ch
+ */
+public class ServiceReferenceUtilTest {
+
+ @Test
+ public void testGetUsingBundleSymbolicNames() {
+ assertThat(new ServiceReferenceUtil().getUsingBundleSymbolicNames(getServiceReference())).isEmpty();
+ }
+
+ @Test
+ public void testGetProperties() {
+ assertThat(new ServiceReferenceUtil().getProperties(getServiceReference())).containsExactly(
+ "property1", "value1",
+ "property2", Arrays.asList(new String[] { "value2.1", "value2.2" }));
+ }
+
+ private ServiceReference<?> getServiceReference() {
+ return new ServiceReference<Object>() {
+
+ @Override
+ public Object getProperty(String key) {
+ if ("property1".equals(key)) {
+ return "value1";
+ } else if ("property2".equals(key)) {
+ return new String[] { "value2.1", "value2.2" };
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public String[] getPropertyKeys() {
+ return new String[] { "property1", "property2"};
+ }
+
+ @Override
+ public Bundle getBundle() {
+ return null;
+ }
+
+ @Override
+ public Bundle[] getUsingBundles() {
+ return null;
+ }
+
+ @Override
+ public boolean isAssignableTo(Bundle bundle, String className) {
+ return false;
+ }
+
+ @Override
+ public int compareTo(Object reference) {
+ return 0;
+ }
+ };
+ }
+
+}
<build>
<plugins>
+ <plugin>
+ <!-- This generates the META-INF/maven/dependencies.properties file
+ which is required by the versionAsInProject() used in SingleFeatureTest -->
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ </plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
- <version>${maven.surefire.version}</version>
<configuration>
<skip>${skip.karaf.featureTest}</skip>
<dependenciesToScan>
<plugin>
<groupId>org.apache.karaf.tooling</groupId>
<artifactId>karaf-maven-plugin</artifactId>
- <version>3.0.8</version>
<extensions>true</extensions>
<configuration>
<aggregateFeatures>true</aggregateFeatures>
</site>
</distributionManagement>
-</project>
\ No newline at end of file
+</project>
</pluginManagement>
<plugins>
+ <plugin>
+ <!-- This generates the META-INF/maven/dependencies.properties file
+ which is required by the versionAsInProject() used in SingleFeatureTest -->
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ </plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
- <version>1</version>
<scope>compile</scope>
</dependency>
<dependency>
<artifactId>org.osgi.core</artifactId>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>bundles-test</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
<build>
import org.slf4j.LoggerFactory;
public class PerFeatureRunner extends Runner implements Filterable, Sortable {
+
private static final Logger LOG = LoggerFactory.getLogger(PerFeatureRunner.class);
+
private final String featureVersion;
private final String featureName;
private final PaxExam delegate;
import static org.opendaylight.odlparent.featuretest.Constants.ORG_OPENDAYLIGHT_FEATURETEST_FEATUREVERSION_PROP;
import static org.opendaylight.odlparent.featuretest.Constants.ORG_OPENDAYLIGHT_FEATURETEST_URI_PROP;
import static org.ops4j.pax.exam.CoreOptions.maven;
+import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.when;
+import static org.ops4j.pax.exam.CoreOptions.wrappedBundle;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.configureConsole;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.features;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.logLevel;
+import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.EnumSet;
import java.util.List;
import java.util.Properties;
-
import javax.inject.Inject;
-
+import org.apache.karaf.bundle.core.BundleService;
import org.apache.karaf.features.Feature;
import org.apache.karaf.features.FeaturesService;
import org.apache.karaf.features.Repository;
import org.apache.karaf.features.internal.model.Features;
import org.apache.karaf.features.internal.model.JaxbUtil;
+import org.eclipse.jdt.annotation.NonNull;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.opendaylight.odlparent.bundlestest.TestBundleDiag;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
import org.ops4j.pax.exam.options.extra.VMOption;
+import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final String ORG_OPS4J_PAX_URL_MVN_REPOSITORIES = "org.ops4j.pax.url.mvn.repositories";
private static final String ETC_ORG_OPS4J_PAX_URL_MVN_CFG = "etc/org.ops4j.pax.url.mvn.cfg";
private static final String ETC_ORG_OPS4J_PAX_LOGGING_CFG = "etc/org.ops4j.pax.logging.cfg";
+
private static final String KEEP_UNPACK_DIRECTORY_PROP = "karaf.keep.unpack";
private static final String PROFILE_PROP = "karaf.featureTest.profile";
+ private static final String BUNDLES_DIAG_SKIP_PROP = "sft.diag.skip";
private static final String LOG4J_LOGGER_ORG_OPENDAYLIGHT_YANGTOOLS_FEATURETEST =
"log4j.logger.org.opendaylight.odlparent.featuretest";
+ "http://repository.springsource.com/maven/bundles/external@id=spring.ebr.external, "
+ "http://zodiac.springsource.com/maven/bundles/release@id=gemini ";
+ @Inject @NonNull
+ private BundleContext bundleContext;
- @Inject
+ @Inject @NonNull
private FeaturesService featuresService;
+ @Inject @NonNull
+ private BundleService bundleService; // NOT BundleStateService, see checkBundleStatesDiag()
+
private String karafVersion;
private String karafDistroVersion;
logLevel(LogLevel.WARN),
mvnLocalRepoOption(),
standardKarafFeatures(),
+ wrappedBundle(maven("org.awaitility", "awaitility").versionAsInProject()), // req. by bundles-test
+ mavenBundle(maven("com.google.guava", "guava").versionAsInProject()), // req. by bundles-test
+ mavenBundle(maven("org.opendaylight.odlparent", "bundles-test").versionAsInProject()),
editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG, LOG4J_LOGGER_ORG_OPENDAYLIGHT_YANGTOOLS_FEATURETEST,
LogLevel.INFO.name()),
editConfigurationFilePut(ETC_ORG_OPS4J_PAX_LOGGING_CFG, "log4j.rootLogger", "INFO, stdout, osgi:*"),
Assert.assertTrue("Failed to install Feature: " + getFeatureName() + " " + getFeatureVersion(),
featuresService.isInstalled(feature));
LOG.info("Successfull installed feature {} {}", getFeatureName(), getFeatureVersion());
+
+ if (!Boolean.getBoolean(BUNDLES_DIAG_SKIP_PROP)
+ && !BLACKLISTED_BROKEN_FEATURES.contains(getFeatureName())) {
+ new TestBundleDiag(bundleContext, bundleService).checkBundleDiagInfos();
+ } else {
+ LOG.warn("SKIPPING TestBundleDiag because system property {} is true or feature is blacklisted: {}",
+ BUNDLES_DIAG_SKIP_PROP, getFeatureName());
+ }
}
+
+ // TODO Figure out why each of these fails the TestBundleDiag, fix it, and remove this.. ;)
+ private static final List<String> BLACKLISTED_BROKEN_FEATURES = ImmutableList.of(
+ // integration/distribution/features-test due to (unclear)
+ "odl-integration-all",
+ // controller/features/mdsal/ due to IllegalStateException: ./configuration/initial/akka.conf is missing
+ "odl-mdsal-broker-local",
+ "odl-mdsal-clustering-commons",
+ "odl-mdsal-distributed-datastore",
+ "odl-mdsal-remoterpc-connector",
+ // aaa/features/authn due to Cassandra expected to be up on
+ "odl-aaa-authn-cassandra-cluster",
+ // 3/18 in bgpcep/features/bgp/ due to NoSuchFileException: etc/....
+ "odl-bgpcep-bgp-rib-impl",
+ "odl-bgpcep-bgp-topology",
+ "odl-bgpcep-bgp-cli",
+ // 1/1 in bgpcep/features/bmp due to NoSuchFileException: etc/opendaylight/bgp
+ "odl-bgpcep-bmp",
+ // 4/8 in lispflowmapping/features due to.. unclear, similar issue to odl-integration-all?
+ "odl-lispflowmapping-mappingservice",
+ "odl-lispflowmapping-mappingservice-shell",
+ "odl-lispflowmapping-neutron",
+ "odl-lispflowmapping-ui",
+ // 1/17 in lispflowmapping/features due to NOK org.opendaylight.groupbasedpolicy
+ // Caused by: org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException
+ "odl-groupbasedpolicy-ne-location-provider",
+ // 1/11 in tsdr/features due to (strange) ClassNotFoundException: odlparent.bundlestest
+ // .TestBundleDiag (works for all other features; class loading issue in that feature?)
+ "odl-hbaseclient",
+ // 1/9 in unimgr/features due missing mdsal, similar to issue to odl-integration-all?
+ "odl-unimgr-netvirt",
+ // 1/19 in bgpcep/features/bgp due to Missing dependencies:
+ // (objectClass=org.opendaylight.protocol.bgp.rib.spi.state.BGPStateConsumer)
+ // NOK org.opendaylight.bgpcep.bgp-openconfig-state
+ "odl-bgpcep-bgp-openconfig-state"
+ );
}
<ignore/>
</action>
</pluginExecution>
+
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>maven-paxexam-plugin</artifactId>
+ <versionRange>[1.2.4,)</versionRange>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore/>
+ </action>
+ </pluginExecution>
+
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
<module>checkstyle</module>
<module>findbugs</module>
<module>license</module>
+ <module>bundles-test</module>
<module>features-test</module>
<!-- Karaf integration -->