Skip to content
Browse files

Modularization of Android Extension

  • Loading branch information...
1 parent 38e1b4b commit c87a8526716bd73b95b379cd04744e57d3de26e5 @kpiwko kpiwko committed Feb 27, 2012
Showing with 3,338 additions and 1,005 deletions.
  1. +4 −0 .gitignore
  2. +27 −12 README.md
  3. +134 −0 android-api/pom.xml
  4. +56 −0 android-api/src/main/java/org/jboss/arquillian/android/api/AndroidBridge.java
  5. +127 −0 android-api/src/main/java/org/jboss/arquillian/android/api/AndroidDevice.java
  6. +43 −0 android-api/src/main/java/org/jboss/arquillian/android/api/AndroidDeviceOutputReciever.java
  7. +47 −0 android-api/src/main/java/org/jboss/arquillian/android/api/AndroidExecutionException.java
  8. +69 −0 android-bom/pom.xml
  9. +75 −0 android-build/pom.xml
  10. +45 −0 android-configuration/pom.xml
  11. +22 −78 ...d-configuration/src/main/java/org/jboss/arquillian/android/configuration/ConfigurationMapper.java
  12. +257 −0 android-configuration/src/main/java/org/jboss/arquillian/android/configuration/SecurityActions.java
  13. +124 −0 android-configuration/src/main/java/org/jboss/arquillian/android/configuration/Validate.java
  14. +63 −0 android-depchain/pom.xml
  15. +102 −0 android-drone/pom.xml
  16. +37 −0 android-drone/src/main/java/org/jboss/arquillian/android/drone/AndroidDroneExtension.java
  17. +125 −0 ...one/src/main/java/org/jboss/arquillian/android/drone/configuration/AndroidDroneConfiguration.java
  18. +27 −0 android-drone/src/main/java/org/jboss/arquillian/android/drone/event/AndroidDroneConfigured.java
  19. +27 −0 android-drone/src/main/java/org/jboss/arquillian/android/drone/event/AndroidWebDriverHubRunning.java
  20. +105 −0 android-drone/src/main/java/org/jboss/arquillian/android/drone/impl/AndroidDroneConfigurator.java
  21. +142 −0 android-drone/src/main/java/org/jboss/arquillian/android/drone/impl/AndroidWebDriverSupport.java
  22. +140 −0 android-drone/src/main/java/org/jboss/arquillian/android/drone/impl/Validate.java
  23. +1 −0 android-drone/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
  24. +16 −25 ...c/test/java/org/jboss/arquillian/android/drone}/impl/AndroidWebDriverSupportEmulatorTestCase.java
  25. +20 −23 ...test/java/org/jboss/arquillian/android/drone}/impl/AndroidWebDriverSupportRealDeviceTestCase.java
  26. +101 −0 android-impl/pom.xml
  27. +11 −10 ...on.java → android-impl/src/main/java/org/jboss/arquillian/android/ArquillianAndroidExtension.java
  28. +40 −0 ...-impl/src/main/java/org/jboss/arquillian/android/configuration/AndroidConfigurationException.java
  29. +26 −32 ...-impl/src/main/java/org/jboss/arquillian/android/configuration/AndroidExtensionConfiguration.java
  30. +25 −12 { → android-impl}/src/main/java/org/jboss/arquillian/android/configuration/AndroidSdk.java
  31. 0 { → android-impl}/src/main/java/org/jboss/arquillian/android/configuration/Validate.java
  32. +91 −0 android-impl/src/main/java/org/jboss/arquillian/android/impl/AndroidBridgeConnector.java
  33. +155 −0 android-impl/src/main/java/org/jboss/arquillian/android/impl/AndroidBridgeImpl.java
  34. +183 −0 android-impl/src/main/java/org/jboss/arquillian/android/impl/AndroidDeviceImpl.java
  35. +74 −25 ...or.java → android-impl/src/main/java/org/jboss/arquillian/android/impl/AndroidDeviceSelector.java
  36. +40 −0 android-impl/src/main/java/org/jboss/arquillian/android/impl/AndroidEmulator.java
  37. +110 −0 android-impl/src/main/java/org/jboss/arquillian/android/impl/AndroidExtensionConfigurator.java
  38. +54 −14 { → android-impl}/src/main/java/org/jboss/arquillian/android/impl/EmulatorShutdown.java
  39. +80 −36 { → android-impl}/src/main/java/org/jboss/arquillian/android/impl/EmulatorStartup.java
  40. +29 −7 { → android-impl}/src/main/java/org/jboss/arquillian/android/impl/ProcessExecutor.java
  41. 0 { → android-impl}/src/main/java/org/jboss/arquillian/android/impl/SecurityActions.java
  42. +23 −7 { → android-impl}/src/main/java/org/jboss/arquillian/android/impl/Validate.java
  43. +1 −0 android-impl/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
  44. +15 −16 ... android-impl/src/test/java/org/jboss/arquillian/android/impl/AndroidBridgeConnectorTestCase.java
  45. +13 −9 ...id-impl}/src/test/java/org/jboss/arquillian/android/impl/AndroidVirtualDeviceCreatorTestCase.java
  46. +17 −17 ...uration → android-impl/src/test/java/org/jboss/arquillian/android/impl}/ConfiguratorTestCase.java
  47. +15 −20 { → android-impl}/src/test/java/org/jboss/arquillian/android/impl/EmulatorTestCase.java
  48. +15 −21 { → android-impl}/src/test/java/org/jboss/arquillian/android/impl/RealDeviceStarterTestCase.java
  49. BIN android-server-2.13.0.apk
  50. BIN android-server-2.6.0.apk
  51. +35 −0 android-spi/pom.xml
  52. +41 −0 android-spi/src/main/java/org/jboss/arquillian/android/spi/event/AndroidBridgeInitialized.java
  53. +27 −0 android-spi/src/main/java/org/jboss/arquillian/android/spi/event/AndroidBridgeTerminated.java
  54. +38 −0 android-spi/src/main/java/org/jboss/arquillian/android/spi/event/AndroidDeviceReady.java
  55. +39 −0 android-spi/src/main/java/org/jboss/arquillian/android/spi/event/AndroidDeviceShutdown.java
  56. +26 −0 android-spi/src/main/java/org/jboss/arquillian/android/spi/event/AndroidExtensionConfigured.java
  57. +31 −0 android-spi/src/main/java/org/jboss/arquillian/android/spi/event/AndroidVirtualDeviceAvailable.java
  58. +31 −0 android-spi/src/main/java/org/jboss/arquillian/android/spi/event/AndroidVirtualDeviceCreated.java
  59. +37 −0 android-spi/src/main/java/org/jboss/arquillian/android/spi/event/AndroidVirtualDeviceEvent.java
  60. 0 {test-example → android-tests}/android-server-2.16.apk
  61. 0 {test-example → android-tests}/android-server-2.6.0.apk
  62. +35 −76 {test-example → android-tests}/pom.xml
  63. +18 −5 .../android → android-tests/src/test/java/org/jboss/arquillian/android/example}/AndroidTestCase.java
  64. +13 −21 {test-example → android-tests}/src/test/resources/arquillian.xml
  65. +14 −108 pom.xml
  66. +0 −18 src/main/java/org/jboss/arquillian/android/AndroidConfigurationException.java
  67. +0 −19 src/main/java/org/jboss/arquillian/android/event/AndroidDebugBridgeInitialized.java
  68. +0 −9 src/main/java/org/jboss/arquillian/android/event/AndroidDeviceAvailable.java
  69. +0 −16 src/main/java/org/jboss/arquillian/android/event/AndroidDeviceReady.java
  70. +0 −5 src/main/java/org/jboss/arquillian/android/event/AndroidDeviceShutdownEvent.java
  71. +0 −13 src/main/java/org/jboss/arquillian/android/event/AndroidDeviceStartupEvent.java
  72. +0 −52 src/main/java/org/jboss/arquillian/android/event/AndroidSdkConfigured.java
  73. +0 −9 src/main/java/org/jboss/arquillian/android/event/AndroidVirtualDeviceAvailable.java
  74. +0 −9 src/main/java/org/jboss/arquillian/android/event/AndroidVirtualDeviceCreated.java
  75. +0 −110 src/main/java/org/jboss/arquillian/android/impl/AndroidDebugBridgeConnector.java
  76. +0 −18 src/main/java/org/jboss/arquillian/android/impl/AndroidEmulator.java
  77. +0 −152 src/main/java/org/jboss/arquillian/android/impl/AndroidWebDriverSupport.java
  78. +0 −1 src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
View
4 .gitignore
@@ -2,6 +2,7 @@
.project
.classpath
.settings/
+bin
# IntelliJ
*.iml
@@ -17,3 +18,6 @@ test-output/
# JBoss AS
transaction.log
+
+# Temporary Android images
+foobar-test-device
View
39 README.md
@@ -20,8 +20,9 @@ where you extracted it. You should also update it via running `android` and navi
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
- <artifactId>arquillian-android</artifactId>
+ <artifactId>arquillian-android-depchain</artifactId>
<version>1.0.0.Alpha1-SNAPSHOT</version>
+ <type>pom</type>
<scope>test</scope>
</dependency>
@@ -39,43 +40,57 @@ where you extracted it. You should also update it via running `android` and navi
3. Set up WebDriver in arquillian.xml
<extension qualifier="webdriver">
- <!-- this is optional if you set u
+ <!-- this is optional if you set -->
<property name="implementationClass">org.openqa.selenium.android.AndroidDriver</property>
+ <!-- this makes WebDriver connect hub on Android device -->
<property name="remoteAddress">http://localhost:14444/wd/hub</property>
</extension>
-4. Set up AndroidSdk in arquillian.xml
+4. Set up Android in arquillian.xml
You should be aware that following might change in the future. You've been warned!
- <extension qualifier="android-sdk">
+ <extension qualifier="android">
+ <!-- this is optional, can be set via ANDROID_HOME property -->
<property name="home">/home/kpiwko/apps/android-sdk-linux_x86</property>
<!-- Nexus S -->
<!-- <property name="serialId">3233E8EDB21700EC</property>-->
<property name="verbose">true</property>
- <property name="androidServerApk">android-server-2.16.apk</property>
<property name="apiLevel">13</property>
<property name="avdName">SnapshotEnabled</property>
- <property name="emulatorStartupTimeout">180000</property>
-
+ <property name="emulatorBootupTimeoutInSeconds">180</property>
</extension>
- Properties explained, required in bold:
+ Properties explained, required in **bold**:
- **home** - ANDROID_HOME, can be ommited if set via ANDROID_HOME property
- **avdName** - name of the Android Virtual Device. It will be either created or reused
- - **androidServerApk** - path to the Android Server APK you've downloaded
- apiLevel - (13) denotates API level, use `android list target` to get more variants
- - serialId - replaces avdName if set, represents a real device. Use `adb devics` to get the list
+ - serialId - replaces avdName if set and availabel, represents a real device. Use `adb devics` to get the list
- skip - (false) skip execution
- verbose - (false) be verbose
- force - (false) force emulator recreationg
- sdSize - (128M) SD card size for emulator
- - emulatorBootupTime - (120000L) maximal time to get emulator started, use Snapshot enabled device if it takes too long
+ - emulatorBootupTimeoutInSeconds - (180) maximal time to get emulator started, use Snapshot enabled device if it takes too long
- emulatorOptions - emulator options
+
+ Emulators are created by default in `${basedir}/${avdName}`.
+
+5. Set up Android Drone in arquillian.xml
+
+ You should be aware that following might change in the future. You've been warned!
+
+ <extension qualifier="android-drone">
+ <property name="androidServerApk">android-server-2.16.apk</property>
+ </extension>
+
+ Properties explained, required in **bold**:
+
+ - **androidServerApk** - path to the Android Server APK you've downloaded
+ - skip - (false) skip execution
+ - verbose - (false) be verbose
- webdriverPortHost - (14444) port on Host connected with port on device
- webdriverPortGuest - (8080) port on Guest connected with port on Host
- Emulators are created by default in target/${avdName}. This might cause problem while running `mvn clean`
View
134 android-api/pom.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/maven-v4_0_0.xsd">
+
+ <!-- Parent -->
+ <parent>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-build</artifactId>
+ <version>1.0.0.Alpha1-SNAPSHOT</version>
+ <relativePath>../android-build/pom.xml</relativePath>
+ </parent>
+
+ <!-- Model Version -->
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- Artifact Configuration -->
+ <artifactId>arquillian-android-api</artifactId>
+ <name>Arquillian Android Extension API</name>
+
+ <developers>
+ <developer>
+ <name>Karel Piwko</name>
+ <email>kpiwko@redhat.com</email>
+ </developer>
+ </developers>
+
+ <properties>
+ <version.arquillian.core>1.0.0.CR7</version.arquillian.core>
+ <version.com.android>r16</version.com.android>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <!-- Arquillian -->
+ <dependency>
+ <groupId>org.jboss.arquillian</groupId>
+ <artifactId>arquillian-bom</artifactId>
+ <version>${version.arquillian.core}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian</groupId>
+ <artifactId>arquillian-build</artifactId>
+ <version>${version.arquillian.core}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.test</groupId>
+ <artifactId>arquillian-test-impl-base</artifactId>
+ <scope>test</scope>
+ <version>${version.arquillian.core}</version>
+ <classifier>tests</classifier>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.core</groupId>
+ <artifactId>arquillian-core-impl-base</artifactId>
+ <scope>test</scope>
+ <version>${version.arquillian.core}</version>
+ <classifier>tests</classifier>
+ </dependency>
+
+ <!-- Android -->
+ <dependency>
+ <groupId>com.android.ddmlib</groupId>
+ <artifactId>ddmlib</artifactId>
+ <version>${version.com.android}</version>
+ </dependency>
+
+ </dependencies>
+ </dependencyManagement>
+
+ <!-- Dependencies -->
+ <dependencies>
+ <!-- Arquillian dependencies -->
+ <dependency>
+ <groupId>org.jboss.arquillian.core</groupId>
+ <artifactId>arquillian-core-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.test</groupId>
+ <artifactId>arquillian-test-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.config</groupId>
+ <artifactId>arquillian-config-api</artifactId>
+ </dependency>
+
+ <!-- Android Dependencies -->
+ <dependency>
+ <groupId>com.android.ddmlib</groupId>
+ <artifactId>ddmlib</artifactId>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.jboss.arquillian.junit</groupId>
+ <artifactId>arquillian-junit-standalone</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.config</groupId>
+ <artifactId>arquillian-config-impl-base</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.test</groupId>
+ <artifactId>arquillian-test-impl-base</artifactId>
+ <scope>test</scope>
+ <classifier>tests</classifier>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.core</groupId>
+ <artifactId>arquillian-core-impl-base</artifactId>
+ <scope>test</scope>
+ <classifier>tests</classifier>
+ </dependency>
+
+ <!-- external dependencies -->
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
View
56 android-api/src/main/java/org/jboss/arquillian/android/api/AndroidBridge.java
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.api;
+
+import java.util.List;
+
+/**
+ * An abstraction of Android Debug Bridge
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+public interface AndroidBridge {
+
+ /**
+ * Lists all devices currently available
+ *
+ * @return List of available devices
+ */
+ List<AndroidDevice> getDevices();
+
+ /**
+ * Connects to the bridge
+ *
+ * @throws AndroidExecutionException
+ */
+ void connect() throws AndroidExecutionException;
+
+ /**
+ * Checks if bridge is connected
+ *
+ * @return {@code true} if connected, {@code false} otherwise
+ */
+ boolean isConnected();
+
+ /**
+ * Disconnects bridge and disposes connection
+ *
+ * @throws AndroidExecutionException
+ */
+ void disconnect() throws AndroidExecutionException;
+}
View
127 android-api/src/main/java/org/jboss/arquillian/android/api/AndroidDevice.java
@@ -0,0 +1,127 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.api;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Representation of Android Device
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+public interface AndroidDevice {
+
+ /**
+ * Returns serial number of device
+ *
+ * @return Serial number
+ */
+ public String getSerialNumber();
+
+ /**
+ * Returns name of Android Virtual Device
+ *
+ * @return Either a virtual device name or {@code null} if device is not an emulator
+ */
+ public String getAvdName();
+
+ /**
+ * Returns a map of properties available for the device. These properties are cached.
+ *
+ * @return A properties map
+ */
+ public Map<String, String> getProperties();
+
+ /**
+ * Returns a value of property with given name
+ *
+ * @param name A key
+ * @return Value of property or {@code null} if not present
+ * @throws IOException
+ * @throws AndroidExecutionException
+ */
+ public String getProperty(String name) throws IOException, AndroidExecutionException;
+
+ /**
+ * Checks if the device is online
+ *
+ * @return {@code true} if device is online, {@code false} otherwise
+ */
+ public boolean isOnline();
+
+ /**
+ * Checks if the device is an emulator
+ *
+ * @return {@code true} if device is an emulator, {@code false} otherwise
+ */
+ public boolean isEmulator();
+
+ /**
+ * Returns if the device is offline
+ *
+ */
+ public boolean isOffline();
+
+ /**
+ * Executes a shell command on the device
+ *
+ * @param command The command to be executed
+ * @param reciever A processor to process command output
+ * @throws AndroidExecutionException
+ * @throws IOException
+ */
+ public void executeShellCommand(String command, AndroidDeviceOutputReciever reciever) throws AndroidExecutionException,
+ IOException;
+
+ /**
+ * Creates a port forwarding between a local and a remote port.
+ *
+ * @param localPort the local port to forward
+ * @param remotePort the remote port.
+ */
+ public void createForward(int localPort, int remotePort) throws AndroidExecutionException, IOException;
+
+ /**
+ * Removes a port forwarding between a local and a remote port.
+ *
+ * @param localPort the local port to forward
+ * @param remotePort the remote port.
+ */
+ public void removeForward(int localPort, int remotePort) throws AndroidExecutionException, IOException;
+
+ /**
+ * Installs an Android application on device. This is a helper method that combines the syncPackageToDevice,
+ * installRemotePackage, and removePackage steps
+ *
+ * @param packageFilePath the absolute file system path to file on local host to install
+ * @param reinstall set to <code>true</code> if re-install of app should be performed
+ * @param extraArgs optional extra arguments to pass. See 'adb shell pm install --help' for available options.
+ * @return a {@link String} with an error code, or <code>null</code> if success.
+ */
+ public void installPackage(File packageFilePath, boolean reinstall, String... extraArgs) throws AndroidExecutionException;
+
+ /**
+ * Uninstalls an package from the device.
+ *
+ * @param packageName the Android application package name to uninstall
+ */
+ public void uninstallPackage(String packageName) throws AndroidExecutionException;
+
+}
View
43 android-api/src/main/java/org/jboss/arquillian/android/api/AndroidDeviceOutputReciever.java
@@ -0,0 +1,43 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.api;
+
+/**
+ * Abstraction of a processor for output written by shell command
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+public interface AndroidDeviceOutputReciever {
+
+ /**
+ * Processes output of a shell command
+ *
+ * @param lines Array of lines returned by shell command to be processed
+ */
+ void processNewLines(String[] lines);
+
+ /**
+ * Checks if command execution was cancelled
+ *
+ * @return {@code true} if command was cancelled, {@code false} otherwise
+ */
+ boolean isCancelled();
+
+ boolean isVerbose();
+
+}
View
47 android-api/src/main/java/org/jboss/arquillian/android/api/AndroidExecutionException.java
@@ -0,0 +1,47 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.api;
+
+/**
+ * A generic error during execution on the Android device
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+public class AndroidExecutionException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public AndroidExecutionException(Throwable cause) {
+ super(cause);
+ }
+
+ public AndroidExecutionException() {
+ }
+
+ public AndroidExecutionException(String msg) {
+ super(msg);
+ }
+
+ public AndroidExecutionException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
View
69 android-bom/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/maven-v4_0_0.xsd">
+
+ <!-- Model Version -->
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- Artifact Configuration -->
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-bom</artifactId>
+ <version>1.0.0.Alpha1-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <name>Arquillian Android Extension Bill of Materials</name>
+
+ <developers>
+ <developer>
+ <name>Karel Piwko</name>
+ <email>kpiwko@redhat.com</email>
+ </developer>
+ </developers>
+
+ <properties>
+ <version.arquillian.core>1.0.0.CR7</version.arquillian.core>
+ <version.com.android>r16</version.com.android>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <!-- Arquillian -->
+ <dependency>
+ <groupId>org.jboss.arquillian</groupId>
+ <artifactId>arquillian-bom</artifactId>
+ <version>${version.arquillian.core}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian</groupId>
+ <artifactId>arquillian-build</artifactId>
+ <version>${version.arquillian.core}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.test</groupId>
+ <artifactId>arquillian-test-impl-base</artifactId>
+ <scope>test</scope>
+ <version>${version.arquillian.core}</version>
+ <classifier>tests</classifier>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.core</groupId>
+ <artifactId>arquillian-core-impl-base</artifactId>
+ <scope>test</scope>
+ <version>${version.arquillian.core}</version>
+ <classifier>tests</classifier>
+ </dependency>
+
+ <!-- Android -->
+ <dependency>
+ <groupId>com.android.ddmlib</groupId>
+ <artifactId>ddmlib</artifactId>
+ <version>${version.com.android}</version>
+ </dependency>
+
+ </dependencies>
+ </dependencyManagement>
+
+</project>
View
75 android-build/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/maven-v4_0_0.xsd">
+
+ <!-- Model Version -->
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- Parent -->
+ <parent>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-parent</artifactId>
+ <version>1.0.0.Alpha1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <!-- Artifact Configuration -->
+ <artifactId>arquillian-android-build</artifactId>
+ <packaging>pom</packaging>
+ <name>Arquillian Android Extension Build</name>
+
+ <developers>
+ <developer>
+ <name>Karel Piwko</name>
+ <email>kpiwko@redhat.com</email>
+ </developer>
+ </developers>
+
+ <properties>
+ <version.arquillian.core>1.0.0.CR7</version.arquillian.core>
+ <version.com.android>r16</version.com.android>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <!-- Arquillian -->
+ <dependency>
+ <groupId>org.jboss.arquillian</groupId>
+ <artifactId>arquillian-bom</artifactId>
+ <version>${version.arquillian.core}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian</groupId>
+ <artifactId>arquillian-build</artifactId>
+ <version>${version.arquillian.core}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.test</groupId>
+ <artifactId>arquillian-test-impl-base</artifactId>
+ <scope>test</scope>
+ <version>${version.arquillian.core}</version>
+ <classifier>tests</classifier>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.core</groupId>
+ <artifactId>arquillian-core-impl-base</artifactId>
+ <scope>test</scope>
+ <version>${version.arquillian.core}</version>
+ <classifier>tests</classifier>
+ </dependency>
+
+ <!-- Android -->
+ <dependency>
+ <groupId>com.android.ddmlib</groupId>
+ <artifactId>ddmlib</artifactId>
+ <version>${version.com.android}</version>
+ </dependency>
+
+ </dependencies>
+ </dependencyManagement>
+
+</project>
View
45 android-configuration/pom.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/maven-v4_0_0.xsd">
+
+ <!-- Parent -->
+ <parent>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-build</artifactId>
+ <version>1.0.0.Alpha1-SNAPSHOT</version>
+ <relativePath>../android-build/pom.xml</relativePath>
+ </parent>
+
+ <!-- Model Version -->
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- Artifact Configuration -->
+ <artifactId>arquillian-android-configuration</artifactId>
+ <name>Arquillian Android Extension Configuration</name>
+
+ <developers>
+ <developer>
+ <name>Karel Piwko</name>
+ <email>kpiwko@redhat.com</email>
+ </developer>
+ </developers>
+
+ <!-- Dependencies -->
+ <dependencies>
+ <!-- Arquillian dependencies -->
+ <dependency>
+ <groupId>org.jboss.arquillian.core</groupId>
+ <artifactId>arquillian-core-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.test</groupId>
+ <artifactId>arquillian-test-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.config</groupId>
+ <artifactId>arquillian-config-api</artifactId>
+ </dependency>
+
+ </dependencies>
+
+</project>
View
100 .../android/impl/AndroidSdkConfigurator.java → ...id/configuration/ConfigurationMapper.java
@@ -14,105 +14,42 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jboss.arquillian.android.impl;
+package org.jboss.arquillian.android.configuration;
+import java.io.File;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.Map;
-import java.util.logging.Logger;
-import org.jboss.arquillian.android.AndroidConfigurationException;
-import org.jboss.arquillian.android.configuration.AndroidSdk;
-import org.jboss.arquillian.android.configuration.AndroidSdkConfiguration;
-import org.jboss.arquillian.android.event.AndroidSdkConfigured;
import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
-import org.jboss.arquillian.config.descriptor.api.ExtensionDef;
-import org.jboss.arquillian.core.api.Event;
-import org.jboss.arquillian.core.api.InstanceProducer;
-import org.jboss.arquillian.core.api.annotation.Inject;
-import org.jboss.arquillian.core.api.annotation.Observes;
-import org.jboss.arquillian.test.spi.annotation.SuiteScoped;
-import org.jboss.arquillian.test.spi.event.suite.BeforeSuite;
/**
- *
- * @author <a href="kpiwko@redhat.com>Karel Piwko</a>
- *
+ * An utility which maps a segment of arquillian.xml file to the extension configuration. It is able to do basic object
+ * conversions, like a string to URL.
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
*/
-public class AndroidSdkConfigurator {
- private static Logger log = Logger.getLogger(AndroidSdkConfigurator.class.getName());
-
- public static final String ANDROID_SDK_EXTENSION_NAME = "android-sdk";
-
- @Inject
- @SuiteScoped
- private InstanceProducer<AndroidSdkConfiguration> androidSdkConfiguration;
-
- @Inject
- @SuiteScoped
- private InstanceProducer<AndroidSdk> androidSdk;
-
- @Inject
- @SuiteScoped
- private InstanceProducer<ProcessExecutor> executor;
-
- @Inject
- private Event<AndroidSdkConfigured> afterConfiguration;
-
- public void configureAndroidSdk(@Observes BeforeSuite event, ArquillianDescriptor descriptor)
- throws AndroidConfigurationException {
-
- AndroidSdkConfiguration configuration = new AndroidSdkConfiguration();
- boolean configured = false;
-
- for (ExtensionDef extensionDef : descriptor.getExtensions()) {
- if (ANDROID_SDK_EXTENSION_NAME.equals(extensionDef.getExtensionName())) {
- ConfigurationMapper.fromArquillianDescriptor(descriptor, configuration, extensionDef.getExtensionProperties());
- configured = true;
- log.fine("Configured Android extension from Arquillian configuration file");
- }
- }
-
- if (configured && configuration.isSkip() != true) {
-
- // validate configuration
- if (configuration.getAvdName() == null && configuration.getSerialId() == null) {
- throw new AndroidConfigurationException(
- "You must provide either \"avdName\" if you want to use an emulator, or \"serialId\" property if you want to use a real device.");
- }
- if (configuration.getAvdName() != null && configuration.getSerialId() != null) {
- log.warning("Both \"avdName\" and \"serialId\" properties are defined, the device specified by \"serialId\" will get priority");
- }
-
- AndroidSdk sdk = new AndroidSdk(configuration);
- androidSdkConfiguration.set(configuration);
- androidSdk.set(sdk);
- executor.set(new ProcessExecutor(configuration));
- afterConfiguration.fire(new AndroidSdkConfigured(configuration, sdk));
- }
- }
-}
-
-class ConfigurationMapper {
+public class ConfigurationMapper {
/**
* Maps Android configuration using Arquillian Descriptor file
- *
+ *
* @param descriptor Arquillian Descriptor
* @param configuration Configuration object
* @param properties A map of name-value pairs
* @return Configured configuration
*/
- public static AndroidSdkConfiguration fromArquillianDescriptor(ArquillianDescriptor descriptor,
- AndroidSdkConfiguration configuration, Map<String, String> properties) {
+ public static <T> T fromArquillianDescriptor(ArquillianDescriptor descriptor, T configuration,
+ Map<String, String> properties) {
Validate.notNull(descriptor, "Descriptor must not be null");
Validate.notNull(configuration, "Configuration must not be null");
- List<Field> fields = SecurityActions.getAccessableFields(AndroidSdkConfiguration.class);
+ List<Field> fields = SecurityActions.getAccessableFields(configuration.getClass());
for (Field f : fields) {
if (properties.containsKey(f.getName())) {
try {
@@ -128,7 +65,7 @@ public static AndroidSdkConfiguration fromArquillianDescriptor(ArquillianDescrip
/**
* A helper boxing method. Returns boxed class for a primitive class
- *
+ *
* @param primitive A primitive class
* @return Boxed class if class was primitive, unchanged class in other cases
*/
@@ -160,9 +97,9 @@ public static AndroidSdkConfiguration fromArquillianDescriptor(ArquillianDescrip
/**
* A helper converting method.
- *
+ *
* Converts string to a class of given type
- *
+ *
* @param <T> Type of returned value
* @param clazz Type of desired value
* @param value String value to be converted
@@ -193,6 +130,13 @@ public static AndroidSdkConfiguration fromArquillianDescriptor(ArquillianDescrip
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Unable to convert value " + value + " to URL", e);
}
+ } else if (File.class.equals(clazz)) {
+ try {
+ return clazz.cast(new File(value));
+ } catch (NullPointerException e) {
+ throw new IllegalArgumentException("Unable to convert value " + value + " to File", e);
+ }
+
}
throw new IllegalArgumentException("Unable to convert value " + value + "to a class: " + clazz.getName());
View
257 ...nfiguration/src/main/java/org/jboss/arquillian/android/configuration/SecurityActions.java
@@ -0,0 +1,257 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.configuration;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * SecurityActions
+ *
+ * A set of privileged actions that are not to leak out of this package
+ *
+ *
+ * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
+ *
+ * @version $Revision: $
+ */
+final class SecurityActions {
+
+ // -------------------------------------------------------------------------------||
+ // Constructor
+ // ------------------------------------------------------------------||
+ // -------------------------------------------------------------------------------||
+
+ /**
+ * No instantiation
+ */
+ private SecurityActions() {
+ throw new UnsupportedOperationException("No instantiation");
+ }
+
+ // -------------------------------------------------------------------------------||
+ // Utility Methods
+ // --------------------------------------------------------------||
+ // -------------------------------------------------------------------------------||
+
+ /**
+ * Obtains the Thread Context ClassLoader
+ */
+ static ClassLoader getThreadContextClassLoader() {
+ return AccessController.doPrivileged(GetTcclAction.INSTANCE);
+ }
+
+ /**
+ * Obtains the Constructor specified from the given Class and argument types
+ *
+ * @param clazz
+ * @param argumentTypes
+ * @return
+ * @throws NoSuchMethodException
+ */
+ static Constructor<?> getConstructor(final Class<?> clazz, final Class<?>... argumentTypes) throws NoSuchMethodException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
+ public Constructor<?> run() throws NoSuchMethodException {
+ return clazz.getConstructor(argumentTypes);
+ }
+ });
+ }
+ // Unwrap
+ catch (final PrivilegedActionException pae) {
+ final Throwable t = pae.getCause();
+ // Rethrow
+ if (t instanceof NoSuchMethodException) {
+ throw (NoSuchMethodException) t;
+ } else {
+ // No other checked Exception thrown by Class.getConstructor
+ try {
+ throw (RuntimeException) t;
+ }
+ // Just in case we've really messed up
+ catch (final ClassCastException cce) {
+ throw new RuntimeException("Obtained unchecked Exception; this code should never be reached", t);
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a new instance by finding a constructor that matches the argumentTypes signature using the arguments for
+ * instantiation.
+ *
+ * @param className Full classname of class to create
+ * @param argumentTypes The constructor argument types
+ * @param arguments The constructor arguments
+ * @return a new instance
+ * @throws IllegalArgumentException if className, argumentTypes, or arguments are null
+ * @throws RuntimeException if any exceptions during creation
+ * @author <a href="mailto:aslak@conduct.no">Aslak Knutsen</a>
+ * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
+ */
+ static <T> T newInstance(final String className, final Class<?>[] argumentTypes, final Object[] arguments,
+ final Class<T> expectedType) {
+ if (className == null) {
+ throw new IllegalArgumentException("ClassName must be specified");
+ }
+ if (argumentTypes == null) {
+ throw new IllegalArgumentException("ArgumentTypes must be specified. Use empty array if no arguments");
+ }
+ if (arguments == null) {
+ throw new IllegalArgumentException("Arguments must be specified. Use empty array if no arguments");
+ }
+ final Object obj;
+ try {
+ final ClassLoader tccl = getThreadContextClassLoader();
+ final Class<?> implClass = Class.forName(className, false, tccl);
+ Constructor<?> constructor = getConstructor(implClass, argumentTypes);
+ obj = constructor.newInstance(arguments);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not create new instance of " + className + ", missing package from classpath?", e);
+ }
+
+ // Cast
+ try {
+ return expectedType.cast(obj);
+ } catch (final ClassCastException cce) {
+ // Reconstruct so we get some useful information
+ throw new ClassCastException("Incorrect expected type, " + expectedType.getName() + ", defined for "
+ + obj.getClass().getName());
+ }
+ }
+
+ static boolean isClassPresent(String name) {
+ try {
+ ClassLoader classLoader = getThreadContextClassLoader();
+ classLoader.loadClass(name);
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+
+ static List<Field> getFieldsWithAnnotation(final Class<?> source, final Class<? extends Annotation> annotationClass) {
+ List<Field> declaredAccessableFields = AccessController.doPrivileged(new PrivilegedAction<List<Field>>() {
+ public List<Field> run() {
+ List<Field> foundFields = new ArrayList<Field>();
+ Class<?> nextSource = source;
+ while (nextSource != Object.class) {
+ for (Field field : nextSource.getDeclaredFields()) {
+ if (field.isAnnotationPresent(annotationClass)) {
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ foundFields.add(field);
+ }
+ }
+ nextSource = nextSource.getSuperclass();
+ }
+ return foundFields;
+ }
+ });
+ return declaredAccessableFields;
+ }
+
+ static boolean isAnnotationPresent(final Annotation[] annotations, final Class<? extends Annotation> needle) {
+ for (Annotation a : annotations) {
+ if (a.annotationType() == needle) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static List<Field> getAccessableFields(final Class<?> source) {
+ List<Field> declaredAccessableFields = AccessController.doPrivileged(new PrivilegedAction<List<Field>>() {
+ public List<Field> run() {
+ List<Field> foundFields = new ArrayList<Field>();
+ for (Field field : source.getDeclaredFields()) {
+ // omit final fields
+ if (Modifier.isFinal(field.getModifiers())) {
+ continue;
+ }
+
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ foundFields.add(field);
+ }
+ return foundFields;
+ }
+ });
+ return declaredAccessableFields;
+ }
+
+ static String getProperty(final String key) {
+ try {
+ String value = AccessController.doPrivileged(new PrivilegedExceptionAction<String>() {
+ public String run() {
+ return System.getProperty(key);
+ }
+ });
+ return value;
+ }
+ // Unwrap
+ catch (final PrivilegedActionException pae) {
+ final Throwable t = pae.getCause();
+ // Rethrow
+ if (t instanceof SecurityException) {
+ throw (SecurityException) t;
+ }
+ if (t instanceof NullPointerException) {
+ throw (NullPointerException) t;
+ } else if (t instanceof IllegalArgumentException) {
+ throw (IllegalArgumentException) t;
+ } else {
+ // No other checked Exception thrown by System.getProperty
+ try {
+ throw (RuntimeException) t;
+ }
+ // Just in case we've really messed up
+ catch (final ClassCastException cce) {
+ throw new RuntimeException("Obtained unchecked Exception; this code should never be reached", t);
+ }
+ }
+ }
+ }
+
+ // -------------------------------------------------------------------------------||
+ // Inner Classes
+ // ----------------------------------------------------------------||
+ // -------------------------------------------------------------------------------||
+
+ /**
+ * Single instance to get the TCCL
+ */
+ private enum GetTcclAction implements PrivilegedAction<ClassLoader> {
+ INSTANCE;
+
+ public ClassLoader run() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ }
+
+}
View
124 android-configuration/src/main/java/org/jboss/arquillian/android/configuration/Validate.java
@@ -0,0 +1,124 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.configuration;
+
+import java.io.File;
+
+/**
+ * Simple validation utility
+ *
+ * @author <a href="@mailto:kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+class Validate {
+
+ public static void stateNotNull(Object object, String message) throws IllegalStateException {
+ if (object == null) {
+ throw new IllegalStateException(message);
+ }
+ }
+
+ /**
+ * Checks that object is not null, throws exception if it is.
+ *
+ * @param obj The object to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if obj is null
+ */
+ public static void notNull(final Object obj, final String message) throws IllegalArgumentException {
+ if (obj == null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Checks that the specified String is not null or empty, throws exception if it is.
+ *
+ * @param string The object to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if string is null
+ */
+ public static void notNullOrEmpty(final String string, final String message) throws IllegalArgumentException {
+ if (string == null || string.length() == 0) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Checks that the specified String is not null or empty and represents a readable file, throws exception if it is empty or
+ * null and does not represent a path to a file.
+ *
+ * @param path The path to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if path is empty, null or invalid
+ */
+ public static void isReadable(final String path, String message) throws IllegalArgumentException {
+ notNullOrEmpty(path, message);
+ File file = new File(path);
+ isReadable(file, message);
+ }
+
+ /**
+ * Checks that the specified File is not null or empty and represents a readable file, throws exception if it is empty or
+ * null and does not represent a path to a file.
+ *
+ * @param file The file to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if file is null or invalid
+ */
+ public static void isReadable(final File file, String message) throws IllegalArgumentException {
+ if (file == null) {
+ throw new IllegalArgumentException(message);
+ }
+ if (!file.exists() || !file.canRead()) {
+ throw new IllegalArgumentException(message);
+ }
+
+ }
+
+ /**
+ * Checks that the specified String is not null or empty and represents a readable directory, throws exception if it is
+ * empty or null and does not represent a path to a directory.
+ *
+ * @param path The path to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if path is empty, null or invalid
+ */
+ public static void isReadableDirectory(final String path, String message) throws IllegalArgumentException {
+ notNullOrEmpty(path, message);
+ File file = new File(path);
+ isReadableDirectory(file, message);
+ }
+
+ /**
+ * Checks that the specified file is not null and represents a readable directory, throws exception if it is empty or null
+ * and does not represent a directory.
+ *
+ * @param file The path to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if file is null or invalid
+ */
+ public static void isReadableDirectory(final File file, String message) throws IllegalArgumentException {
+ if (file == null) {
+ throw new IllegalArgumentException(message);
+ }
+ if (!file.exists() || !file.isDirectory() || !file.canRead() || !file.canExecute()) {
+ throw new IllegalArgumentException(message);
+ }
+
+ }
+}
View
63 android-depchain/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/maven-v4_0_0.xsd">
+
+ <!-- Model Version -->
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- Parent -->
+ <parent>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-parent</artifactId>
+ <version>1.0.0.Alpha1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <!-- Artifact Configuration -->
+ <artifactId>arquillian-android-depchain</artifactId>
+ <packaging>pom</packaging>
+ <name>Arquillian Android Extension Dependency Chain</name>
+
+ <developers>
+ <developer>
+ <name>Karel Piwko</name>
+ <email>kpiwko@redhat.com</email>
+ </developer>
+ </developers>
+
+ <properties>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-configuration</artifactId>
+ <version>${project.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-spi</artifactId>
+ <version>${project.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-drone</artifactId>
+ <version>${project.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-impl</artifactId>
+ <version>${project.version}</version>
+ <scope>runtime</scope>
+ </dependency>
+ </dependencies>
+
+</project>
View
102 android-drone/pom.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/maven-v4_0_0.xsd">
+ <!-- Model Version -->
+ <modelVersion>4.0.0</modelVersion>
+
+ <!-- Parent -->
+ <parent>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-build</artifactId>
+ <version>1.0.0.Alpha1-SNAPSHOT</version>
+ <relativePath>../android-build/pom.xml</relativePath>
+ </parent>
+
+ <!-- Artifact Configuration -->
+ <artifactId>arquillian-android-drone</artifactId>
+ <name>Arquillian Android Extension Drone Support</name>
+
+ <developers>
+ <developer>
+ <name>Karel Piwko</name>
+ <email>kpiwko@redhat.com</email>
+ </developer>
+ </developers>
+
+ <!-- Dependencies -->
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-configuration</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <!-- Arquillian dependencies -->
+ <dependency>
+ <groupId>org.jboss.arquillian.core</groupId>
+ <artifactId>arquillian-core-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.test</groupId>
+ <artifactId>arquillian-test-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.config</groupId>
+ <artifactId>arquillian-config-api</artifactId>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-android-impl</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.junit</groupId>
+ <artifactId>arquillian-junit-standalone</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.config</groupId>
+ <artifactId>arquillian-config-impl-base</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.test</groupId>
+ <artifactId>arquillian-test-impl-base</artifactId>
+ <scope>test</scope>
+ <classifier>tests</classifier>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.core</groupId>
+ <artifactId>arquillian-core-impl-base</artifactId>
+ <scope>test</scope>
+ <classifier>tests</classifier>
+ </dependency>
+
+ <!-- external dependencies -->
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
View
37 android-drone/src/main/java/org/jboss/arquillian/android/drone/AndroidDroneExtension.java
@@ -0,0 +1,37 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.drone;
+
+import org.jboss.arquillian.android.drone.impl.AndroidDroneConfigurator;
+import org.jboss.arquillian.android.drone.impl.AndroidWebDriverSupport;
+import org.jboss.arquillian.core.spi.LoadableExtension;
+
+/**
+ * An extension for Android Drone support in Arquillian
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+public class AndroidDroneExtension implements LoadableExtension {
+
+ @Override
+ public void register(ExtensionBuilder builder) {
+ builder.observer(AndroidDroneConfigurator.class);
+ builder.observer(AndroidWebDriverSupport.class);
+ }
+
+}
View
125 ...main/java/org/jboss/arquillian/android/drone/configuration/AndroidDroneConfiguration.java
@@ -0,0 +1,125 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.drone.configuration;
+
+import java.io.File;
+
+/**
+ * Configuration for Android Drone in Arquillian
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+public class AndroidDroneConfiguration {
+
+ private File androidServerApk = new File("android-server.apk");
+
+ private File webdriverLogFile = new File("target/android-webdriver-monkey.log");
+
+ private int webdriverPortHost = 14444;
+
+ private int webdriverPortGuest = 8080;
+
+ private boolean verbose = false;
+
+ private boolean skip = false;
+
+ /**
+ * @return the verbose
+ */
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ /**
+ * @param verbose the verbose to set
+ */
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ /**
+ * @return the androidServerApk
+ */
+ public File getAndroidServerApk() {
+ return androidServerApk;
+ }
+
+ /**
+ * @param androidServerApk the androidServerApk to set
+ */
+ public void setAndroidServerApk(File androidServerApk) {
+ this.androidServerApk = androidServerApk;
+ }
+
+ /**
+ * @return the webdriverLogFile
+ */
+ public File getWebdriverLogFile() {
+ return webdriverLogFile;
+ }
+
+ /**
+ * @param webdriverLogFile the webdriverLogFile to set
+ */
+ public void setWebdriverLogFile(File webdriverLogFile) {
+ this.webdriverLogFile = webdriverLogFile;
+ }
+
+ /**
+ * @return the webdriverPortHost
+ */
+ public int getWebdriverPortHost() {
+ return webdriverPortHost;
+ }
+
+ /**
+ * @param webdriverPortHost the webdriverPortHost to set
+ */
+ public void setWebdriverPortHost(int webdriverPortHost) {
+ this.webdriverPortHost = webdriverPortHost;
+ }
+
+ /**
+ * @return the webdriverPortGuest
+ */
+ public int getWebdriverPortGuest() {
+ return webdriverPortGuest;
+ }
+
+ /**
+ * @param webdriverPortGuest the webdriverPortGuest to set
+ */
+ public void setWebdriverPortGuest(int webdriverPortGuest) {
+ this.webdriverPortGuest = webdriverPortGuest;
+ }
+
+ /**
+ * @return the skip
+ */
+ public boolean isSkip() {
+ return skip;
+ }
+
+ /**
+ * @param skip the skip to set
+ */
+ public void setSkip(boolean skip) {
+ this.skip = skip;
+ }
+
+}
View
27 ...-drone/src/main/java/org/jboss/arquillian/android/drone/event/AndroidDroneConfigured.java
@@ -0,0 +1,27 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.drone.event;
+
+/**
+ * Event representing that an Android Drone extension was configured
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+public class AndroidDroneConfigured {
+
+}
View
27 ...ne/src/main/java/org/jboss/arquillian/android/drone/event/AndroidWebDriverHubRunning.java
@@ -0,0 +1,27 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.drone.event;
+
+/**
+ * Event representing and Android Web Driver Hub on device is running and ports are redirected
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+public class AndroidWebDriverHubRunning {
+
+}
View
105 ...drone/src/main/java/org/jboss/arquillian/android/drone/impl/AndroidDroneConfigurator.java
@@ -0,0 +1,105 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.drone.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.logging.Logger;
+
+import org.jboss.arquillian.android.configuration.ConfigurationMapper;
+import org.jboss.arquillian.android.drone.configuration.AndroidDroneConfiguration;
+import org.jboss.arquillian.android.drone.event.AndroidDroneConfigured;
+import org.jboss.arquillian.android.spi.event.AndroidExtensionConfigured;
+import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
+import org.jboss.arquillian.config.descriptor.api.ExtensionDef;
+import org.jboss.arquillian.core.api.Event;
+import org.jboss.arquillian.core.api.InstanceProducer;
+import org.jboss.arquillian.core.api.annotation.Inject;
+import org.jboss.arquillian.core.api.annotation.Observes;
+import org.jboss.arquillian.test.spi.annotation.SuiteScoped;
+
+/**
+ * Creator of Arquillian Drone configuration. Note that this observer has a higher priority so it is executed before the rest of
+ * Android Extension.
+ *
+ * Observes:
+ * <ul>
+ * <li>{@link AndroidExtensionConfigured}</li>
+ * </ul>
+ *
+ * Creates:
+ * <ul>
+ * <li>{@link AndroidDroneConfiguration}</li>
+ * </ul>
+ *
+ * Fires:
+ * <ul>
+ * <li>{@link AndroidDroneConfigured}</li>
+ * </ul>
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+public class AndroidDroneConfigurator {
+ private static final Logger log = Logger.getLogger(AndroidDroneConfigurator.class.getName());
+
+ public static final String ANDROID_DRONE_EXTENSION_NAME = "android-drone";
+
+ @Inject
+ @SuiteScoped
+ private InstanceProducer<AndroidDroneConfiguration> androidDroneConfiguration;
+
+ @Inject
+ private Event<AndroidDroneConfigured> afterConfiguration;
+
+ // we need to configure Android Drone extension before AndroidBridge is initialized
+ public void configureAndroidDrone(@Observes(precedence = 10) AndroidExtensionConfigured event,
+ ArquillianDescriptor descriptor) {
+
+ AndroidDroneConfiguration configuration = new AndroidDroneConfiguration();
+ boolean configured = false;
+
+ for (ExtensionDef extensionDef : descriptor.getExtensions()) {
+ if (ANDROID_DRONE_EXTENSION_NAME.equals(extensionDef.getExtensionName())) {
+ ConfigurationMapper.fromArquillianDescriptor(descriptor, configuration, extensionDef.getExtensionProperties());
+ configured = true;
+ log.fine("Configured Android Drone extension from Arquillian configuration file");
+ }
+ }
+
+ if (configured && configuration.isSkip() != true) {
+ Validate.isReadable(configuration.getAndroidServerApk(), "You must provide a valid path to Android Server APK: "
+ + configuration.getAndroidServerApk());
+
+ File webdriverLog = configuration.getWebdriverLogFile();
+
+ Validate.notNull(webdriverLog, "You must provide a valid path to Android Webdriver Monkey log file: "
+ + configuration.getWebdriverLogFile());
+
+ // create the log file if not present
+ try {
+ webdriverLog.createNewFile();
+ } catch (IOException e) {
+ throw new IllegalStateException("Unable to create Android Webdriver Monkey log file at "
+ + webdriverLog.getAbsolutePath(), e);
+ }
+
+ androidDroneConfiguration.set(configuration);
+ afterConfiguration.fire(new AndroidDroneConfigured());
+ }
+ }
+}
View
142 ...-drone/src/main/java/org/jboss/arquillian/android/drone/impl/AndroidWebDriverSupport.java
@@ -0,0 +1,142 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.drone.impl;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.logging.Logger;
+
+import org.jboss.arquillian.android.api.AndroidDevice;
+import org.jboss.arquillian.android.api.AndroidDeviceOutputReciever;
+import org.jboss.arquillian.android.api.AndroidExecutionException;
+import org.jboss.arquillian.android.drone.configuration.AndroidDroneConfiguration;
+import org.jboss.arquillian.android.drone.event.AndroidWebDriverHubRunning;
+import org.jboss.arquillian.android.spi.event.AndroidDeviceReady;
+import org.jboss.arquillian.core.api.Event;
+import org.jboss.arquillian.core.api.annotation.Inject;
+import org.jboss.arquillian.core.api.annotation.Observes;
+
+/**
+ * Configurator of Android Web Driver Hub. Installs Android WebDriver Hub on the device and sets port forwarding.
+ *
+ * Observes:
+ * <ul>
+ * <li>{@link AndroidDeviceReady}</li>
+ * </ul>
+ *
+ * Fires:
+ * <ul>
+ * <li>{@link AndroidWebDriverHubRunning}</li>
+ * </ul>
+ *
+ * @author <a href="kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+public class AndroidWebDriverSupport {
+ private static final Logger log = Logger.getLogger(AndroidWebDriverSupport.class.getName());
+
+ private static final String START_WEBDRIVER_HUB_CMD = "am start -a android.intent.action.MAIN -n org.openqa.selenium.android.app/.MainActivity";
+ private static final String TOP_CMD = "top -n 1";
+ private static final String WEBDRIVER_HUB_NAME = "org.openqa.selenium.android.app";
+
+ @Inject
+ private Event<AndroidWebDriverHubRunning> androidWebDriverHubRunning;
+
+ public void prepareWebDriverEnvironment(@Observes AndroidDeviceReady event, AndroidDroneConfiguration configuration,
+ AndroidDevice device) throws AndroidExecutionException, IOException {
+
+ log.info("Installing Android Server APK for WebDriver support");
+ device.installPackage(configuration.getAndroidServerApk(), true);
+
+ // start selenium server
+ WebDriverMonkey monkey = new WebDriverMonkey(configuration.getWebdriverLogFile(), configuration.isVerbose());
+ device.executeShellCommand(START_WEBDRIVER_HUB_CMD, monkey);
+
+ // check the process of selenium server is present
+ waitUntilSeleniumStarted(device, monkey);
+
+ // forward ports
+ log.info("Creating port forwaring for WebDriver support");
+ device.createForward(configuration.getWebdriverPortHost(), configuration.getWebdriverPortGuest());
+
+ androidWebDriverHubRunning.fire(new AndroidWebDriverHubRunning());
+ }
+
+ private void waitUntilSeleniumStarted(AndroidDevice device, WebDriverMonkey monkey) throws IOException,
+ AndroidExecutionException {
+
+ log.info("Starting Web Driver Hub on Android device");
+ for (int i = 0; i < 5; i++) {
+ device.executeShellCommand(TOP_CMD, monkey);
+ if (monkey.isWebDriverHubStarted()) {
+ return;
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ throw new AndroidExecutionException("Unable to start Android Server for WebDriver support.");
+ }
+
+ private static class WebDriverMonkey implements AndroidDeviceOutputReciever {
+
+ private final boolean verbose;
+ private final Writer output;
+
+ private boolean started = false;
+
+ public WebDriverMonkey(File output, boolean verbose) throws IOException {
+ this.output = new FileWriter(output);
+ this.verbose = verbose;
+ }
+
+ @Override
+ public void processNewLines(String[] lines) {
+ for (String line : lines) {
+ try {
+ output.append(line).append("\n").flush();
+ } catch (IOException e) {
+ // ignore output
+ }
+ if (line.contains(WEBDRIVER_HUB_NAME)) {
+ this.started = true;
+ }
+ }
+
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ public boolean isWebDriverHubStarted() {
+ return started;
+ }
+
+ }
+
+}
View
140 android-drone/src/main/java/org/jboss/arquillian/android/drone/impl/Validate.java
@@ -0,0 +1,140 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed 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.jboss.arquillian.android.drone.impl;
+
+import java.io.File;
+
+/**
+ * Simple validation utility
+ *
+ * @author <a href="@mailto:kpiwko@redhat.com">Karel Piwko</a>
+ *
+ */
+class Validate {
+
+ public static void stateNotNull(Object object, String message) throws IllegalStateException {
+ if (object == null) {
+ throw new IllegalStateException(message);
+ }
+ }
+
+ /**
+ * Checks that object is not null, throws exception if it is.
+ *
+ * @param obj The object to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if obj is null
+ */
+ public static void notNull(final Object obj, final String message) throws IllegalArgumentException {
+ if (obj == null) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Checks that the specified String is not null or empty, throws exception if it is.
+ *
+ * @param string The object to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if string is null
+ */
+ public static void notNullOrEmpty(final String string, final String message) throws IllegalArgumentException {
+ if (string == null || string.length() == 0) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Checks that the specified String is not null or empty and represents a readable file, throws exception if it is empty or
+ * null and does not represent a path to a file.
+ *
+ * @param path The path to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if path is empty, null or invalid
+ */
+ public static void isReadable(final String path, String message) throws IllegalArgumentException {
+ notNullOrEmpty(path, message);
+ File file = new File(path);
+ isReadable(file, message);
+ }
+
+ /**
+ * Checks that the specified File is not null or empty and represents a readable file, throws exception if it is empty or
+ * null and does not represent a path to a file.
+ *
+ * @param file The file to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if file is null or invalid
+ */
+ public static void isReadable(final File file, String message) throws IllegalArgumentException {
+ if (file == null) {
+ throw new IllegalArgumentException(message);
+ }
+ if (!file.exists() || !file.canRead()) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Checks that the specified File is not null or empty and represents a writeable file, throws exception if it is empty or
+ * null and does not represent a path to a file.
+ *
+ * @param file The file to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if file is null or invalid
+ */
+ public static void isWriteable(final File file, String message) throws IllegalArgumentException {
+ if (file == null) {
+ throw new IllegalArgumentException(message);
+ }
+ if (!file.exists() || !file.canWrite()) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Checks that the specified String is not null or empty and represents a readable directory, throws exception if it is
+ * empty or null and does not represent a path to a directory.
+ *
+ * @param path The path to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if path is empty, null or invalid
+ */
+ public static void isReadableDirectory(final String path, String message) throws IllegalArgumentException {
+ notNullOrEmpty(path, message);
+ File file = new File(path);
+ isReadableDirectory(file, message);
+ }
+
+ /**
+ * Checks that the specified file is not null and represents a readable directory, throws exception if it is empty or null
+ * and does not represent a directory.
+ *
+ * @param file The path to check
+ * @param message The exception message
+ * @throws IllegalArgumentException Thrown if file is null or invalid
+ */
+ public static void isReadableDirectory(final File file, String message) throws IllegalArgumentException {
+ if (file == null) {
+ throw new IllegalArgumentException(message);
+ }
+ if (!file.exists() || !file.isDirectory() || !file.canRead() || !file.canExecute()) {
+ throw new IllegalArgumentException(message);
+ }
+
+ }
+}
View
1 ...rone/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
@@ -0,0 +1 @@
+org.jboss.arquillian.android.drone.AndroidDroneExtension
View
41 ...roidWebDriverSupportEmulatorTestCase.java → ...roidWebDriverSupportEmulatorTestCase.java
@@ -14,14 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jboss.arquillian.android.impl;
+package org.jboss.arquillian.android.drone.impl;
import java.lang.reflect.Method;
import java.util.List;
-import org.jboss.arquillian.android.event.AndroidDebugBridgeInitialized;
-import org.jboss.arquillian.android.event.AndroidDeviceReady;
-import org.jboss.arquillian.android.event.AndroidDeviceShutdownEvent;
+import org.jboss.arquillian.android.api.AndroidBridge;
+import org.jboss.arquillian.android.spi.event.AndroidBridgeInitialized;
+import org.jboss.arquillian.android.spi.event.AndroidDeviceReady;
+import org.jboss.arquillian.android.spi.event.AndroidDeviceShutdown;
import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
import org.jboss.arquillian.core.api.annotation.ApplicationScoped;
import org.jboss.arquillian.core.spi.ServiceLoader;
@@ -37,43 +38,37 @@
import org.jboss.arquillian.test.test.AbstractTestTestBase;
import org.jboss.shrinkwrap.descriptor.api.Descriptors;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
-import com.android.ddmlib.AndroidDebugBridge;
-import com.android.ddmlib.IDevice;
-
/**
* Tests Destroyer activation when no context was created (no @Drone) won't fail
*
* @author <a href="kpiwko@redhat.com">Karel Piwko</a>
*
*/
@RunWith(MockitoJUnitRunner.class)
+@Ignore
public class AndroidWebDriverSupportEmulatorTestCase extends AbstractTestTestBase {
@Mock
private ServiceLoader serviceLoader;
@Override
protected void addExtensions(List<Class<?>> extensions) {
- extensions.add(AndroidSdkConfigurator.class);
- extensions.add(AndroidVirtualDeviceCreator.class);
- extensions.add(AndroidDebugBridgeConnector.class);
- extensions.add(EmulatorStartup.class);
extensions.add(AndroidWebDriverSupport.class);
- extensions.add(EmulatorShutdown.class);
}
@org.junit.Before
public void setMocks() {
- ArquillianDescriptor desc = Descriptors.create(ArquillianDescriptor.class)
- .extension(AndroidSdkConfigurator.ANDROID_SDK_EXTENSION_NAME).property("force", "false")
- .property("verbose", "true").property("avdName", "webdriver-emulator").property("apiLevel", "9")
- .property("emulatorBootupTimeoutInSeconds", "120").property("androidServerApk", "android-server-2.6.0.apk")
- .property("sdSize", "256M");
+ ArquillianDescriptor desc = Descriptors.create(ArquillianDescriptor.class).extension("android")
+ .property("force", "false").property("verbose", "true").property("avdName", "webdriver-emulator")
+ .property("apiLevel", "9").property("emulatorBootupTimeoutInSeconds", "120").property("sdSize", "256M")
+ .extension(AndroidDroneConfigurator.ANDROID_DRONE_EXTENSION_NAME)
+ .property("androidServerApk", "android-server-2.6.0.apk");
bind(ApplicationScoped.class, ServiceLoader.class, serviceLoader);
bind(ApplicationScoped.class, ArquillianDescriptor.class, desc);
@@ -96,14 +91,10 @@ public void webDriverServerInitialized() throws Exception {
getManager().getContext(TestContext.class).activate(instance);
fire(new BeforeSuite());
- AndroidDebugBridge adb = getManager().getContext(SuiteContext.class).getObjectStore().get(AndroidDebugBridge.class);
+ AndroidBridge adb = getManager().getContext(SuiteContext.class).getObjectStore().get(AndroidBridge.class);
Assert.assertTrue("AndroidDebugBridge is connected", adb.isConnected());
- Assert.assertTrue("At least one device is connected to AndroidDebugBridge", adb.getDevices().length > 0);
-
- for (IDevice d : adb.getDevices()) {
- System.out.println("Device: " + (d.isEmulator() ? "E " : "R ") + d.getAvdName() + " / " + d.getSerialNumber());
- }
+ Assert.assertFalse("At least one device is connected to AndroidDebugBridge", adb.getDevices().isEmpty());
Thread.sleep(10000);
@@ -115,9 +106,9 @@ public void webDriverServerInitialized() throws Exception {
fire(new AfterSuite());
- assertEventFired(AndroidDebugBridgeInitialized.class, 1);
+ assertEventFired(AndroidBridgeInitialized.class, 1);
assertEventFired(AndroidDeviceReady.class, 1);
- assertEventFired(AndroidDeviceShutdownEvent.class, 1);
+ assertEventFired(AndroidDeviceShutdown.class, 1);
}
static class DummyClass {
View
43 ...idWebDriverSupportRealDeviceTestCase.java → ...idWebDriverSupportRealDeviceTestCase.java
@@ -14,13 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.jboss.arquillian.android.impl;
+package org.jboss.arquillian.android.drone.impl;
import java.lang.reflect.Method;
import java.util.List;
-import org.jboss.arquillian.android.event.AndroidDebugBridgeInitialized;
-import org.jboss.arquillian.android.event.AndroidDeviceReady;
+import org.jboss.arquillian.android.api.AndroidBridge;
+import org.jboss.arquillian.android.spi.event.AndroidBridgeInitialized;
+import org.jboss.arquillian.android.spi.event.AndroidDeviceReady;
+import org.jboss.arquillian.android.spi.event.AndroidDeviceShutdown;
import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
import org.jboss.arquillian.core.api.annotation.ApplicationScoped;
import org.jboss.arquillian.core.spi.ServiceLoader;