Created
February 29, 2016 11:44
-
-
Save Steve973/0d45d471372f6a2551b7 to your computer and use it in GitHub Desktop.
Test support class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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