Skip to content

Instantly share code, notes, and snippets.

@Steve973
Created February 29, 2016 11:44
Show Gist options
  • Save Steve973/0d45d471372f6a2551b7 to your computer and use it in GitHub Desktop.
Save Steve973/0d45d471372f6a2551b7 to your computer and use it in GitHub Desktop.
Test support class
package example.itest
import org.apache.karaf.features.BootFinished
import org.apache.karaf.features.FeaturesService
import org.jboss.arquillian.test.api.ArquillianResource
import org.jboss.shrinkwrap.api.ShrinkWrap
import org.jboss.shrinkwrap.api.spec.JavaArchive
import org.jboss.shrinkwrap.osgi.api.BndProjectBuilder
import org.junit.Assert
import org.osgi.framework.Bundle
import org.osgi.framework.BundleContext
import org.osgi.framework.FrameworkUtil
import org.osgi.framework.InvalidSyntaxException
import org.osgi.util.tracker.ServiceTracker
import org.slf4j.LoggerFactory
import example.itest.marker.IntegrationTest
import spock.lang.Specification
import javax.inject.Inject
import java.util.concurrent.TimeUnit
import static org.osgi.framework.Constants.OBJECTCLASS
/**
* This class provides common behavior for tests that use
* Arquillian to verify assets/services inside of a remote
* (read: external, separate process) REDBUS container.
*/
@SuppressWarnings('GroovyUnusedDeclaration')
public abstract class ExampleTestSupport extends Specification {
protected static final def LOGGER = LoggerFactory.getLogger ExampleTestSupport
protected static final def BUNDLE_TIMEOUT = TimeUnit.MINUTES.toMillis 5
/**
* When the test bundle is injected into the running REDBUS container,
* it is injected here.
*/
@ArquillianResource
protected Bundle bundle
/**
* This is the injection point for the root bundle context.
*/
@ArquillianResource
protected BundleContext bundleContext
/**
* This is the injection point for the {@link FeaturesService}.
*/
@Inject
protected FeaturesService featuresService
/**
* To make sure the tests run only when the boot features are fully installed
*/
@Inject
public BootFinished bootFinished
/**
* Creates a deployable test bundle using bnd. This includes a common properties
* file to add OSGi manifest headers to each test bundle.
* @param testSpecificBndFile The bnd file for test-specific OSGi header inclusion.
* @param classesToAdd an optional parameter to specify classes to add to the test bundle
* @return a test bundle that Arquillian will deploy.
*/
def static JavaArchive createJavaArchive(File testSpecificBndFile, Class... classesToAdd) {
def classList = classesToAdd?.toList() ?: new ArrayList<Class>()
classList.add IntegrationTest
ShrinkWrap.create(BndProjectBuilder, 'exampleIntegrationTest')
.setBndFile(testSpecificBndFile)
.addProjectPropertiesFile(getConfigFile('bnd.common.properties'))
.generateManifest(true)
.as(JavaArchive)
.addClasses(classList.toArray(new Class[classList.size()]))
}
/**
* Creates a deployable test bundle using bnd. This includes a common properties
* file to add OSGi manifest headers to each test bundle.
* @param manifestHeaders a map of manifest headers for test-specific OSGi header inclusion.
* @param classesToAdd an optional parameter to specify classes to add to the test bundle
* @return a test bundle that Arquillian will deploy.
*/
def static JavaArchive createJavaArchive(Map<String, String> manifestHeaders, Class... classesToAdd) {
File tmp = File.createTempFile 'test-specific-properties', '.bnd'
tmp.withWriter { out ->
manifestHeaders.each { k, v ->
out.println "${k}: ${v}"
}
}
tmp.deleteOnExit()
createJavaArchive tmp, classesToAdd
}
/**
* Creates a deployable test bundle using bnd. This includes a common properties
* file to add OSGi manifest headers to each test bundle.
* @param name a string to prepend to the Bundle-SymbolicName value. Result will be: <name>.arquillian-test-bundle
* @param classesToAdd an optional parameter to specify classes to add to the test bundle
* @return a test bundle that Arquillian will deploy.
*/
def static JavaArchive createJavaArchive(String name, Class... classesToAdd) {
createJavaArchive(['Bundle-SymbolicName': "${name}.arquillian-test-bundle"], classesToAdd)
}
def 'Test bundle context injection'() throws Exception {
expect: 'The bundle context is not null'
bundleContext
and: 'The bundleId of the bundle represented by the context is bundle 0'
0L == bundleContext.bundle.bundleId
}
def 'Test bundle injection and state'() throws Exception {
expect: 'Assert that the bundle is injected'
bundle
and: 'The bundle has been started (in the test class @before/setup method)'
bundle.state == Bundle.ACTIVE
and: 'Assert the bundle context'
bundle.bundleContext
}
static def getConfigFile(String path) {
def inputStream = ExampleTestSupport.classLoader.getResourceAsStream path
if (!inputStream) {
throw new RuntimeException("Config resource ${path} not found")
}
def fileName = path.subSequence(path.lastIndexOf('/') + 1, path.length()) as String
def prefix = fileName.subSequence(0, fileName.lastIndexOf('.')) as String
def suffix = fileName.subSequence(fileName.lastIndexOf('.') + 1, fileName.length()) as String
final File tempFile = File.createTempFile prefix, suffix
tempFile.write inputStream.text
tempFile.deleteOnExit()
tempFile
}
protected <T> T getOsgiService(Class<T> type, String filter, long timeout) {
def tracker
def classNameFilter = "(${OBJECTCLASS}=${type.name})"
try {
String flt = classNameFilter
if (filter) {
flt = filter.startsWith('(') ? "(&${classNameFilter}${filter})" : "(&${classNameFilter}(${filter}))"
}
def osgiFilter = FrameworkUtil.createFilter flt
tracker = new ServiceTracker(bundleContext, osgiFilter, null)
tracker.open true
// Note that the tracker is not closed to keep the reference
// What happens if the service reference changes?
def svc = type.cast tracker.waitForService(timeout)
if (!svc) {
def dictionary = bundleContext.bundle.headers
LOGGER.error "Test bundle headers: ${explode dictionary}"
bundleContext.getAllServiceReferences(null, null).each { ref ->
LOGGER.error "ServiceReference: ${ref}"
}
bundleContext.getAllServiceReferences(null, flt).each { ref ->
LOGGER.error "Filtered ServiceReference: ${ref}"
}
throw new RuntimeException("Gave up waiting for service ${flt}")
}
type.cast svc
} catch (InvalidSyntaxException e) {
throw new IllegalArgumentException('Invalid filter', e)
} catch (InterruptedException e) {
throw new RuntimeException(e)
}
}
protected def waitForService(String filter, long timeout) throws InvalidSyntaxException, InterruptedException {
def st = new ServiceTracker<>(bundleContext, bundleContext.createFilter(filter), null)
try {
st.open()
st.waitForService timeout
} finally {
st.close()
}
}
protected def waitBundleState(String symbolicName, int state, long timeoutMs = BUNDLE_TIMEOUT) {
long endTime = System.currentTimeMillis() + timeoutMs
while (System.currentTimeMillis() < endTime) {
def bundle = findBundleByName symbolicName
if (bundle != null && bundle.state == state) {
return bundle
}
try {
Thread.sleep 500
} catch (InterruptedException e) {
throw new IllegalStateException(e)
}
}
throw new Exception("Manadatory bundle ${symbolicName} not found.")
}
/**
* Explode the dictionary into a comma-delimited list of key=value pairs
*/
static def explode(Dictionary dictionary) {
dictionary.keys().collect { key ->
"${key}:${dictionary.get(key)}"
}.join ', '
}
protected def assertFeatureInstalled(String featureName) throws Exception {
def splitFeatureName = featureName.split '/'
def name = splitFeatureName[0]
def version = (splitFeatureName.length == 2) ? splitFeatureName[1] : null
assertFeatureInstalled name, version
}
protected def assertFeatureInstalled(String featureName, String featureVersion) throws Exception {
def featureToAssert = featuresService.getFeatures(featureName, featureVersion).first()
if (!featuresService.listInstalledFeatures().contains(featureToAssert)) {
Assert.fail "Feature ${featureName}${(featureVersion != null ? "/${featureVersion}" : '')} should be installed but is not"
}
}
protected def assertFeaturesInstalled(String... expectedFeatures) throws Exception {
def expectedFeaturesSet = expectedFeatures as Collection<String>
def installedFeatures = featuresService.listInstalledFeatures().collect { feature ->
feature.name
}
Assert.assertTrue "Expecting the following features to be installed: ${expectedFeaturesSet} but found ${installedFeatures}",
installedFeatures.containsAll(expectedFeaturesSet)
}
protected def assertFeatureNotInstalled(String featureName) throws Exception {
def splitFeatureName = featureName.split '/'
def name = splitFeatureName[0]
def version = (splitFeatureName.length == 2) ? splitFeatureName[1] : null
assertFeatureNotInstalled name, version
}
protected def assertFeatureNotInstalled(String featureName, String featureVersion) throws Exception {
def featureToAssert = featuresService.getFeatures(featureName, featureVersion).first()
if (featuresService.listInstalledFeatures().contains(featureToAssert)) {
Assert.fail "Feature ${featureName}${(featureVersion != null ? "/${featureVersion}" : '')} should not be installed but it is"
}
}
protected def findBundleByName(String symbolicName) {
bundleContext.bundles.find { currentBundle ->
currentBundle.symbolicName.equals symbolicName
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment